diff --git a/.gitignore b/.gitignore index 3a417656e2..27ad002970 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,11 @@ config-all-devices.* config-all-disas.* config-host.* config-target.* -trace.h -trace.c -trace-dtrace.h -trace-dtrace.dtrace +trace/generated-tracers.h +trace/generated-tracers.c +trace/generated-tracers-dtrace.h +trace/generated-tracers-dtrace.dtrace +libcacard/trace/generated-tracers.c *-timestamp *-softmmu *-darwin-user @@ -70,6 +71,7 @@ fsdev/virtfs-proxy-helper.pod *.tp *.vr *.d +!scripts/qemu-guest-agent/fsfreeze-hook.d *.o *.lo *.la @@ -81,12 +83,15 @@ fsdev/virtfs-proxy-helper.pod patches pc-bios/bios-pq/status pc-bios/vgabios-pq/status +pc-bios/optionrom/linuxboot.asm pc-bios/optionrom/linuxboot.bin pc-bios/optionrom/linuxboot.raw pc-bios/optionrom/linuxboot.img +pc-bios/optionrom/multiboot.asm pc-bios/optionrom/multiboot.bin pc-bios/optionrom/multiboot.raw pc-bios/optionrom/multiboot.img +pc-bios/optionrom/kvmvapic.asm pc-bios/optionrom/kvmvapic.bin pc-bios/optionrom/kvmvapic.raw pc-bios/optionrom/kvmvapic.img diff --git a/MAINTAINERS b/MAINTAINERS index c1b16c5fe4..0ca7e1da8c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -98,6 +98,7 @@ S: Maintained F: target-ppc/ S390 +M: Richard Henderson M: Alexander Graf S: Maintained F: target-s390x/ @@ -132,13 +133,18 @@ Guest CPU Cores (KVM): ---------------------- Overall -M: Avi Kivity +M: Gleb Natapov M: Marcelo Tosatti L: kvm@vger.kernel.org S: Supported F: kvm-* F: */kvm.* +ARM +M: Peter Maydell +S: Maintained +F: target-arm/kvm.c + PPC M: Alexander Graf S: Maintained @@ -150,7 +156,7 @@ S: Maintained F: target-s390x/kvm.c X86 -M: Avi Kivity +M: Gleb Natapov M: Marcelo Tosatti L: kvm@vger.kernel.org S: Supported @@ -379,7 +385,7 @@ New World M: Alexander Graf L: qemu-ppc@nongnu.org S: Maintained -F: hw/ppc_newworld.c +F: hw/ppc/mac_newworld.c F: hw/unin_pci.c F: hw/dec_pci.[hc] @@ -387,15 +393,16 @@ Old World M: Alexander Graf L: qemu-ppc@nongnu.org S: Maintained -F: hw/ppc_oldworld.c +F: hw/ppc/mac_oldworld.c F: hw/grackle_pci.c PReP M: Andreas Färber L: qemu-ppc@nongnu.org S: Odd Fixes -F: hw/ppc_prep.c +F: hw/ppc/prep.c F: hw/prep_pci.[hc] +F: hw/pc87312.[hc] sPAPR M: David Gibson @@ -447,6 +454,14 @@ M: Alexander Graf S: Maintained F: hw/s390-*.c +S390 Virtio-ccw +M: Cornelia Huck +M: Alexander Graf +S: Supported +F: hw/s390x/s390-virtio-ccw.c +F: hw/s390x/css.[hc] +T: git git://github.com/cohuck/qemu virtio-ccw-upstr + UniCore32 Machines ------------- PKUnity-3 SoC initramfs-with-busybox @@ -558,6 +573,12 @@ M: Stefan Hajnoczi S: Supported F: hw/virtio-blk* +virtio-ccw +M: Cornelia Huck +S: Supported +F: hw/s390x/virtio-ccw.[hc] +T: git git://github.com/cohuck/qemu virtio-ccw-upstr + virtio-serial M: Amit Shah S: Supported @@ -600,6 +621,7 @@ M: Andreas Färber S: Supported F: qom/cpu.c F: include/qemu/cpu.h +F: target-i386/cpu.c Device Tree M: Peter Crosthwaite diff --git a/Makefile b/Makefile index a0321dd7f0..69151787ff 100644 --- a/Makefile +++ b/Makefile @@ -31,12 +31,15 @@ ifneq ($(filter-out %clean,$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail)) endif endif -GENERATED_HEADERS = config-host.h trace.h qemu-options.def -ifeq ($(TRACE_BACKEND),dtrace) -GENERATED_HEADERS += trace-dtrace.h -endif +GENERATED_HEADERS = config-host.h qemu-options.def GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h -GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c trace.c +GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c + +GENERATED_HEADERS += trace/generated-tracers.h +ifeq ($(TRACE_BACKEND),dtrace) +GENERATED_HEADERS += trace/generated-tracers-dtrace.h +endif +GENERATED_SOURCES += trace/generated-tracers.c # Don't try to regenerate Makefile or configure # We don't generate any of them @@ -63,7 +66,7 @@ endif SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory) BUILD_DIR=$(BUILD_DIR) SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS)) -SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %/config-devices.mak.d, $(TARGET_DIRS)) +SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS)) ifeq ($(SUBDIR_DEVICES_MAK),) config-all-devices.mak: @@ -100,6 +103,17 @@ defconfig: -include config-all-devices.mak -include config-all-disas.mak +CONFIG_SOFTMMU := $(if $(filter %-softmmu,$(TARGET_DIRS)),y) +CONFIG_USER_ONLY := $(if $(filter %-user,$(TARGET_DIRS)),y) +CONFIG_ALL=y + +ifneq ($(wildcard config-host.mak),) +include $(SRC_PATH)/Makefile.objs +include $(SRC_PATH)/tests/Makefile +endif +ifeq ($(CONFIG_SMARTCARD_NSS),y) +include $(SRC_PATH)/libcacard/Makefile +endif all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all @@ -109,30 +123,23 @@ qemu-options.def: $(SRC_PATH)/qemu-options.hx $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@") SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) +SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES)) + +$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak subdir-%: $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,) -ifneq ($(wildcard config-host.mak),) -include $(SRC_PATH)/Makefile.objs -endif - -subdir-libcacard: $(oslib-obj-y) $(trace-obj-y) qemu-timer-common.o - subdir-pixman: pixman/Makefile $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pixman V="$(V)" all,) pixman/Makefile: $(SRC_PATH)/pixman/configure - (cd pixman; CFLAGS="$(CFLAGS) -fPIC" $(SRC_PATH)/pixman/configure $(AUTOCONF_HOST) --disable-gtk --disable-shared --enable-static) + (cd pixman; CFLAGS="$(CFLAGS) -fPIC $(extra_cflags) $(extra_ldflags)" $(SRC_PATH)/pixman/configure $(AUTOCONF_HOST) --disable-gtk --disable-shared --enable-static) $(SRC_PATH)/pixman/configure: (cd $(SRC_PATH)/pixman; autoreconf -v --install) -$(SUBDIR_RULES): libqemustub.a - -$(filter %-softmmu,$(SUBDIR_RULES)): $(universal-obj-y) $(trace-obj-y) $(common-obj-y) $(extra-obj-y) - -$(filter %-user,$(SUBDIR_RULES)): $(universal-obj-y) $(trace-obj-y) $(user-obj-y) +$(SUBDIR_RULES): libqemuutil.a libqemustub.a $(common-obj-y) ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) romsubdir-%: @@ -148,41 +155,25 @@ version.o: $(SRC_PATH)/version.rc config-host.h $(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@") version-obj-$(CONFIG_WIN32) += version.o +Makefile: $(version-obj-y) ###################################################################### -# Build library with stubs +# Build libraries libqemustub.a: $(stub-obj-y) - -###################################################################### -# Support building shared library libcacard - -.PHONY: libcacard.la install-libcacard -libcacard.la: $(oslib-obj-y) qemu-timer-common.o $(trace-obj-y) - $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C libcacard V="$(V)" TARGET_DIR="$*/" libcacard.la,) - -install-libcacard: libcacard.la - $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C libcacard V="$(V)" TARGET_DIR="$*/" install-libcacard,) +libqemuutil.a: $(util-obj-y) ###################################################################### qemu-img.o: qemu-img-cmds.h -tools-obj-y = $(oslib-obj-y) $(trace-obj-y) qemu-tool.o qemu-timer.o \ - main-loop.o iohandler.o error.o -tools-obj-$(CONFIG_POSIX) += compatfd.o - -qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y) libqemustub.a -qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y) libqemustub.a -qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y) libqemustub.a +qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a +qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a +qemu-io$(EXESUF): qemu-io.o cmd.o $(block-obj-y) libqemuutil.a libqemustub.a qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o -vscclient$(EXESUF): LIBS += $(libcacard_libs) -vscclient$(EXESUF): $(libcacard-y) $(oslib-obj-y) $(trace-obj-y) libcacard/vscclient.o libqemustub.a - $(call LINK, $^) - -fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o oslib-posix.o $(trace-obj-y) +fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o libqemuutil.a libqemustub.a fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx @@ -193,10 +184,6 @@ qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated gen-out-type = $(subst .,-,$(suffix $@)) -ifneq ($(wildcard config-host.mak),) -include $(SRC_PATH)/tests/Makefile -endif - qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\ @@ -222,26 +209,27 @@ $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.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) $(oslib-obj-y) $(trace-obj-y) $(qapi-obj-y) $(qobject-obj-y) $(version-obj-y) libqemustub.a +qemu-ga$(EXESUF): $(qga-obj-y) libqemuutil.a libqemustub.a $(call LINK, $^) clean: # avoid old build problems by removing potentially incorrect old files rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h rm -f qemu-options.def - find . -name '*.[od]' -exec rm -f {} + - rm -f *.a *.lo $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ + find . -name '*.[oda]' -type f -exec rm -f {} + + find . -name '*.l[oa]' -type f -exec rm -f {} + + rm -f $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ rm -Rf .libs rm -f qemu-img-cmds.h - rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp @# May not be present in GENERATED_HEADERS - rm -f trace-dtrace.h trace-dtrace.h-timestamp + rm -f trace/generated-tracers-dtrace.dtrace* + rm -f trace/generated-tracers-dtrace.h* rm -f $(foreach f,$(GENERATED_HEADERS),$(f) $(f)-timestamp) rm -f $(foreach f,$(GENERATED_SOURCES),$(f) $(f)-timestamp) rm -rf qapi-generated rm -rf qga/qapi-generated $(MAKE) -C tests/tcg clean - for d in $(ALL_SUBDIRS) libcacard; do \ + for d in $(ALL_SUBDIRS); do \ if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ rm -f $$d/qemu-options.def; \ done @@ -277,6 +265,7 @@ bepo ifdef INSTALL_BLOBS BLOBS=bios.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \ +acpi-dsdt.aml q35-acpi-dsdt.aml \ ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc \ pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ @@ -327,6 +316,9 @@ ifneq ($(BLOBS),) set -e; for x in $(BLOBS); do \ $(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \ done +endif +ifeq ($(CONFIG_GTK),y) + $(MAKE) -C po $@ endif $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps" set -e; for x in $(KEYMAPS); do \ @@ -342,7 +334,8 @@ test speed: all .PHONY: TAGS TAGS: - find "$(SRC_PATH)" -name '*.[hc]' -print0 | xargs -0 etags + rm -f $@ + find "$(SRC_PATH)" -name '*.[hc]' -exec etags --append {} + cscope: rm -f ./cscope.* diff --git a/Makefile.objs b/Makefile.objs index 4ef0a718d2..f99841ce54 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -1,58 +1,31 @@ ####################################################################### -# Stub library, linked in tools +# Common libraries for tools and emulators stub-obj-y = stubs/ - -####################################################################### -# Target-independent parts used in system and user emulation -universal-obj-y = -universal-obj-y += qemu-log.o - -####################################################################### -# QObject -qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o -qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o -qobject-obj-y += qerror.o error.o qemu-error.o - -universal-obj-y += $(qobject-obj-y) - -####################################################################### -# QOM -qom-obj-y = qom/ - -universal-obj-y += $(qom-obj-y) - -####################################################################### -# oslib-obj-y is code depending on the OS (win32 vs posix) -oslib-obj-y = osdep.o cutils.o qemu-timer-common.o -oslib-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o -oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o - -####################################################################### -# coroutines -coroutine-obj-y = qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o -coroutine-obj-y += qemu-coroutine-sleep.o -ifeq ($(CONFIG_UCONTEXT_COROUTINE),y) -coroutine-obj-$(CONFIG_POSIX) += coroutine-ucontext.o -else -ifeq ($(CONFIG_SIGALTSTACK_COROUTINE),y) -coroutine-obj-$(CONFIG_POSIX) += coroutine-sigaltstack.o -else -coroutine-obj-$(CONFIG_POSIX) += coroutine-gthread.o -endif -endif -coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o +util-obj-y = util/ qobject/ qapi/ trace/ ####################################################################### # block-obj-y is code used by both qemu system emulation and qemu-img -block-obj-y = iov.o cache-utils.o qemu-option.o module.o async.o -block-obj-y += nbd.o block.o blockjob.o aes.o qemu-config.o -block-obj-y += thread-pool.o qemu-progress.o qemu-sockets.o uri.o notify.o -block-obj-y += $(coroutine-obj-y) $(qobject-obj-y) $(version-obj-y) -block-obj-$(CONFIG_POSIX) += event_notifier-posix.o aio-posix.o -block-obj-$(CONFIG_WIN32) += event_notifier-win32.o aio-win32.o +block-obj-y = async.o thread-pool.o +block-obj-y += nbd.o block.o blockjob.o +block-obj-y += main-loop.o iohandler.o qemu-timer.o +block-obj-$(CONFIG_POSIX) += aio-posix.o +block-obj-$(CONFIG_WIN32) += aio-win32.o block-obj-y += block/ -block-obj-y += $(qapi-obj-y) qapi-types.o qapi-visit.o +block-obj-y += qapi-types.o qapi-visit.o + +block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o +block-obj-y += qemu-coroutine-sleep.o +ifeq ($(CONFIG_UCONTEXT_COROUTINE),y) +block-obj-$(CONFIG_POSIX) += coroutine-ucontext.o +else +ifeq ($(CONFIG_SIGALTSTACK_COROUTINE),y) +block-obj-$(CONFIG_POSIX) += coroutine-sigaltstack.o +else +block-obj-$(CONFIG_POSIX) += coroutine-gthread.o +endif +endif +block-obj-$(CONFIG_WIN32) += coroutine-win32.o ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy) # Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add. @@ -60,142 +33,6 @@ ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy) CONFIG_REALLY_VIRTFS=y endif -###################################################################### -# Target independent part of system emulation. The long term path is to -# suppress *all* target specific code in case of system emulation, i.e. a -# single QEMU executable should support all CPUs and machines. - -common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/ -common-obj-y += net/ -common-obj-y += qom/ -common-obj-y += readline.o -common-obj-y += $(oslib-obj-y) -common-obj-$(CONFIG_WIN32) += os-win32.o -common-obj-$(CONFIG_POSIX) += os-posix.o - -common-obj-$(CONFIG_LINUX) += fsdev/ -extra-obj-$(CONFIG_LINUX) += fsdev/ - -common-obj-y += tcg-runtime.o host-utils.o main-loop.o -common-obj-y += migration.o migration-tcp.o -common-obj-y += migration.o migration-tcp.o -common-obj-y += qemu-char.o #aio.o -common-obj-y += block-migration.o iohandler.o -common-obj-y += bitmap.o bitops.o -common-obj-y += page_cache.o - -common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o -common-obj-$(CONFIG_WIN32) += version.o - -common-obj-$(CONFIG_SPICE) += spice-qemu-char.o - -common-obj-y += audio/ -common-obj-y += hw/ -extra-obj-y += hw/ - -common-obj-y += ui/ -common-obj-y += bt-host.o bt-vhci.o - -common-obj-y += dma-helpers.o -common-obj-y += acl.o -common-obj-$(CONFIG_POSIX) += compatfd.o -common-obj-y += qemu-timer.o qemu-timer-common.o -common-obj-y += qtest.o -common-obj-y += vl.o - -common-obj-$(CONFIG_SLIRP) += slirp/ - -common-obj-y += backends/ - -###################################################################### -# libseccomp -ifeq ($(CONFIG_SECCOMP),y) -common-obj-y += qemu-seccomp.o -endif - -###################################################################### -# libuser - -user-obj-y = -user-obj-y += envlist.o path.o -user-obj-y += tcg-runtime.o host-utils.o -user-obj-y += cache-utils.o -user-obj-y += module.o -user-obj-y += qemu-user.o -user-obj-y += qom/ - -###################################################################### -# disassemblers -# NOTE: the disassembler code is only needed for debugging - -universal-obj-y += disas/ - -###################################################################### -# trace - -ifeq ($(TRACE_BACKEND),dtrace) -TRACE_H_EXTRA_DEPS=trace-dtrace.h -endif -trace.h: trace.h-timestamp $(TRACE_H_EXTRA_DEPS) -trace.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(TRACETOOL) \ - --format=h \ - --backend=$(TRACE_BACKEND) \ - < $< > $@," GEN trace.h") - @cmp -s $@ trace.h || cp $@ trace.h - -trace.c: trace.c-timestamp -trace.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(TRACETOOL) \ - --format=c \ - --backend=$(TRACE_BACKEND) \ - < $< > $@," GEN trace.c") - @cmp -s $@ trace.c || cp $@ trace.c - -trace.o: trace.c $(GENERATED_HEADERS) - -trace-dtrace.h: trace-dtrace.dtrace - $(call quiet-command,dtrace -o $@ -h -s $<, " GEN trace-dtrace.h") - -# 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 -trace-dtrace.dtrace: trace-dtrace.dtrace-timestamp -trace-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(TRACETOOL) \ - --format=d \ - --backend=$(TRACE_BACKEND) \ - < $< > $@," GEN trace-dtrace.dtrace") - @cmp -s $@ trace-dtrace.dtrace || cp $@ trace-dtrace.dtrace - -trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS) - $(call quiet-command,dtrace -o $@ -G -s $<, " GEN trace-dtrace.o") - -ifeq ($(LIBTOOL),) -trace-dtrace.lo: trace-dtrace.dtrace - @echo "missing libtool. please install and rerun configure."; exit 1 -else -trace-dtrace.lo: trace-dtrace.dtrace - $(call quiet-command,$(LIBTOOL) --mode=compile --tag=CC dtrace -o $@ -G -s $<, " lt GEN trace-dtrace.o") -endif - -trace/simple.o: trace/simple.c $(GENERATED_HEADERS) - -trace-obj-$(CONFIG_TRACE_DTRACE) += trace-dtrace.o -ifneq ($(TRACE_BACKEND),dtrace) -trace-obj-y = trace.o -endif - -trace-obj-$(CONFIG_TRACE_DEFAULT) += trace/default.o -trace-obj-$(CONFIG_TRACE_SIMPLE) += trace/simple.o -trace-obj-$(CONFIG_TRACE_SIMPLE) += qemu-timer-common.o -trace-obj-$(CONFIG_TRACE_STDERR) += trace/stderr.o -trace-obj-y += trace/control.o - -$(trace-obj-y): $(GENERATED_HEADERS) - -universal-obj-y += $(trace-obj-y) - ###################################################################### # smartcard @@ -205,24 +42,69 @@ libcacard-y += libcacard/vcard_emul_nss.o libcacard-y += libcacard/vcard_emul_type.o libcacard-y += libcacard/card_7816.o +###################################################################### +# Target independent part of system emulation. The long term path is to +# suppress *all* target specific code in case of system emulation, i.e. a +# single QEMU executable should support all CPUs and machines. + +ifeq ($(CONFIG_SOFTMMU),y) +common-obj-y = $(block-obj-y) blockdev.o blockdev-nbd.o block/ +common-obj-y += net/ +common-obj-y += readline.o +common-obj-y += qdev-monitor.o device-hotplug.o +common-obj-$(CONFIG_WIN32) += os-win32.o +common-obj-$(CONFIG_POSIX) += os-posix.o + +common-obj-$(CONFIG_LINUX) += fsdev/ + +common-obj-y += migration.o migration-tcp.o +common-obj-y += qemu-char.o #aio.o +common-obj-y += block-migration.o +common-obj-y += page_cache.o xbzrle.o + +common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o + +common-obj-$(CONFIG_SPICE) += spice-qemu-char.o + +common-obj-y += audio/ +common-obj-y += hw/ + +common-obj-y += ui/ +common-obj-y += bt-host.o bt-vhci.o + +common-obj-y += dma-helpers.o +common-obj-y += vl.o +common-obj-y += tpm/ + +common-obj-$(CONFIG_SLIRP) += slirp/ + +common-obj-y += backends/ + +common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o + common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y) ###################################################################### # qapi -qapi-obj-y = qapi/ -qapi-obj-y += qapi-types.o qapi-visit.o - common-obj-y += qmp-marshal.o qapi-visit.o qapi-types.o common-obj-y += qmp.o hmp.o +endif -universal-obj-y += $(qapi-obj-y) +####################################################################### +# Target-independent parts used in system and user emulation +common-obj-y += qemu-log.o +common-obj-y += tcg-runtime.o +common-obj-y += hw/ +common-obj-y += qom/ +common-obj-y += disas/ ###################################################################### # guest agent -qga-obj-y = qga/ module.o qemu-tool.o -qga-obj-$(CONFIG_POSIX) += qemu-sockets.o qemu-option.o +# FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed +# by libqemuutil.a. These should be moved to a separate .json schema. +qga-obj-y = qga/ qapi-types.o qapi-visit.o vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) @@ -232,12 +114,8 @@ QEMU_CFLAGS+=$(GLIB_CFLAGS) nested-vars += \ stub-obj-y \ + util-obj-y \ qga-obj-y \ - qom-obj-y \ - qapi-obj-y \ block-obj-y \ - user-obj-y \ - common-obj-y \ - universal-obj-y \ - extra-obj-y + common-obj-y dummy := $(call unnest-vars) diff --git a/Makefile.target b/Makefile.target index be8b8b86a5..2bd6d1471c 100644 --- a/Makefile.target +++ b/Makefile.target @@ -54,7 +54,7 @@ $(QEMU_PROG).stp: $(SRC_PATH)/trace-events --binary=$(bindir)/$(QEMU_PROG) \ --target-arch=$(TARGET_ARCH) \ --target-type=$(TARGET_TYPE) \ - < $< > $@," GEN $(QEMU_PROG).stp") + < $< > $@," GEN $(TARGET_DIR)$(QEMU_PROG).stp") else stap: endif @@ -69,13 +69,12 @@ all: $(PROGS) stap obj-y = exec.o translate-all.o cpu-exec.o obj-y += tcg/tcg.o tcg/optimize.o obj-$(CONFIG_TCG_INTERPRETER) += tci.o +obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o obj-y += fpu/softfloat.o obj-y += target-$(TARGET_BASE_ARCH)/ obj-y += disas.o obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o -tci-dis.o: QEMU_CFLAGS += -I$(SRC_PATH)/tcg -I$(SRC_PATH)/tcg/tci - ######################################################### # Linux user emulator target @@ -84,7 +83,7 @@ ifdef CONFIG_LINUX_USER QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) -I$(SRC_PATH)/linux-user obj-y += linux-user/ -obj-y += gdbstub.o thunk.o user-exec.o $(oslib-obj-y) +obj-y += gdbstub.o thunk.o user-exec.o endif #CONFIG_LINUX_USER @@ -96,7 +95,7 @@ ifdef CONFIG_BSD_USER QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH) obj-y += bsd-user/ -obj-y += gdbstub.o user-exec.o $(oslib-obj-y) +obj-y += gdbstub.o user-exec.o endif #CONFIG_BSD_USER @@ -110,7 +109,9 @@ CONFIG_NO_GET_MEMORY_MAPPING = $(if $(subst n,,$(CONFIG_HAVE_GET_MEMORY_MAPPING) CONFIG_NO_CORE_DUMP = $(if $(subst n,,$(CONFIG_HAVE_CORE_DUMP)),n,y) obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o +obj-y += qtest.o obj-y += hw/ +obj-$(CONFIG_FDT) += device_tree.o obj-$(CONFIG_KVM) += kvm-all.o obj-$(CONFIG_NO_KVM) += kvm-stub.o obj-y += memory.o savevm.o cputlb.o @@ -146,22 +147,16 @@ nested-vars += obj-y include $(SRC_PATH)/Makefile.objs all-obj-y = $(obj-y) -all-obj-y += $(addprefix ../, $(universal-obj-y)) - -ifdef CONFIG_SOFTMMU all-obj-y += $(addprefix ../, $(common-obj-y)) -else -all-obj-y += $(addprefix ../, $(user-obj-y)) -endif #CONFIG_LINUX_USER ifdef QEMU_PROGW # The linker builds a windows executable. Make also a console executable. -$(QEMU_PROGW): $(all-obj-y) ../libqemustub.a +$(QEMU_PROGW): $(all-obj-y) ../libqemuutil.a ../libqemustub.a $(call LINK,$^) $(QEMU_PROG): $(QEMU_PROGW) $(call quiet-command,$(OBJCOPY) --subsystem console $(QEMU_PROGW) $(QEMU_PROG)," GEN $(TARGET_DIR)$(QEMU_PROG)") else -$(QEMU_PROG): $(all-obj-y) ../libqemustub.a +$(QEMU_PROG): $(all-obj-y) ../libqemuutil.a ../libqemustub.a $(call LINK,$^) endif diff --git a/TODO b/TODO deleted file mode 100644 index 1d4c638f27..0000000000 --- a/TODO +++ /dev/null @@ -1,37 +0,0 @@ -General: -------- -- cycle counter for all archs -- cpu_interrupt() win32/SMP fix -- merge PIC spurious interrupt patch -- warning for OS/2: must not use 128 MB memory (merge bochs cmos patch ?) -- config file (at least for windows/Mac OS X) -- update doc: PCI infos. -- basic VGA optimizations -- better code fetch -- do not resize vga if invalid size. -- TLB code protection support for PPC -- disable SMC handling for ARM/SPARC/PPC (not finished) -- see undefined flags for BTx insn -- keyboard output buffer filling timing emulation -- tests for each target CPU -- fix all remaining thread lock issues (must put TBs in a specific invalid - state, find a solution for tb_flush()). - -ppc specific: ------------- -- TLB invalidate not needed if msr_pr changes -- enable shift optimizations ? - -linux-user specific: -------------------- -- remove threading support as it cannot work at this point -- improve IPC syscalls -- more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit - issues, fix 16 bit uid issues) -- use kernel traps for unaligned accesses on ARM ? - - -lower priority: --------------- -- int15 ah=86: use better timing -- use -msoft-float on ARM diff --git a/VERSION b/VERSION index 52356d37e0..8fe423b4ca 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.50 +1.4.50 diff --git a/aio-posix.c b/aio-posix.c index 88d09e1cfb..b68eccd40c 100644 --- a/aio-posix.c +++ b/aio-posix.c @@ -25,6 +25,7 @@ struct AioHandler IOHandler *io_write; AioFlushHandler *io_flush; int deleted; + int pollfds_idx; void *opaque; QLIST_ENTRY(AioHandler) node; }; @@ -85,9 +86,10 @@ void aio_set_fd_handler(AioContext *ctx, node->io_write = io_write; node->io_flush = io_flush; node->opaque = opaque; + node->pollfds_idx = -1; - node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP : 0); - node->pfd.events |= (io_write ? G_IO_OUT : 0); + node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP | G_IO_ERR : 0); + node->pfd.events |= (io_write ? G_IO_OUT | G_IO_ERR : 0); } aio_notify(ctx); @@ -110,13 +112,6 @@ bool aio_pending(AioContext *ctx) QLIST_FOREACH(node, &ctx->aio_handlers, node) { int revents; - /* - * FIXME: right now we cannot get G_IO_HUP and G_IO_ERR because - * main-loop.c is still select based (due to the slirp legacy). - * If main-loop.c ever switches to poll, G_IO_ERR should be - * tested too. Dispatching G_IO_ERR to both handlers should be - * okay, since handlers need to be ready for spurious wakeups. - */ revents = node->pfd.revents & node->pfd.events; if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) { return true; @@ -129,30 +124,12 @@ bool aio_pending(AioContext *ctx) return false; } -bool aio_poll(AioContext *ctx, bool blocking) +static bool aio_dispatch(AioContext *ctx) { - static struct timeval tv0; AioHandler *node; - fd_set rdfds, wrfds; - int max_fd = -1; - int ret; - bool busy, progress; - - progress = false; + bool progress = false; /* - * If there are callbacks left that have been queued, we need to call then. - * Do not call select in this case, because it is possible that the caller - * does not need a complete flush (as is the case for qemu_aio_wait loops). - */ - if (aio_bh_poll(ctx)) { - blocking = false; - progress = true; - } - - /* - * Then dispatch any pending callbacks from the GSource. - * * We have to walk very carefully in case qemu_aio_set_fd_handler is * called while we're walking. */ @@ -166,12 +143,15 @@ bool aio_poll(AioContext *ctx, bool blocking) revents = node->pfd.revents & node->pfd.events; node->pfd.revents = 0; - /* See comment in aio_pending. */ - if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) { + if (!node->deleted && + (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) && + node->io_read) { node->io_read(node->opaque); progress = true; } - if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write) { + if (!node->deleted && + (revents & (G_IO_OUT | G_IO_ERR)) && + node->io_write) { node->io_write(node->opaque); progress = true; } @@ -186,6 +166,30 @@ bool aio_poll(AioContext *ctx, bool blocking) g_free(tmp); } } + return progress; +} + +bool aio_poll(AioContext *ctx, bool blocking) +{ + AioHandler *node; + int ret; + bool busy, progress; + + progress = false; + + /* + * If there are callbacks left that have been queued, we need to call them. + * Do not call select in this case, because it is possible that the caller + * does not need a complete flush (as is the case for qemu_aio_wait loops). + */ + if (aio_bh_poll(ctx)) { + blocking = false; + progress = true; + } + + if (aio_dispatch(ctx)) { + progress = true; + } if (progress && !blocking) { return true; @@ -193,12 +197,13 @@ bool aio_poll(AioContext *ctx, bool blocking) ctx->walking_handlers++; - FD_ZERO(&rdfds); - FD_ZERO(&wrfds); + g_array_set_size(ctx->pollfds, 0); - /* fill fd sets */ + /* fill pollfds */ busy = false; QLIST_FOREACH(node, &ctx->aio_handlers, node) { + node->pollfds_idx = -1; + /* If there aren't pending AIO operations, don't invoke callbacks. * Otherwise, if there are no AIO requests, qemu_aio_wait() would * wait indefinitely. @@ -209,13 +214,13 @@ bool aio_poll(AioContext *ctx, bool blocking) } busy = true; } - if (!node->deleted && node->io_read) { - FD_SET(node->pfd.fd, &rdfds); - max_fd = MAX(max_fd, node->pfd.fd + 1); - } - if (!node->deleted && node->io_write) { - FD_SET(node->pfd.fd, &wrfds); - max_fd = MAX(max_fd, node->pfd.fd + 1); + if (!node->deleted && node->pfd.events) { + GPollFD pfd = { + .fd = node->pfd.fd, + .events = node->pfd.events, + }; + node->pollfds_idx = ctx->pollfds->len; + g_array_append_val(ctx->pollfds, pfd); } } @@ -227,42 +232,24 @@ bool aio_poll(AioContext *ctx, bool blocking) } /* wait until next event */ - ret = select(max_fd, &rdfds, &wrfds, NULL, blocking ? NULL : &tv0); + ret = g_poll((GPollFD *)ctx->pollfds->data, + ctx->pollfds->len, + blocking ? -1 : 0); /* if we have any readable fds, dispatch event */ if (ret > 0) { - /* we have to walk very carefully in case - * qemu_aio_set_fd_handler is called while we're walking */ - node = QLIST_FIRST(&ctx->aio_handlers); - while (node) { - AioHandler *tmp; - - ctx->walking_handlers++; - - if (!node->deleted && - FD_ISSET(node->pfd.fd, &rdfds) && - node->io_read) { - node->io_read(node->opaque); - progress = true; - } - if (!node->deleted && - FD_ISSET(node->pfd.fd, &wrfds) && - node->io_write) { - node->io_write(node->opaque); - progress = true; - } - - tmp = node; - node = QLIST_NEXT(node, node); - - ctx->walking_handlers--; - - if (!ctx->walking_handlers && tmp->deleted) { - QLIST_REMOVE(tmp, node); - g_free(tmp); + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + if (node->pollfds_idx != -1) { + GPollFD *pfd = &g_array_index(ctx->pollfds, GPollFD, + node->pollfds_idx); + node->pfd.revents = pfd->revents; } } + if (aio_dispatch(ctx)) { + progress = true; + } } - return progress; + assert(progress || busy); + return true; } diff --git a/aio-win32.c b/aio-win32.c index f5ea027f8c..38723bf1d3 100644 --- a/aio-win32.c +++ b/aio-win32.c @@ -214,5 +214,6 @@ bool aio_poll(AioContext *ctx, bool blocking) events[ret - WAIT_OBJECT_0] = events[--count]; } - return progress; + assert(progress || busy); + return true; } diff --git a/arch_init.c b/arch_init.c index 86f85443d7..98e2bc6f55 100644 --- a/arch_init.c +++ b/arch_init.c @@ -293,8 +293,7 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data, if (!cache_is_cached(XBZRLE.cache, current_addr)) { if (!last_stage) { - cache_insert(XBZRLE.cache, current_addr, - g_memdup(current_data, TARGET_PAGE_SIZE)); + cache_insert(XBZRLE.cache, current_addr, current_data); } acct_info.xbzrle_cache_miss++; return -1; @@ -379,6 +378,8 @@ static inline bool migration_bitmap_set_dirty(MemoryRegion *mr, return ret; } +/* Needs iothread lock! */ + static void migration_bitmap_sync(void) { RAMBlock *block; @@ -414,6 +415,7 @@ static void migration_bitmap_sync(void) if (end_time > start_time + 1000) { s->dirty_pages_rate = num_dirty_pages_period * 1000 / (end_time - start_time); + s->dirty_bytes_rate = s->dirty_pages_rate * TARGET_PAGE_SIZE; start_time = end_time; num_dirty_pages_period = 0; } @@ -567,10 +569,6 @@ static int ram_save_setup(QEMUFile *f, void *opaque) bitmap_set(migration_bitmap, 0, ram_pages); migration_dirty_pages = ram_pages; - qemu_mutex_lock_ramlist(); - bytes_transferred = 0; - reset_ram_globals(); - if (migrate_use_xbzrle()) { XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() / TARGET_PAGE_SIZE, @@ -584,8 +582,14 @@ static int ram_save_setup(QEMUFile *f, void *opaque) acct_clear(); } + qemu_mutex_lock_iothread(); + qemu_mutex_lock_ramlist(); + bytes_transferred = 0; + reset_ram_globals(); + memory_global_dirty_log_start(); migration_bitmap_sync(); + qemu_mutex_unlock_iothread(); qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); @@ -642,12 +646,13 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) i++; } + qemu_mutex_unlock_ramlist(); + if (ret < 0) { bytes_transferred += total_sent; return ret; } - qemu_mutex_unlock_ramlist(); qemu_put_be64(f, RAM_SAVE_FLAG_EOS); total_sent += 8; bytes_transferred += total_sent; @@ -657,9 +662,8 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) static int ram_save_complete(QEMUFile *f, void *opaque) { - migration_bitmap_sync(); - qemu_mutex_lock_ramlist(); + migration_bitmap_sync(); /* try transferring iterative blocks of memory */ @@ -689,7 +693,9 @@ static uint64_t ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size) remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE; if (remaining_size < max_size) { + qemu_mutex_lock_iothread(); migration_bitmap_sync(); + qemu_mutex_unlock_iothread(); remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE; } return remaining_size; @@ -851,9 +857,6 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) qemu_get_buffer(f, host, TARGET_PAGE_SIZE); } else if (flags & RAM_SAVE_FLAG_XBZRLE) { - if (!migrate_use_xbzrle()) { - return -EINVAL; - } void *host = host_from_stream_offset(f, addr, flags); if (!host) { return -EINVAL; diff --git a/async.c b/async.c index 72d268ae35..90fe906539 100644 --- a/async.c +++ b/async.c @@ -24,6 +24,7 @@ #include "qemu-common.h" #include "block/aio.h" +#include "block/thread-pool.h" #include "qemu/main-loop.h" /***********************************************************/ @@ -172,8 +173,10 @@ aio_ctx_finalize(GSource *source) { AioContext *ctx = (AioContext *) source; + thread_pool_free(ctx->thread_pool); aio_set_event_notifier(ctx, &ctx->notifier, NULL, NULL); event_notifier_cleanup(&ctx->notifier); + g_array_free(ctx->pollfds, TRUE); } static GSourceFuncs aio_source_funcs = { @@ -189,6 +192,14 @@ GSource *aio_get_g_source(AioContext *ctx) return &ctx->source; } +ThreadPool *aio_get_thread_pool(AioContext *ctx) +{ + if (!ctx->thread_pool) { + ctx->thread_pool = thread_pool_new(ctx); + } + return ctx->thread_pool; +} + void aio_notify(AioContext *ctx) { event_notifier_set(&ctx->notifier); @@ -198,6 +209,8 @@ AioContext *aio_context_new(void) { AioContext *ctx; ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext)); + ctx->pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); + ctx->thread_pool = NULL; event_notifier_init(&ctx->notifier, false); aio_set_event_notifier(ctx, &ctx->notifier, (EventNotifierHandler *) diff --git a/audio/audio.c b/audio/audio.c index 1510b598a6..02bb8861f8 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -828,8 +828,9 @@ static int audio_attach_capture (HWVoiceOut *hw) QLIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries); QLIST_INSERT_HEAD (&hw->cap_head, sc, entries); #ifdef DEBUG_CAPTURE - asprintf (&sw->name, "for %p %d,%d,%d", - hw, sw->info.freq, sw->info.bits, sw->info.nchannels); + sw->name = g_strdup_printf ("for %p %d,%d,%d", + hw, sw->info.freq, sw->info.bits, + sw->info.nchannels); dolog ("Added %s active = %d\n", sw->name, sw->active); #endif if (sw->active) { diff --git a/backends/Makefile.objs b/backends/Makefile.objs index 883676106b..464bc3e220 100644 --- a/backends/Makefile.objs +++ b/backends/Makefile.objs @@ -1,2 +1,6 @@ common-obj-y += rng.o rng-egd.o common-obj-$(CONFIG_POSIX) += rng-random.o + +common-obj-y += msmouse.o +common-obj-$(CONFIG_BRLAPI) += baum.o +$(obj)/baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) diff --git a/hw/baum.c b/backends/baum.c similarity index 99% rename from hw/baum.c rename to backends/baum.c index 09dcb9cc74..d7d658c224 100644 --- a/hw/baum.c +++ b/backends/baum.c @@ -24,8 +24,7 @@ #include "qemu-common.h" #include "char/char.h" #include "qemu/timer.h" -#include "usb.h" -#include "baum.h" +#include "hw/usb.h" #include #include #include @@ -562,7 +561,7 @@ static void baum_close(struct CharDriverState *chr) g_free(baum); } -CharDriverState *chr_baum_init(QemuOpts *opts) +CharDriverState *chr_baum_init(void) { BaumDriverState *baum; CharDriverState *chr; @@ -625,3 +624,10 @@ fail_handle: g_free(baum); return NULL; } + +static void register_types(void) +{ + register_char_driver_qapi("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL); +} + +type_init(register_types); diff --git a/hw/msmouse.c b/backends/msmouse.c similarity index 93% rename from hw/msmouse.c rename to backends/msmouse.c index ef47aed4e9..61052fe783 100644 --- a/hw/msmouse.c +++ b/backends/msmouse.c @@ -25,7 +25,6 @@ #include "qemu-common.h" #include "char/char.h" #include "ui/console.h" -#include "msmouse.h" #define MSMOUSE_LO6(n) ((n) & 0x3f) #define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6) @@ -64,7 +63,7 @@ static void msmouse_chr_close (struct CharDriverState *chr) g_free (chr); } -CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts) +CharDriverState *qemu_chr_open_msmouse(void) { CharDriverState *chr; @@ -76,3 +75,10 @@ CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts) return chr; } + +static void register_types(void) +{ + register_char_driver_qapi("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL); +} + +type_init(register_types); diff --git a/backends/rng-egd.c b/backends/rng-egd.c index fd41b53188..5e012e9e30 100644 --- a/backends/rng-egd.c +++ b/backends/rng-egd.c @@ -207,7 +207,7 @@ static void rng_egd_class_init(ObjectClass *klass, void *data) rbc->opened = rng_egd_opened; } -static TypeInfo rng_egd_info = { +static const TypeInfo rng_egd_info = { .name = TYPE_RNG_EGD, .parent = TYPE_RNG_BACKEND, .instance_size = sizeof(RngEgd), diff --git a/backends/rng-random.c b/backends/rng-random.c index d479ce8c56..acd20afad2 100644 --- a/backends/rng-random.c +++ b/backends/rng-random.c @@ -74,7 +74,7 @@ static void rng_random_opened(RngBackend *b, Error **errp) error_set(errp, QERR_INVALID_PARAMETER_VALUE, "filename", "a valid filename"); } else { - s->fd = open(s->filename, O_RDONLY | O_NONBLOCK); + s->fd = qemu_open(s->filename, O_RDONLY | O_NONBLOCK); if (s->fd == -1) { error_set(errp, QERR_OPEN_FILE_FAILED, s->filename); @@ -130,7 +130,7 @@ static void rng_random_finalize(Object *obj) qemu_set_fd_handler(s->fd, NULL, NULL, NULL); if (s->fd != -1) { - close(s->fd); + qemu_close(s->fd); } g_free(s->filename); @@ -144,7 +144,7 @@ static void rng_random_class_init(ObjectClass *klass, void *data) rbc->opened = rng_random_opened; } -static TypeInfo rng_random_info = { +static const TypeInfo rng_random_info = { .name = TYPE_RNG_RANDOM, .parent = TYPE_RNG_BACKEND, .instance_size = sizeof(RndRandom), diff --git a/backends/rng.c b/backends/rng.c index 48a5840cd5..3d3389802e 100644 --- a/backends/rng.c +++ b/backends/rng.c @@ -76,7 +76,7 @@ static void rng_backend_init(Object *obj) NULL); } -static TypeInfo rng_backend_info = { +static const TypeInfo rng_backend_info = { .name = TYPE_RNG_BACKEND, .parent = TYPE_OBJECT, .instance_size = sizeof(RngBackend), diff --git a/block-migration.c b/block-migration.c index 6acf3e1682..2fd7699794 100644 --- a/block-migration.c +++ b/block-migration.c @@ -23,7 +23,8 @@ #include "sysemu/blockdev.h" #include -#define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS) +#define BLOCK_SIZE (1 << 20) +#define BDRV_SECTORS_PER_DIRTY_CHUNK (BLOCK_SIZE >> BDRV_SECTOR_BITS) #define BLK_MIG_FLAG_DEVICE_BLOCK 0x01 #define BLK_MIG_FLAG_EOS 0x02 @@ -42,19 +43,24 @@ #endif typedef struct BlkMigDevState { + /* Written during setup phase. Can be read without a lock. */ BlockDriverState *bs; - int bulk_completed; int shared_base; + int64_t total_sectors; + QSIMPLEQ_ENTRY(BlkMigDevState) entry; + + /* Only used by migration thread. Does not need a lock. */ + int bulk_completed; int64_t cur_sector; int64_t cur_dirty; - int64_t completed_sectors; - int64_t total_sectors; - int64_t dirty; - QSIMPLEQ_ENTRY(BlkMigDevState) entry; + + /* Protected by block migration lock. */ unsigned long *aio_bitmap; + int64_t completed_sectors; } BlkMigDevState; typedef struct BlkMigBlock { + /* Only used by migration thread. */ uint8_t *buf; BlkMigDevState *bmds; int64_t sector; @@ -62,26 +68,49 @@ typedef struct BlkMigBlock { struct iovec iov; QEMUIOVector qiov; BlockDriverAIOCB *aiocb; + + /* Protected by block migration lock. */ int ret; QSIMPLEQ_ENTRY(BlkMigBlock) entry; } BlkMigBlock; typedef struct BlkMigState { + /* Written during setup phase. Can be read without a lock. */ int blk_enable; int shared_base; QSIMPLEQ_HEAD(bmds_list, BlkMigDevState) bmds_list; + int64_t total_sector_sum; + + /* Protected by lock. */ QSIMPLEQ_HEAD(blk_list, BlkMigBlock) blk_list; int submitted; int read_done; + + /* Only used by migration thread. Does not need a lock. */ int transferred; - int64_t total_sector_sum; int prev_progress; int bulk_completed; - long double prev_time_offset; + + /* Lock must be taken _inside_ the iothread lock. */ + QemuMutex lock; } BlkMigState; static BlkMigState block_mig_state; +static void blk_mig_lock(void) +{ + qemu_mutex_lock(&block_mig_state.lock); +} + +static void blk_mig_unlock(void) +{ + qemu_mutex_unlock(&block_mig_state.lock); +} + +/* Must run outside of the iothread lock during the bulk phase, + * or the VM will stall. + */ + static void blk_send(QEMUFile *f, BlkMigBlock * blk) { int len; @@ -108,9 +137,11 @@ uint64_t blk_mig_bytes_transferred(void) BlkMigDevState *bmds; uint64_t sum = 0; + blk_mig_lock(); QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { sum += bmds->completed_sectors; } + blk_mig_unlock(); return sum << BDRV_SECTOR_BITS; } @@ -130,6 +161,9 @@ uint64_t blk_mig_bytes_total(void) return sum << BDRV_SECTOR_BITS; } + +/* Called with migration lock held. */ + static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector) { int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK; @@ -142,6 +176,8 @@ static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector) } } +/* Called with migration lock held. */ + static void bmds_set_aio_inflight(BlkMigDevState *bmds, int64_t sector_num, int nb_sectors, int set) { @@ -176,23 +212,26 @@ static void alloc_aio_bitmap(BlkMigDevState *bmds) bmds->aio_bitmap = g_malloc0(bitmap_size); } +/* Never hold migration lock when yielding to the main loop! */ + static void blk_mig_read_cb(void *opaque, int ret) { - long double curr_time = qemu_get_clock_ns(rt_clock); BlkMigBlock *blk = opaque; + blk_mig_lock(); blk->ret = ret; - block_mig_state.prev_time_offset = curr_time; - QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry); bmds_set_aio_inflight(blk->bmds, blk->sector, blk->nr_sectors, 0); block_mig_state.submitted--; block_mig_state.read_done++; assert(block_mig_state.submitted >= 0); + blk_mig_unlock(); } +/* Called with no lock taken. */ + static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) { int64_t total_sectors = bmds->total_sectors; @@ -202,11 +241,13 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) int nr_sectors; if (bmds->shared_base) { + qemu_mutex_lock_iothread(); while (cur_sector < total_sectors && !bdrv_is_allocated(bs, cur_sector, MAX_IS_ALLOCATED_SEARCH, &nr_sectors)) { cur_sector += nr_sectors; } + qemu_mutex_unlock_iothread(); } if (cur_sector >= total_sectors) { @@ -235,26 +276,29 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); - if (block_mig_state.submitted == 0) { - block_mig_state.prev_time_offset = qemu_get_clock_ns(rt_clock); - } + blk_mig_lock(); + block_mig_state.submitted++; + blk_mig_unlock(); + qemu_mutex_lock_iothread(); blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov, nr_sectors, blk_mig_read_cb, blk); - block_mig_state.submitted++; bdrv_reset_dirty(bs, cur_sector, nr_sectors); - bmds->cur_sector = cur_sector + nr_sectors; + qemu_mutex_unlock_iothread(); + bmds->cur_sector = cur_sector + nr_sectors; return (bmds->cur_sector >= total_sectors); } +/* Called with iothread lock taken. */ + static void set_dirty_tracking(int enable) { BlkMigDevState *bmds; QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - bdrv_set_dirty_tracking(bmds->bs, enable); + bdrv_set_dirty_tracking(bmds->bs, enable ? BLOCK_SIZE : 0); } } @@ -304,6 +348,8 @@ static void init_blk_migration(QEMUFile *f) bdrv_iterate(init_blk_migration_it, NULL); } +/* Called with no lock taken. */ + static int blk_mig_save_bulked_block(QEMUFile *f) { int64_t completed_sector_sum = 0; @@ -350,6 +396,8 @@ static void blk_mig_reset_dirty_cursor(void) } } +/* Called with iothread lock taken. */ + static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, int is_async) { @@ -360,8 +408,12 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, int ret = -EIO; for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) { + blk_mig_lock(); if (bmds_aio_inflight(bmds, sector)) { + blk_mig_unlock(); bdrv_drain_all(); + } else { + blk_mig_unlock(); } if (bdrv_get_dirty(bmds->bs, sector)) { @@ -381,14 +433,13 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); - if (block_mig_state.submitted == 0) { - block_mig_state.prev_time_offset = qemu_get_clock_ns(rt_clock); - } - blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov, nr_sectors, blk_mig_read_cb, blk); + + blk_mig_lock(); block_mig_state.submitted++; bmds_set_aio_inflight(bmds, sector, nr_sectors, 1); + blk_mig_unlock(); } else { ret = bdrv_read(bmds->bs, sector, blk->buf, nr_sectors); if (ret < 0) { @@ -416,7 +467,9 @@ error: return ret; } -/* return value: +/* Called with iothread lock taken. + * + * return value: * 0: too much data for max_downtime * 1: few enough data for max_downtime */ @@ -435,6 +488,8 @@ static int blk_mig_save_dirty_block(QEMUFile *f, int is_async) return ret; } +/* Called with no locks taken. */ + static int flush_blks(QEMUFile *f) { BlkMigBlock *blk; @@ -444,6 +499,7 @@ static int flush_blks(QEMUFile *f) __FUNCTION__, block_mig_state.submitted, block_mig_state.read_done, block_mig_state.transferred); + blk_mig_lock(); while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) { if (qemu_file_rate_limit(f)) { break; @@ -452,9 +508,12 @@ static int flush_blks(QEMUFile *f) ret = blk->ret; break; } - blk_send(f, blk); QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry); + blk_mig_unlock(); + blk_send(f, blk); + blk_mig_lock(); + g_free(blk->buf); g_free(blk); @@ -462,6 +521,7 @@ static int flush_blks(QEMUFile *f) block_mig_state.transferred++; assert(block_mig_state.read_done >= 0); } + blk_mig_unlock(); DPRINTF("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__, block_mig_state.submitted, block_mig_state.read_done, @@ -469,6 +529,8 @@ static int flush_blks(QEMUFile *f) return ret; } +/* Called with iothread lock taken. */ + static int64_t get_remaining_dirty(void) { BlkMigDevState *bmds; @@ -478,9 +540,11 @@ static int64_t get_remaining_dirty(void) dirty += bdrv_get_dirty_count(bmds->bs); } - return dirty * BLOCK_SIZE; + return dirty << BDRV_SECTOR_BITS; } +/* Called with iothread lock taken. */ + static void blk_mig_cleanup(void) { BlkMigDevState *bmds; @@ -490,6 +554,7 @@ static void blk_mig_cleanup(void) set_dirty_tracking(0); + blk_mig_lock(); while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) { QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry); bdrv_set_in_use(bmds->bs, 0); @@ -503,6 +568,7 @@ static void blk_mig_cleanup(void) g_free(blk->buf); g_free(blk); } + blk_mig_unlock(); } static void block_migration_cancel(void *opaque) @@ -517,73 +583,78 @@ static int block_save_setup(QEMUFile *f, void *opaque) DPRINTF("Enter save live setup submitted %d transferred %d\n", block_mig_state.submitted, block_mig_state.transferred); + qemu_mutex_lock_iothread(); init_blk_migration(f); /* start track dirty blocks */ set_dirty_tracking(1); + qemu_mutex_unlock_iothread(); ret = flush_blks(f); - if (ret) { - blk_mig_cleanup(); - return ret; - } - blk_mig_reset_dirty_cursor(); - qemu_put_be64(f, BLK_MIG_FLAG_EOS); - return 0; + return ret; } static int block_save_iterate(QEMUFile *f, void *opaque) { int ret; + int64_t last_ftell = qemu_ftell(f); DPRINTF("Enter save live iterate submitted %d transferred %d\n", block_mig_state.submitted, block_mig_state.transferred); ret = flush_blks(f); if (ret) { - blk_mig_cleanup(); return ret; } blk_mig_reset_dirty_cursor(); /* control the rate of transfer */ + blk_mig_lock(); while ((block_mig_state.submitted + block_mig_state.read_done) * BLOCK_SIZE < qemu_file_get_rate_limit(f)) { + blk_mig_unlock(); if (block_mig_state.bulk_completed == 0) { /* first finish the bulk phase */ if (blk_mig_save_bulked_block(f) == 0) { /* finished saving bulk on all devices */ block_mig_state.bulk_completed = 1; } + ret = 0; } else { + /* Always called with iothread lock taken for + * simplicity, block_save_complete also calls it. + */ + qemu_mutex_lock_iothread(); ret = blk_mig_save_dirty_block(f, 1); - if (ret != 0) { - /* no more dirty blocks */ - break; - } + qemu_mutex_unlock_iothread(); + } + if (ret < 0) { + return ret; + } + blk_mig_lock(); + if (ret != 0) { + /* no more dirty blocks */ + break; } } - if (ret) { - blk_mig_cleanup(); - return ret; - } + blk_mig_unlock(); ret = flush_blks(f); if (ret) { - blk_mig_cleanup(); return ret; } qemu_put_be64(f, BLK_MIG_FLAG_EOS); - - return 0; + return qemu_ftell(f) - last_ftell; } +/* Called with iothread lock taken. */ + static int block_save_complete(QEMUFile *f, void *opaque) { int ret; @@ -593,7 +664,6 @@ static int block_save_complete(QEMUFile *f, void *opaque) ret = flush_blks(f); if (ret) { - blk_mig_cleanup(); return ret; } @@ -601,16 +671,17 @@ static int block_save_complete(QEMUFile *f, void *opaque) /* we know for sure that save bulk is completed and all async read completed */ + blk_mig_lock(); assert(block_mig_state.submitted == 0); + blk_mig_unlock(); do { ret = blk_mig_save_dirty_block(f, 0); + if (ret < 0) { + return ret; + } } while (ret == 0); - blk_mig_cleanup(); - if (ret) { - return ret; - } /* report completion */ qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS); @@ -618,15 +689,30 @@ static int block_save_complete(QEMUFile *f, void *opaque) qemu_put_be64(f, BLK_MIG_FLAG_EOS); + blk_mig_cleanup(); return 0; } static uint64_t block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size) { + /* Estimate pending number of bytes to send */ + uint64_t pending; - DPRINTF("Enter save live pending %ld\n", get_remaining_dirty()); + qemu_mutex_lock_iothread(); + blk_mig_lock(); + pending = get_remaining_dirty() + + block_mig_state.submitted * BLOCK_SIZE + + block_mig_state.read_done * BLOCK_SIZE; - return get_remaining_dirty(); + /* Report at least one block pending during bulk phase */ + if (pending == 0 && !block_mig_state.bulk_completed) { + pending = BLOCK_SIZE; + } + blk_mig_unlock(); + qemu_mutex_unlock_iothread(); + + DPRINTF("Enter save live pending %" PRIu64 "\n", pending); + return pending; } static int block_load(QEMUFile *f, void *opaque, int version_id) @@ -694,7 +780,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) (addr == 100) ? '\n' : '\r'); fflush(stdout); } else if (!(flags & BLK_MIG_FLAG_EOS)) { - fprintf(stderr, "Unknown flags\n"); + fprintf(stderr, "Unknown block migration flags: %#x\n", flags); return -EINVAL; } ret = qemu_file_get_error(f); @@ -735,6 +821,7 @@ void blk_mig_init(void) { QSIMPLEQ_INIT(&block_mig_state.bmds_list); QSIMPLEQ_INIT(&block_mig_state.blk_list); + qemu_mutex_init(&block_mig_state.lock); register_savevm_live(NULL, "block", 0, 1, &savevm_block_handlers, &block_mig_state); diff --git a/block.c b/block.c index 4e28c55bc7..037e15e065 100644 --- a/block.c +++ b/block.c @@ -155,10 +155,6 @@ void bdrv_io_limits_enable(BlockDriverState *bs) { qemu_co_queue_init(&bs->throttled_reqs); bs->block_timer = qemu_new_timer_ns(vm_clock, bdrv_block_timer, bs); - bs->slice_time = 5 * BLOCK_IO_SLICE_TIME; - bs->slice_start = qemu_get_clock_ns(vm_clock); - bs->slice_end = bs->slice_start + bs->slice_time; - memset(&bs->io_base, 0, sizeof(bs->io_base)); bs->io_limits_enabled = true; } @@ -527,7 +523,7 @@ static int find_image_format(BlockDriverState *bs, const char *filename, int ret = 0; /* Return the raw BlockDriver * to scsi-generic devices or empty drives */ - if (bs->sg || !bdrv_is_inserted(bs)) { + if (bs->sg || !bdrv_is_inserted(bs) || bdrv_getlength(bs) == 0) { drv = bdrv_find_format("raw"); if (!drv) { ret = -ENOENT; @@ -584,6 +580,26 @@ static int refresh_total_sectors(BlockDriverState *bs, int64_t hint) return 0; } +/** + * Set open flags for a given discard mode + * + * Return 0 on success, -1 if the discard mode was invalid. + */ +int bdrv_parse_discard_flags(const char *mode, int *flags) +{ + *flags &= ~BDRV_O_UNMAP; + + if (!strcmp(mode, "off") || !strcmp(mode, "ignore")) { + /* do nothing */ + } else if (!strcmp(mode, "on") || !strcmp(mode, "unmap")) { + *flags |= BDRV_O_UNMAP; + } else { + return -1; + } + + return 0; +} + /** * Set open flags for a given cache mode * @@ -649,15 +665,18 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags) /* * Common part for opening disk images and files + * + * Removes all processed options from *options. */ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, - const char *filename, + const char *filename, QDict *options, int flags, BlockDriver *drv) { int ret, open_flags; assert(drv != NULL); assert(bs->file == NULL); + assert(options == NULL || bs->options != options); trace_bdrv_open_common(bs, filename, flags, drv->format_name); @@ -694,7 +713,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, } else { assert(file != NULL); bs->file = file; - ret = drv->bdrv_open(bs, open_flags); + ret = drv->bdrv_open(bs, options, open_flags); } if (ret < 0) { @@ -736,7 +755,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags) } bs = bdrv_new(""); - ret = bdrv_open_common(bs, NULL, filename, flags, drv); + ret = bdrv_open_common(bs, NULL, filename, NULL, flags, drv); if (ret < 0) { bdrv_delete(bs); return ret; @@ -772,7 +791,8 @@ int bdrv_open_backing_file(BlockDriverState *bs) /* backing files always opened read-only */ back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT); - ret = bdrv_open(bs->backing_hd, backing_filename, back_flags, back_drv); + ret = bdrv_open(bs->backing_hd, backing_filename, NULL, + back_flags, back_drv); if (ret < 0) { bdrv_delete(bs->backing_hd); bs->backing_hd = NULL; @@ -784,15 +804,29 @@ int bdrv_open_backing_file(BlockDriverState *bs) /* * Opens a disk image (raw, qcow2, vmdk, ...) + * + * options is a QDict of options to pass to the block drivers, or NULL for an + * empty set of options. The reference to the QDict belongs to the block layer + * after the call (even on failure), so if the caller intends to reuse the + * dictionary, it needs to use QINCREF() before calling bdrv_open. */ -int bdrv_open(BlockDriverState *bs, const char *filename, int flags, - BlockDriver *drv) +int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, + int flags, BlockDriver *drv) { int ret; /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ char tmp_filename[PATH_MAX + 1]; BlockDriverState *file = NULL; + /* NULL means an empty set of options */ + if (options == NULL) { + options = qdict_new(); + } + + bs->options = options; + options = qdict_clone_shallow(options); + + /* For snapshot=on, create a temporary qcow2 overlay */ if (flags & BDRV_O_SNAPSHOT) { BlockDriverState *bs1; int64_t total_size; @@ -806,10 +840,10 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, /* if there is a backing file, use it */ bs1 = bdrv_new(""); - ret = bdrv_open(bs1, filename, 0, drv); + ret = bdrv_open(bs1, filename, NULL, 0, drv); if (ret < 0) { bdrv_delete(bs1); - return ret; + goto fail; } total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK; @@ -820,15 +854,17 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename)); if (ret < 0) { - return ret; + goto fail; } /* Real path is meaningless for protocols */ - if (is_protocol) + if (is_protocol) { snprintf(backing_filename, sizeof(backing_filename), "%s", filename); - else if (!realpath(filename, backing_filename)) - return -errno; + } else if (!realpath(filename, backing_filename)) { + ret = -errno; + goto fail; + } bdrv_qcow2 = bdrv_find_format("qcow2"); options = parse_option_parameters("", bdrv_qcow2->create_options, NULL); @@ -843,7 +879,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, ret = bdrv_create(bdrv_qcow2, tmp_filename, options); free_option_parameters(options); if (ret < 0) { - return ret; + goto fail; } filename = tmp_filename; @@ -858,7 +894,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, ret = bdrv_file_open(&file, filename, bdrv_open_flags(bs, flags)); if (ret < 0) { - return ret; + goto fail; } /* Find the right image format driver */ @@ -871,7 +907,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, } /* Open the image */ - ret = bdrv_open_common(bs, file, filename, flags, drv); + ret = bdrv_open_common(bs, file, filename, options, flags, drv); if (ret < 0) { goto unlink_and_fail; } @@ -885,11 +921,22 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags, if ((flags & BDRV_O_NO_BACKING) == 0) { ret = bdrv_open_backing_file(bs); if (ret < 0) { - bdrv_close(bs); - return ret; + goto close_and_fail; } } + /* Check if any unknown options were used */ + if (qdict_size(options) != 0) { + const QDictEntry *entry = qdict_first(options); + qerror_report(ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by " + "device '%s' doesn't support the option '%s'", + drv->format_name, bs->device_name, entry->key); + + ret = -EINVAL; + goto close_and_fail; + } + QDECREF(options); + if (!bdrv_key_required(bs)) { bdrv_dev_change_media_cb(bs, true); } @@ -908,6 +955,15 @@ unlink_and_fail: if (bs->is_temporary) { unlink(filename); } +fail: + QDECREF(bs->options); + QDECREF(options); + bs->options = NULL; + return ret; + +close_and_fail: + bdrv_close(bs); + QDECREF(options); return ret; } @@ -1177,6 +1233,8 @@ void bdrv_close(BlockDriverState *bs) bs->valid_key = 0; bs->sg = 0; bs->growable = 0; + QDECREF(bs->options); + bs->options = NULL; if (bs->file != NULL) { bdrv_delete(bs->file); @@ -1290,7 +1348,6 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest, bs_dest->iostatus = bs_src->iostatus; /* dirty bitmap */ - bs_dest->dirty_count = bs_src->dirty_count; bs_dest->dirty_bitmap = bs_src->dirty_bitmap; /* job */ @@ -1625,9 +1682,11 @@ int bdrv_commit_all(void) BlockDriverState *bs; QTAILQ_FOREACH(bs, &bdrv_states, list) { - int ret = bdrv_commit(bs); - if (ret < 0) { - return ret; + if (bs->drv && bs->backing_hd) { + int ret = bdrv_commit(bs); + if (ret < 0) { + return ret; + } } } return 0; @@ -1678,10 +1737,10 @@ static void tracked_request_begin(BdrvTrackedRequest *req, /** * Round a region to cluster boundaries */ -static void round_to_clusters(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, - int64_t *cluster_sector_num, - int *cluster_nb_sectors) +void bdrv_round_to_clusters(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + int64_t *cluster_sector_num, + int *cluster_nb_sectors) { BlockDriverInfo bdi; @@ -1723,8 +1782,8 @@ static void coroutine_fn wait_for_overlapping_requests(BlockDriverState *bs, * CoR read and write operations are atomic and guest writes cannot * interleave between them. */ - round_to_clusters(bs, sector_num, nb_sectors, - &cluster_sector_num, &cluster_nb_sectors); + bdrv_round_to_clusters(bs, sector_num, nb_sectors, + &cluster_sector_num, &cluster_nb_sectors); do { retry = false; @@ -2039,36 +2098,6 @@ int bdrv_read_unthrottled(BlockDriverState *bs, int64_t sector_num, return ret; } -#define BITS_PER_LONG (sizeof(unsigned long) * 8) - -static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int dirty) -{ - int64_t start, end; - unsigned long val, idx, bit; - - start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK; - end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK; - - for (; start <= end; start++) { - idx = start / BITS_PER_LONG; - bit = start % BITS_PER_LONG; - val = bs->dirty_bitmap[idx]; - if (dirty) { - if (!(val & (1UL << bit))) { - bs->dirty_count++; - val |= 1UL << bit; - } - } else { - if (val & (1UL << bit)) { - bs->dirty_count--; - val &= ~(1UL << bit); - } - } - bs->dirty_bitmap[idx] = val; - } -} - /* Return < 0 if error. Important errors are: -EIO generic I/O error (may happen for all errors) -ENOMEDIUM No media inserted. @@ -2220,8 +2249,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs, /* Cover entire cluster so no additional backing file I/O is required when * allocating cluster in the image file. */ - round_to_clusters(bs, sector_num, nb_sectors, - &cluster_sector_num, &cluster_nb_sectors); + bdrv_round_to_clusters(bs, sector_num, nb_sectors, + &cluster_sector_num, &cluster_nb_sectors); trace_bdrv_co_do_copy_on_readv(bs, sector_num, nb_sectors, cluster_sector_num, cluster_nb_sectors); @@ -2462,6 +2491,10 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset) return -EACCES; if (bdrv_in_use(bs)) return -EBUSY; + + /* There better not be any in-flight IOs when we truncate the device. */ + bdrv_drain_all(); + ret = drv->bdrv_truncate(bs, offset); if (ret == 0) { ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); @@ -2716,6 +2749,7 @@ int bdrv_has_zero_init(BlockDriverState *bs) typedef struct BdrvCoIsAllocatedData { BlockDriverState *bs; + BlockDriverState *base; int64_t sector_num; int nb_sectors; int *pnum; @@ -2835,7 +2869,9 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top, * * [sector_num+x, nr_sectors] allocated. */ - if (n > pnum_inter) { + if (n > pnum_inter && + (intermediate == top || + sector_num + pnum_inter < intermediate->total_sectors)) { n = pnum_inter; } @@ -2846,6 +2882,44 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top, return 0; } +/* Coroutine wrapper for bdrv_is_allocated_above() */ +static void coroutine_fn bdrv_is_allocated_above_co_entry(void *opaque) +{ + BdrvCoIsAllocatedData *data = opaque; + BlockDriverState *top = data->bs; + BlockDriverState *base = data->base; + + data->ret = bdrv_co_is_allocated_above(top, base, data->sector_num, + data->nb_sectors, data->pnum); + data->done = true; +} + +/* + * Synchronous wrapper around bdrv_co_is_allocated_above(). + * + * See bdrv_co_is_allocated_above() for details. + */ +int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, + int64_t sector_num, int nb_sectors, int *pnum) +{ + Coroutine *co; + BdrvCoIsAllocatedData data = { + .bs = top, + .base = base, + .sector_num = sector_num, + .nb_sectors = nb_sectors, + .pnum = pnum, + .done = false, + }; + + co = qemu_coroutine_create(bdrv_is_allocated_above_co_entry); + qemu_coroutine_enter(co, &data); + while (!data.done) { + qemu_aio_wait(); + } + return data.ret; +} + BlockInfo *bdrv_query_info(BlockDriverState *bs) { BlockInfo *info = g_malloc0(sizeof(*info)); @@ -2867,8 +2941,9 @@ BlockInfo *bdrv_query_info(BlockDriverState *bs) if (bs->dirty_bitmap) { info->has_dirty = true; info->dirty = g_malloc0(sizeof(*info->dirty)); - info->dirty->count = bdrv_get_dirty_count(bs) * - BDRV_SECTORS_PER_DIRTY_CHUNK * BDRV_SECTOR_SIZE; + info->dirty->count = bdrv_get_dirty_count(bs) * BDRV_SECTOR_SIZE; + info->dirty->granularity = + ((int64_t) BDRV_SECTOR_SIZE << hbitmap_granularity(bs->dirty_bitmap)); } if (bs->drv) { @@ -3157,7 +3232,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, if (bs->file) { drv->bdrv_close(bs); ret = bdrv_snapshot_goto(bs->file, snapshot_id); - open_ret = drv->bdrv_open(bs, bs->open_flags); + open_ret = drv->bdrv_open(bs, NULL, bs->open_flags); if (open_ret < 0) { bdrv_delete(bs->file); bs->drv = NULL; @@ -3338,11 +3413,7 @@ char *get_human_readable_size(char *buf, int buf_size, int64_t size) char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn) { char buf1[128], date_buf[128], clock_buf[128]; -#ifdef _WIN32 - struct tm *ptm; -#else struct tm tm; -#endif time_t ti; int64_t secs; @@ -3352,15 +3423,9 @@ char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn) "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); } else { ti = sn->date_sec; -#ifdef _WIN32 - ptm = localtime(&ti); - strftime(date_buf, sizeof(date_buf), - "%Y-%m-%d %H:%M:%S", ptm); -#else localtime_r(&ti, &tm); strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm); -#endif secs = sn->vm_clock_nsec / 1000000000; snprintf(clock_buf, sizeof(clock_buf), "%02d:%02d:%02d.%03d", @@ -4184,7 +4249,18 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, return -EIO; } else if (bs->read_only) { return -EROFS; - } else if (bs->drv->bdrv_co_discard) { + } + + if (bs->dirty_bitmap) { + bdrv_reset_dirty(bs, sector_num, nb_sectors); + } + + /* Do nothing if disabled. */ + if (!(bs->open_flags & BDRV_O_UNMAP)) { + return 0; + } + + if (bs->drv->bdrv_co_discard) { return bs->drv->bdrv_co_discard(bs, sector_num, nb_sectors); } else if (bs->drv->bdrv_aio_discard) { BlockDriverAIOCB *acb; @@ -4323,22 +4399,36 @@ void *qemu_blockalign(BlockDriverState *bs, size_t size) return qemu_memalign((bs && bs->buffer_alignment) ? bs->buffer_alignment : 512, size); } -void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable) +/* + * Check if all memory in this vector is sector aligned. + */ +bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) +{ + int i; + + for (i = 0; i < qiov->niov; i++) { + if ((uintptr_t) qiov->iov[i].iov_base % bs->buffer_alignment) { + return false; + } + } + + return true; +} + +void bdrv_set_dirty_tracking(BlockDriverState *bs, int granularity) { int64_t bitmap_size; - bs->dirty_count = 0; - if (enable) { - if (!bs->dirty_bitmap) { - bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) + - BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG - 1; - bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG; + assert((granularity & (granularity - 1)) == 0); - bs->dirty_bitmap = g_new0(unsigned long, bitmap_size); - } + if (granularity) { + granularity >>= BDRV_SECTOR_BITS; + assert(!bs->dirty_bitmap); + bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS); + bs->dirty_bitmap = hbitmap_alloc(bitmap_size, ffs(granularity) - 1); } else { if (bs->dirty_bitmap) { - g_free(bs->dirty_bitmap); + hbitmap_free(bs->dirty_bitmap); bs->dirty_bitmap = NULL; } } @@ -4346,67 +4436,37 @@ void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable) int bdrv_get_dirty(BlockDriverState *bs, int64_t sector) { - int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK; - - if (bs->dirty_bitmap && - (sector << BDRV_SECTOR_BITS) < bdrv_getlength(bs)) { - return !!(bs->dirty_bitmap[chunk / BITS_PER_LONG] & - (1UL << (chunk % BITS_PER_LONG))); + if (bs->dirty_bitmap) { + return hbitmap_get(bs->dirty_bitmap, sector); } else { return 0; } } -int64_t bdrv_get_next_dirty(BlockDriverState *bs, int64_t sector) +void bdrv_dirty_iter_init(BlockDriverState *bs, HBitmapIter *hbi) { - int64_t chunk; - int bit, elem; - - /* Avoid an infinite loop. */ - assert(bs->dirty_count > 0); - - sector = (sector | (BDRV_SECTORS_PER_DIRTY_CHUNK - 1)) + 1; - chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK; - - QEMU_BUILD_BUG_ON(sizeof(bs->dirty_bitmap[0]) * 8 != BITS_PER_LONG); - elem = chunk / BITS_PER_LONG; - bit = chunk % BITS_PER_LONG; - for (;;) { - if (sector >= bs->total_sectors) { - sector = 0; - bit = elem = 0; - } - if (bit == 0 && bs->dirty_bitmap[elem] == 0) { - sector += BDRV_SECTORS_PER_DIRTY_CHUNK * BITS_PER_LONG; - elem++; - } else { - if (bs->dirty_bitmap[elem] & (1UL << bit)) { - return sector; - } - sector += BDRV_SECTORS_PER_DIRTY_CHUNK; - if (++bit == BITS_PER_LONG) { - bit = 0; - elem++; - } - } - } + hbitmap_iter_init(hbi, bs->dirty_bitmap, 0); } void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors) { - set_dirty_bitmap(bs, cur_sector, nr_sectors, 1); + hbitmap_set(bs->dirty_bitmap, cur_sector, nr_sectors); } void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors) { - set_dirty_bitmap(bs, cur_sector, nr_sectors, 0); + hbitmap_reset(bs->dirty_bitmap, cur_sector, nr_sectors); } int64_t bdrv_get_dirty_count(BlockDriverState *bs) { - return bs->dirty_count; + if (bs->dirty_bitmap) { + return hbitmap_count(bs->dirty_bitmap); + } else { + return 0; + } } void bdrv_set_in_use(BlockDriverState *bs, int in_use) @@ -4483,7 +4543,8 @@ bdrv_acct_done(BlockDriverState *bs, BlockAcctCookie *cookie) void bdrv_img_create(const char *filename, const char *fmt, const char *base_filename, const char *base_fmt, - char *options, uint64_t img_size, int flags, Error **errp) + char *options, uint64_t img_size, int flags, + Error **errp, bool quiet) { QEMUOptionParameter *param = NULL, *create_options = NULL; QEMUOptionParameter *backing_fmt, *backing_file, *size; @@ -4575,7 +4636,8 @@ void bdrv_img_create(const char *filename, const char *fmt, bs = bdrv_new(""); - ret = bdrv_open(bs, backing_file->value.s, back_flags, backing_drv); + ret = bdrv_open(bs, backing_file->value.s, NULL, back_flags, + backing_drv); if (ret < 0) { error_setg_errno(errp, -ret, "Could not open '%s'", backing_file->value.s); @@ -4592,10 +4654,11 @@ void bdrv_img_create(const char *filename, const char *fmt, } } - printf("Formatting '%s', fmt=%s ", filename, fmt); - print_option_parameters(param); - puts(""); - + if (!quiet) { + printf("Formatting '%s', fmt=%s ", filename, fmt); + print_option_parameters(param); + puts(""); + } ret = bdrv_create(drv, filename, param); if (ret < 0) { if (ret == -ENOTSUP) { @@ -4618,3 +4681,9 @@ out: bdrv_delete(bs); } } + +AioContext *bdrv_get_aio_context(BlockDriverState *bs) +{ + /* Currently BlockDriverState always uses the main loop AioContext */ + return qemu_get_aio_context(); +} diff --git a/block/blkverify.c b/block/blkverify.c index a7dd45909b..2086d97234 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -98,7 +98,7 @@ static int blkverify_open(BlockDriverState *bs, const char *filename, int flags) /* Open the test file */ s->test_file = bdrv_new(""); - ret = bdrv_open(s->test_file, filename, flags, NULL); + ret = bdrv_open(s->test_file, filename, NULL, flags, NULL); if (ret < 0) { bdrv_delete(s->test_file); s->test_file = NULL; diff --git a/block/bochs.c b/block/bochs.c index 1b1d9cdbe5..d7078c0775 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -108,17 +108,19 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int bochs_open(BlockDriverState *bs, int flags) +static int bochs_open(BlockDriverState *bs, QDict *options, int flags) { BDRVBochsState *s = bs->opaque; int i; struct bochs_header bochs; struct bochs_header_v1 header_v1; + int ret; bs->read_only = 1; // no write support yet - if (bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)) != sizeof(bochs)) { - goto fail; + ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); + if (ret < 0) { + return ret; } if (strcmp(bochs.magic, HEADER_MAGIC) || @@ -126,7 +128,7 @@ static int bochs_open(BlockDriverState *bs, int flags) strcmp(bochs.subtype, GROWING_TYPE) || ((le32_to_cpu(bochs.version) != HEADER_VERSION) && (le32_to_cpu(bochs.version) != HEADER_V1))) { - goto fail; + return -EMEDIUMTYPE; } if (le32_to_cpu(bochs.version) == HEADER_V1) { @@ -138,9 +140,13 @@ static int bochs_open(BlockDriverState *bs, int flags) s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog); s->catalog_bitmap = g_malloc(s->catalog_size * 4); - if (bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, - s->catalog_size * 4) != s->catalog_size * 4) - goto fail; + + ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, + s->catalog_size * 4); + if (ret < 0) { + goto fail; + } + for (i = 0; i < s->catalog_size; i++) le32_to_cpus(&s->catalog_bitmap[i]); @@ -153,8 +159,10 @@ static int bochs_open(BlockDriverState *bs, int flags) qemu_co_mutex_init(&s->lock); return 0; - fail: - return -1; + +fail: + g_free(s->catalog_bitmap); + return ret; } static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) diff --git a/block/cloop.c b/block/cloop.c index 5a0d0d805f..6ea7cf4046 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -53,31 +53,36 @@ static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int cloop_open(BlockDriverState *bs, int flags) +static int cloop_open(BlockDriverState *bs, QDict *options, int flags) { BDRVCloopState *s = bs->opaque; uint32_t offsets_size, max_compressed_block_size = 1, i; + int ret; bs->read_only = 1; /* read header */ - if (bdrv_pread(bs->file, 128, &s->block_size, 4) < 4) { - goto cloop_close; + ret = bdrv_pread(bs->file, 128, &s->block_size, 4); + if (ret < 0) { + return ret; } s->block_size = be32_to_cpu(s->block_size); - if (bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4) < 4) { - goto cloop_close; + ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4); + if (ret < 0) { + return ret; } s->n_blocks = be32_to_cpu(s->n_blocks); /* read offsets */ offsets_size = s->n_blocks * sizeof(uint64_t); s->offsets = g_malloc(offsets_size); - if (bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size) < - offsets_size) { - goto cloop_close; + + ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size); + if (ret < 0) { + goto fail; } + for(i=0;in_blocks;i++) { s->offsets[i] = be64_to_cpu(s->offsets[i]); if (i > 0) { @@ -92,7 +97,8 @@ static int cloop_open(BlockDriverState *bs, int flags) s->compressed_block = g_malloc(max_compressed_block_size + 1); s->uncompressed_block = g_malloc(s->block_size); if (inflateInit(&s->zstream) != Z_OK) { - goto cloop_close; + ret = -EINVAL; + goto fail; } s->current_block = s->n_blocks; @@ -101,8 +107,11 @@ static int cloop_open(BlockDriverState *bs, int flags) qemu_co_mutex_init(&s->lock); return 0; -cloop_close: - return -1; +fail: + g_free(s->offsets); + g_free(s->compressed_block); + g_free(s->uncompressed_block); + return ret; } static inline int cloop_read_block(BlockDriverState *bs, int block_num) diff --git a/block/commit.c b/block/commit.c index 61ebdba54f..553447efe7 100644 --- a/block/commit.c +++ b/block/commit.c @@ -65,7 +65,7 @@ static void coroutine_fn commit_run(void *opaque) BlockDriverState *active = s->active; BlockDriverState *top = s->top; BlockDriverState *base = s->base; - BlockDriverState *overlay_bs = NULL; + BlockDriverState *overlay_bs; int64_t sector_num, end; int ret = 0; int n = 0; @@ -92,8 +92,6 @@ static void coroutine_fn commit_run(void *opaque) } } - overlay_bs = bdrv_find_overlay(active, top); - end = s->common.len >> BDRV_SECTOR_BITS; buf = qemu_blockalign(top, COMMIT_BUFFER_SIZE); @@ -156,7 +154,8 @@ exit_restore_reopen: if (s->base_flags != bdrv_get_flags(base)) { bdrv_reopen(base, s->base_flags, NULL); } - if (s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) { + overlay_bs = bdrv_find_overlay(active, top); + if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) { bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL); } diff --git a/block/cow.c b/block/cow.c index a33ce950d4..d73e08cf92 100644 --- a/block/cow.c +++ b/block/cow.c @@ -58,7 +58,7 @@ static int cow_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int cow_open(BlockDriverState *bs, int flags) +static int cow_open(BlockDriverState *bs, QDict *options, int flags) { BDRVCowState *s = bs->opaque; struct cow_header_v2 cow_header; @@ -73,7 +73,7 @@ static int cow_open(BlockDriverState *bs, int flags) } if (be32_to_cpu(cow_header.magic) != COW_MAGIC) { - ret = -EINVAL; + ret = -EMEDIUMTYPE; goto fail; } diff --git a/block/curl.c b/block/curl.c index 47df9524ea..98947dac32 100644 --- a/block/curl.c +++ b/block/curl.c @@ -34,6 +34,10 @@ #define DPRINTF(fmt, ...) do { } while (0) #endif +#define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \ + CURLPROTO_FTP | CURLPROTO_FTPS | \ + CURLPROTO_TFTP) + #define CURL_NUM_STATES 8 #define CURL_NUM_ACB 8 #define SECTOR_SIZE 512 @@ -302,6 +306,17 @@ static CURLState *curl_init_state(BDRVCURLState *s) curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg); curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1); + /* Restrict supported protocols to avoid security issues in the more + * obscure protocols. For example, do not allow POP3/SMTP/IMAP see + * CVE-2013-0249. + * + * Restricting protocols is only supported from 7.19.4 upwards. + */ +#if LIBCURL_VERSION_NUM >= 0x071304 + curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS); + curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS); +#endif + #ifdef DEBUG_VERBOSE curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1); #endif diff --git a/block/dmg.c b/block/dmg.c index ac397dc8f7..c1066df13a 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -57,29 +57,42 @@ static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static off_t read_off(BlockDriverState *bs, int64_t offset) +static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result) { - uint64_t buffer; - if (bdrv_pread(bs->file, offset, &buffer, 8) < 8) - return 0; - return be64_to_cpu(buffer); + uint64_t buffer; + int ret; + + ret = bdrv_pread(bs->file, offset, &buffer, 8); + if (ret < 0) { + return ret; + } + + *result = be64_to_cpu(buffer); + return 0; } -static off_t read_uint32(BlockDriverState *bs, int64_t offset) +static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) { - uint32_t buffer; - if (bdrv_pread(bs->file, offset, &buffer, 4) < 4) - return 0; - return be32_to_cpu(buffer); + uint32_t buffer; + int ret; + + ret = bdrv_pread(bs->file, offset, &buffer, 4); + if (ret < 0) { + return ret; + } + + *result = be32_to_cpu(buffer); + return 0; } -static int dmg_open(BlockDriverState *bs, int flags) +static int dmg_open(BlockDriverState *bs, QDict *options, int flags) { BDRVDMGState *s = bs->opaque; - off_t info_begin,info_end,last_in_offset,last_out_offset; - uint32_t count; + uint64_t info_begin,info_end,last_in_offset,last_out_offset; + uint32_t count, tmp; uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i; int64_t offset; + int ret; bs->read_only = 1; s->n_chunks = 0; @@ -88,21 +101,32 @@ static int dmg_open(BlockDriverState *bs, int flags) /* read offset of info blocks */ offset = bdrv_getlength(bs->file); if (offset < 0) { + ret = offset; goto fail; } offset -= 0x1d8; - info_begin = read_off(bs, offset); - if (info_begin == 0) { - goto fail; - } - - if (read_uint32(bs, info_begin) != 0x100) { + ret = read_uint64(bs, offset, &info_begin); + if (ret < 0) { + goto fail; + } else if (info_begin == 0) { + ret = -EINVAL; goto fail; } - count = read_uint32(bs, info_begin + 4); - if (count == 0) { + ret = read_uint32(bs, info_begin, &tmp); + if (ret < 0) { + goto fail; + } else if (tmp != 0x100) { + ret = -EINVAL; + goto fail; + } + + ret = read_uint32(bs, info_begin + 4, &count); + if (ret < 0) { + goto fail; + } else if (count == 0) { + ret = -EINVAL; goto fail; } info_end = info_begin + count; @@ -114,12 +138,20 @@ static int dmg_open(BlockDriverState *bs, int flags) while (offset < info_end) { uint32_t type; - count = read_uint32(bs, offset); - if(count==0) - goto fail; + ret = read_uint32(bs, offset, &count); + if (ret < 0) { + goto fail; + } else if (count == 0) { + ret = -EINVAL; + goto fail; + } offset += 4; - type = read_uint32(bs, offset); + ret = read_uint32(bs, offset, &type); + if (ret < 0) { + goto fail; + } + if (type == 0x6d697368 && count >= 244) { int new_size, chunk_count; @@ -134,8 +166,11 @@ static int dmg_open(BlockDriverState *bs, int flags) s->sectors = g_realloc(s->sectors, new_size); s->sectorcounts = g_realloc(s->sectorcounts, new_size); - for(i=s->n_chunks;in_chunks+chunk_count;i++) { - s->types[i] = read_uint32(bs, offset); + for (i = s->n_chunks; i < s->n_chunks + chunk_count; i++) { + ret = read_uint32(bs, offset, &s->types[i]); + if (ret < 0) { + goto fail; + } offset += 4; if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) { if(s->types[i]==0xffffffff) { @@ -149,17 +184,31 @@ static int dmg_open(BlockDriverState *bs, int flags) } offset += 4; - s->sectors[i] = last_out_offset+read_off(bs, offset); - offset += 8; + ret = read_uint64(bs, offset, &s->sectors[i]); + if (ret < 0) { + goto fail; + } + s->sectors[i] += last_out_offset; + offset += 8; - s->sectorcounts[i] = read_off(bs, offset); - offset += 8; + ret = read_uint64(bs, offset, &s->sectorcounts[i]); + if (ret < 0) { + goto fail; + } + offset += 8; - s->offsets[i] = last_in_offset+read_off(bs, offset); - offset += 8; + ret = read_uint64(bs, offset, &s->offsets[i]); + if (ret < 0) { + goto fail; + } + s->offsets[i] += last_in_offset; + offset += 8; - s->lengths[i] = read_off(bs, offset); - offset += 8; + ret = read_uint64(bs, offset, &s->lengths[i]); + if (ret < 0) { + goto fail; + } + offset += 8; if(s->lengths[i]>max_compressed_size) max_compressed_size = s->lengths[i]; @@ -173,15 +222,25 @@ static int dmg_open(BlockDriverState *bs, int flags) /* initialize zlib engine */ s->compressed_chunk = g_malloc(max_compressed_size+1); s->uncompressed_chunk = g_malloc(512*max_sectors_per_chunk); - if(inflateInit(&s->zstream) != Z_OK) - goto fail; + if(inflateInit(&s->zstream) != Z_OK) { + ret = -EINVAL; + goto fail; + } s->current_chunk = s->n_chunks; qemu_co_mutex_init(&s->lock); return 0; + fail: - return -1; + g_free(s->types); + g_free(s->offsets); + g_free(s->lengths); + g_free(s->sectors); + g_free(s->sectorcounts); + g_free(s->compressed_chunk); + g_free(s->uncompressed_chunk); + return ret; } static inline int is_sector_in_chunk(BDRVDMGState* s, @@ -296,15 +355,15 @@ static coroutine_fn int dmg_co_read(BlockDriverState *bs, int64_t sector_num, static void dmg_close(BlockDriverState *bs) { BDRVDMGState *s = bs->opaque; - if(s->n_chunks>0) { - free(s->types); - free(s->offsets); - free(s->lengths); - free(s->sectors); - free(s->sectorcounts); - } - free(s->compressed_chunk); - free(s->uncompressed_chunk); + + g_free(s->types); + g_free(s->offsets); + g_free(s->lengths); + g_free(s->sectors); + g_free(s->sectorcounts); + g_free(s->compressed_chunk); + g_free(s->uncompressed_chunk); + inflateEnd(&s->zstream); } diff --git a/block/gluster.c b/block/gluster.c index 0f2c32a3fa..ccd684d360 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -217,7 +217,7 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename) ret = glfs_init(glfs); if (ret) { error_report("Gluster connection failed for server=%s port=%d " - "volume=%s image=%s transport=%s\n", gconf->server, gconf->port, + "volume=%s image=%s transport=%s", gconf->server, gconf->port, gconf->volname, gconf->image, gconf->transport); goto out; } diff --git a/block/iscsi.c b/block/iscsi.c index 041ee07de3..3d529213ff 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -48,6 +48,7 @@ typedef struct IscsiLun { int block_size; uint64_t num_blocks; int events; + QEMUTimer *nop_timer; } IscsiLun; typedef struct IscsiAIOCB { @@ -59,13 +60,20 @@ typedef struct IscsiAIOCB { uint8_t *buf; int status; int canceled; + int retries; size_t read_size; size_t read_offset; + int64_t sector_num; + int nb_sectors; #ifdef __linux__ sg_io_hdr_t *ioh; #endif } IscsiAIOCB; +#define NOP_INTERVAL 5000 +#define MAX_NOP_FAILURES 3 +#define ISCSI_CMD_RETRIES 5 + static void iscsi_bh_cb(void *p) { @@ -73,6 +81,9 @@ iscsi_bh_cb(void *p) qemu_bh_delete(acb->bh); + g_free(acb->buf); + acb->buf = NULL; + if (acb->canceled == 0) { acb->common.cb(acb->common.opaque, acb->status); } @@ -184,6 +195,8 @@ iscsi_process_write(void *arg) iscsi_set_events(iscsilun); } +static int +iscsi_aio_writev_acb(IscsiAIOCB *acb); static void iscsi_aio_write16_cb(struct iscsi_context *iscsi, int status, @@ -194,13 +207,26 @@ iscsi_aio_write16_cb(struct iscsi_context *iscsi, int status, trace_iscsi_aio_write16_cb(iscsi, status, acb, acb->canceled); g_free(acb->buf); + acb->buf = NULL; if (acb->canceled != 0) { return; } acb->status = 0; - if (status < 0) { + if (status != 0) { + if (status == SCSI_STATUS_CHECK_CONDITION + && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION + && acb->retries-- > 0) { + if (acb->task != NULL) { + scsi_free_scsi_task(acb->task); + acb->task = NULL; + } + if (iscsi_aio_writev_acb(acb) == 0) { + iscsi_set_events(acb->iscsilun); + return; + } + } error_report("Failed to write16 data to iSCSI lun. %s", iscsi_get_error(iscsi)); acb->status = -EIO; @@ -214,6 +240,80 @@ static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun) return sector * BDRV_SECTOR_SIZE / iscsilun->block_size; } +static int +iscsi_aio_writev_acb(IscsiAIOCB *acb) +{ + struct iscsi_context *iscsi = acb->iscsilun->iscsi; + size_t size; + uint32_t num_sectors; + uint64_t lba; +#if !defined(LIBISCSI_FEATURE_IOVECTOR) + struct iscsi_data data; +#endif + int ret; + + acb->canceled = 0; + acb->bh = NULL; + acb->status = -EINPROGRESS; + acb->buf = NULL; + + /* this will allow us to get rid of 'buf' completely */ + size = acb->nb_sectors * BDRV_SECTOR_SIZE; + +#if !defined(LIBISCSI_FEATURE_IOVECTOR) + data.size = MIN(size, acb->qiov->size); + + /* if the iovec only contains one buffer we can pass it directly */ + if (acb->qiov->niov == 1) { + data.data = acb->qiov->iov[0].iov_base; + } else { + acb->buf = g_malloc(data.size); + qemu_iovec_to_buf(acb->qiov, 0, acb->buf, data.size); + data.data = acb->buf; + } +#endif + + acb->task = malloc(sizeof(struct scsi_task)); + if (acb->task == NULL) { + error_report("iSCSI: Failed to allocate task for scsi WRITE16 " + "command. %s", iscsi_get_error(iscsi)); + return -1; + } + memset(acb->task, 0, sizeof(struct scsi_task)); + + acb->task->xfer_dir = SCSI_XFER_WRITE; + acb->task->cdb_size = 16; + acb->task->cdb[0] = 0x8a; + lba = sector_qemu2lun(acb->sector_num, acb->iscsilun); + *(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32); + *(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff); + num_sectors = size / acb->iscsilun->block_size; + *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors); + acb->task->expxferlen = size; + +#if defined(LIBISCSI_FEATURE_IOVECTOR) + ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task, + iscsi_aio_write16_cb, + NULL, + acb); +#else + ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task, + iscsi_aio_write16_cb, + &data, + acb); +#endif + if (ret != 0) { + g_free(acb->buf); + return -1; + } + +#if defined(LIBISCSI_FEATURE_IOVECTOR) + scsi_task_set_iov_out(acb->task, (struct scsi_iovec*) acb->qiov->iov, acb->qiov->niov); +#endif + + return 0; +} + static BlockDriverAIOCB * iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, @@ -221,66 +321,32 @@ iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num, void *opaque) { IscsiLun *iscsilun = bs->opaque; - struct iscsi_context *iscsi = iscsilun->iscsi; IscsiAIOCB *acb; - size_t size; - uint32_t num_sectors; - uint64_t lba; - struct iscsi_data data; acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); - trace_iscsi_aio_writev(iscsi, sector_num, nb_sectors, opaque, acb); + trace_iscsi_aio_writev(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb); - acb->iscsilun = iscsilun; - acb->qiov = qiov; + acb->iscsilun = iscsilun; + acb->qiov = qiov; + acb->nb_sectors = nb_sectors; + acb->sector_num = sector_num; + acb->retries = ISCSI_CMD_RETRIES; - acb->canceled = 0; - acb->bh = NULL; - acb->status = -EINPROGRESS; - - /* XXX we should pass the iovec to write16 to avoid the extra copy */ - /* this will allow us to get rid of 'buf' completely */ - size = nb_sectors * BDRV_SECTOR_SIZE; - acb->buf = g_malloc(size); - qemu_iovec_to_buf(acb->qiov, 0, acb->buf, size); - - acb->task = malloc(sizeof(struct scsi_task)); - if (acb->task == NULL) { - error_report("iSCSI: Failed to allocate task for scsi WRITE16 " - "command. %s", iscsi_get_error(iscsi)); - qemu_aio_release(acb); - return NULL; - } - memset(acb->task, 0, sizeof(struct scsi_task)); - - acb->task->xfer_dir = SCSI_XFER_WRITE; - acb->task->cdb_size = 16; - acb->task->cdb[0] = 0x8a; - lba = sector_qemu2lun(sector_num, iscsilun); - *(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32); - *(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff); - num_sectors = size / iscsilun->block_size; - *(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors); - acb->task->expxferlen = size; - - data.data = acb->buf; - data.size = size; - - if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task, - iscsi_aio_write16_cb, - &data, - acb) != 0) { - scsi_free_scsi_task(acb->task); - g_free(acb->buf); + if (iscsi_aio_writev_acb(acb) != 0) { + if (acb->task) { + scsi_free_scsi_task(acb->task); + } qemu_aio_release(acb); return NULL; } iscsi_set_events(iscsilun); - return &acb->common; } +static int +iscsi_aio_readv_acb(IscsiAIOCB *acb); + static void iscsi_aio_read16_cb(struct iscsi_context *iscsi, int status, void *command_data, void *opaque) @@ -295,6 +361,18 @@ iscsi_aio_read16_cb(struct iscsi_context *iscsi, int status, acb->status = 0; if (status != 0) { + if (status == SCSI_STATUS_CHECK_CONDITION + && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION + && acb->retries-- > 0) { + if (acb->task != NULL) { + scsi_free_scsi_task(acb->task); + acb->task = NULL; + } + if (iscsi_aio_readv_acb(acb) == 0) { + iscsi_set_events(acb->iscsilun); + return; + } + } error_report("Failed to read16 data from iSCSI lun. %s", iscsi_get_error(iscsi)); acb->status = -EIO; @@ -303,32 +381,20 @@ iscsi_aio_read16_cb(struct iscsi_context *iscsi, int status, iscsi_schedule_bh(acb); } -static BlockDriverAIOCB * -iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num, - QEMUIOVector *qiov, int nb_sectors, - BlockDriverCompletionFunc *cb, - void *opaque) +static int +iscsi_aio_readv_acb(IscsiAIOCB *acb) { - IscsiLun *iscsilun = bs->opaque; - struct iscsi_context *iscsi = iscsilun->iscsi; - IscsiAIOCB *acb; - size_t qemu_read_size; - int i; + struct iscsi_context *iscsi = acb->iscsilun->iscsi; uint64_t lba; uint32_t num_sectors; - - qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors; - - acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); - trace_iscsi_aio_readv(iscsi, sector_num, nb_sectors, opaque, acb); - - acb->iscsilun = iscsilun; - acb->qiov = qiov; + int ret; +#if !defined(LIBISCSI_FEATURE_IOVECTOR) + int i; +#endif acb->canceled = 0; acb->bh = NULL; acb->status = -EINPROGRESS; - acb->read_size = qemu_read_size; acb->buf = NULL; /* If LUN blocksize is bigger than BDRV_BLOCK_SIZE a read from QEMU @@ -336,30 +402,29 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num, * data. */ acb->read_offset = 0; - if (iscsilun->block_size > BDRV_SECTOR_SIZE) { - uint64_t bdrv_offset = BDRV_SECTOR_SIZE * sector_num; + if (acb->iscsilun->block_size > BDRV_SECTOR_SIZE) { + uint64_t bdrv_offset = BDRV_SECTOR_SIZE * acb->sector_num; - acb->read_offset = bdrv_offset % iscsilun->block_size; + acb->read_offset = bdrv_offset % acb->iscsilun->block_size; } - num_sectors = (qemu_read_size + iscsilun->block_size + num_sectors = (acb->read_size + acb->iscsilun->block_size + acb->read_offset - 1) - / iscsilun->block_size; + / acb->iscsilun->block_size; acb->task = malloc(sizeof(struct scsi_task)); if (acb->task == NULL) { error_report("iSCSI: Failed to allocate task for scsi READ16 " "command. %s", iscsi_get_error(iscsi)); - qemu_aio_release(acb); - return NULL; + return -1; } memset(acb->task, 0, sizeof(struct scsi_task)); acb->task->xfer_dir = SCSI_XFER_READ; - lba = sector_qemu2lun(sector_num, iscsilun); - acb->task->expxferlen = qemu_read_size; + lba = sector_qemu2lun(acb->sector_num, acb->iscsilun); + acb->task->expxferlen = acb->read_size; - switch (iscsilun->type) { + switch (acb->iscsilun->type) { case TYPE_DISK: acb->task->cdb_size = 16; acb->task->cdb[0] = 0x88; @@ -375,26 +440,59 @@ iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num, break; } - if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task, - iscsi_aio_read16_cb, - NULL, - acb) != 0) { - scsi_free_scsi_task(acb->task); - qemu_aio_release(acb); - return NULL; + ret = iscsi_scsi_command_async(iscsi, acb->iscsilun->lun, acb->task, + iscsi_aio_read16_cb, + NULL, + acb); + if (ret != 0) { + return -1; } +#if defined(LIBISCSI_FEATURE_IOVECTOR) + scsi_task_set_iov_in(acb->task, (struct scsi_iovec*) acb->qiov->iov, acb->qiov->niov); +#else for (i = 0; i < acb->qiov->niov; i++) { scsi_task_add_data_in_buffer(acb->task, acb->qiov->iov[i].iov_len, acb->qiov->iov[i].iov_base); } +#endif + return 0; +} + +static BlockDriverAIOCB * +iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + IscsiLun *iscsilun = bs->opaque; + IscsiAIOCB *acb; + + acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); + trace_iscsi_aio_readv(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb); + + acb->nb_sectors = nb_sectors; + acb->sector_num = sector_num; + acb->iscsilun = iscsilun; + acb->qiov = qiov; + acb->read_size = BDRV_SECTOR_SIZE * (size_t)acb->nb_sectors; + acb->retries = ISCSI_CMD_RETRIES; + + if (iscsi_aio_readv_acb(acb) != 0) { + if (acb->task) { + scsi_free_scsi_task(acb->task); + } + qemu_aio_release(acb); + return NULL; + } iscsi_set_events(iscsilun); - return &acb->common; } +static int +iscsi_aio_flush_acb(IscsiAIOCB *acb); static void iscsi_synccache10_cb(struct iscsi_context *iscsi, int status, @@ -407,7 +505,19 @@ iscsi_synccache10_cb(struct iscsi_context *iscsi, int status, } acb->status = 0; - if (status < 0) { + if (status != 0) { + if (status == SCSI_STATUS_CHECK_CONDITION + && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION + && acb->retries-- > 0) { + if (acb->task != NULL) { + scsi_free_scsi_task(acb->task); + acb->task = NULL; + } + if (iscsi_aio_flush_acb(acb) == 0) { + iscsi_set_events(acb->iscsilun); + return; + } + } error_report("Failed to sync10 data on iSCSI lun. %s", iscsi_get_error(iscsi)); acb->status = -EIO; @@ -416,28 +526,43 @@ iscsi_synccache10_cb(struct iscsi_context *iscsi, int status, iscsi_schedule_bh(acb); } -static BlockDriverAIOCB * -iscsi_aio_flush(BlockDriverState *bs, - BlockDriverCompletionFunc *cb, void *opaque) +static int +iscsi_aio_flush_acb(IscsiAIOCB *acb) { - IscsiLun *iscsilun = bs->opaque; - struct iscsi_context *iscsi = iscsilun->iscsi; - IscsiAIOCB *acb; + struct iscsi_context *iscsi = acb->iscsilun->iscsi; - acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); - - acb->iscsilun = iscsilun; acb->canceled = 0; acb->bh = NULL; acb->status = -EINPROGRESS; + acb->buf = NULL; - acb->task = iscsi_synchronizecache10_task(iscsi, iscsilun->lun, + acb->task = iscsi_synchronizecache10_task(iscsi, acb->iscsilun->lun, 0, 0, 0, 0, iscsi_synccache10_cb, acb); if (acb->task == NULL) { error_report("iSCSI: Failed to send synchronizecache10 command. %s", iscsi_get_error(iscsi)); + return -1; + } + + return 0; +} + +static BlockDriverAIOCB * +iscsi_aio_flush(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque) +{ + IscsiLun *iscsilun = bs->opaque; + + IscsiAIOCB *acb; + + acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); + + acb->iscsilun = iscsilun; + acb->retries = ISCSI_CMD_RETRIES; + + if (iscsi_aio_flush_acb(acb) != 0) { qemu_aio_release(acb); return NULL; } @@ -447,6 +572,8 @@ iscsi_aio_flush(BlockDriverState *bs, return &acb->common; } +static int iscsi_aio_discard_acb(IscsiAIOCB *acb); + static void iscsi_unmap_cb(struct iscsi_context *iscsi, int status, void *command_data, void *opaque) @@ -458,7 +585,19 @@ iscsi_unmap_cb(struct iscsi_context *iscsi, int status, } acb->status = 0; - if (status < 0) { + if (status != 0) { + if (status == SCSI_STATUS_CHECK_CONDITION + && acb->task->sense.key == SCSI_SENSE_UNIT_ATTENTION + && acb->retries-- > 0) { + if (acb->task != NULL) { + scsi_free_scsi_task(acb->task); + acb->task = NULL; + } + if (iscsi_aio_discard_acb(acb) == 0) { + iscsi_set_events(acb->iscsilun); + return; + } + } error_report("Failed to unmap data on iSCSI lun. %s", iscsi_get_error(iscsi)); acb->status = -EIO; @@ -467,33 +606,50 @@ iscsi_unmap_cb(struct iscsi_context *iscsi, int status, iscsi_schedule_bh(acb); } -static BlockDriverAIOCB * -iscsi_aio_discard(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, - BlockDriverCompletionFunc *cb, void *opaque) -{ - IscsiLun *iscsilun = bs->opaque; - struct iscsi_context *iscsi = iscsilun->iscsi; - IscsiAIOCB *acb; +static int iscsi_aio_discard_acb(IscsiAIOCB *acb) { + struct iscsi_context *iscsi = acb->iscsilun->iscsi; struct unmap_list list[1]; - acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); - - acb->iscsilun = iscsilun; acb->canceled = 0; acb->bh = NULL; acb->status = -EINPROGRESS; + acb->buf = NULL; - list[0].lba = sector_qemu2lun(sector_num, iscsilun); - list[0].num = nb_sectors * BDRV_SECTOR_SIZE / iscsilun->block_size; + list[0].lba = sector_qemu2lun(acb->sector_num, acb->iscsilun); + list[0].num = acb->nb_sectors * BDRV_SECTOR_SIZE / acb->iscsilun->block_size; - acb->task = iscsi_unmap_task(iscsi, iscsilun->lun, + acb->task = iscsi_unmap_task(iscsi, acb->iscsilun->lun, 0, 0, &list[0], 1, iscsi_unmap_cb, acb); if (acb->task == NULL) { error_report("iSCSI: Failed to send unmap command. %s", iscsi_get_error(iscsi)); + return -1; + } + + return 0; +} + +static BlockDriverAIOCB * +iscsi_aio_discard(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + IscsiLun *iscsilun = bs->opaque; + IscsiAIOCB *acb; + + acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque); + + acb->iscsilun = iscsilun; + acb->nb_sectors = nb_sectors; + acb->sector_num = sector_num; + acb->retries = ISCSI_CMD_RETRIES; + + if (iscsi_aio_discard_acb(acb) != 0) { + if (acb->task) { + scsi_free_scsi_task(acb->task); + } qemu_aio_release(acb); return NULL; } @@ -762,6 +918,91 @@ static char *parse_initiator_name(const char *target) } } +#if defined(LIBISCSI_FEATURE_NOP_COUNTER) +static void iscsi_nop_timed_event(void *opaque) +{ + IscsiLun *iscsilun = opaque; + + if (iscsi_get_nops_in_flight(iscsilun->iscsi) > MAX_NOP_FAILURES) { + error_report("iSCSI: NOP timeout. Reconnecting..."); + iscsi_reconnect(iscsilun->iscsi); + } + + if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) { + error_report("iSCSI: failed to sent NOP-Out. Disabling NOP messages."); + return; + } + + qemu_mod_timer(iscsilun->nop_timer, qemu_get_clock_ms(rt_clock) + NOP_INTERVAL); + iscsi_set_events(iscsilun); +} +#endif + +static int iscsi_readcapacity_sync(IscsiLun *iscsilun) +{ + struct scsi_task *task = NULL; + struct scsi_readcapacity10 *rc10 = NULL; + struct scsi_readcapacity16 *rc16 = NULL; + int ret = 0; + int retries = ISCSI_CMD_RETRIES; + +try_again: + switch (iscsilun->type) { + case TYPE_DISK: + task = iscsi_readcapacity16_sync(iscsilun->iscsi, iscsilun->lun); + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + if (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION + && task->sense.key == SCSI_SENSE_UNIT_ATTENTION + && retries-- > 0) { + scsi_free_scsi_task(task); + goto try_again; + } + error_report("iSCSI: failed to send readcapacity16 command."); + ret = -EINVAL; + goto out; + } + rc16 = scsi_datain_unmarshall(task); + if (rc16 == NULL) { + error_report("iSCSI: Failed to unmarshall readcapacity16 data."); + ret = -EINVAL; + goto out; + } + iscsilun->block_size = rc16->block_length; + iscsilun->num_blocks = rc16->returned_lba + 1; + break; + case TYPE_ROM: + task = iscsi_readcapacity10_sync(iscsilun->iscsi, iscsilun->lun, 0, 0); + if (task == NULL || task->status != SCSI_STATUS_GOOD) { + error_report("iSCSI: failed to send readcapacity10 command."); + ret = -EINVAL; + goto out; + } + rc10 = scsi_datain_unmarshall(task); + if (rc10 == NULL) { + error_report("iSCSI: Failed to unmarshall readcapacity10 data."); + ret = -EINVAL; + goto out; + } + iscsilun->block_size = rc10->block_size; + if (rc10->lba == 0) { + /* blank disk loaded */ + iscsilun->num_blocks = 0; + } else { + iscsilun->num_blocks = rc10->lba + 1; + } + break; + default: + break; + } + +out: + if (task) { + scsi_free_scsi_task(task); + } + + return ret; +} + /* * We support iscsi url's on the form * iscsi://[%@][:]// @@ -773,8 +1014,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) struct iscsi_url *iscsi_url = NULL; struct scsi_task *task = NULL; struct scsi_inquiry_standard *inq = NULL; - struct scsi_readcapacity10 *rc10 = NULL; - struct scsi_readcapacity16 *rc16 = NULL; char *initiator_name = NULL; int ret; @@ -864,50 +1103,9 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) iscsilun->type = inq->periperal_device_type; - scsi_free_scsi_task(task); - - switch (iscsilun->type) { - case TYPE_DISK: - task = iscsi_readcapacity16_sync(iscsi, iscsilun->lun); - if (task == NULL || task->status != SCSI_STATUS_GOOD) { - error_report("iSCSI: failed to send readcapacity16 command."); - ret = -EINVAL; - goto out; - } - rc16 = scsi_datain_unmarshall(task); - if (rc16 == NULL) { - error_report("iSCSI: Failed to unmarshall readcapacity16 data."); - ret = -EINVAL; - goto out; - } - iscsilun->block_size = rc16->block_length; - iscsilun->num_blocks = rc16->returned_lba + 1; - break; - case TYPE_ROM: - task = iscsi_readcapacity10_sync(iscsi, iscsilun->lun, 0, 0); - if (task == NULL || task->status != SCSI_STATUS_GOOD) { - error_report("iSCSI: failed to send readcapacity10 command."); - ret = -EINVAL; - goto out; - } - rc10 = scsi_datain_unmarshall(task); - if (rc10 == NULL) { - error_report("iSCSI: Failed to unmarshall readcapacity10 data."); - ret = -EINVAL; - goto out; - } - iscsilun->block_size = rc10->block_size; - if (rc10->lba == 0) { - /* blank disk loaded */ - iscsilun->num_blocks = 0; - } else { - iscsilun->num_blocks = rc10->lba + 1; - } - break; - default: - break; + if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) { + goto out; } - bs->total_sectors = iscsilun->num_blocks * iscsilun->block_size / BDRV_SECTOR_SIZE ; @@ -920,7 +1118,11 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) bs->sg = 1; } - ret = 0; +#if defined(LIBISCSI_FEATURE_NOP_COUNTER) + /* Set up a timer for sending out iSCSI NOPs */ + iscsilun->nop_timer = qemu_new_timer_ms(rt_clock, iscsi_nop_timed_event, iscsilun); + qemu_mod_timer(iscsilun->nop_timer, qemu_get_clock_ms(rt_clock) + NOP_INTERVAL); +#endif out: if (initiator_name != NULL) { @@ -947,16 +1149,94 @@ static void iscsi_close(BlockDriverState *bs) IscsiLun *iscsilun = bs->opaque; struct iscsi_context *iscsi = iscsilun->iscsi; + if (iscsilun->nop_timer) { + qemu_del_timer(iscsilun->nop_timer); + qemu_free_timer(iscsilun->nop_timer); + } qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL); iscsi_destroy_context(iscsi); memset(iscsilun, 0, sizeof(IscsiLun)); } +static int iscsi_truncate(BlockDriverState *bs, int64_t offset) +{ + IscsiLun *iscsilun = bs->opaque; + int ret = 0; + + if (iscsilun->type != TYPE_DISK) { + return -ENOTSUP; + } + + if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) { + return ret; + } + + if (offset > iscsi_getlength(bs)) { + return -EINVAL; + } + + return 0; +} + static int iscsi_has_zero_init(BlockDriverState *bs) { return 0; } +static int iscsi_create(const char *filename, QEMUOptionParameter *options) +{ + int ret = 0; + int64_t total_size = 0; + BlockDriverState bs; + IscsiLun *iscsilun = NULL; + + memset(&bs, 0, sizeof(BlockDriverState)); + + /* Read out options */ + while (options && options->name) { + if (!strcmp(options->name, "size")) { + total_size = options->value.n / BDRV_SECTOR_SIZE; + } + options++; + } + + bs.opaque = g_malloc0(sizeof(struct IscsiLun)); + iscsilun = bs.opaque; + + ret = iscsi_open(&bs, filename, 0); + if (ret != 0) { + goto out; + } + if (iscsilun->nop_timer) { + qemu_del_timer(iscsilun->nop_timer); + qemu_free_timer(iscsilun->nop_timer); + } + if (iscsilun->type != TYPE_DISK) { + ret = -ENODEV; + goto out; + } + if (bs.total_sectors < total_size) { + ret = -ENOSPC; + } + + ret = 0; +out: + if (iscsilun->iscsi != NULL) { + iscsi_destroy_context(iscsilun->iscsi); + } + g_free(bs.opaque); + return ret; +} + +static QEMUOptionParameter iscsi_create_options[] = { + { + .name = BLOCK_OPT_SIZE, + .type = OPT_SIZE, + .help = "Virtual disk size" + }, + { NULL } +}; + static BlockDriver bdrv_iscsi = { .format_name = "iscsi", .protocol_name = "iscsi", @@ -964,8 +1244,11 @@ static BlockDriver bdrv_iscsi = { .instance_size = sizeof(IscsiLun), .bdrv_file_open = iscsi_open, .bdrv_close = iscsi_close, + .bdrv_create = iscsi_create, + .create_options = iscsi_create_options, .bdrv_getlength = iscsi_getlength, + .bdrv_truncate = iscsi_truncate, .bdrv_aio_readv = iscsi_aio_readv, .bdrv_aio_writev = iscsi_aio_writev, @@ -980,9 +1263,36 @@ static BlockDriver bdrv_iscsi = { #endif }; +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 = "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", + }, + { /* end of list */ } + }, +}; + static void iscsi_block_init(void) { bdrv_register(&bdrv_iscsi); + qemu_add_opts(&qemu_iscsi_opts); } block_init(iscsi_block_init); diff --git a/block/mirror.c b/block/mirror.c index 8aeacbf12c..a62ad86c28 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -15,17 +15,17 @@ #include "block/blockjob.h" #include "block/block_int.h" #include "qemu/ratelimit.h" +#include "qemu/bitmap.h" -enum { - /* - * Size of data buffer for populating the image file. This should be large - * enough to process multiple clusters in a single call, so that populating - * contiguous regions of the image is efficient. - */ - BLOCK_SIZE = 512 * BDRV_SECTORS_PER_DIRTY_CHUNK, /* in bytes */ -}; +#define SLICE_TIME 100000000ULL /* ns */ +#define MAX_IN_FLIGHT 16 -#define SLICE_TIME 100000000ULL /* ns */ +/* The mirroring buffer is a list of granularity-sized chunks. + * Free chunks are organized in a list. + */ +typedef struct MirrorBuffer { + QSIMPLEQ_ENTRY(MirrorBuffer) next; +} MirrorBuffer; typedef struct MirrorBlockJob { BlockJob common; @@ -36,9 +36,26 @@ typedef struct MirrorBlockJob { bool synced; bool should_complete; int64_t sector_num; + int64_t granularity; + size_t buf_size; + unsigned long *cow_bitmap; + HBitmapIter hbi; uint8_t *buf; + QSIMPLEQ_HEAD(, MirrorBuffer) buf_free; + int buf_free_count; + + unsigned long *in_flight_bitmap; + int in_flight; + int ret; } MirrorBlockJob; +typedef struct MirrorOp { + MirrorBlockJob *s; + QEMUIOVector qiov; + int64_t sector_num; + int nb_sectors; +} MirrorOp; + static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, int error) { @@ -52,51 +69,234 @@ static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, } } -static int coroutine_fn mirror_iteration(MirrorBlockJob *s, - BlockErrorAction *p_action) +static void mirror_iteration_done(MirrorOp *op, int ret) +{ + MirrorBlockJob *s = op->s; + struct iovec *iov; + int64_t chunk_num; + int i, nb_chunks, sectors_per_chunk; + + trace_mirror_iteration_done(s, op->sector_num, op->nb_sectors, ret); + + s->in_flight--; + iov = op->qiov.iov; + for (i = 0; i < op->qiov.niov; i++) { + MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base; + QSIMPLEQ_INSERT_TAIL(&s->buf_free, buf, next); + s->buf_free_count++; + } + + sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; + chunk_num = op->sector_num / sectors_per_chunk; + nb_chunks = op->nb_sectors / sectors_per_chunk; + bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks); + if (s->cow_bitmap && ret >= 0) { + bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); + } + + g_slice_free(MirrorOp, op); + qemu_coroutine_enter(s->common.co, NULL); +} + +static void mirror_write_complete(void *opaque, int ret) +{ + MirrorOp *op = opaque; + MirrorBlockJob *s = op->s; + if (ret < 0) { + BlockDriverState *source = s->common.bs; + BlockErrorAction action; + + bdrv_set_dirty(source, op->sector_num, op->nb_sectors); + action = mirror_error_action(s, false, -ret); + if (action == BDRV_ACTION_REPORT && s->ret >= 0) { + s->ret = ret; + } + } + mirror_iteration_done(op, ret); +} + +static void mirror_read_complete(void *opaque, int ret) +{ + MirrorOp *op = opaque; + MirrorBlockJob *s = op->s; + if (ret < 0) { + BlockDriverState *source = s->common.bs; + BlockErrorAction action; + + bdrv_set_dirty(source, op->sector_num, op->nb_sectors); + action = mirror_error_action(s, true, -ret); + if (action == BDRV_ACTION_REPORT && s->ret >= 0) { + s->ret = ret; + } + + mirror_iteration_done(op, ret); + return; + } + bdrv_aio_writev(s->target, op->sector_num, &op->qiov, op->nb_sectors, + mirror_write_complete, op); +} + +static void coroutine_fn mirror_iteration(MirrorBlockJob *s) { BlockDriverState *source = s->common.bs; - BlockDriverState *target = s->target; - QEMUIOVector qiov; - int ret, nb_sectors; - int64_t end; - struct iovec iov; + int nb_sectors, sectors_per_chunk, nb_chunks; + int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector; + MirrorOp *op; + s->sector_num = hbitmap_iter_next(&s->hbi); + if (s->sector_num < 0) { + bdrv_dirty_iter_init(source, &s->hbi); + s->sector_num = hbitmap_iter_next(&s->hbi); + trace_mirror_restart_iter(s, bdrv_get_dirty_count(source)); + assert(s->sector_num >= 0); + } + + hbitmap_next_sector = s->sector_num; + sector_num = s->sector_num; + sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; end = s->common.len >> BDRV_SECTOR_BITS; - s->sector_num = bdrv_get_next_dirty(source, s->sector_num); - nb_sectors = MIN(BDRV_SECTORS_PER_DIRTY_CHUNK, end - s->sector_num); - bdrv_reset_dirty(source, s->sector_num, nb_sectors); + + /* Extend the QEMUIOVector to include all adjacent blocks that will + * be copied in this operation. + * + * We have to do this if we have no backing file yet in the destination, + * and the cluster size is very large. Then we need to do COW ourselves. + * The first time a cluster is copied, copy it entirely. Note that, + * because both the granularity and the cluster size are powers of two, + * the number of sectors to copy cannot exceed one cluster. + * + * We also want to extend the QEMUIOVector to include more adjacent + * dirty blocks if possible, to limit the number of I/O operations and + * run efficiently even with a small granularity. + */ + nb_chunks = 0; + nb_sectors = 0; + next_sector = sector_num; + next_chunk = sector_num / sectors_per_chunk; + + /* Wait for I/O to this cluster (from a previous iteration) to be done. */ + while (test_bit(next_chunk, s->in_flight_bitmap)) { + trace_mirror_yield_in_flight(s, sector_num, s->in_flight); + qemu_coroutine_yield(); + } + + do { + int added_sectors, added_chunks; + + if (!bdrv_get_dirty(source, next_sector) || + test_bit(next_chunk, s->in_flight_bitmap)) { + assert(nb_sectors > 0); + break; + } + + added_sectors = sectors_per_chunk; + if (s->cow_bitmap && !test_bit(next_chunk, s->cow_bitmap)) { + bdrv_round_to_clusters(s->target, + next_sector, added_sectors, + &next_sector, &added_sectors); + + /* On the first iteration, the rounding may make us copy + * sectors before the first dirty one. + */ + if (next_sector < sector_num) { + assert(nb_sectors == 0); + sector_num = next_sector; + next_chunk = next_sector / sectors_per_chunk; + } + } + + added_sectors = MIN(added_sectors, end - (sector_num + nb_sectors)); + added_chunks = (added_sectors + sectors_per_chunk - 1) / sectors_per_chunk; + + /* When doing COW, it may happen that there is not enough space for + * a full cluster. Wait if that is the case. + */ + while (nb_chunks == 0 && s->buf_free_count < added_chunks) { + trace_mirror_yield_buf_busy(s, nb_chunks, s->in_flight); + qemu_coroutine_yield(); + } + if (s->buf_free_count < nb_chunks + added_chunks) { + trace_mirror_break_buf_busy(s, nb_chunks, s->in_flight); + break; + } + + /* We have enough free space to copy these sectors. */ + bitmap_set(s->in_flight_bitmap, next_chunk, added_chunks); + + nb_sectors += added_sectors; + nb_chunks += added_chunks; + next_sector += added_sectors; + next_chunk += added_chunks; + } while (next_sector < end); + + /* Allocate a MirrorOp that is used as an AIO callback. */ + op = g_slice_new(MirrorOp); + op->s = s; + op->sector_num = sector_num; + op->nb_sectors = nb_sectors; + + /* Now make a QEMUIOVector taking enough granularity-sized chunks + * from s->buf_free. + */ + qemu_iovec_init(&op->qiov, nb_chunks); + next_sector = sector_num; + while (nb_chunks-- > 0) { + MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free); + QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next); + s->buf_free_count--; + qemu_iovec_add(&op->qiov, buf, s->granularity); + + /* Advance the HBitmapIter in parallel, so that we do not examine + * the same sector twice. + */ + if (next_sector > hbitmap_next_sector && bdrv_get_dirty(source, next_sector)) { + hbitmap_next_sector = hbitmap_iter_next(&s->hbi); + } + + next_sector += sectors_per_chunk; + } + + bdrv_reset_dirty(source, sector_num, nb_sectors); /* Copy the dirty cluster. */ - iov.iov_base = s->buf; - iov.iov_len = nb_sectors * 512; - qemu_iovec_init_external(&qiov, &iov, 1); + s->in_flight++; + trace_mirror_one_iteration(s, sector_num, nb_sectors); + bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors, + mirror_read_complete, op); +} - trace_mirror_one_iteration(s, s->sector_num, nb_sectors); - ret = bdrv_co_readv(source, s->sector_num, nb_sectors, &qiov); - if (ret < 0) { - *p_action = mirror_error_action(s, true, -ret); - goto fail; - } - ret = bdrv_co_writev(target, s->sector_num, nb_sectors, &qiov); - if (ret < 0) { - *p_action = mirror_error_action(s, false, -ret); - s->synced = false; - goto fail; - } - return 0; +static void mirror_free_init(MirrorBlockJob *s) +{ + int granularity = s->granularity; + size_t buf_size = s->buf_size; + uint8_t *buf = s->buf; -fail: - /* Try again later. */ - bdrv_set_dirty(source, s->sector_num, nb_sectors); - return ret; + assert(s->buf_free_count == 0); + QSIMPLEQ_INIT(&s->buf_free); + while (buf_size != 0) { + MirrorBuffer *cur = (MirrorBuffer *)buf; + QSIMPLEQ_INSERT_TAIL(&s->buf_free, cur, next); + s->buf_free_count++; + buf_size -= granularity; + buf += granularity; + } +} + +static void mirror_drain(MirrorBlockJob *s) +{ + while (s->in_flight > 0) { + qemu_coroutine_yield(); + } } static void coroutine_fn mirror_run(void *opaque) { MirrorBlockJob *s = opaque; BlockDriverState *bs = s->common.bs; - int64_t sector_num, end; + int64_t sector_num, end, sectors_per_chunk, length; + uint64_t last_pause_ns; + BlockDriverInfo bdi; + char backing_filename[1024]; int ret = 0; int n; @@ -105,20 +305,39 @@ static void coroutine_fn mirror_run(void *opaque) } s->common.len = bdrv_getlength(bs); - if (s->common.len < 0) { + if (s->common.len <= 0) { block_job_completed(&s->common, s->common.len); return; } + length = (bdrv_getlength(bs) + s->granularity - 1) / s->granularity; + s->in_flight_bitmap = bitmap_new(length); + + /* If we have no backing file yet in the destination, we cannot let + * the destination do COW. Instead, we copy sectors around the + * dirty data if needed. We need a bitmap to do that. + */ + bdrv_get_backing_filename(s->target, backing_filename, + sizeof(backing_filename)); + if (backing_filename[0] && !s->target->backing_hd) { + bdrv_get_info(s->target, &bdi); + if (s->granularity < bdi.cluster_size) { + s->buf_size = MAX(s->buf_size, bdi.cluster_size); + s->cow_bitmap = bitmap_new(length); + } + } + end = s->common.len >> BDRV_SECTOR_BITS; - s->buf = qemu_blockalign(bs, BLOCK_SIZE); + s->buf = qemu_blockalign(bs, s->buf_size); + sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; + mirror_free_init(s); if (s->mode != MIRROR_SYNC_MODE_NONE) { /* First part, loop on the sectors and initialize the dirty bitmap. */ BlockDriverState *base; base = s->mode == MIRROR_SYNC_MODE_FULL ? NULL : bs->backing_hd; for (sector_num = 0; sector_num < end; ) { - int64_t next = (sector_num | (BDRV_SECTORS_PER_DIRTY_CHUNK - 1)) + 1; + int64_t next = (sector_num | (sectors_per_chunk - 1)) + 1; ret = bdrv_co_is_allocated_above(bs, base, sector_num, next - sector_num, &n); @@ -136,24 +355,40 @@ static void coroutine_fn mirror_run(void *opaque) } } - s->sector_num = -1; + bdrv_dirty_iter_init(bs, &s->hbi); + last_pause_ns = qemu_get_clock_ns(rt_clock); for (;;) { uint64_t delay_ns; int64_t cnt; bool should_complete; + if (s->ret < 0) { + ret = s->ret; + goto immediate_exit; + } + cnt = bdrv_get_dirty_count(bs); - if (cnt != 0) { - BlockErrorAction action = BDRV_ACTION_REPORT; - ret = mirror_iteration(s, &action); - if (ret < 0 && action == BDRV_ACTION_REPORT) { - goto immediate_exit; + + /* Note that even when no rate limit is applied we need to yield + * periodically with no pending I/O so that qemu_aio_flush() returns. + * We do so every SLICE_TIME nanoseconds, or when there is an error, + * or when the source is clean, whichever comes first. + */ + if (qemu_get_clock_ns(rt_clock) - last_pause_ns < SLICE_TIME && + s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) { + if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 || + (cnt == 0 && s->in_flight > 0)) { + trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt); + qemu_coroutine_yield(); + continue; + } else if (cnt != 0) { + mirror_iteration(s); + continue; } - cnt = bdrv_get_dirty_count(bs); } should_complete = false; - if (cnt == 0) { + if (s->in_flight == 0 && cnt == 0) { trace_mirror_before_flush(s); ret = bdrv_flush(s->target); if (ret < 0) { @@ -196,23 +431,20 @@ static void coroutine_fn mirror_run(void *opaque) trace_mirror_before_sleep(s, cnt, s->synced); if (!s->synced) { /* Publish progress */ - s->common.offset = end * BDRV_SECTOR_SIZE - cnt * BLOCK_SIZE; + s->common.offset = (end - cnt) * BDRV_SECTOR_SIZE; if (s->common.speed) { - delay_ns = ratelimit_calculate_delay(&s->limit, BDRV_SECTORS_PER_DIRTY_CHUNK); + delay_ns = ratelimit_calculate_delay(&s->limit, sectors_per_chunk); } else { delay_ns = 0; } - /* Note that even when no rate limit is applied we need to yield - * with no pending I/O here so that bdrv_drain_all() returns. - */ block_job_sleep_ns(&s->common, rt_clock, delay_ns); if (block_job_is_cancelled(&s->common)) { break; } } else if (!should_complete) { - delay_ns = (cnt == 0 ? SLICE_TIME : 0); + delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0); block_job_sleep_ns(&s->common, rt_clock, delay_ns); } else if (cnt == 0) { /* The two disks are in sync. Exit and report successful @@ -222,11 +454,24 @@ static void coroutine_fn mirror_run(void *opaque) s->common.cancelled = false; break; } + last_pause_ns = qemu_get_clock_ns(rt_clock); } immediate_exit: - g_free(s->buf); - bdrv_set_dirty_tracking(bs, false); + if (s->in_flight > 0) { + /* We get here only if something went wrong. Either the job failed, + * or it was cancelled prematurely so that we do not guarantee that + * the target is a copy of the source. + */ + assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common))); + mirror_drain(s); + } + + assert(s->in_flight == 0); + qemu_vfree(s->buf); + g_free(s->cow_bitmap); + g_free(s->in_flight_bitmap); + bdrv_set_dirty_tracking(bs, 0); bdrv_iostatus_disable(s->target); if (s->should_complete && ret == 0) { if (bdrv_get_flags(s->target) != bdrv_get_flags(s->common.bs)) { @@ -288,14 +533,28 @@ static BlockJobType mirror_job_type = { }; void mirror_start(BlockDriverState *bs, BlockDriverState *target, - int64_t speed, MirrorSyncMode mode, - BlockdevOnError on_source_error, + int64_t speed, int64_t granularity, int64_t buf_size, + MirrorSyncMode mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, BlockDriverCompletionFunc *cb, void *opaque, Error **errp) { MirrorBlockJob *s; + if (granularity == 0) { + /* Choose the default granularity based on the target file's cluster + * size, clamped between 4k and 64k. */ + BlockDriverInfo bdi; + if (bdrv_get_info(target, &bdi) >= 0 && bdi.cluster_size != 0) { + granularity = MAX(4096, bdi.cluster_size); + granularity = MIN(65536, granularity); + } else { + granularity = 65536; + } + } + + assert ((granularity & (granularity - 1)) == 0); + if ((on_source_error == BLOCKDEV_ON_ERROR_STOP || on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) && !bdrv_iostatus_is_enabled(bs)) { @@ -312,7 +571,10 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, s->on_target_error = on_target_error; s->target = target; s->mode = mode; - bdrv_set_dirty_tracking(bs, true); + s->granularity = granularity; + s->buf_size = MAX(buf_size, granularity); + + bdrv_set_dirty_tracking(bs, granularity); bdrv_set_enable_write_cache(s->target, true); bdrv_set_on_error(s->target, on_target_error, on_target_error); bdrv_iostatus_enable(s->target); diff --git a/block/parallels.c b/block/parallels.c index 377375046f..18b3ac0b28 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -68,19 +68,23 @@ static int parallels_probe(const uint8_t *buf, int buf_size, const char *filenam return 0; } -static int parallels_open(BlockDriverState *bs, int flags) +static int parallels_open(BlockDriverState *bs, QDict *options, int flags) { BDRVParallelsState *s = bs->opaque; int i; struct parallels_header ph; + int ret; bs->read_only = 1; // no write support yet - if (bdrv_pread(bs->file, 0, &ph, sizeof(ph)) != sizeof(ph)) + ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph)); + if (ret < 0) { goto fail; + } if (memcmp(ph.magic, HEADER_MAGIC, 16) || - (le32_to_cpu(ph.version) != HEADER_VERSION)) { + (le32_to_cpu(ph.version) != HEADER_VERSION)) { + ret = -EMEDIUMTYPE; goto fail; } @@ -90,18 +94,21 @@ static int parallels_open(BlockDriverState *bs, int flags) s->catalog_size = le32_to_cpu(ph.catalog_entries); s->catalog_bitmap = g_malloc(s->catalog_size * 4); - if (bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4) != - s->catalog_size * 4) - goto fail; + + ret = bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4); + if (ret < 0) { + goto fail; + } + for (i = 0; i < s->catalog_size; i++) le32_to_cpus(&s->catalog_bitmap[i]); qemu_co_mutex_init(&s->lock); return 0; + fail: - if (s->catalog_bitmap) - g_free(s->catalog_bitmap); - return -1; + g_free(s->catalog_bitmap); + return ret; } static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) diff --git a/block/qcow.c b/block/qcow.c index 4276610afd..f6750a5bd3 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -92,7 +92,7 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int qcow_open(BlockDriverState *bs, int flags) +static int qcow_open(BlockDriverState *bs, QDict *options, int flags) { BDRVQcowState *s = bs->opaque; int len, i, shift, ret; @@ -112,7 +112,7 @@ static int qcow_open(BlockDriverState *bs, int flags) be64_to_cpus(&header.l1_table_offset); if (header.magic != QCOW_MAGIC) { - ret = -EINVAL; + ret = -EMEDIUMTYPE; goto fail; } if (header.version != QCOW_VERSION) { diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 56fccf9487..d72d063e6d 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -454,6 +454,9 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, *cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK; break; case QCOW2_CLUSTER_ZERO: + if (s->qcow_version < 3) { + return -EIO; + } c = count_contiguous_clusters(nb_clusters, s->cluster_size, &l2_table[l2_index], 0, QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO); @@ -668,7 +671,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) } /* Update L2 table. */ - if (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS) { + if (s->use_lazy_refcounts) { qcow2_mark_dirty(bs); } if (qcow2_need_accurate_refcounts(s)) { diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 6a95aa6c92..9bfb390519 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -201,7 +201,10 @@ static int alloc_refcount_block(BlockDriverState *bs, *refcount_block = NULL; /* We write to the refcount table, so we might depend on L2 tables */ - qcow2_cache_flush(bs, s->l2_table_cache); + ret = qcow2_cache_flush(bs, s->l2_table_cache); + if (ret < 0) { + return ret; + } /* Allocate the refcount block itself and mark it as used */ int64_t new_block = alloc_clusters_noref(bs, s->cluster_size); @@ -237,7 +240,10 @@ static int alloc_refcount_block(BlockDriverState *bs, goto fail_block; } - bdrv_flush(bs->file); + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + goto fail_block; + } /* Initialize the new refcount block only after updating its refcount, * update_refcount uses the refcount cache itself */ @@ -526,8 +532,6 @@ static int update_cluster_refcount(BlockDriverState *bs, return ret; } - bdrv_flush(bs->file); - return get_refcount(bs, cluster_index); } @@ -663,7 +667,11 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) } } - bdrv_flush(bs->file); + /* The cluster refcount was incremented, either by qcow2_alloc_clusters() + * or explicitly by update_cluster_refcount(). Refcount blocks must be + * flushed before the caller's L2 table updates. + */ + qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); return offset; } @@ -737,11 +745,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, * l1_table_offset when it is the current s->l1_table_offset! Be careful * when changing this! */ if (l1_table_offset != s->l1_table_offset) { - if (l1_size2 != 0) { - l1_table = g_malloc0(align_offset(l1_size2, 512)); - } else { - l1_table = NULL; - } + l1_table = g_malloc0(align_offset(l1_size2, 512)); l1_allocated = 1; if (bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2) != l1_size2) @@ -786,10 +790,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, if (ret < 0) { goto fail; } - - /* TODO Flushing once for the whole function should - * be enough */ - bdrv_flush(bs->file); } /* compressed clusters are never modified */ refcount = 2; @@ -845,7 +845,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, } } - ret = 0; + ret = bdrv_flush(bs); fail: if (l2_table) { qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); @@ -918,6 +918,12 @@ static void inc_refcounts(BlockDriverState *bs, } } +/* Flags for check_refcounts_l1() and check_refcounts_l2() */ +enum { + CHECK_OFLAG_COPIED = 0x1, /* check QCOW_OFLAG_COPIED matches refcount */ + CHECK_FRAG_INFO = 0x2, /* update BlockFragInfo counters */ +}; + /* * Increases the refcount in the given refcount table for the all clusters * referenced in the L2 table. While doing so, performs some checks on L2 @@ -928,10 +934,11 @@ static void inc_refcounts(BlockDriverState *bs, */ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, uint16_t *refcount_table, int refcount_table_size, int64_t l2_offset, - int check_copied) + int flags) { BDRVQcowState *s = bs->opaque; uint64_t *l2_table, l2_entry; + uint64_t next_contiguous_offset = 0; int i, l2_size, nb_csectors, refcount; /* Read L2 table from disk */ @@ -962,6 +969,18 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, l2_entry &= s->cluster_offset_mask; inc_refcounts(bs, res, refcount_table, refcount_table_size, l2_entry & ~511, nb_csectors * 512); + + if (flags & CHECK_FRAG_INFO) { + res->bfi.allocated_clusters++; + res->bfi.compressed_clusters++; + + /* Compressed clusters are fragmented by nature. Since they + * take up sub-sector space but we only have sector granularity + * I/O we need to re-read the same sectors even for adjacent + * compressed clusters. + */ + res->bfi.fragmented_clusters++; + } break; case QCOW2_CLUSTER_ZERO: @@ -975,7 +994,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, /* QCOW_OFLAG_COPIED must be set iff refcount == 1 */ uint64_t offset = l2_entry & L2E_OFFSET_MASK; - if (check_copied) { + if (flags & CHECK_OFLAG_COPIED) { refcount = get_refcount(bs, offset >> s->cluster_bits); if (refcount < 0) { fprintf(stderr, "Can't get refcount for offset %" @@ -989,6 +1008,15 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, } } + if (flags & CHECK_FRAG_INFO) { + res->bfi.allocated_clusters++; + if (next_contiguous_offset && + offset != next_contiguous_offset) { + res->bfi.fragmented_clusters++; + } + next_contiguous_offset = offset + s->cluster_size; + } + /* Mark cluster as used */ inc_refcounts(bs, res, refcount_table,refcount_table_size, offset, s->cluster_size); @@ -1032,7 +1060,7 @@ static int check_refcounts_l1(BlockDriverState *bs, uint16_t *refcount_table, int refcount_table_size, int64_t l1_table_offset, int l1_size, - int check_copied) + int flags) { BDRVQcowState *s = bs->opaque; uint64_t *l1_table, l2_offset, l1_size2; @@ -1061,7 +1089,7 @@ static int check_refcounts_l1(BlockDriverState *bs, l2_offset = l1_table[i]; if (l2_offset) { /* QCOW_OFLAG_COPIED must be set iff refcount == 1 */ - if (check_copied) { + if (flags & CHECK_OFLAG_COPIED) { refcount = get_refcount(bs, (l2_offset & ~QCOW_OFLAG_COPIED) >> s->cluster_bits); if (refcount < 0) { @@ -1090,7 +1118,7 @@ static int check_refcounts_l1(BlockDriverState *bs, /* Process and check L2 entries */ ret = check_refcounts_l2(bs, res, refcount_table, - refcount_table_size, l2_offset, check_copied); + refcount_table_size, l2_offset, flags); if (ret < 0) { goto fail; } @@ -1116,7 +1144,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) { BDRVQcowState *s = bs->opaque; - int64_t size, i; + int64_t size, i, highest_cluster; int nb_clusters, refcount1, refcount2; QCowSnapshot *sn; uint16_t *refcount_table; @@ -1124,6 +1152,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, size = bdrv_getlength(bs->file); nb_clusters = size_to_clusters(s, size); + res->bfi.total_clusters = nb_clusters; refcount_table = g_malloc0(nb_clusters * sizeof(uint16_t)); /* header */ @@ -1132,7 +1161,8 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, /* current L1 table */ ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, - s->l1_table_offset, s->l1_size, 1); + s->l1_table_offset, s->l1_size, + CHECK_OFLAG_COPIED | CHECK_FRAG_INFO); if (ret < 0) { goto fail; } @@ -1187,7 +1217,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, } /* compare ref counts */ - for(i = 0; i < nb_clusters; i++) { + for (i = 0, highest_cluster = 0; i < nb_clusters; i++) { refcount1 = get_refcount(bs, i); if (refcount1 < 0) { fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n", @@ -1197,6 +1227,11 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, } refcount2 = refcount_table[i]; + + if (refcount1 > 0 || refcount2 > 0) { + highest_cluster = i; + } + if (refcount1 != refcount2) { /* Check if we're allowed to fix the mismatch */ @@ -1231,6 +1266,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, } } + res->image_end_offset = (highest_cluster + 1) * s->cluster_size; ret = 0; fail: diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index eb8fcd5549..992a5c865c 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -180,11 +180,14 @@ static int qcow2_write_snapshots(BlockDriverState *bs) /* Allocate space for the new snapshot list */ snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size); - bdrv_flush(bs->file); offset = snapshots_offset; if (offset < 0) { return offset; } + ret = bdrv_flush(bs); + if (ret < 0) { + return ret; + } /* Write all snapshots to the new list */ for(i = 0; i < s->nb_snapshots; i++) { @@ -378,11 +381,6 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) goto fail; } - ret = bdrv_flush(bs); - if (ret < 0) { - goto fail; - } - /* Append the new snapshot to the snapshot list */ new_snapshot_list = g_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot)); if (s->snapshots) { diff --git a/block/qcow2.c b/block/qcow2.c index d603f98a9c..1f99866cf4 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -285,11 +285,26 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result, return ret; } -static int qcow2_open(BlockDriverState *bs, int flags) +static QemuOptsList qcow2_runtime_opts = { + .name = "qcow2", + .head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head), + .desc = { + { + .name = "lazy_refcounts", + .type = QEMU_OPT_BOOL, + .help = "Postpone refcount updates", + }, + { /* end of list */ } + }, +}; + +static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) { BDRVQcowState *s = bs->opaque; int len, i, ret = 0; QCowHeader header; + QemuOpts *opts; + Error *local_err = NULL; uint64_t ext_end; ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); @@ -311,7 +326,7 @@ static int qcow2_open(BlockDriverState *bs, int flags) be32_to_cpus(&header.nb_snapshots); if (header.magic != QCOW_MAGIC) { - ret = -EINVAL; + ret = -EMEDIUMTYPE; goto fail; } if (header.version < 2 || header.version > 3) { @@ -495,6 +510,28 @@ static int qcow2_open(BlockDriverState *bs, int flags) } } + /* Enable lazy_refcounts according to image and command line options */ + opts = qemu_opts_create_nofail(&qcow2_runtime_opts); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); + ret = -EINVAL; + goto fail; + } + + s->use_lazy_refcounts = qemu_opt_get_bool(opts, "lazy_refcounts", + (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)); + + qemu_opts_del(opts); + + if (s->use_lazy_refcounts && s->qcow_version < 3) { + qerror_report(ERROR_CLASS_GENERIC_ERROR, "Lazy refcounts require " + "a qcow2 image with at least qemu 1.1 compatibility level"); + ret = -EINVAL; + goto fail; + } + #ifdef DEBUG_ALLOC { BdrvCheckResult result = {0}; @@ -584,7 +621,7 @@ static int coroutine_fn qcow2_co_is_allocated(BlockDriverState *bs, *pnum = 0; } - return (cluster_offset != 0); + return (cluster_offset != 0) || (ret == QCOW2_CLUSTER_ZERO); } /* handle reading after the end of the backing file */ @@ -665,10 +702,6 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, break; case QCOW2_CLUSTER_ZERO: - if (s->qcow_version < 3) { - ret = -EIO; - goto fail; - } qemu_iovec_memset(&hd_qiov, 0, 0, 512 * cur_nr_sectors); break; @@ -759,7 +792,7 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, QEMUIOVector hd_qiov; uint64_t bytes_done = 0; uint8_t *cluster_data = NULL; - QCowL2Meta *l2meta; + QCowL2Meta *l2meta = NULL; trace_qcow2_writev_start_req(qemu_coroutine_self(), sector_num, remaining_sectors); @@ -912,7 +945,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs) qcow2_close(bs); memset(s, 0, sizeof(BDRVQcowState)); - qcow2_open(bs, flags); + qcow2_open(bs, NULL, flags); if (crypt_method) { s->crypt_method = crypt_method; @@ -1265,7 +1298,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, */ BlockDriver* drv = bdrv_find_format("qcow2"); assert(drv != NULL); - ret = bdrv_open(bs, filename, + ret = bdrv_open(bs, filename, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv); if (ret < 0) { goto out; diff --git a/block/qcow2.h b/block/qcow2.h index 718b52baca..103abdb2c0 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -173,6 +173,7 @@ typedef struct BDRVQcowState { int flags; int qcow_version; + bool use_lazy_refcounts; uint64_t incompatible_features; uint64_t compatible_features; diff --git a/block/qed.c b/block/qed.c index cf85d8f2b4..46e12b358b 100644 --- a/block/qed.c +++ b/block/qed.c @@ -373,7 +373,7 @@ static void bdrv_qed_rebind(BlockDriverState *bs) s->bs = bs; } -static int bdrv_qed_open(BlockDriverState *bs, int flags) +static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags) { BDRVQEDState *s = bs->opaque; QEDHeader le_header; @@ -390,7 +390,7 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags) qed_header_le_to_cpu(&le_header, &s->header); if (s->header.magic != QED_MAGIC) { - return -EINVAL; + return -EMEDIUMTYPE; } if (s->header.features & ~QED_FEATURE_MASK) { /* image uses unsupported feature bits */ @@ -1526,7 +1526,7 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs) bdrv_qed_close(bs); memset(s, 0, sizeof(BDRVQEDState)); - bdrv_qed_open(bs, bs->open_flags); + bdrv_qed_open(bs, NULL, bs->open_flags); } static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result, diff --git a/block/raw-aio.h b/block/raw-aio.h index e77f361148..c61f1595d9 100644 --- a/block/raw-aio.h +++ b/block/raw-aio.h @@ -20,11 +20,14 @@ #define QEMU_AIO_WRITE 0x0002 #define QEMU_AIO_IOCTL 0x0004 #define QEMU_AIO_FLUSH 0x0008 +#define QEMU_AIO_DISCARD 0x0010 #define QEMU_AIO_TYPE_MASK \ - (QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH) + (QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH| \ + QEMU_AIO_DISCARD) /* AIO flags */ #define QEMU_AIO_MISALIGNED 0x1000 +#define QEMU_AIO_BLKDEV 0x2000 /* linux-aio.c - Linux native implementation */ diff --git a/block/raw-posix.c b/block/raw-posix.c index 91159c7887..8a3cdbc1f3 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -59,6 +59,9 @@ #ifdef CONFIG_FIEMAP #include #endif +#ifdef CONFIG_FALLOCATE_PUNCH_HOLE +#include +#endif #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) #include #include @@ -138,6 +141,7 @@ typedef struct BDRVRawState { #ifdef CONFIG_XFS bool is_xfs : 1; #endif + bool has_discard : 1; } BDRVRawState; typedef struct BDRVRawReopenState { @@ -159,7 +163,7 @@ typedef struct RawPosixAIOData { void *aio_ioctl_buf; }; int aio_niov; - size_t aio_nbytes; + uint64_t aio_nbytes; #define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */ off_t aio_offset; int aio_type; @@ -289,6 +293,7 @@ static int raw_open_common(BlockDriverState *bs, const char *filename, } #endif + s->has_discard = 1; #ifdef CONFIG_XFS if (platform_test_xfs_fd(s->fd)) { s->is_xfs = 1; @@ -340,11 +345,20 @@ static int raw_reopen_prepare(BDRVReopenState *state, raw_s->fd = -1; - int fcntl_flags = O_APPEND | O_ASYNC | O_NONBLOCK; + int fcntl_flags = O_APPEND | O_NONBLOCK; #ifdef O_NOATIME fcntl_flags |= O_NOATIME; #endif +#ifdef O_ASYNC + /* Not all operating systems have O_ASYNC, and those that don't + * will not let us track the state into raw_s->open_flags (typically + * you achieve the same effect with an ioctl, for example I_SETSIG + * on Solaris). But we do not use O_ASYNC, so that's fine. + */ + assert((s->open_flags & O_ASYNC) == 0); +#endif + if ((raw_s->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) { /* dup the original fd */ /* TODO: use qemu fcntl wrapper */ @@ -430,22 +444,6 @@ static void raw_reopen_abort(BDRVReopenState *state) #endif */ -/* - * Check if all memory in this vector is sector aligned. - */ -static int qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) -{ - int i; - - for (i = 0; i < qiov->niov; i++) { - if ((uintptr_t) qiov->iov[i].iov_base % bs->buffer_alignment) { - return 0; - } - } - - return 1; -} - static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb) { int ret; @@ -455,15 +453,7 @@ static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb) return -errno; } - /* - * This looks weird, but the aio code only considers a request - * successful if it has written the full number of bytes. - * - * Now we overload aio_nbytes as aio_ioctl_cmd for the ioctl command, - * so in fact we return the ioctl command here to make posix_aio_read() - * happy.. - */ - return aiocb->aio_nbytes; + return 0; } static ssize_t handle_aiocb_flush(RawPosixAIOData *aiocb) @@ -642,6 +632,72 @@ static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb) return nbytes; } +#ifdef CONFIG_XFS +static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes) +{ + struct xfs_flock64 fl; + + memset(&fl, 0, sizeof(fl)); + fl.l_whence = SEEK_SET; + fl.l_start = offset; + fl.l_len = bytes; + + if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) { + DEBUG_BLOCK_PRINT("cannot punch hole (%s)\n", strerror(errno)); + return -errno; + } + + return 0; +} +#endif + +static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb) +{ + int ret = -EOPNOTSUPP; + BDRVRawState *s = aiocb->bs->opaque; + + if (s->has_discard == 0) { + return 0; + } + + if (aiocb->aio_type & QEMU_AIO_BLKDEV) { +#ifdef BLKDISCARD + do { + uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes }; + if (ioctl(aiocb->aio_fildes, BLKDISCARD, range) == 0) { + return 0; + } + } while (errno == EINTR); + + ret = -errno; +#endif + } else { +#ifdef CONFIG_XFS + if (s->is_xfs) { + return xfs_discard(s, aiocb->aio_offset, aiocb->aio_nbytes); + } +#endif + +#ifdef CONFIG_FALLOCATE_PUNCH_HOLE + do { + if (fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + aiocb->aio_offset, aiocb->aio_nbytes) == 0) { + return 0; + } + } while (errno == EINTR); + + ret = -errno; +#endif + } + + if (ret == -ENODEV || ret == -ENOSYS || ret == -EOPNOTSUPP || + ret == -ENOTTY) { + s->has_discard = 0; + ret = 0; + } + return ret; +} + static int aio_worker(void *arg) { RawPosixAIOData *aiocb = arg; @@ -676,6 +732,9 @@ static int aio_worker(void *arg) case QEMU_AIO_IOCTL: ret = handle_aiocb_ioctl(aiocb); break; + case QEMU_AIO_DISCARD: + ret = handle_aiocb_discard(aiocb); + break; default: fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); ret = -EINVAL; @@ -691,6 +750,7 @@ static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd, BlockDriverCompletionFunc *cb, void *opaque, int type) { RawPosixAIOData *acb = g_slice_new(RawPosixAIOData); + ThreadPool *pool; acb->bs = bs; acb->aio_type = type; @@ -704,7 +764,8 @@ static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd, acb->aio_offset = sector_num * 512; trace_paio_submit(acb, opaque, sector_num, nb_sectors, type); - return thread_pool_submit_aio(aio_worker, acb, cb, opaque); + pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); + return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); } static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs, @@ -722,7 +783,7 @@ static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs, * driver that it needs to copy the buffer. */ if ((bs->open_flags & BDRV_O_NOCACHE)) { - if (!qiov_is_aligned(bs, qiov)) { + if (!bdrv_qiov_is_aligned(bs, qiov)) { type |= QEMU_AIO_MISALIGNED; #ifdef CONFIG_LINUX_AIO } else if (s->use_aio) { @@ -1076,37 +1137,14 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs, } } -#ifdef CONFIG_XFS -static int xfs_discard(BDRVRawState *s, int64_t sector_num, int nb_sectors) +static coroutine_fn BlockDriverAIOCB *raw_aio_discard(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) { - struct xfs_flock64 fl; - - memset(&fl, 0, sizeof(fl)); - fl.l_whence = SEEK_SET; - fl.l_start = sector_num << 9; - fl.l_len = (int64_t)nb_sectors << 9; - - if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) { - DEBUG_BLOCK_PRINT("cannot punch hole (%s)\n", strerror(errno)); - return -errno; - } - - return 0; -} -#endif - -static coroutine_fn int raw_co_discard(BlockDriverState *bs, - int64_t sector_num, int nb_sectors) -{ -#ifdef CONFIG_XFS BDRVRawState *s = bs->opaque; - if (s->is_xfs) { - return xfs_discard(s, sector_num, nb_sectors); - } -#endif - - return 0; + return paio_submit(bs, s->fd, sector_num, NULL, nb_sectors, + cb, opaque, QEMU_AIO_DISCARD); } static QEMUOptionParameter raw_create_options[] = { @@ -1129,12 +1167,12 @@ static BlockDriver bdrv_file = { .bdrv_reopen_abort = raw_reopen_abort, .bdrv_close = raw_close, .bdrv_create = raw_create, - .bdrv_co_discard = raw_co_discard, .bdrv_co_is_allocated = raw_co_is_allocated, .bdrv_aio_readv = raw_aio_readv, .bdrv_aio_writev = raw_aio_writev, .bdrv_aio_flush = raw_aio_flush, + .bdrv_aio_discard = raw_aio_discard, .bdrv_truncate = raw_truncate, .bdrv_getlength = raw_getlength, @@ -1221,9 +1259,43 @@ static int hdev_probe_device(const char *filename) return 0; } +static int check_hdev_writable(BDRVRawState *s) +{ +#if defined(BLKROGET) + /* Linux block devices can be configured "read-only" using blockdev(8). + * This is independent of device node permissions and therefore open(2) + * with O_RDWR succeeds. Actual writes fail with EPERM. + * + * bdrv_open() is supposed to fail if the disk is read-only. Explicitly + * check for read-only block devices so that Linux block devices behave + * properly. + */ + struct stat st; + int readonly = 0; + + if (fstat(s->fd, &st)) { + return -errno; + } + + if (!S_ISBLK(st.st_mode)) { + return 0; + } + + if (ioctl(s->fd, BLKROGET, &readonly) < 0) { + return -errno; + } + + if (readonly) { + return -EACCES; + } +#endif /* defined(BLKROGET) */ + return 0; +} + static int hdev_open(BlockDriverState *bs, const char *filename, int flags) { BDRVRawState *s = bs->opaque; + int ret; #if defined(__APPLE__) && defined(__MACH__) if (strstart(filename, "/dev/cdrom", NULL)) { @@ -1264,7 +1336,20 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags) } #endif - return raw_open_common(bs, filename, flags, 0); + ret = raw_open_common(bs, filename, flags, 0); + if (ret < 0) { + return ret; + } + + if (flags & BDRV_O_RDWR) { + ret = check_hdev_writable(s); + if (ret < 0) { + raw_close(bs); + return ret; + } + } + + return ret; } #if defined(__linux__) @@ -1330,6 +1415,7 @@ static BlockDriverAIOCB *hdev_aio_ioctl(BlockDriverState *bs, { BDRVRawState *s = bs->opaque; RawPosixAIOData *acb; + ThreadPool *pool; if (fd_open(bs) < 0) return NULL; @@ -1341,7 +1427,8 @@ static BlockDriverAIOCB *hdev_aio_ioctl(BlockDriverState *bs, acb->aio_offset = 0; acb->aio_ioctl_buf = buf; acb->aio_ioctl_cmd = req; - return thread_pool_submit_aio(aio_worker, acb, cb, opaque); + pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); + return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); } #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) @@ -1363,6 +1450,19 @@ static int fd_open(BlockDriverState *bs) #endif /* !linux && !FreeBSD */ +static coroutine_fn BlockDriverAIOCB *hdev_aio_discard(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BDRVRawState *s = bs->opaque; + + if (fd_open(bs) < 0) { + return NULL; + } + return paio_submit(bs, s->fd, sector_num, NULL, nb_sectors, + cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV); +} + static int hdev_create(const char *filename, QEMUOptionParameter *options) { int fd; @@ -1415,6 +1515,7 @@ static BlockDriver bdrv_host_device = { .bdrv_aio_readv = raw_aio_readv, .bdrv_aio_writev = raw_aio_writev, .bdrv_aio_flush = raw_aio_flush, + .bdrv_aio_discard = hdev_aio_discard, .bdrv_truncate = raw_truncate, .bdrv_getlength = raw_getlength, @@ -1776,6 +1877,40 @@ static BlockDriver bdrv_host_cdrom = { }; #endif /* __FreeBSD__ */ +#ifdef CONFIG_LINUX_AIO +/** + * Return the file descriptor for Linux AIO + * + * This function is a layering violation and should be removed when it becomes + * possible to call the block layer outside the global mutex. It allows the + * caller to hijack the file descriptor so I/O can be performed outside the + * block layer. + */ +int raw_get_aio_fd(BlockDriverState *bs) +{ + BDRVRawState *s; + + if (!bs->drv) { + return -ENOMEDIUM; + } + + if (bs->drv == bdrv_find_format("raw")) { + bs = bs->file; + } + + /* raw-posix has several protocols so just check for raw_aio_readv */ + if (bs->drv->bdrv_aio_readv != raw_aio_readv) { + return -ENOTSUP; + } + + s = bs->opaque; + if (!s->use_aio) { + return -ENOTSUP; + } + return s->fd; +} +#endif /* CONFIG_LINUX_AIO */ + static void bdrv_file_init(void) { /* diff --git a/block/raw-win32.c b/block/raw-win32.c index f58334be08..18e0068b26 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -144,6 +144,7 @@ static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile, BlockDriverCompletionFunc *cb, void *opaque, int type) { RawWin32AIOData *acb = g_slice_new(RawWin32AIOData); + ThreadPool *pool; acb->bs = bs; acb->hfile = hfile; @@ -157,7 +158,8 @@ static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile, acb->aio_offset = sector_num * 512; trace_paio_submit(acb, opaque, sector_num, nb_sectors, type); - return thread_pool_submit_aio(aio_worker, acb, cb, opaque); + pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); + return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); } int qemu_ftruncate64(int fd, int64_t length) @@ -314,11 +316,11 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset) */ dwPtrLow = SetFilePointer(s->hfile, low, &high, FILE_BEGIN); if (dwPtrLow == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { - fprintf(stderr, "SetFilePointer error: %d\n", GetLastError()); + fprintf(stderr, "SetFilePointer error: %lu\n", GetLastError()); return -EIO; } if (SetEndOfFile(s->hfile) == 0) { - fprintf(stderr, "SetEndOfFile error: %d\n", GetLastError()); + fprintf(stderr, "SetEndOfFile error: %lu\n", GetLastError()); return -EIO; } return 0; diff --git a/block/raw.c b/block/raw.c index 75812db3c2..ce10422006 100644 --- a/block/raw.c +++ b/block/raw.c @@ -3,7 +3,7 @@ #include "block/block_int.h" #include "qemu/module.h" -static int raw_open(BlockDriverState *bs, int flags) +static int raw_open(BlockDriverState *bs, QDict *options, int flags) { bs->sg = bs->file->sg; return 0; diff --git a/block/sheepdog.c b/block/sheepdog.c index 13dc023fdb..4245328569 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -13,6 +13,7 @@ */ #include "qemu-common.h" +#include "qemu/uri.h" #include "qemu/error-report.h" #include "qemu/sockets.h" #include "block/block_int.h" @@ -21,7 +22,7 @@ #define SD_PROTO_VER 0x01 #define SD_DEFAULT_ADDR "localhost" -#define SD_DEFAULT_PORT "7000" +#define SD_DEFAULT_PORT 7000 #define SD_OP_CREATE_AND_WRITE_OBJ 0x01 #define SD_OP_READ_OBJ 0x02 @@ -36,7 +37,8 @@ #define SD_FLAG_CMD_WRITE 0x01 #define SD_FLAG_CMD_COW 0x02 -#define SD_FLAG_CMD_CACHE 0x04 +#define SD_FLAG_CMD_CACHE 0x04 /* Writeback mode for cache */ +#define SD_FLAG_CMD_DIRECT 0x08 /* Don't use cache */ #define SD_RES_SUCCESS 0x00 /* Success */ #define SD_RES_UNKNOWN 0x01 /* Unknown error */ @@ -144,7 +146,7 @@ typedef struct SheepdogVdiReq { uint32_t id; uint32_t data_length; uint64_t vdi_size; - uint32_t base_vdi_id; + uint32_t vdi_id; uint32_t copies; uint32_t snapid; uint32_t pad[3]; @@ -265,6 +267,7 @@ typedef struct AIOReq { enum AIOCBState { AIOCB_WRITE_UDATA, AIOCB_READ_UDATA, + AIOCB_FLUSH_CACHE, }; struct SheepdogAIOCB { @@ -293,12 +296,11 @@ typedef struct BDRVSheepdogState { char name[SD_MAX_VDI_LEN]; bool is_snapshot; - bool cache_enabled; + uint32_t cache_flags; - char *addr; - char *port; + char *host_spec; + bool is_unix; int fd; - int flush_fd; CoMutex lock; Coroutine *co_send; @@ -426,12 +428,11 @@ static const AIOCBInfo sd_aiocb_info = { }; static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov, - int64_t sector_num, int nb_sectors, - BlockDriverCompletionFunc *cb, void *opaque) + int64_t sector_num, int nb_sectors) { SheepdogAIOCB *acb; - acb = qemu_aio_get(&sd_aiocb_info, bs, cb, opaque); + acb = qemu_aio_get(&sd_aiocb_info, bs, NULL, NULL); acb->qiov = qiov; @@ -446,56 +447,31 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov, return acb; } -static int connect_to_sdog(const char *addr, const char *port) +static int connect_to_sdog(BDRVSheepdogState *s) { - char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; - int fd, ret; - struct addrinfo hints, *res, *res0; + int fd; + Error *err = NULL; - if (!addr) { - addr = SD_DEFAULT_ADDR; - port = SD_DEFAULT_PORT; - } + if (s->is_unix) { + fd = unix_connect(s->host_spec, &err); + } else { + fd = inet_connect(s->host_spec, &err); - memset(&hints, 0, sizeof(hints)); - hints.ai_socktype = SOCK_STREAM; - - ret = getaddrinfo(addr, port, &hints, &res0); - if (ret) { - error_report("unable to get address info %s, %s", - addr, strerror(errno)); - return -errno; - } - - for (res = res0; res; res = res->ai_next) { - ret = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), - sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); - if (ret) { - continue; - } - - fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (fd < 0) { - continue; - } - - reconnect: - ret = connect(fd, res->ai_addr, res->ai_addrlen); - if (ret < 0) { - if (errno == EINTR) { - goto reconnect; + if (err == NULL) { + int ret = socket_set_nodelay(fd); + if (ret < 0) { + error_report("%s", strerror(errno)); } - close(fd); - break; } - - dprintf("connected to %s:%s\n", addr, port); - goto success; } - fd = -errno; - error_report("failed connect to %s:%s", addr, port); -success: - freeaddrinfo(res0); + + if (err != NULL) { + qerror_report_err(err); + error_free(err); + } else { + socket_set_nonblock(fd); + } + return fd; } @@ -525,6 +501,13 @@ static void restart_co_req(void *opaque) qemu_coroutine_enter(co, NULL); } +static int have_co_req(void *opaque) +{ + /* this handler is set only when there is a pending request, so + * always returns 1. */ + return 1; +} + typedef struct SheepdogReqCo { int sockfd; SheepdogReq *hdr; @@ -547,15 +530,14 @@ static coroutine_fn void do_co_req(void *opaque) unsigned int *rlen = srco->rlen; co = qemu_coroutine_self(); - qemu_aio_set_fd_handler(sockfd, NULL, restart_co_req, NULL, co); + qemu_aio_set_fd_handler(sockfd, NULL, restart_co_req, have_co_req, co); - socket_set_block(sockfd); ret = send_co_req(sockfd, hdr, data, wlen); if (ret < 0) { goto out; } - qemu_aio_set_fd_handler(sockfd, restart_co_req, NULL, NULL, co); + qemu_aio_set_fd_handler(sockfd, restart_co_req, NULL, have_co_req, co); ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr)); if (ret < sizeof(*hdr)) { @@ -578,8 +560,9 @@ static coroutine_fn void do_co_req(void *opaque) } ret = 0; out: + /* there is at most one request for this sockfd, so it is safe to + * set each handler to NULL. */ qemu_aio_set_fd_handler(sockfd, NULL, NULL, NULL, NULL); - socket_set_nonblock(sockfd); srco->ret = ret; srco->finished = true; @@ -714,16 +697,17 @@ static void coroutine_fn aio_read_response(void *opaque) * and max_dirty_data_idx are changed to include updated * index between them. */ - s->inode.data_vdi_id[idx] = s->inode.vdi_id; - s->max_dirty_data_idx = MAX(idx, s->max_dirty_data_idx); - s->min_dirty_data_idx = MIN(idx, s->min_dirty_data_idx); - + if (rsp.result == SD_RES_SUCCESS) { + s->inode.data_vdi_id[idx] = s->inode.vdi_id; + s->max_dirty_data_idx = MAX(idx, s->max_dirty_data_idx); + s->min_dirty_data_idx = MIN(idx, s->min_dirty_data_idx); + } /* * Some requests may be blocked because simultaneous * create requests are not allowed, so we search the * pending requests here. */ - send_pending_req(s, vid_to_data_oid(s->inode.vdi_id, idx)); + send_pending_req(s, aio_req->oid); } break; case AIOCB_READ_UDATA: @@ -734,6 +718,13 @@ static void coroutine_fn aio_read_response(void *opaque) goto out; } break; + case AIOCB_FLUSH_CACHE: + if (rsp.result == SD_RES_INVALID_PARMS) { + dprintf("disable cache since the server doesn't support it\n"); + s->cache_flags = SD_FLAG_CMD_DIRECT; + rsp.result = SD_RES_SUCCESS; + } + break; } if (rsp.result != SD_RES_SUCCESS) { @@ -779,15 +770,6 @@ static int aio_flush_request(void *opaque) !QLIST_EMPTY(&s->pending_aio_head); } -static int set_nodelay(int fd) -{ - int ret, opt; - - opt = 1; - ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, sizeof(opt)); - return ret; -} - /* * Return a socket discriptor to read/write objects. * @@ -796,29 +778,86 @@ static int set_nodelay(int fd) */ static int get_sheep_fd(BDRVSheepdogState *s) { - int ret, fd; + int fd; - fd = connect_to_sdog(s->addr, s->port); + fd = connect_to_sdog(s); if (fd < 0) { - error_report("%s", strerror(errno)); return fd; } - socket_set_nonblock(fd); - - ret = set_nodelay(fd); - if (ret) { - error_report("%s", strerror(errno)); - closesocket(fd); - return -errno; - } - qemu_aio_set_fd_handler(fd, co_read_response, NULL, aio_flush_request, s); return fd; } +static int sd_parse_uri(BDRVSheepdogState *s, const char *filename, + char *vdi, uint32_t *snapid, char *tag) +{ + URI *uri; + QueryParams *qp = NULL; + int ret = 0; + + uri = uri_parse(filename); + if (!uri) { + return -EINVAL; + } + + /* transport */ + if (!strcmp(uri->scheme, "sheepdog")) { + s->is_unix = false; + } else if (!strcmp(uri->scheme, "sheepdog+tcp")) { + s->is_unix = false; + } else if (!strcmp(uri->scheme, "sheepdog+unix")) { + s->is_unix = true; + } else { + ret = -EINVAL; + goto out; + } + + if (uri->path == NULL || !strcmp(uri->path, "/")) { + ret = -EINVAL; + goto out; + } + pstrcpy(vdi, SD_MAX_VDI_LEN, uri->path + 1); + + qp = query_params_parse(uri->query); + if (qp->n > 1 || (s->is_unix && !qp->n) || (!s->is_unix && qp->n)) { + ret = -EINVAL; + goto out; + } + + if (s->is_unix) { + /* sheepdog+unix:///vdiname?socket=path */ + if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) { + ret = -EINVAL; + goto out; + } + s->host_spec = g_strdup(qp->p[0].value); + } else { + /* sheepdog[+tcp]://[host:port]/vdiname */ + s->host_spec = g_strdup_printf("%s:%d", uri->server ?: SD_DEFAULT_ADDR, + uri->port ?: SD_DEFAULT_PORT); + } + + /* snapshot tag */ + if (uri->fragment) { + *snapid = strtoul(uri->fragment, NULL, 10); + if (*snapid == 0) { + pstrcpy(tag, SD_MAX_VDI_TAG_LEN, uri->fragment); + } + } else { + *snapid = CURRENT_VDI_ID; /* search current vdi */ + } + +out: + if (qp) { + query_params_free(qp); + } + uri_free(uri); + return ret; +} + /* - * Parse a filename + * Parse a filename (old syntax) * * filename must be one of the following formats: * 1. [vdiname] @@ -837,9 +876,11 @@ static int get_sheep_fd(BDRVSheepdogState *s) static int parse_vdiname(BDRVSheepdogState *s, const char *filename, char *vdi, uint32_t *snapid, char *tag) { - char *p, *q; - int nr_sep; + char *p, *q, *uri; + const char *host_spec, *vdi_spec; + int nr_sep, ret; + strstart(filename, "sheepdog:", (const char **)&filename); p = q = g_strdup(filename); /* count the number of separators */ @@ -852,38 +893,32 @@ static int parse_vdiname(BDRVSheepdogState *s, const char *filename, } p = q; - /* use the first two tokens as hostname and port number. */ + /* use the first two tokens as host_spec. */ if (nr_sep >= 2) { - s->addr = p; + host_spec = p; p = strchr(p, ':'); - *p++ = '\0'; - - s->port = p; + p++; p = strchr(p, ':'); *p++ = '\0'; } else { - s->addr = NULL; - s->port = 0; + host_spec = ""; } - pstrcpy(vdi, SD_MAX_VDI_LEN, p); + vdi_spec = p; - p = strchr(vdi, ':'); + p = strchr(vdi_spec, ':'); if (p) { - *p++ = '\0'; - *snapid = strtoul(p, NULL, 10); - if (*snapid == 0) { - pstrcpy(tag, SD_MAX_VDI_TAG_LEN, p); - } - } else { - *snapid = CURRENT_VDI_ID; /* search current vdi */ + *p++ = '#'; } - if (s->addr == NULL) { - g_free(q); - } + uri = g_strdup_printf("sheepdog://%s/%s", host_spec, vdi_spec); - return 0; + ret = sd_parse_uri(s, uri, vdi, snapid, tag); + + g_free(q); + g_free(uri); + + return ret; } static int find_vdi_name(BDRVSheepdogState *s, char *filename, uint32_t snapid, @@ -895,7 +930,7 @@ static int find_vdi_name(BDRVSheepdogState *s, char *filename, uint32_t snapid, unsigned int wlen, rlen = 0; char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN]; - fd = connect_to_sdog(s->addr, s->port); + fd = connect_to_sdog(s); if (fd < 0) { return fd; } @@ -948,7 +983,7 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, { int nr_copies = s->inode.nr_copies; SheepdogObjReq hdr; - unsigned int wlen; + unsigned int wlen = 0; int ret; uint64_t oid = aio_req->oid; unsigned int datalen = aio_req->data_len; @@ -962,22 +997,27 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, memset(&hdr, 0, sizeof(hdr)); - if (aiocb_type == AIOCB_READ_UDATA) { - wlen = 0; + switch (aiocb_type) { + case AIOCB_FLUSH_CACHE: + hdr.opcode = SD_OP_FLUSH_VDI; + break; + case AIOCB_READ_UDATA: hdr.opcode = SD_OP_READ_OBJ; hdr.flags = flags; - } else if (create) { + break; + case AIOCB_WRITE_UDATA: + if (create) { + hdr.opcode = SD_OP_CREATE_AND_WRITE_OBJ; + } else { + hdr.opcode = SD_OP_WRITE_OBJ; + } wlen = datalen; - hdr.opcode = SD_OP_CREATE_AND_WRITE_OBJ; - hdr.flags = SD_FLAG_CMD_WRITE | flags; - } else { - wlen = datalen; - hdr.opcode = SD_OP_WRITE_OBJ; hdr.flags = SD_FLAG_CMD_WRITE | flags; + break; } - if (s->cache_enabled) { - hdr.flags |= SD_FLAG_CMD_CACHE; + if (s->cache_flags) { + hdr.flags |= s->cache_flags; } hdr.oid = oid; @@ -1022,7 +1062,7 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, static int read_write_object(int fd, char *buf, uint64_t oid, int copies, unsigned int datalen, uint64_t offset, - bool write, bool create, bool cache) + bool write, bool create, uint32_t cache_flags) { SheepdogObjReq hdr; SheepdogObjRsp *rsp = (SheepdogObjRsp *)&hdr; @@ -1046,9 +1086,7 @@ static int read_write_object(int fd, char *buf, uint64_t oid, int copies, hdr.opcode = SD_OP_READ_OBJ; } - if (cache) { - hdr.flags |= SD_FLAG_CMD_CACHE; - } + hdr.flags |= cache_flags; hdr.oid = oid; hdr.data_length = datalen; @@ -1071,18 +1109,19 @@ static int read_write_object(int fd, char *buf, uint64_t oid, int copies, } static int read_object(int fd, char *buf, uint64_t oid, int copies, - unsigned int datalen, uint64_t offset, bool cache) + unsigned int datalen, uint64_t offset, + uint32_t cache_flags) { return read_write_object(fd, buf, oid, copies, datalen, offset, false, - false, cache); + false, cache_flags); } static int write_object(int fd, char *buf, uint64_t oid, int copies, unsigned int datalen, uint64_t offset, bool create, - bool cache) + uint32_t cache_flags) { return read_write_object(fd, buf, oid, copies, datalen, offset, true, - create, cache); + create, cache_flags); } static int sd_open(BlockDriverState *bs, const char *filename, int flags) @@ -1094,16 +1133,19 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags) uint32_t snapid; char *buf = NULL; - strstart(filename, "sheepdog:", (const char **)&filename); - QLIST_INIT(&s->inflight_aio_head); QLIST_INIT(&s->pending_aio_head); s->fd = -1; memset(vdi, 0, sizeof(vdi)); memset(tag, 0, sizeof(tag)); - if (parse_vdiname(s, filename, vdi, &snapid, tag) < 0) { - ret = -EINVAL; + + if (strstr(filename, "://")) { + ret = sd_parse_uri(s, filename, vdi, &snapid, tag); + } else { + ret = parse_vdiname(s, filename, vdi, &snapid, tag); + } + if (ret < 0) { goto out; } s->fd = get_sheep_fd(s); @@ -1117,12 +1159,13 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags) goto out; } - s->cache_enabled = true; - s->flush_fd = connect_to_sdog(s->addr, s->port); - if (s->flush_fd < 0) { - error_report("failed to connect"); - ret = s->flush_fd; - goto out; + /* + * QEMU block layer emulates writethrough cache as 'writeback + flush', so + * we always set SD_FLAG_CMD_CACHE (writeback cache) as default. + */ + s->cache_flags = SD_FLAG_CMD_CACHE; + if (flags & BDRV_O_NOCACHE) { + s->cache_flags = SD_FLAG_CMD_DIRECT; } if (snapid || tag[0] != '\0') { @@ -1130,16 +1173,15 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags) s->is_snapshot = true; } - fd = connect_to_sdog(s->addr, s->port); + fd = connect_to_sdog(s); if (fd < 0) { - error_report("failed to connect"); ret = fd; goto out; } buf = g_malloc(SD_INODE_SIZE); ret = read_object(fd, buf, vid_to_vdi_oid(vid), 0, SD_INODE_SIZE, 0, - s->cache_enabled); + s->cache_flags); closesocket(fd); @@ -1165,9 +1207,8 @@ out: return ret; } -static int do_sd_create(char *filename, int64_t vdi_size, - uint32_t base_vid, uint32_t *vdi_id, int snapshot, - const char *addr, const char *port) +static int do_sd_create(BDRVSheepdogState *s, char *filename, int64_t vdi_size, + uint32_t base_vid, uint32_t *vdi_id, int snapshot) { SheepdogVdiReq hdr; SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr; @@ -1175,7 +1216,7 @@ static int do_sd_create(char *filename, int64_t vdi_size, unsigned int wlen, rlen = 0; char buf[SD_MAX_VDI_LEN]; - fd = connect_to_sdog(addr, port); + fd = connect_to_sdog(s); if (fd < 0) { return fd; } @@ -1188,7 +1229,7 @@ static int do_sd_create(char *filename, int64_t vdi_size, memset(&hdr, 0, sizeof(hdr)); hdr.opcode = SD_OP_NEW_VDI; - hdr.base_vdi_id = base_vid; + hdr.vdi_id = base_vid; wlen = SD_MAX_VDI_LEN; @@ -1271,17 +1312,17 @@ static int sd_create(const char *filename, QEMUOptionParameter *options) char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN]; uint32_t snapid; bool prealloc = false; - const char *vdiname; s = g_malloc0(sizeof(BDRVSheepdogState)); - strstart(filename, "sheepdog:", &vdiname); - memset(vdi, 0, sizeof(vdi)); memset(tag, 0, sizeof(tag)); - if (parse_vdiname(s, vdiname, vdi, &snapid, tag) < 0) { - error_report("invalid filename"); - ret = -EINVAL; + if (strstr(filename, "://")) { + ret = sd_parse_uri(s, filename, vdi, &snapid, tag); + } else { + ret = parse_vdiname(s, filename, vdi, &snapid, tag); + } + if (ret < 0) { goto out; } @@ -1342,7 +1383,7 @@ static int sd_create(const char *filename, QEMUOptionParameter *options) bdrv_delete(bs); } - ret = do_sd_create(vdi, vdi_size, base_vid, &vid, 0, s->addr, s->port); + ret = do_sd_create(s, vdi, vdi_size, base_vid, &vid, 0); if (!prealloc || ret) { goto out; } @@ -1363,7 +1404,7 @@ static void sd_close(BlockDriverState *bs) dprintf("%s\n", s->name); - fd = connect_to_sdog(s->addr, s->port); + fd = connect_to_sdog(s); if (fd < 0) { return; } @@ -1371,6 +1412,7 @@ static void sd_close(BlockDriverState *bs) memset(&hdr, 0, sizeof(hdr)); hdr.opcode = SD_OP_RELEASE_VDI; + hdr.vdi_id = s->inode.vdi_id; wlen = strlen(s->name) + 1; hdr.data_length = wlen; hdr.flags = SD_FLAG_CMD_WRITE; @@ -1386,10 +1428,7 @@ static void sd_close(BlockDriverState *bs) qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL); closesocket(s->fd); - if (s->cache_enabled) { - closesocket(s->flush_fd); - } - g_free(s->addr); + g_free(s->host_spec); } static int64_t sd_getlength(BlockDriverState *bs) @@ -1413,7 +1452,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset) return -EINVAL; } - fd = connect_to_sdog(s->addr, s->port); + fd = connect_to_sdog(s); if (fd < 0) { return fd; } @@ -1422,7 +1461,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset) datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); s->inode.vdi_size = offset; ret = write_object(fd, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id), - s->inode.nr_copies, datalen, 0, false, s->cache_enabled); + s->inode.nr_copies, datalen, 0, false, s->cache_flags); close(fd); if (ret < 0) { @@ -1489,23 +1528,21 @@ static int sd_create_branch(BDRVSheepdogState *s) buf = g_malloc(SD_INODE_SIZE); - ret = do_sd_create(s->name, s->inode.vdi_size, s->inode.vdi_id, &vid, 1, - s->addr, s->port); + ret = do_sd_create(s, s->name, s->inode.vdi_size, s->inode.vdi_id, &vid, 1); if (ret) { goto out; } dprintf("%" PRIx32 " is created.\n", vid); - fd = connect_to_sdog(s->addr, s->port); + fd = connect_to_sdog(s); if (fd < 0) { - error_report("failed to connect"); ret = fd; goto out; } ret = read_object(fd, buf, vid_to_vdi_oid(vid), s->inode.nr_copies, - SD_INODE_SIZE, 0, s->cache_enabled); + SD_INODE_SIZE, 0, s->cache_flags); closesocket(fd); @@ -1661,7 +1698,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, bs->total_sectors = sector_num + nb_sectors; } - acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors, NULL, NULL); + acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors); acb->aio_done_func = sd_write_done; acb->aiocb_type = AIOCB_WRITE_UDATA; @@ -1682,7 +1719,7 @@ static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num, SheepdogAIOCB *acb; int ret; - acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors, NULL, NULL); + acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors); acb->aiocb_type = AIOCB_READ_UDATA; acb->aio_done_func = sd_finish_aiocb; @@ -1700,39 +1737,31 @@ static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num, static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs) { BDRVSheepdogState *s = bs->opaque; - SheepdogObjReq hdr = { 0 }; - SheepdogObjRsp *rsp = (SheepdogObjRsp *)&hdr; - SheepdogInode *inode = &s->inode; + SheepdogAIOCB *acb; + AIOReq *aio_req; int ret; - unsigned int wlen = 0, rlen = 0; - if (!s->cache_enabled) { + if (s->cache_flags != SD_FLAG_CMD_CACHE) { return 0; } - hdr.opcode = SD_OP_FLUSH_VDI; - hdr.oid = vid_to_vdi_oid(inode->vdi_id); + acb = sd_aio_setup(bs, NULL, 0, 0); + acb->aiocb_type = AIOCB_FLUSH_CACHE; + acb->aio_done_func = sd_finish_aiocb; - ret = do_req(s->flush_fd, (SheepdogReq *)&hdr, NULL, &wlen, &rlen); - if (ret) { - error_report("failed to send a request to the sheep"); + aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id), + 0, 0, 0, 0, 0); + QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); + ret = add_aio_request(s, aio_req, NULL, 0, false, acb->aiocb_type); + if (ret < 0) { + error_report("add_aio_request is failed"); + free_aio_req(s, aio_req); + qemu_aio_release(acb); return ret; } - if (rsp->result == SD_RES_INVALID_PARMS) { - dprintf("disable write cache since the server doesn't support it\n"); - - s->cache_enabled = false; - closesocket(s->flush_fd); - return 0; - } - - if (rsp->result != SD_RES_SUCCESS) { - error_report("%s", sd_strerror(rsp->result)); - return -EIO; - } - - return 0; + qemu_coroutine_yield(); + return acb->ret; } static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) @@ -1766,21 +1795,21 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); /* refresh inode. */ - fd = connect_to_sdog(s->addr, s->port); + fd = connect_to_sdog(s); if (fd < 0) { ret = fd; goto cleanup; } ret = write_object(fd, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id), - s->inode.nr_copies, datalen, 0, false, s->cache_enabled); + s->inode.nr_copies, datalen, 0, false, s->cache_flags); if (ret < 0) { error_report("failed to write snapshot's inode."); goto cleanup; } - ret = do_sd_create(s->name, s->inode.vdi_size, s->inode.vdi_id, &new_vid, 1, - s->addr, s->port); + ret = do_sd_create(s, s->name, s->inode.vdi_size, s->inode.vdi_id, &new_vid, + 1); if (ret < 0) { error_report("failed to create inode for snapshot. %s", strerror(errno)); @@ -1790,7 +1819,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) inode = (SheepdogInode *)g_malloc(datalen); ret = read_object(fd, (char *)inode, vid_to_vdi_oid(new_vid), - s->inode.nr_copies, datalen, 0, s->cache_enabled); + s->inode.nr_copies, datalen, 0, s->cache_flags); if (ret < 0) { error_report("failed to read new inode info. %s", strerror(errno)); @@ -1835,16 +1864,15 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) goto out; } - fd = connect_to_sdog(s->addr, s->port); + fd = connect_to_sdog(s); if (fd < 0) { - error_report("failed to connect"); ret = fd; goto out; } buf = g_malloc(SD_INODE_SIZE); ret = read_object(fd, buf, vid_to_vdi_oid(vid), s->inode.nr_copies, - SD_INODE_SIZE, 0, s->cache_enabled); + SD_INODE_SIZE, 0, s->cache_flags); closesocket(fd); @@ -1899,7 +1927,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) vdi_inuse = g_malloc(max); - fd = connect_to_sdog(s->addr, s->port); + fd = connect_to_sdog(s); if (fd < 0) { ret = fd; goto out; @@ -1926,9 +1954,8 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) hval = fnv_64a_buf(s->name, strlen(s->name), FNV1A_64_INIT); start_nr = hval & (SD_NR_VDIS - 1); - fd = connect_to_sdog(s->addr, s->port); + fd = connect_to_sdog(s); if (fd < 0) { - error_report("failed to connect"); ret = fd; goto out; } @@ -1941,7 +1968,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) /* we don't need to read entire object */ ret = read_object(fd, (char *)&inode, vid_to_vdi_oid(vid), 0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0, - s->cache_enabled); + s->cache_flags); if (ret) { continue; @@ -1985,7 +2012,7 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data, uint32_t vdi_index; uint64_t offset; - fd = connect_to_sdog(s->addr, s->port); + fd = connect_to_sdog(s); if (fd < 0) { return fd; } @@ -2002,11 +2029,11 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data, if (load) { ret = read_object(fd, (char *)data, vmstate_oid, s->inode.nr_copies, data_len, offset, - s->cache_enabled); + s->cache_flags); } else { ret = write_object(fd, (char *)data, vmstate_oid, s->inode.nr_copies, data_len, offset, create, - s->cache_enabled); + s->cache_flags); } if (ret < 0) { @@ -2060,7 +2087,7 @@ static QEMUOptionParameter sd_create_options[] = { { NULL } }; -BlockDriver bdrv_sheepdog = { +static BlockDriver bdrv_sheepdog = { .format_name = "sheepdog", .protocol_name = "sheepdog", .instance_size = sizeof(BDRVSheepdogState), @@ -2085,8 +2112,60 @@ BlockDriver bdrv_sheepdog = { .create_options = sd_create_options, }; +static BlockDriver bdrv_sheepdog_tcp = { + .format_name = "sheepdog", + .protocol_name = "sheepdog+tcp", + .instance_size = sizeof(BDRVSheepdogState), + .bdrv_file_open = sd_open, + .bdrv_close = sd_close, + .bdrv_create = sd_create, + .bdrv_getlength = sd_getlength, + .bdrv_truncate = sd_truncate, + + .bdrv_co_readv = sd_co_readv, + .bdrv_co_writev = sd_co_writev, + .bdrv_co_flush_to_disk = sd_co_flush_to_disk, + + .bdrv_snapshot_create = sd_snapshot_create, + .bdrv_snapshot_goto = sd_snapshot_goto, + .bdrv_snapshot_delete = sd_snapshot_delete, + .bdrv_snapshot_list = sd_snapshot_list, + + .bdrv_save_vmstate = sd_save_vmstate, + .bdrv_load_vmstate = sd_load_vmstate, + + .create_options = sd_create_options, +}; + +static BlockDriver bdrv_sheepdog_unix = { + .format_name = "sheepdog", + .protocol_name = "sheepdog+unix", + .instance_size = sizeof(BDRVSheepdogState), + .bdrv_file_open = sd_open, + .bdrv_close = sd_close, + .bdrv_create = sd_create, + .bdrv_getlength = sd_getlength, + .bdrv_truncate = sd_truncate, + + .bdrv_co_readv = sd_co_readv, + .bdrv_co_writev = sd_co_writev, + .bdrv_co_flush_to_disk = sd_co_flush_to_disk, + + .bdrv_snapshot_create = sd_snapshot_create, + .bdrv_snapshot_goto = sd_snapshot_goto, + .bdrv_snapshot_delete = sd_snapshot_delete, + .bdrv_snapshot_list = sd_snapshot_list, + + .bdrv_save_vmstate = sd_save_vmstate, + .bdrv_load_vmstate = sd_load_vmstate, + + .create_options = sd_create_options, +}; + static void bdrv_sheepdog_init(void) { bdrv_register(&bdrv_sheepdog); + bdrv_register(&bdrv_sheepdog_tcp); + bdrv_register(&bdrv_sheepdog_unix); } block_init(bdrv_sheepdog_init); diff --git a/block/vdi.c b/block/vdi.c index 021abaa227..2662d89af6 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -246,7 +246,7 @@ static void vdi_header_print(VdiHeader *header) { char uuid[37]; logout("text %s", header->text); - logout("signature 0x%04x\n", header->signature); + logout("signature 0x%08x\n", header->signature); logout("header size 0x%04x\n", header->header_size); logout("image type 0x%04x\n", header->image_type); logout("image flags 0x%04x\n", header->image_flags); @@ -364,15 +364,17 @@ static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename) return result; } -static int vdi_open(BlockDriverState *bs, int flags) +static int vdi_open(BlockDriverState *bs, QDict *options, int flags) { BDRVVdiState *s = bs->opaque; VdiHeader header; size_t bmap_size; + int ret; logout("\n"); - if (bdrv_read(bs->file, 0, (uint8_t *)&header, 1) < 0) { + ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1); + if (ret < 0) { goto fail; } @@ -390,33 +392,45 @@ static int vdi_open(BlockDriverState *bs, int flags) header.disk_size &= ~(SECTOR_SIZE - 1); } - if (header.version != VDI_VERSION_1_1) { + if (header.signature != VDI_SIGNATURE) { + logout("bad vdi signature %08x\n", header.signature); + ret = -EMEDIUMTYPE; + goto fail; + } else if (header.version != VDI_VERSION_1_1) { logout("unsupported version %u.%u\n", header.version >> 16, header.version & 0xffff); + ret = -ENOTSUP; goto fail; } else if (header.offset_bmap % SECTOR_SIZE != 0) { /* We only support block maps which start on a sector boundary. */ logout("unsupported block map offset 0x%x B\n", header.offset_bmap); + ret = -ENOTSUP; goto fail; } else if (header.offset_data % SECTOR_SIZE != 0) { /* We only support data blocks which start on a sector boundary. */ logout("unsupported data offset 0x%x B\n", header.offset_data); + ret = -ENOTSUP; goto fail; } else if (header.sector_size != SECTOR_SIZE) { logout("unsupported sector size %u B\n", header.sector_size); + ret = -ENOTSUP; goto fail; } else if (header.block_size != 1 * MiB) { logout("unsupported block size %u B\n", header.block_size); + ret = -ENOTSUP; goto fail; } else if (header.disk_size > (uint64_t)header.blocks_in_image * header.block_size) { logout("unsupported disk size %" PRIu64 " B\n", header.disk_size); + ret = -ENOTSUP; goto fail; } else if (!uuid_is_null(header.uuid_link)) { logout("link uuid != 0, unsupported\n"); + ret = -ENOTSUP; goto fail; } else if (!uuid_is_null(header.uuid_parent)) { logout("parent uuid != 0, unsupported\n"); + ret = -ENOTSUP; goto fail; } @@ -429,10 +443,9 @@ static int vdi_open(BlockDriverState *bs, int flags) bmap_size = header.blocks_in_image * sizeof(uint32_t); bmap_size = (bmap_size + SECTOR_SIZE - 1) / SECTOR_SIZE; - if (bmap_size > 0) { - s->bmap = g_malloc(bmap_size * SECTOR_SIZE); - } - if (bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) { + s->bmap = g_malloc(bmap_size * SECTOR_SIZE); + ret = bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size); + if (ret < 0) { goto fail_free_bmap; } @@ -448,7 +461,7 @@ static int vdi_open(BlockDriverState *bs, int flags) g_free(s->bmap); fail: - return -1; + return ret; } static int vdi_reopen_prepare(BDRVReopenState *state, diff --git a/block/vmdk.c b/block/vmdk.c index 19298c2a3e..e92104a830 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -616,7 +616,7 @@ static int vmdk_open_sparse(BlockDriverState *bs, return vmdk_open_vmdk4(bs, file, flags); break; default: - return -EINVAL; + return -EMEDIUMTYPE; break; } } @@ -641,7 +641,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, * RW [size in sectors] SPARSE "file-name.vmdk" */ flat_offset = -1; - ret = sscanf(p, "%10s %" SCNd64 " %10s %511s %" SCNd64, + ret = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64, access, §ors, type, fname, &flat_offset); if (ret < 4 || strcmp(access, "RW")) { goto next_line; @@ -653,14 +653,6 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, return -EINVAL; } - /* trim the quotation marks around */ - if (fname[0] == '"') { - memmove(fname, fname + 1, strlen(fname)); - if (strlen(fname) <= 1 || fname[strlen(fname) - 1] != '"') { - return -EINVAL; - } - fname[strlen(fname) - 1] = '\0'; - } if (sectors <= 0 || (strcmp(type, "FLAT") && strcmp(type, "SPARSE")) || (strcmp(access, "RW"))) { @@ -718,7 +710,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, } buf[2047] = '\0'; if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) { - return -EINVAL; + return -EMEDIUMTYPE; } if (strcmp(ct, "monolithicFlat") && strcmp(ct, "twoGbMaxExtentSparse") && @@ -731,7 +723,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, return vmdk_parse_extents(buf, bs, bs->file->filename); } -static int vmdk_open(BlockDriverState *bs, int flags) +static int vmdk_open(BlockDriverState *bs, QDict *options, int flags) { int ret; BDRVVmdkState *s = bs->opaque; @@ -1442,6 +1434,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) int fd, idx = 0; char desc[BUF_SIZE]; int64_t total_size = 0, filesize; + const char *adapter_type = NULL; const char *backing_file = NULL; const char *fmt = NULL; int flags = 0; @@ -1453,6 +1446,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) const char *desc_extent_line; char parent_desc_line[BUF_SIZE] = ""; uint32_t parent_cid = 0xffffffff; + uint32_t number_heads = 16; const char desc_template[] = "# Disk DescriptorFile\n" "version=1\n" @@ -1469,9 +1463,9 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) "\n" "ddb.virtualHWVersion = \"%d\"\n" "ddb.geometry.cylinders = \"%" PRId64 "\"\n" - "ddb.geometry.heads = \"16\"\n" + "ddb.geometry.heads = \"%d\"\n" "ddb.geometry.sectors = \"63\"\n" - "ddb.adapterType = \"ide\"\n"; + "ddb.adapterType = \"%s\"\n"; if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) { return -EINVAL; @@ -1480,6 +1474,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) while (options && options->name) { if (!strcmp(options->name, BLOCK_OPT_SIZE)) { total_size = options->value.n; + } else if (!strcmp(options->name, BLOCK_OPT_ADAPTER_TYPE)) { + adapter_type = options->value.s; } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { backing_file = options->value.s; } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) { @@ -1489,6 +1485,20 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) } options++; } + if (!adapter_type) { + adapter_type = "ide"; + } else if (strcmp(adapter_type, "ide") && + strcmp(adapter_type, "buslogic") && + strcmp(adapter_type, "lsilogic") && + strcmp(adapter_type, "legacyESX")) { + fprintf(stderr, "VMDK: Unknown adapter type: '%s'.\n", adapter_type); + return -EINVAL; + } + if (strcmp(adapter_type, "ide") != 0) { + /* that's the number of heads with which vmware operates when + creating, exporting, etc. vmdk files with a non-ide adapter type */ + number_heads = 255; + } if (!fmt) { /* Default format to monolithicSparse */ fmt = "monolithicSparse"; @@ -1517,7 +1527,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) if (backing_file) { char parent_filename[PATH_MAX]; BlockDriverState *bs = bdrv_new(""); - ret = bdrv_open(bs, backing_file, 0, NULL); + ret = bdrv_open(bs, backing_file, NULL, 0, NULL); if (ret != 0) { bdrv_delete(bs); return ret; @@ -1576,7 +1586,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) parent_desc_line, ext_desc_lines, (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), - total_size / (int64_t)(63 * 16 * 512)); + total_size / (int64_t)(63 * number_heads * 512), number_heads, + adapter_type); if (split || flat) { fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, @@ -1660,6 +1671,12 @@ static QEMUOptionParameter vmdk_create_options[] = { .type = OPT_SIZE, .help = "Virtual disk size" }, + { + .name = BLOCK_OPT_ADAPTER_TYPE, + .type = OPT_STRING, + .help = "Virtual adapter type, can be one of " + "ide (default), lsilogic, buslogic or legacyESX" + }, { .name = BLOCK_OPT_BACKING_FILE, .type = OPT_STRING, diff --git a/block/vpc.c b/block/vpc.c index 7948609e50..3cad52e54c 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -155,7 +155,7 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int vpc_open(BlockDriverState *bs, int flags) +static int vpc_open(BlockDriverState *bs, QDict *options, int flags) { BDRVVPCState *s = bs->opaque; int i; @@ -163,24 +163,33 @@ static int vpc_open(BlockDriverState *bs, int flags) struct vhd_dyndisk_header* dyndisk_header; uint8_t buf[HEADER_SIZE]; uint32_t checksum; - int err = -1; int disk_type = VHD_DYNAMIC; + int ret; - if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE) + ret = bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE); + if (ret < 0) { goto fail; + } footer = (struct vhd_footer*) s->footer_buf; if (strncmp(footer->creator, "conectix", 8)) { int64_t offset = bdrv_getlength(bs->file); - if (offset < HEADER_SIZE) { + if (offset < 0) { + ret = offset; + goto fail; + } else if (offset < HEADER_SIZE) { + ret = -EINVAL; goto fail; } + /* If a fixed disk, the footer is found only at the end of the file */ - if (bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, HEADER_SIZE) - != HEADER_SIZE) { + ret = bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, + HEADER_SIZE); + if (ret < 0) { goto fail; } if (strncmp(footer->creator, "conectix", 8)) { + ret = -EMEDIUMTYPE; goto fail; } disk_type = VHD_FIXED; @@ -203,19 +212,21 @@ static int vpc_open(BlockDriverState *bs, int flags) /* Allow a maximum disk size of approximately 2 TB */ if (bs->total_sectors >= 65535LL * 255 * 255) { - err = -EFBIG; + ret = -EFBIG; goto fail; } if (disk_type == VHD_DYNAMIC) { - if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, - HEADER_SIZE) != HEADER_SIZE) { + ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, + HEADER_SIZE); + if (ret < 0) { goto fail; } dyndisk_header = (struct vhd_dyndisk_header *) buf; if (strncmp(dyndisk_header->magic, "cxsparse", 8)) { + ret = -EINVAL; goto fail; } @@ -226,8 +237,10 @@ static int vpc_open(BlockDriverState *bs, int flags) s->pagetable = g_malloc(s->max_table_entries * 4); s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); - if (bdrv_pread(bs->file, s->bat_offset, s->pagetable, - s->max_table_entries * 4) != s->max_table_entries * 4) { + + ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, + s->max_table_entries * 4); + if (ret < 0) { goto fail; } @@ -265,8 +278,13 @@ static int vpc_open(BlockDriverState *bs, int flags) migrate_add_blocker(s->migration_blocker); return 0; - fail: - return err; + +fail: + g_free(s->pagetable); +#ifdef CACHE + g_free(s->pageentry_u8); +#endif + return ret; } static int vpc_reopen_prepare(BDRVReopenState *state, diff --git a/block/vvfat.c b/block/vvfat.c index 83706ce556..b8eb38ab36 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -529,13 +529,9 @@ static inline uint8_t fat_chksum(const direntry_t* entry) /* if return_time==0, this returns the fat_date, else the fat_time */ static uint16_t fat_datetime(time_t time,int return_time) { struct tm* t; -#ifdef _WIN32 - t=localtime(&time); /* this is not thread safe */ -#else struct tm t1; t = &t1; localtime_r(&time,t); -#endif if(return_time) return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11)); return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9)); @@ -2834,7 +2830,7 @@ static int enable_write_target(BDRVVVFATState *s) return -1; } - ret = bdrv_open(s->qcow, s->qcow_filename, + ret = bdrv_open(s->qcow, s->qcow_filename, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow); if (ret < 0) { return ret; diff --git a/block/win32-aio.c b/block/win32-aio.c index 46a5db78cc..5d0fbbfb7d 100644 --- a/block/win32-aio.c +++ b/block/win32-aio.c @@ -29,6 +29,7 @@ #include "block/aio.h" #include "raw-aio.h" #include "qemu/event_notifier.h" +#include "qemu/iov.h" #include #include @@ -80,15 +81,9 @@ static void win32_aio_process_completion(QEMUWin32AIOState *s, if (!waiocb->is_linear) { if (ret == 0 && waiocb->is_read) { QEMUIOVector *qiov = waiocb->qiov; - char *p = waiocb->buf; - int i; - - for (i = 0; i < qiov->niov; ++i) { - memcpy(p, qiov->iov[i].iov_base, qiov->iov[i].iov_len); - p += qiov->iov[i].iov_len; - } - g_free(waiocb->buf); + iov_from_buf(qiov->iov, qiov->niov, 0, waiocb->buf, qiov->size); } + qemu_vfree(waiocb->buf); } @@ -153,13 +148,7 @@ BlockDriverAIOCB *win32_aio_submit(BlockDriverState *bs, if (qiov->niov > 1) { waiocb->buf = qemu_blockalign(bs, qiov->size); if (type & QEMU_AIO_WRITE) { - char *p = waiocb->buf; - int i; - - for (i = 0; i < qiov->niov; ++i) { - memcpy(p, qiov->iov[i].iov_base, qiov->iov[i].iov_len); - p += qiov->iov[i].iov_len; - } + iov_to_buf(qiov->iov, qiov->niov, 0, waiocb->buf, qiov->size); } waiocb->is_linear = false; } else { diff --git a/blockdev.c b/blockdev.c index e0e24c9c11..9a076bf8ad 100644 --- a/blockdev.c +++ b/blockdev.c @@ -5,6 +5,29 @@ * * This work is licensed under the terms of the GNU GPL, version 2 or * later. See the COPYING file in the top-level directory. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * 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 "sysemu/blockdev.h" @@ -22,6 +45,7 @@ #include "sysemu/arch_init.h" static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); +extern QemuOptsList qemu_common_drive_opts; static const char *const if_name[IF_COUNT] = { [IF_NONE] = "none", @@ -191,6 +215,7 @@ static void drive_uninit(DriveInfo *dinfo) bdrv_delete(dinfo->bdrv); g_free(dinfo->id); QTAILQ_REMOVE(&drives, dinfo, next); + g_free(dinfo->serial); g_free(dinfo); } @@ -255,7 +280,7 @@ static int parse_block_error_action(const char *buf, bool is_read) } } -static bool do_check_io_limits(BlockIOLimit *io_limits) +static bool do_check_io_limits(BlockIOLimit *io_limits, Error **errp) { bool bps_flag; bool iops_flag; @@ -269,13 +294,25 @@ static bool do_check_io_limits(BlockIOLimit *io_limits) && ((io_limits->iops[BLOCK_IO_LIMIT_READ] != 0) || (io_limits->iops[BLOCK_IO_LIMIT_WRITE] != 0)); if (bps_flag || iops_flag) { + error_setg(errp, "bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) " + "cannot be used at the same time"); + return false; + } + + if (io_limits->bps[BLOCK_IO_LIMIT_TOTAL] < 0 || + io_limits->bps[BLOCK_IO_LIMIT_WRITE] < 0 || + io_limits->bps[BLOCK_IO_LIMIT_READ] < 0 || + io_limits->iops[BLOCK_IO_LIMIT_TOTAL] < 0 || + io_limits->iops[BLOCK_IO_LIMIT_WRITE] < 0 || + io_limits->iops[BLOCK_IO_LIMIT_READ] < 0) { + error_setg(errp, "bps and iops values must be 0 or greater"); return false; } return true; } -DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) +DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type) { const char *buf; const char *file = NULL; @@ -298,10 +335,37 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) bool copy_on_read; bool locked; int ret; + Error *error = NULL; + QemuOpts *opts; + QDict *bs_opts; + const char *id; translation = BIOS_ATA_TRANSLATION_AUTO; media = MEDIA_DISK; + /* Check common options by copying from all_opts to opts, all other options + * are stored in bs_opts. */ + id = qemu_opts_id(all_opts); + opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error); + if (error_is_set(&error)) { + qerror_report_err(error); + error_free(error); + return NULL; + } + + bs_opts = qdict_new(); + qemu_opts_to_qdict(all_opts, bs_opts); + qemu_opts_absorb_qdict(opts, bs_opts, &error); + if (error_is_set(&error)) { + qerror_report_err(error); + error_free(error); + return NULL; + } + + if (id) { + qdict_del(bs_opts, "id"); + } + /* extract parameters */ bus_id = qemu_opt_get_number(opts, "bus", 0); unit_id = qemu_opt_get_number(opts, "unit", -1); @@ -381,6 +445,13 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) } } + if ((buf = qemu_opt_get(opts, "discard")) != NULL) { + if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) { + error_report("invalid discard option"); + return NULL; + } + } + bdrv_flags |= BDRV_O_CACHE_WB; if ((buf = qemu_opt_get(opts, "cache")) != NULL) { if (bdrv_parse_cache_flags(buf, &bdrv_flags) != 0) { @@ -430,9 +501,9 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) io_limits.iops[BLOCK_IO_LIMIT_WRITE] = qemu_opt_get_number(opts, "iops_wr", 0); - if (!do_check_io_limits(&io_limits)) { - error_report("bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) " - "cannot be used at the same time"); + if (!do_check_io_limits(&io_limits, &error)) { + error_report("%s", error_get_pretty(error)); + error_free(error); return NULL; } @@ -547,10 +618,12 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) dinfo->heads = heads; dinfo->secs = secs; dinfo->trans = translation; - dinfo->opts = opts; + dinfo->opts = all_opts; dinfo->refcount = 1; - dinfo->serial = serial; dinfo->locked = locked; + if (serial != NULL) { + dinfo->serial = g_strdup(serial); + } QTAILQ_INSERT_TAIL(&drives, dinfo, next); bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error); @@ -571,17 +644,20 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) case IF_MTD: break; case IF_VIRTIO: + { /* add virtio block device */ - opts = qemu_opts_create_nofail(qemu_find_opts("device")); + QemuOpts *devopts; + devopts = qemu_opts_create_nofail(qemu_find_opts("device")); if (arch_type == QEMU_ARCH_S390X) { - qemu_opt_set(opts, "driver", "virtio-blk-s390"); + qemu_opt_set(devopts, "driver", "virtio-blk-s390"); } else { - qemu_opt_set(opts, "driver", "virtio-blk-pci"); + qemu_opt_set(devopts, "driver", "virtio-blk-pci"); } - qemu_opt_set(opts, "drive", dinfo->id); + qemu_opt_set(devopts, "drive", dinfo->id); if (devaddr) - qemu_opt_set(opts, "addr", devaddr); + qemu_opt_set(devopts, "addr", devaddr); break; + } default: abort(); } @@ -619,18 +695,30 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) error_report("warning: disabling copy_on_read on readonly drive"); } - ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv); + ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv); + bs_opts = NULL; + if (ret < 0) { - error_report("could not open disk image %s: %s", - file, strerror(-ret)); + if (ret == -EMEDIUMTYPE) { + error_report("could not open disk image %s: not in %s format", + file, drv->format_name); + } else { + error_report("could not open disk image %s: %s", + file, strerror(-ret)); + } goto err; } if (bdrv_key_required(dinfo->bdrv)) autostart = 0; + + qemu_opts_del(opts); + return dinfo; err: + qemu_opts_del(opts); + QDECREF(bs_opts); bdrv_delete(dinfo->bdrv); g_free(dinfo->id); QTAILQ_REMOVE(&drives, dinfo, next); @@ -646,21 +734,17 @@ void do_commit(Monitor *mon, const QDict *qdict) if (!strcmp(device, "all")) { ret = bdrv_commit_all(); - if (ret == -EBUSY) { - qerror_report(QERR_DEVICE_IN_USE, device); - return; - } } else { bs = bdrv_find(device); if (!bs) { - qerror_report(QERR_DEVICE_NOT_FOUND, device); + monitor_printf(mon, "Device '%s' not found\n", device); return; } ret = bdrv_commit(bs); - if (ret == -EBUSY) { - qerror_report(QERR_DEVICE_IN_USE, device); - return; - } + } + if (ret < 0) { + monitor_printf(mon, "'commit' error for '%s': %s\n", device, + strerror(-ret)); } } @@ -794,7 +878,7 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp) bdrv_img_create(new_image_file, format, states->old_bs->filename, states->old_bs->drv->format_name, - NULL, -1, flags, &local_err); + NULL, -1, flags, &local_err, false); if (error_is_set(&local_err)) { error_propagate(errp, local_err); goto delete_and_fail; @@ -803,7 +887,9 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp) /* We will manually add the backing_hd field to the bs later */ states->new_bs = bdrv_new(""); - ret = bdrv_open(states->new_bs, new_image_file, + /* TODO Inherit bs->options or only take explicit options with an + * extended QMP command? */ + ret = bdrv_open(states->new_bs, new_image_file, NULL, flags | BDRV_O_NO_BACKING, drv); if (ret != 0) { error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file); @@ -904,7 +990,7 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename, int bdrv_flags, BlockDriver *drv, const char *password, Error **errp) { - if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) { + if (bdrv_open(bs, filename, NULL, bdrv_flags, drv) < 0) { error_set(errp, QERR_OPEN_FILE_FAILED, filename); return; } @@ -978,8 +1064,7 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, io_limits.iops[BLOCK_IO_LIMIT_READ] = iops_rd; io_limits.iops[BLOCK_IO_LIMIT_WRITE]= iops_wr; - if (!do_check_io_limits(&io_limits)) { - error_set(errp, QERR_INVALID_PARAMETER_COMBINATION); + if (!do_check_io_limits(&io_limits, errp)) { return; } @@ -1192,16 +1277,19 @@ void qmp_block_commit(const char *device, drive_get_ref(drive_get_by_blockdev(bs)); } +#define DEFAULT_MIRROR_BUF_SIZE (10 << 20) + void qmp_drive_mirror(const char *device, const char *target, bool has_format, const char *format, enum MirrorSyncMode sync, bool has_mode, enum NewImageMode mode, bool has_speed, int64_t speed, + bool has_granularity, uint32_t granularity, + bool has_buf_size, int64_t buf_size, bool has_on_source_error, BlockdevOnError on_source_error, bool has_on_target_error, BlockdevOnError on_target_error, Error **errp) { - BlockDriverInfo bdi; BlockDriverState *bs; BlockDriverState *source, *target_bs; BlockDriver *proto_drv; @@ -1223,6 +1311,21 @@ void qmp_drive_mirror(const char *device, const char *target, if (!has_mode) { mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; } + if (!has_granularity) { + granularity = 0; + } + if (!has_buf_size) { + buf_size = DEFAULT_MIRROR_BUF_SIZE; + } + + if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { + error_set(errp, QERR_INVALID_PARAMETER, device); + return; + } + if (granularity & (granularity - 1)) { + error_set(errp, QERR_INVALID_PARAMETER, device); + return; + } bs = bdrv_find(device); if (!bs) { @@ -1263,13 +1366,13 @@ void qmp_drive_mirror(const char *device, const char *target, return; } + bdrv_get_geometry(bs, &size); + size *= 512; if (sync == MIRROR_SYNC_MODE_FULL && mode != NEW_IMAGE_MODE_EXISTING) { /* create new image w/o backing file */ assert(format && drv); - bdrv_get_geometry(bs, &size); - size *= 512; bdrv_img_create(target, format, - NULL, NULL, NULL, size, flags, &local_err); + NULL, NULL, NULL, size, flags, &local_err, false); } else { switch (mode) { case NEW_IMAGE_MODE_EXISTING: @@ -1280,7 +1383,7 @@ void qmp_drive_mirror(const char *device, const char *target, bdrv_img_create(target, format, source->filename, source->drv->format_name, - NULL, -1, flags, &local_err); + NULL, size, flags, &local_err, false); break; default: abort(); @@ -1292,8 +1395,11 @@ void qmp_drive_mirror(const char *device, const char *target, return; } + /* Mirroring takes care of copy-on-write using the source's backing + * file. + */ target_bs = bdrv_new(""); - ret = bdrv_open(target_bs, target, flags | BDRV_O_NO_BACKING, drv); + ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv); if (ret < 0) { bdrv_delete(target_bs); @@ -1301,18 +1407,8 @@ void qmp_drive_mirror(const char *device, const char *target, return; } - /* We need a backing file if we will copy parts of a cluster. */ - if (bdrv_get_info(target_bs, &bdi) >= 0 && bdi.cluster_size != 0 && - bdi.cluster_size >= BDRV_SECTORS_PER_DIRTY_CHUNK * 512) { - ret = bdrv_open_backing_file(target_bs); - if (ret < 0) { - bdrv_delete(target_bs); - error_set(errp, QERR_OPEN_FILE_FAILED, target); - return; - } - } - - mirror_start(bs, target_bs, speed, sync, on_source_error, on_target_error, + mirror_start(bs, target_bs, speed, granularity, buf_size, sync, + on_source_error, on_target_error, block_job_cb, bs, &local_err); if (local_err != NULL) { bdrv_delete(target_bs); @@ -1431,3 +1527,141 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp) bdrv_iterate(do_qmp_query_block_jobs_one, &prev); return dummy.next; } + +QemuOptsList qemu_common_drive_opts = { + .name = "drive", + .head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head), + .desc = { + { + .name = "bus", + .type = QEMU_OPT_NUMBER, + .help = "bus number", + },{ + .name = "unit", + .type = QEMU_OPT_NUMBER, + .help = "unit number (i.e. lun for scsi)", + },{ + .name = "if", + .type = QEMU_OPT_STRING, + .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", + },{ + .name = "index", + .type = QEMU_OPT_NUMBER, + .help = "index number", + },{ + .name = "cyls", + .type = QEMU_OPT_NUMBER, + .help = "number of cylinders (ide disk geometry)", + },{ + .name = "heads", + .type = QEMU_OPT_NUMBER, + .help = "number of heads (ide disk geometry)", + },{ + .name = "secs", + .type = QEMU_OPT_NUMBER, + .help = "number of sectors (ide disk geometry)", + },{ + .name = "trans", + .type = QEMU_OPT_STRING, + .help = "chs translation (auto, lba. none)", + },{ + .name = "media", + .type = QEMU_OPT_STRING, + .help = "media type (disk, cdrom)", + },{ + .name = "snapshot", + .type = QEMU_OPT_BOOL, + .help = "enable/disable snapshot mode", + },{ + .name = "file", + .type = QEMU_OPT_STRING, + .help = "disk image", + },{ + .name = "discard", + .type = QEMU_OPT_STRING, + .help = "discard operation (ignore/off, unmap/on)", + },{ + .name = "cache", + .type = QEMU_OPT_STRING, + .help = "host cache usage (none, writeback, writethrough, " + "directsync, unsafe)", + },{ + .name = "aio", + .type = QEMU_OPT_STRING, + .help = "host AIO implementation (threads, native)", + },{ + .name = "format", + .type = QEMU_OPT_STRING, + .help = "disk format (raw, qcow2, ...)", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "disk serial number", + },{ + .name = "rerror", + .type = QEMU_OPT_STRING, + .help = "read error action", + },{ + .name = "werror", + .type = QEMU_OPT_STRING, + .help = "write error action", + },{ + .name = "addr", + .type = QEMU_OPT_STRING, + .help = "pci address (virtio only)", + },{ + .name = "readonly", + .type = QEMU_OPT_BOOL, + .help = "open drive file as read-only", + },{ + .name = "iops", + .type = QEMU_OPT_NUMBER, + .help = "limit total I/O operations per second", + },{ + .name = "iops_rd", + .type = QEMU_OPT_NUMBER, + .help = "limit read operations per second", + },{ + .name = "iops_wr", + .type = QEMU_OPT_NUMBER, + .help = "limit write operations per second", + },{ + .name = "bps", + .type = QEMU_OPT_NUMBER, + .help = "limit total bytes per second", + },{ + .name = "bps_rd", + .type = QEMU_OPT_NUMBER, + .help = "limit read bytes per second", + },{ + .name = "bps_wr", + .type = QEMU_OPT_NUMBER, + .help = "limit write bytes per second", + },{ + .name = "copy-on-read", + .type = QEMU_OPT_BOOL, + .help = "copy read data from backing file into image file", + },{ + .name = "boot", + .type = QEMU_OPT_BOOL, + .help = "(deprecated, ignored)", + },{ + .name = "locked", + .type = QEMU_OPT_BOOL, + .help = "emulate a security locked drive", + }, + { /* end of list */ } + }, +}; + +QemuOptsList qemu_drive_opts = { + .name = "drive", + .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head), + .desc = { + /* + * no elements => accept any params + * validation will happen later + */ + { /* end of list */ } + }, +}; diff --git a/bsd-user/main.c b/bsd-user/main.c index 1dc033046b..cc8498187a 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -34,8 +34,6 @@ #include "qemu/timer.h" #include "qemu/envlist.h" -#define DEBUG_LOGFILE "/tmp/qemu.log" - int singlestep; #if defined(CONFIG_USE_GUEST_BASE) unsigned long mmap_min_addr; @@ -691,11 +689,12 @@ static void usage(void) "-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n" "\n" "Debug options:\n" - "-d options activate log (default logfile=%s)\n" - "-D logfile override default logfile location\n" - "-p pagesize set the host page size to 'pagesize'\n" - "-singlestep always run in singlestep mode\n" - "-strace log system calls\n" + "-d item1[,...] enable logging of specified items\n" + " (use '-d help' for a list of log items)\n" + "-D logfile write logs to 'logfile' (default stderr)\n" + "-p pagesize set the host page size to 'pagesize'\n" + "-singlestep always run in singlestep mode\n" + "-strace log system calls\n" "\n" "Environment variables:\n" "QEMU_STRACE Print system calls and arguments similar to the\n" @@ -709,8 +708,7 @@ static void usage(void) , TARGET_ARCH, interp_prefix, - x86_stack_size, - DEBUG_LOGFILE); + x86_stack_size); exit(1); } @@ -733,7 +731,7 @@ int main(int argc, char **argv) { const char *filename; const char *cpu_model; - const char *log_file = DEBUG_LOGFILE; + const char *log_file = NULL; const char *log_mask = NULL; struct target_pt_regs regs1, *regs = ®s1; struct image_info info1, *info = &info1; @@ -861,20 +859,16 @@ int main(int argc, char **argv) } /* init debug */ - cpu_set_log_filename(log_file); + qemu_set_log_filename(log_file); if (log_mask) { int mask; - const CPULogItem *item; - mask = cpu_str_to_log_mask(log_mask); + mask = qemu_str_to_log_mask(log_mask); if (!mask) { - printf("Log items (comma separated):\n"); - for (item = cpu_log_items; item->mask != 0; item++) { - printf("%-10s %s\n", item->name, item->help); - } + qemu_print_log_usage(stdout); exit(1); } - cpu_set_log(mask); + qemu_set_log(mask); } if (optind >= argc) { @@ -917,7 +911,7 @@ int main(int argc, char **argv) fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } -#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC) +#if defined(TARGET_SPARC) || defined(TARGET_PPC) cpu_reset(ENV_GET_CPU(env)); #endif thread_env = env; diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 5d6cffc458..aae8ea10be 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -74,7 +74,7 @@ void mmap_unlock(void) } #endif -void *qemu_vmalloc(size_t size) +static void *bsd_vmalloc(size_t size) { void *p; mmap_lock(); @@ -98,7 +98,7 @@ void *g_malloc(size_t size) { char * p; size += 16; - p = qemu_vmalloc(size); + p = bsd_vmalloc(size); *(size_t *)p = size; return p + 16; } diff --git a/configure b/configure index a20dbd7b11..6abf3bda17 100755 --- a/configure +++ b/configure @@ -158,6 +158,7 @@ vnc_tls="" vnc_sasl="" vnc_jpeg="" vnc_png="" +vnc_ws="" xen="" xen_ctrl_version="" xen_pci_passthrough="" @@ -176,6 +177,8 @@ strip_opt="yes" tcg_interpreter="no" bigendian="no" mingw32="no" +gcov="no" +gcov_tool="gcov" EXESUF="" prefix="/usr/local" mandir="\${prefix}/share/man" @@ -212,7 +215,6 @@ trace_backend="nop" trace_file="trace" spice="" rbd="" -smartcard="" smartcard_nss="" usb_redir="" opengl="" @@ -223,6 +225,10 @@ libiscsi="" coroutine="" seccomp="" glusterfs="" +virtio_blk_data_plane="" +gtk="" +gtkabi="2.0" +tpm="no" # parse CC options first for opt do @@ -237,8 +243,10 @@ for opt do --cpu=*) cpu="$optarg" ;; --extra-cflags=*) QEMU_CFLAGS="$optarg $QEMU_CFLAGS" + EXTRA_CFLAGS="$optarg" ;; --extra-ldflags=*) LDFLAGS="$optarg $LDFLAGS" + EXTRA_LDFLAGS="$optarg" ;; --enable-debug-info) debug_info="yes" ;; @@ -261,6 +269,8 @@ else fi ar="${AR-${cross_prefix}ar}" +as="${AS-${cross_prefix}as}" +cpp="${CPP-$cc -E}" objcopy="${OBJCOPY-${cross_prefix}objcopy}" ld="${LD-${cross_prefix}ld}" libtool="${LIBTOOL-${cross_prefix}libtool}" @@ -599,6 +609,8 @@ for opt do ;; --python=*) python="$optarg" ;; + --gcov=*) gcov_tool="$optarg" + ;; --smbd=*) smbd="$optarg" ;; --extra-cflags=*) @@ -619,6 +631,8 @@ for opt do ;; --enable-gprof) gprof="yes" ;; + --enable-gcov) gcov="yes" + ;; --static) static="yes" LDFLAGS="-static $LDFLAGS" @@ -656,6 +670,8 @@ for opt do ;; --without-system-pixman) pixman="internal" ;; + --without-pixman) pixman="none" + ;; --disable-sdl) sdl="no" ;; --enable-sdl) sdl="yes" @@ -712,6 +728,10 @@ for opt do ;; --enable-vnc-png) vnc_png="yes" ;; + --disable-vnc-ws) vnc_ws="no" + ;; + --enable-vnc-ws) vnc_ws="yes" + ;; --disable-slirp) slirp="no" ;; --disable-uuid) uuid="no" @@ -850,10 +870,6 @@ for opt do ;; --enable-xfsctl) xfs="yes" ;; - --disable-smartcard) smartcard="no" - ;; - --enable-smartcard) smartcard="yes" - ;; --disable-smartcard-nss) smartcard_nss="no" ;; --enable-smartcard-nss) smartcard_nss="yes" @@ -880,6 +896,18 @@ for opt do ;; --enable-glusterfs) glusterfs="yes" ;; + --disable-virtio-blk-data-plane) virtio_blk_data_plane="no" + ;; + --enable-virtio-blk-data-plane) virtio_blk_data_plane="yes" + ;; + --disable-gtk) gtk="no" + ;; + --enable-gtk) gtk="yes" + ;; + --with-gtkabi=*) gtkabi="$optarg" + ;; + --enable-tpm) tpm="yes" + ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; esac @@ -961,6 +989,10 @@ microblaze-linux-user \ microblazeel-linux-user \ mips-linux-user \ mipsel-linux-user \ +mips64-linux-user \ +mips64el-linux-user \ +mipsn32-linux-user \ +mipsn32el-linux-user \ or32-linux-user \ ppc-linux-user \ ppc64-linux-user \ @@ -1030,6 +1062,8 @@ echo " --disable-strip disable stripping binaries" echo " --disable-werror disable compilation abort on warning" echo " --disable-sdl disable SDL" echo " --enable-sdl enable SDL" +echo " --disable-gtk disable gtk UI" +echo " --enable-gtk enable gtk UI" echo " --disable-virtfs disable VirtFS" echo " --enable-virtfs enable VirtFS" echo " --disable-vnc disable VNC" @@ -1057,6 +1091,8 @@ echo " --disable-vnc-jpeg disable JPEG lossy compression for VNC server" echo " --enable-vnc-jpeg enable JPEG lossy compression for VNC server" echo " --disable-vnc-png disable PNG compression for VNC server (default)" echo " --enable-vnc-png enable PNG compression for VNC server" +echo " --disable-vnc-ws disable Websockets support for VNC server" +echo " --enable-vnc-ws enable Websockets support for VNC server" echo " --disable-curses disable curses output" echo " --enable-curses enable curses output" echo " --disable-curl disable curl connectivity" @@ -1089,7 +1125,6 @@ echo " --fmod-inc path to FMOD includes" echo " --oss-lib path to OSS library" echo " --enable-uname-release=R Return R for uname -r in usermode emulation" echo " --cpu=CPU Build for host CPU [$cpu]" -echo " --sparc_cpu=V Build qemu for Sparc architecture v7, v8, v8plus, v8plusa, v9" echo " --disable-uuid disable uuid support" echo " --enable-uuid enable uuid support" echo " --disable-vde disable support for vde network" @@ -1114,8 +1149,6 @@ echo " --enable-spice enable spice" echo " --enable-rbd enable building the rados block device (rbd)" echo " --disable-libiscsi disable iscsi support" echo " --enable-libiscsi enable iscsi support" -echo " --disable-smartcard disable smartcard support" -echo " --enable-smartcard enable smartcard support" echo " --disable-smartcard-nss disable smartcard nss support" echo " --enable-smartcard-nss enable smartcard nss support" echo " --disable-usb-redir disable usb network redirection support" @@ -1128,6 +1161,9 @@ echo " --with-coroutine=BACKEND coroutine backend. Supported options:" echo " gthread, ucontext, sigaltstack, windows" echo " --enable-glusterfs enable GlusterFS backend" echo " --disable-glusterfs disable GlusterFS backend" +echo " --enable-gcov enable test coverage analysis with gcov" +echo " --gcov=GCOV use specified gcov [$gcov_tool]" +echo " --enable-tpm enable TPM support" echo "" echo "NOTE: The object files are built at the place where configure is launched" exit 1 @@ -1162,7 +1198,7 @@ fi z_version=`cut -f3 -d. $source_path/VERSION` if test -z "$werror" ; then - if test "$z_version" = "50" -a \ + if test -d "$source_path/.git" -a \ "$linux" = "yes" ; then werror="yes" else @@ -1416,7 +1452,7 @@ fi if test "$seccomp" != "no" ; then if $pkg_config --atleast-version=1.0.0 libseccomp --modversion >/dev/null 2>&1; then - LIBS=`$pkg_config --libs libseccomp` + libs_softmmu="$libs_softmmu `$pkg_config --libs libseccomp`" seccomp="yes" else if test "$seccomp" = "yes"; then @@ -1617,6 +1653,36 @@ if test "$sparse" != "no" ; then fi fi +########################################## +# GTK probe + +if test "$gtk" != "no"; then + gtkpackage="gtk+-$gtkabi" + if test "$gtkabi" = "3.0" ; then + gtkversion="3.0.0" + vtepackage="vte-2.90" + vteversion="0.32.0" + else + gtkversion="2.18.0" + vtepackage="vte" + vteversion="0.24.0" + fi + if $pkg_config --exists "$gtkpackage >= $gtkversion" && \ + $pkg_config --exists "$vtepackage >= $vteversion"; then + gtk_cflags=`$pkg_config --cflags $gtkpackage 2>/dev/null` + gtk_libs=`$pkg_config --libs $gtkpackage 2>/dev/null` + vte_cflags=`$pkg_config --cflags $vtepackage 2>/dev/null` + vte_libs=`$pkg_config --libs $vtepackage 2>/dev/null` + libs_softmmu="$gtk_libs $vte_libs $libs_softmmu" + gtk="yes" + else + if test "$gtk" = "yes" ; then + feature_not_found "gtk" + fi + gtk="no" + fi +fi + ########################################## # SDL probe @@ -1701,8 +1767,8 @@ EOF fi ########################################## -# VNC TLS detection -if test "$vnc" = "yes" -a "$vnc_tls" != "no" ; then +# VNC TLS/WS detection +if test "$vnc" = "yes" -a \( "$vnc_tls" != "no" -o "$vnc_ws" != "no" \) ; then cat > $TMPC < int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; } @@ -1710,14 +1776,23 @@ EOF vnc_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null` vnc_tls_libs=`$pkg_config --libs gnutls 2> /dev/null` if compile_prog "$vnc_tls_cflags" "$vnc_tls_libs" ; then - vnc_tls=yes + if test "$vnc_tls" != "no" ; then + vnc_tls=yes + fi + if test "$vnc_ws" != "no" ; then + vnc_ws=yes + fi libs_softmmu="$vnc_tls_libs $libs_softmmu" QEMU_CFLAGS="$QEMU_CFLAGS $vnc_tls_cflags" else if test "$vnc_tls" = "yes" ; then feature_not_found "vnc-tls" fi + if test "$vnc_ws" = "yes" ; then + feature_not_found "vnc-ws" + fi vnc_tls=no + vnc_ws=no fi fi @@ -2028,7 +2103,7 @@ fi if test "$mingw32" = "yes" ; then curses_list="-lpdcurses" else - curses_list="-lncurses -lcurses" + curses_list="-lncurses:-lcurses:$($pkg_config --libs ncurses 2>/dev/null)" fi if test "$curses" != "no" ; then @@ -2041,13 +2116,16 @@ int main(void) { return s != 0; } EOF + IFS=: for curses_lib in $curses_list; do + unset IFS if compile_prog "" "$curses_lib" ; then curses_found=yes libs_softmmu="$curses_lib $libs_softmmu" break fi done + unset IFS if test "$curses_found" = "yes" ; then curses=yes else @@ -2130,13 +2208,25 @@ fi # pixman support probe if test "$pixman" = ""; then - if $pkg_config pixman-1 > /dev/null 2>&1; then + if test "$want_tools" = "no" -a "$softmmu" = "no"; then + pixman="none" + elif $pkg_config pixman-1 > /dev/null 2>&1; then pixman="system" else pixman="internal" fi fi -if test "$pixman" = "system"; then +if test "$pixman" = "none"; then + if test "$want_tools" != "no" -o "$softmmu" != "no"; then + echo "ERROR: pixman disabled but system emulation or tools build" + echo " enabled. You can turn off pixman only if you also" + echo " disable all system emulation targets and the tools" + echo " build with '--disable-tools --disable-system'." + exit 1 + fi + pixman_cflags= + pixman_libs= +elif test "$pixman" = "system"; then pixman_cflags=`$pkg_config --cflags pixman-1 2>/dev/null` pixman_libs=`$pkg_config --libs pixman-1 2>/dev/null` else @@ -2259,6 +2349,17 @@ EOF fi fi +########################################## +# adjust virtio-blk-data-plane based on linux-aio + +if test "$virtio_blk_data_plane" = "yes" -a \ + "$linux_aio" != "yes" ; then + echo "Error: virtio-blk-data-plane requires Linux AIO, please try --enable-linux-aio" + exit 1 +elif test -z "$virtio_blk_data_plane" ; then + virtio_blk_data_plane=$linux_aio +fi + ########################################## # attr probe @@ -2323,6 +2424,7 @@ int main(void) { return 0; } EOF if compile_prog "" "$fdt_libs" ; then fdt=yes + libs_softmmu="$libs_softmmu $fdt_libs" else if test "$fdt" = "yes" ; then feature_not_found "fdt" @@ -2345,7 +2447,7 @@ if test "$opengl" != "no" ; then int main(void) { return GL_VERSION != 0; } EOF else - opengl_libs="-lGL" + opengl_libs="-lGL -lX11" cat > $TMPC << EOF #include #include @@ -2560,6 +2662,22 @@ if compile_prog "" "" ; then fallocate=yes fi +# check for fallocate hole punching +fallocate_punch_hole=no +cat > $TMPC << EOF +#include +#include + +int main(void) +{ + fallocate(0, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + fallocate_punch_hole=yes +fi + # check for sync_file_range sync_file_range=no cat > $TMPC << EOF @@ -2659,6 +2777,20 @@ if compile_prog "" "" ; then epoll_pwait=yes fi +# check for sendfile support +sendfile=no +cat > $TMPC << EOF +#include + +int main(void) +{ + return sendfile(0, 0, 0, 0); +} +EOF +if compile_prog "" "" ; then + sendfile=yes +fi + # Check if tools are available to build documentation. if test "$docs" != "no" ; then if has makeinfo && has pod2man; then @@ -2681,7 +2813,7 @@ if compile_prog "" "" ; then byteswap_h=yes fi -# Search for bswap_32 function +# Search for bswap32 function bswap_h=no cat > $TMPC << EOF #include @@ -2703,7 +2835,13 @@ if test "$libiscsi" != "no" ; then #include int main(void) { iscsi_unmap_sync(NULL,0,0,0,NULL,0); return 0; } EOF - if compile_prog "" "-liscsi" ; then + if $pkg_config --atleast-version=1.7.0 libiscsi --modversion >/dev/null 2>&1; then + libiscsi="yes" + libiscsi_cflags=$($pkg_config --cflags libiscsi 2>/dev/null) + libiscsi_libs=$($pkg_config --libs libiscsi 2>/dev/null) + CFLAGS="$CFLAGS $libiscsi_cflags" + LIBS="$LIBS $libiscsi_libs" + elif compile_prog "" "-liscsi" ; then libiscsi="yes" LIBS="$LIBS -liscsi" else @@ -2771,7 +2909,7 @@ EOF spice_cflags=$($pkg_config --cflags spice-protocol spice-server 2>/dev/null) spice_libs=$($pkg_config --libs spice-protocol spice-server 2>/dev/null) if $pkg_config --atleast-version=0.12.0 spice-server >/dev/null 2>&1 && \ - $pkg_config --atleast-version=0.12.2 spice-protocol > /dev/null 2>&1 && \ + $pkg_config --atleast-version=0.12.3 spice-protocol > /dev/null 2>&1 && \ compile_prog "$spice_cflags" "$spice_libs" ; then spice="yes" libs_softmmu="$libs_softmmu $spice_libs" @@ -2787,47 +2925,42 @@ EOF fi # check for libcacard for smartcard support -if test "$smartcard" != "no" ; then - smartcard="yes" - smartcard_cflags="" - # TODO - what's the minimal nss version we support? - if test "$smartcard_nss" != "no"; then - cat > $TMPC << EOF +smartcard_cflags="" +# TODO - what's the minimal nss version we support? +if test "$smartcard_nss" != "no"; then + cat > $TMPC << EOF #include int main(void) { PK11_FreeSlot(0); return 0; } EOF - smartcard_includes="-I\$(SRC_PATH)/libcacard" - libcacard_libs="$($pkg_config --libs nss 2>/dev/null) $glib_libs" - libcacard_cflags="$($pkg_config --cflags nss 2>/dev/null) $glib_cflags" - test_cflags="$libcacard_cflags" - # The header files in nss < 3.13.3 have a bug which causes them to - # emit a warning. If we're going to compile QEMU with -Werror, then - # test that the headers don't have this bug. Otherwise we would pass - # the configure test but fail to compile QEMU later. - if test "$werror" = "yes"; then - test_cflags="-Werror $test_cflags" - fi - if $pkg_config --atleast-version=3.12.8 nss >/dev/null 2>&1 && \ - compile_prog "$test_cflags" "$libcacard_libs"; then - smartcard_nss="yes" - QEMU_CFLAGS="$QEMU_CFLAGS $libcacard_cflags" - QEMU_INCLUDES="$QEMU_INCLUDES $smartcard_includes" - libs_softmmu="$libcacard_libs $libs_softmmu" - else - if test "$smartcard_nss" = "yes"; then - feature_not_found "nss" - fi - smartcard_nss="no" - fi + smartcard_includes="-I\$(SRC_PATH)/libcacard" + libcacard_libs="$($pkg_config --libs nss 2>/dev/null) $glib_libs" + libcacard_cflags="$($pkg_config --cflags nss 2>/dev/null) $glib_cflags" + test_cflags="$libcacard_cflags" + # The header files in nss < 3.13.3 have a bug which causes them to + # emit a warning. If we're going to compile QEMU with -Werror, then + # test that the headers don't have this bug. Otherwise we would pass + # the configure test but fail to compile QEMU later. + if test "$werror" = "yes"; then + test_cflags="-Werror $test_cflags" + fi + if test -n "$libtool" && + $pkg_config --atleast-version=3.12.8 nss >/dev/null 2>&1 && \ + compile_prog "$test_cflags" "$libcacard_libs"; then + smartcard_nss="yes" + QEMU_CFLAGS="$QEMU_CFLAGS $libcacard_cflags" + QEMU_INCLUDES="$QEMU_INCLUDES $smartcard_includes" + libs_softmmu="$libcacard_libs $libs_softmmu" + else + if test "$smartcard_nss" = "yes"; then + feature_not_found "nss" + fi + smartcard_nss="no" fi -fi -if test "$smartcard" = "no" ; then - smartcard_nss="no" fi # check for usbredirparser for usb network redirection support if test "$usb_redir" != "no" ; then - if $pkg_config --atleast-version=0.5.3 libusbredirparser-0.5 >/dev/null 2>&1 ; then + if $pkg_config --atleast-version=0.6 libusbredirparser-0.5 >/dev/null 2>&1 ; then usb_redir="yes" usb_redir_cflags=$($pkg_config --cflags libusbredirparser-0.5 2>/dev/null) usb_redir_libs=$($pkg_config --libs libusbredirparser-0.5 2>/dev/null) @@ -3039,20 +3172,27 @@ if compile_prog "" "" ; then fi ######################################## -# check whether we can disable the -Wunused-but-set-variable -# option with a pragma (this is needed to silence a warning in -# some versions of the valgrind VALGRIND_STACK_DEREGISTER macro.) -# This test has to be compiled with -Werror as otherwise an -# unknown pragma is only a warning. +# check whether we can disable warning option with a pragma (this is needed +# to silence warnings in the headers of some versions of external libraries). +# This test has to be compiled with -Werror as otherwise an unknown pragma is +# only a warning. +# +# If we can't selectively disable warning in the code, disable -Werror so that +# the build doesn't fail anyway. + pragma_disable_unused_but_set=no cat > $TMPC << EOF #pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#pragma GCC diagnostic ignored "-Wstrict-prototypes" + int main(void) { return 0; } EOF if compile_prog "-Werror" "" ; then - pragma_disable_unused_but_set=yes + pragma_diagnostic_available=yes +else + werror=no fi ######################################## @@ -3085,14 +3225,49 @@ if compile_prog "" "" ; then has_environ=yes fi +######################################## +# check if cpuid.h is usable. + +cpuid_h=no +cat > $TMPC << EOF +#include +int main(void) { + return 0; +} +EOF +if compile_prog "" "" ; then + cpuid_h=yes +fi + +######################################## +# check if __[u]int128_t is usable. + +int128=no +cat > $TMPC << EOF +__int128_t a; +__uint128_t b; +int main (void) { + a = a + b; + b = a * b; + return 0; +} +EOF +if compile_prog "" "" ; then + int128=yes +fi + ########################################## # End of CC checks # After here, no more $cc or $ld runs -if test "$debug" = "no" ; then +if test "$gcov" = "yes" ; then + CFLAGS="-fprofile-arcs -ftest-coverage -g $CFLAGS" + LDFLAGS="-fprofile-arcs -ftest-coverage $LDFLAGS" +elif test "$debug" = "no" ; then CFLAGS="-O2 -D_FORTIFY_SOURCE=2 $CFLAGS" fi + # Disable zero malloc errors for official releases unless explicitly told to # enable/disable if test -z "$zero_malloc" ; then @@ -3132,6 +3307,7 @@ fi qemu_confdir=$sysconfdir$confsuffix qemu_datadir=$datadir$confsuffix +qemu_localedir="$datadir/locale" tools="" if test "$want_tools" = "yes" ; then @@ -3158,9 +3334,6 @@ if test "$softmmu" = yes ; then tools="qemu-ga\$(EXESUF) $tools" fi fi - if test "$smartcard_nss" = "yes" ; then - tools="vscclient\$(EXESUF) $tools" - fi fi # Mac OS X ships with a broken assembler @@ -3218,6 +3391,7 @@ if test "$darwin" = "yes" ; then fi echo "pixman $pixman" echo "SDL support $sdl" +echo "GTK support $gtk" echo "curses support $curses" echo "curl support $curl" echo "mingw32 support $mingw32" @@ -3232,6 +3406,7 @@ if test "$vnc" = "yes" ; then echo "VNC SASL support $vnc_sasl" echo "VNC JPEG support $vnc_jpeg" echo "VNC PNG support $vnc_png" + echo "VNC WS support $vnc_ws" fi if test -n "$sparc_cpu"; then echo "Target Sparc Arch $sparc_cpu" @@ -3273,6 +3448,10 @@ echo "build guest agent $guest_agent" echo "seccomp support $seccomp" echo "coroutine backend $coroutine_backend" echo "GlusterFS support $glusterfs" +echo "virtio-blk-data-plane $virtio_blk_data_plane" +echo "gcov $gcov_tool" +echo "gcov enabled $gcov" +echo "TPM support $tpm" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -3281,6 +3460,8 @@ fi config_host_mak="config-host.mak" config_host_ld="config-host.ld" +echo "# Automatically generated by configure - do not modify" >config-all-disas.mak + echo "# Automatically generated by configure - do not modify" > $config_host_mak printf "# Configured with:" >> $config_host_mak printf " '%s'" "$0" "$@" >> $config_host_mak @@ -3299,6 +3480,9 @@ echo "qemu_datadir=$qemu_datadir" >> $config_host_mak echo "qemu_docdir=$qemu_docdir" >> $config_host_mak echo "qemu_localstatedir=$local_statedir" >> $config_host_mak echo "qemu_helperdir=$libexecdir" >> $config_host_mak +echo "extra_cflags=$EXTRA_CFLAGS" >> $config_host_mak +echo "extra_ldflags=$EXTRA_LDFLAGS" >> $config_host_mak +echo "qemu_localedir=$qemu_localedir" >> $config_host_mak echo "ARCH=$ARCH" >> $config_host_mak if test "$debug_tcg" = "yes" ; then @@ -3403,6 +3587,10 @@ fi if test "$vnc_png" = "yes" ; then echo "CONFIG_VNC_PNG=y" >> $config_host_mak fi +if test "$vnc_ws" = "yes" ; then + echo "CONFIG_VNC_WS=y" >> $config_host_mak + echo "VNC_WS_CFLAGS=$vnc_ws_cflags" >> $config_host_mak +fi if test "$fnmatch" = "yes" ; then echo "CONFIG_FNMATCH=y" >> $config_host_mak fi @@ -3451,6 +3639,9 @@ fi if test "$fallocate" = "yes" ; then echo "CONFIG_FALLOCATE=y" >> $config_host_mak fi +if test "$fallocate_punch_hole" = "yes" ; then + echo "CONFIG_FALLOCATE_PUNCH_HOLE=y" >> $config_host_mak +fi if test "$sync_file_range" = "yes" ; then echo "CONFIG_SYNC_FILE_RANGE=y" >> $config_host_mak fi @@ -3469,6 +3660,9 @@ fi if test "$epoll_pwait" = "yes" ; then echo "CONFIG_EPOLL_PWAIT=y" >> $config_host_mak fi +if test "$sendfile" = "yes" ; then + echo "CONFIG_SENDFILE=y" >> $config_host_mak +fi if test "$inotify" = "yes" ; then echo "CONFIG_INOTIFY=y" >> $config_host_mak fi @@ -3493,6 +3687,11 @@ if test "$bluez" = "yes" ; then echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak fi echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak +if test "$gtk" = "yes" ; then + echo "CONFIG_GTK=y" >> $config_host_mak + echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak + echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak +fi if test "$xen" = "yes" ; then echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak @@ -3544,10 +3743,6 @@ if test "$spice" = "yes" ; then echo "CONFIG_SPICE=y" >> $config_host_mak fi -if test "$smartcard" = "yes" ; then - echo "CONFIG_SMARTCARD=y" >> $config_host_mak -fi - if test "$smartcard_nss" = "yes" ; then echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak echo "libcacard_libs=$libcacard_libs" >> $config_host_mak @@ -3598,8 +3793,8 @@ if test "$linux_magic_h" = "yes" ; then echo "CONFIG_LINUX_MAGIC_H=y" >> $config_host_mak fi -if test "$pragma_disable_unused_but_set" = "yes" ; then - echo "CONFIG_PRAGMA_DISABLE_UNUSED_BUT_SET=y" >> $config_host_mak +if test "$pragma_diagnostic_available" = "yes" ; then + echo "CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE=y" >> $config_host_mak fi if test "$valgrind_h" = "yes" ; then @@ -3610,14 +3805,26 @@ if test "$has_environ" = "yes" ; then echo "CONFIG_HAS_ENVIRON=y" >> $config_host_mak fi +if test "$cpuid_h" = "yes" ; then + echo "CONFIG_CPUID_H=y" >> $config_host_mak +fi + +if test "$int128" = "yes" ; then + echo "CONFIG_INT128=y" >> $config_host_mak +fi + if test "$glusterfs" = "yes" ; then echo "CONFIG_GLUSTERFS=y" >> $config_host_mak fi +if test "$virtio_blk_data_plane" = "yes" ; then + echo "CONFIG_VIRTIO_BLK_DATA_PLANE=y" >> $config_host_mak +fi + # USB host support case "$usb" in linux) - echo "HOST_USB=linux" >> $config_host_mak + echo "HOST_USB=linux legacy" >> $config_host_mak ;; bsd) echo "HOST_USB=bsd" >> $config_host_mak @@ -3663,13 +3870,21 @@ echo "MAKE=$make" >> $config_host_mak echo "INSTALL=$install" >> $config_host_mak echo "INSTALL_DIR=$install -d -m 0755" >> $config_host_mak echo "INSTALL_DATA=$install -c -m 0644" >> $config_host_mak -echo "INSTALL_PROG=$install -c -m 0755" >> $config_host_mak +if test -n "$libtool"; then + echo "INSTALL_PROG=\$(LIBTOOL) --mode=install $install -c -m 0755" >> $config_host_mak + echo "INSTALL_LIB=\$(LIBTOOL) --mode=install $install -c -m 0644" >> $config_host_mak +else + echo "INSTALL_PROG=$install -c -m 0755" >> $config_host_mak + echo "INSTALL_LIB=$install -c -m 0644" >> $config_host_mak +fi echo "PYTHON=$python" >> $config_host_mak echo "CC=$cc" >> $config_host_mak echo "CC_I386=$cc_i386" >> $config_host_mak echo "HOST_CC=$host_cc" >> $config_host_mak echo "OBJCC=$objcc" >> $config_host_mak echo "AR=$ar" >> $config_host_mak +echo "AS=$as" >> $config_host_mak +echo "CPP=$cpp" >> $config_host_mak echo "OBJCOPY=$objcopy" >> $config_host_mak echo "LD=$ld" >> $config_host_mak echo "WINDRES=$windres" >> $config_host_mak @@ -3696,6 +3911,10 @@ echo "EXESUF=$EXESUF" >> $config_host_mak echo "LIBS_QGA+=$libs_qga" >> $config_host_mak echo "POD2MAN=$POD2MAN" >> $config_host_mak echo "TRANSLATE_OPT_CFLAGS=$TRANSLATE_OPT_CFLAGS" >> $config_host_mak +if test "$gcov" = "yes" ; then + echo "CONFIG_GCOV=y" >> $config_host_mak + echo "GCOV=$gcov_tool" >> $config_host_mak +fi # generate list of library paths for linker script @@ -3808,7 +4027,6 @@ case "$target_arch2" in target_nptl="yes" gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" target_llong_alignment=4 - target_libs_softmmu="$fdt_libs" ;; cris) target_nptl="yes" @@ -3826,7 +4044,6 @@ case "$target_arch2" in TARGET_ARCH=microblaze bflt="yes" target_nptl="yes" - target_libs_softmmu="$fdt_libs" ;; mips|mipsel) TARGET_ARCH=mips @@ -3834,9 +4051,10 @@ case "$target_arch2" in target_nptl="yes" ;; mipsn32|mipsn32el) - TARGET_ARCH=mipsn32 + TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips echo "TARGET_ABI_MIPSN32=y" >> $config_target_mak + echo "TARGET_ABI32=y" >> $config_target_mak ;; mips64|mips64el) TARGET_ARCH=mips64 @@ -3851,21 +4069,18 @@ case "$target_arch2" in ppc) gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" target_nptl="yes" - target_libs_softmmu="$fdt_libs" ;; ppcemb) TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" target_nptl="yes" - target_libs_softmmu="$fdt_libs" ;; ppc64) TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" target_long_alignment=8 - target_libs_softmmu="$fdt_libs" ;; ppc64abi32) TARGET_ARCH=ppc64 @@ -3873,7 +4088,6 @@ case "$target_arch2" in TARGET_ABI_DIR=ppc echo "TARGET_ABI32=y" >> $config_target_mak gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" - target_libs_softmmu="$fdt_libs" ;; sh4|sh4eb) TARGET_ARCH=sh4 @@ -3955,7 +4169,7 @@ case "$target_arch2" in echo "CONFIG_NO_XEN=y" >> $config_target_mak esac case "$target_arch2" in - i386|x86_64|ppcemb|ppc|ppc64|s390x) + arm|i386|x86_64|ppcemb|ppc|ppc64|s390x) # Make sure the target and host cpus are compatible if test "$kvm" = "yes" -a "$target_softmmu" = "yes" -a \ \( "$target_arch2" = "$cpu" -o \ @@ -3975,18 +4189,12 @@ case "$target_arch2" in i386|x86_64) echo "CONFIG_HAVE_GET_MEMORY_MAPPING=y" >> $config_target_mak esac -if test "$target_arch2" = "ppc64" -a "$fdt" = "yes"; then - echo "CONFIG_PSERIES=y" >> $config_target_mak -fi if test "$target_bigendian" = "yes" ; then echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak fi if test "$target_softmmu" = "yes" ; then echo "CONFIG_SOFTMMU=y" >> $config_target_mak echo "LIBS+=$libs_softmmu $target_libs_softmmu" >> $config_target_mak - if test "$smartcard_nss" = "yes" ; then - echo "subdir-$target: subdir-libcacard" >> $config_host_mak - fi case "$target_arch2" in i386|x86_64) echo "CONFIG_HAVE_CORE_DUMP=y" >> $config_target_mak @@ -4155,6 +4363,12 @@ if test "$gprof" = "yes" ; then fi fi +if test "$tpm" = "yes"; then + if test "$target_softmmu" = "yes" ; then + echo "CONFIG_TPM=y" >> $config_host_mak + fi +fi + if test "$ARCH" = "tci"; then linker_script="" else @@ -4187,15 +4401,16 @@ DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32" DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas" DIRS="$DIRS roms/seabios roms/vgabios" DIRS="$DIRS qapi-generated" -DIRS="$DIRS libcacard libcacard/libcacard libcacard/trace" FILES="Makefile tests/tcg/Makefile qdict-test-data.txt" FILES="$FILES tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit" -FILES="$FILES tests/tcg/lm32/Makefile libcacard/Makefile" +FILES="$FILES tests/tcg/lm32/Makefile po/Makefile" FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps" FILES="$FILES pc-bios/spapr-rtas/Makefile" FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile" +FILES="$FILES pc-bios/qemu-icon.bmp" for bios_file in \ $source_path/pc-bios/*.bin \ + $source_path/pc-bios/*.aml \ $source_path/pc-bios/*.rom \ $source_path/pc-bios/*.dtb \ $source_path/pc-bios/openbios-* \ @@ -4215,9 +4430,10 @@ for rom in seabios vgabios ; do config_mak=roms/$rom/config.mak echo "# Automatically generated by configure - do not modify" > $config_mak echo "SRC_PATH=$source_path/roms/$rom" >> $config_mak + echo "AS=$as" >> $config_mak echo "CC=$cc" >> $config_mak echo "BCC=bcc" >> $config_mak - echo "CPP=${cross_prefix}cpp" >> $config_mak + echo "CPP=$cpp" >> $config_mak echo "OBJCOPY=objcopy" >> $config_mak echo "IASL=iasl" >> $config_mak echo "LD=$ld" >> $config_mak diff --git a/coroutine-sigaltstack.c b/coroutine-sigaltstack.c index e37ebac9c4..3de0bb33bd 100644 --- a/coroutine-sigaltstack.c +++ b/coroutine-sigaltstack.c @@ -33,19 +33,10 @@ #include "qemu-common.h" #include "block/coroutine_int.h" -enum { - /* Maximum free pool size prevents holding too many freed coroutines */ - POOL_MAX_SIZE = 64, -}; - -/** Free list to speed up creation */ -static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool); -static unsigned int pool_size; - typedef struct { Coroutine base; void *stack; - jmp_buf env; + sigjmp_buf env; } CoroutineUContext; /** @@ -59,7 +50,7 @@ typedef struct { CoroutineUContext leader; /** Information for the signal handler (trampoline) */ - jmp_buf tr_reenter; + sigjmp_buf tr_reenter; volatile sig_atomic_t tr_called; void *tr_handler; } CoroutineThreadState; @@ -85,17 +76,6 @@ static void qemu_coroutine_thread_cleanup(void *opaque) g_free(s); } -static void __attribute__((destructor)) coroutine_cleanup(void) -{ - Coroutine *co; - Coroutine *tmp; - - QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) { - g_free(DO_UPCAST(CoroutineUContext, base, co)->stack); - g_free(co); - } -} - static void __attribute__((constructor)) coroutine_init(void) { int ret; @@ -115,8 +95,8 @@ static void __attribute__((constructor)) coroutine_init(void) static void coroutine_bootstrap(CoroutineUContext *self, Coroutine *co) { /* Initialize longjmp environment and switch back the caller */ - if (!setjmp(self->env)) { - longjmp(*(jmp_buf *)co->entry_arg, 1); + if (!sigsetjmp(self->env, 0)) { + siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); } while (true) { @@ -145,14 +125,14 @@ static void coroutine_trampoline(int signal) /* * Here we have to do a bit of a ping pong between the caller, given that * this is a signal handler and we have to do a return "soon". Then the - * caller can reestablish everything and do a longjmp here again. + * caller can reestablish everything and do a siglongjmp here again. */ - if (!setjmp(coTS->tr_reenter)) { + if (!sigsetjmp(coTS->tr_reenter, 0)) { return; } /* - * Ok, the caller has longjmp'ed back to us, so now prepare + * Ok, the caller has siglongjmp'ed back to us, so now prepare * us for the real machine state switching. We have to jump * into another function here to get a new stack context for * the auto variables (which have to be auto-variables @@ -164,7 +144,7 @@ static void coroutine_trampoline(int signal) coroutine_bootstrap(self, co); } -static Coroutine *coroutine_new(void) +Coroutine *qemu_coroutine_new(void) { const size_t stack_size = 1 << 20; CoroutineUContext *co; @@ -179,7 +159,7 @@ static Coroutine *coroutine_new(void) /* The way to manipulate stack is with the sigaltstack function. We * prepare a stack, with it delivering a signal to ourselves and then - * put setjmp/longjmp where needed. + * put sigsetjmp/siglongjmp where needed. * This has been done keeping coroutine-ucontext as a model and with the * pth ideas (GNU Portable Threads). See coroutine-ucontext for the basics * of the coroutines and see pth_mctx.c (from the pth project) for the @@ -220,7 +200,7 @@ static Coroutine *coroutine_new(void) /* * Now transfer control onto the signal stack and set it up. - * It will return immediately via "return" after the setjmp() + * It will return immediately via "return" after the sigsetjmp() * was performed. Be careful here with race conditions. The * signal can be delivered the first time sigsuspend() is * called. @@ -261,8 +241,8 @@ static Coroutine *coroutine_new(void) * type-conversion warnings related to the `volatile' qualifier and * the fact that `jmp_buf' usually is an array type. */ - if (!setjmp(old_env)) { - longjmp(coTS->tr_reenter, 1); + if (!sigsetjmp(old_env, 0)) { + siglongjmp(coTS->tr_reenter, 1); } /* @@ -272,31 +252,10 @@ static Coroutine *coroutine_new(void) return &co->base; } -Coroutine *qemu_coroutine_new(void) -{ - Coroutine *co; - - co = QSLIST_FIRST(&pool); - if (co) { - QSLIST_REMOVE_HEAD(&pool, pool_next); - pool_size--; - } else { - co = coroutine_new(); - } - return co; -} - void qemu_coroutine_delete(Coroutine *co_) { CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); - if (pool_size < POOL_MAX_SIZE) { - QSLIST_INSERT_HEAD(&pool, &co->base, pool_next); - co->base.caller = NULL; - pool_size++; - return; - } - g_free(co->stack); g_free(co); } @@ -311,9 +270,9 @@ CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, s->current = to_; - ret = setjmp(from->env); + ret = sigsetjmp(from->env, 0); if (ret == 0) { - longjmp(to->env, action); + siglongjmp(to->env, action); } return ret; } diff --git a/coroutine-ucontext.c b/coroutine-ucontext.c index 2ed703a3ed..867a662de0 100644 --- a/coroutine-ucontext.c +++ b/coroutine-ucontext.c @@ -34,19 +34,10 @@ #include #endif -enum { - /* Maximum free pool size prevents holding too many freed coroutines */ - POOL_MAX_SIZE = 64, -}; - -/** Free list to speed up creation */ -static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool); -static unsigned int pool_size; - typedef struct { Coroutine base; void *stack; - jmp_buf env; + sigjmp_buf env; #ifdef CONFIG_VALGRIND_H unsigned int valgrind_stack_id; @@ -96,17 +87,6 @@ static void qemu_coroutine_thread_cleanup(void *opaque) g_free(s); } -static void __attribute__((destructor)) coroutine_cleanup(void) -{ - Coroutine *co; - Coroutine *tmp; - - QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) { - g_free(DO_UPCAST(CoroutineUContext, base, co)->stack); - g_free(co); - } -} - static void __attribute__((constructor)) coroutine_init(void) { int ret; @@ -130,8 +110,8 @@ static void coroutine_trampoline(int i0, int i1) co = &self->base; /* Initialize longjmp environment and switch back the caller */ - if (!setjmp(self->env)) { - longjmp(*(jmp_buf *)co->entry_arg, 1); + if (!sigsetjmp(self->env, 0)) { + siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); } while (true) { @@ -140,19 +120,20 @@ static void coroutine_trampoline(int i0, int i1) } } -static Coroutine *coroutine_new(void) +Coroutine *qemu_coroutine_new(void) { const size_t stack_size = 1 << 20; CoroutineUContext *co; ucontext_t old_uc, uc; - jmp_buf old_env; + sigjmp_buf old_env; union cc_arg arg = {0}; - /* The ucontext functions preserve signal masks which incurs a system call - * overhead. setjmp()/longjmp() does not preserve signal masks but only - * works on the current stack. Since we need a way to create and switch to - * a new stack, use the ucontext functions for that but setjmp()/longjmp() - * for everything else. + /* The ucontext functions preserve signal masks which incurs a + * system call overhead. sigsetjmp(buf, 0)/siglongjmp() does not + * preserve signal masks but only works on the current stack. + * Since we need a way to create and switch to a new stack, use + * the ucontext functions for that but sigsetjmp()/siglongjmp() for + * everything else. */ if (getcontext(&uc) == -1) { @@ -178,29 +159,15 @@ static Coroutine *coroutine_new(void) makecontext(&uc, (void (*)(void))coroutine_trampoline, 2, arg.i[0], arg.i[1]); - /* swapcontext() in, longjmp() back out */ - if (!setjmp(old_env)) { + /* swapcontext() in, siglongjmp() back out */ + if (!sigsetjmp(old_env, 0)) { swapcontext(&old_uc, &uc); } return &co->base; } -Coroutine *qemu_coroutine_new(void) -{ - Coroutine *co; - - co = QSLIST_FIRST(&pool); - if (co) { - QSLIST_REMOVE_HEAD(&pool, pool_next); - pool_size--; - } else { - co = coroutine_new(); - } - return co; -} - #ifdef CONFIG_VALGRIND_H -#ifdef CONFIG_PRAGMA_DISABLE_UNUSED_BUT_SET +#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE /* Work around an unused variable in the valgrind.h macro... */ #pragma GCC diagnostic ignored "-Wunused-but-set-variable" #endif @@ -208,7 +175,7 @@ static inline void valgrind_stack_deregister(CoroutineUContext *co) { VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id); } -#ifdef CONFIG_PRAGMA_DISABLE_UNUSED_BUT_SET +#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE #pragma GCC diagnostic error "-Wunused-but-set-variable" #endif #endif @@ -217,13 +184,6 @@ void qemu_coroutine_delete(Coroutine *co_) { CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); - if (pool_size < POOL_MAX_SIZE) { - QSLIST_INSERT_HEAD(&pool, &co->base, pool_next); - co->base.caller = NULL; - pool_size++; - return; - } - #ifdef CONFIG_VALGRIND_H valgrind_stack_deregister(co); #endif @@ -242,9 +202,9 @@ CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, s->current = to_; - ret = setjmp(from->env); + ret = sigsetjmp(from->env, 0); if (ret == 0) { - longjmp(to->env, action); + siglongjmp(to->env, action); } return ret; } diff --git a/cpu-exec.c b/cpu-exec.c index 19ebb4a924..94fedc5805 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -23,8 +23,6 @@ #include "qemu/atomic.h" #include "sysemu/qtest.h" -int tb_invalidated_flag; - //#define CONFIG_DEBUG_EXEC bool qemu_cpu_has_work(CPUState *cpu) @@ -34,8 +32,10 @@ bool qemu_cpu_has_work(CPUState *cpu) void cpu_loop_exit(CPUArchState *env) { - env->current_tb = NULL; - longjmp(env->jmp_env, 1); + CPUState *cpu = ENV_GET_CPU(env); + + cpu->current_tb = NULL; + siglongjmp(env->jmp_env, 1); } /* exit the current TB from a signal handler. The host registers are @@ -47,16 +47,38 @@ void cpu_resume_from_signal(CPUArchState *env, void *puc) /* XXX: restore cpu registers saved in host registers */ env->exception_index = -1; - longjmp(env->jmp_env, 1); + siglongjmp(env->jmp_env, 1); } #endif +/* Execute a TB, and fix up the CPU state afterwards if necessary */ +static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr) +{ + CPUArchState *env = cpu->env_ptr; + tcg_target_ulong next_tb = tcg_qemu_tb_exec(env, tb_ptr); + if ((next_tb & TB_EXIT_MASK) > TB_EXIT_IDX1) { + /* We didn't start executing this TB (eg because the instruction + * counter hit zero); we must restore the guest PC to the address + * of the start of the TB. + */ + TranslationBlock *tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK); + cpu_pc_from_tb(env, tb); + } + if ((next_tb & TB_EXIT_MASK) == TB_EXIT_REQUESTED) { + /* We were asked to stop executing TBs (probably a pending + * interrupt. We've now stopped, so clear the flag. + */ + cpu->tcg_exit_req = 0; + } + return next_tb; +} + /* Execute the code without caching the generated code. An interpreter could be used if available. */ static void cpu_exec_nocache(CPUArchState *env, int max_cycles, TranslationBlock *orig_tb) { - tcg_target_ulong next_tb; + CPUState *cpu = ENV_GET_CPU(env); TranslationBlock *tb; /* Should never happen. @@ -66,16 +88,10 @@ static void cpu_exec_nocache(CPUArchState *env, int max_cycles, tb = tb_gen_code(env, orig_tb->pc, orig_tb->cs_base, orig_tb->flags, max_cycles); - env->current_tb = tb; + cpu->current_tb = tb; /* execute the generated code */ - next_tb = tcg_qemu_tb_exec(env, tb->tc_ptr); - env->current_tb = NULL; - - if ((next_tb & 3) == 2) { - /* Restore PC. This may happen if async event occurs before - the TB starts executing. */ - cpu_pc_from_tb(env, tb); - } + cpu_tb_exec(cpu, tb->tc_ptr); + cpu->current_tb = NULL; tb_phys_invalidate(tb, -1); tb_free(tb); } @@ -90,13 +106,13 @@ static TranslationBlock *tb_find_slow(CPUArchState *env, tb_page_addr_t phys_pc, phys_page1; target_ulong virt_page2; - tb_invalidated_flag = 0; + tcg_ctx.tb_ctx.tb_invalidated_flag = 0; /* find translated block using physical mappings */ phys_pc = get_page_addr_code(env, pc); phys_page1 = phys_pc & TARGET_PAGE_MASK; h = tb_phys_hash_func(phys_pc); - ptb1 = &tb_phys_hash[h]; + ptb1 = &tcg_ctx.tb_ctx.tb_phys_hash[h]; for(;;) { tb = *ptb1; if (!tb) @@ -128,8 +144,8 @@ static TranslationBlock *tb_find_slow(CPUArchState *env, /* Move the last found TB to the head of the list */ if (likely(*ptb1)) { *ptb1 = tb->phys_hash_next; - tb->phys_hash_next = tb_phys_hash[h]; - tb_phys_hash[h] = tb; + tb->phys_hash_next = tcg_ctx.tb_ctx.tb_phys_hash[h]; + tcg_ctx.tb_ctx.tb_phys_hash[h] = tb; } /* we add the TB in the virtual pc hash table */ env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb; @@ -182,23 +198,27 @@ volatile sig_atomic_t exit_request; int cpu_exec(CPUArchState *env) { CPUState *cpu = ENV_GET_CPU(env); +#if !(defined(CONFIG_USER_ONLY) && \ + (defined(TARGET_M68K) || defined(TARGET_PPC) || defined(TARGET_S390X))) + CPUClass *cc = CPU_GET_CLASS(cpu); +#endif int ret, interrupt_request; TranslationBlock *tb; uint8_t *tc_ptr; tcg_target_ulong next_tb; - if (env->halted) { + if (cpu->halted) { if (!cpu_has_work(cpu)) { return EXCP_HALTED; } - env->halted = 0; + cpu->halted = 0; } cpu_single_env = env; if (unlikely(exit_request)) { - env->exit_request = 1; + cpu->exit_request = 1; } #if defined(TARGET_I386) @@ -233,7 +253,7 @@ int cpu_exec(CPUArchState *env) /* prepare setjmp context for exception handling */ for(;;) { - if (setjmp(env->jmp_env) == 0) { + if (sigsetjmp(env->jmp_env, 0) == 0) { /* if an exception is pending, we execute it here */ if (env->exception_index >= 0) { if (env->exception_index >= EXCP_INTERRUPT) { @@ -249,12 +269,12 @@ int cpu_exec(CPUArchState *env) which will be handled outside the cpu execution loop */ #if defined(TARGET_I386) - do_interrupt(env); + cc->do_interrupt(cpu); #endif ret = env->exception_index; break; #else - do_interrupt(env); + cc->do_interrupt(cpu); env->exception_index = -1; #endif } @@ -262,14 +282,14 @@ int cpu_exec(CPUArchState *env) next_tb = 0; /* force lookup of first TB */ for(;;) { - interrupt_request = env->interrupt_request; + interrupt_request = cpu->interrupt_request; if (unlikely(interrupt_request)) { if (unlikely(env->singlestep_enabled & SSTEP_NOIRQ)) { /* Mask out external interrupts for this step. */ interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; } if (interrupt_request & CPU_INTERRUPT_DEBUG) { - env->interrupt_request &= ~CPU_INTERRUPT_DEBUG; + cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; env->exception_index = EXCP_DEBUG; cpu_loop_exit(env); } @@ -277,8 +297,8 @@ int cpu_exec(CPUArchState *env) defined(TARGET_PPC) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) || \ defined(TARGET_MICROBLAZE) || defined(TARGET_LM32) || defined(TARGET_UNICORE32) if (interrupt_request & CPU_INTERRUPT_HALT) { - env->interrupt_request &= ~CPU_INTERRUPT_HALT; - env->halted = 1; + cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; + cpu->halted = 1; env->exception_index = EXCP_HLT; cpu_loop_exit(env); } @@ -286,7 +306,7 @@ int cpu_exec(CPUArchState *env) #if defined(TARGET_I386) #if !defined(CONFIG_USER_ONLY) if (interrupt_request & CPU_INTERRUPT_POLL) { - env->interrupt_request &= ~CPU_INTERRUPT_POLL; + cpu->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(env->apic_state); } #endif @@ -303,17 +323,17 @@ int cpu_exec(CPUArchState *env) !(env->hflags & HF_SMM_MASK)) { cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0); - env->interrupt_request &= ~CPU_INTERRUPT_SMI; + cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; do_smm_enter(env); next_tb = 0; } else if ((interrupt_request & CPU_INTERRUPT_NMI) && !(env->hflags2 & HF2_NMI_MASK)) { - env->interrupt_request &= ~CPU_INTERRUPT_NMI; + cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; env->hflags2 |= HF2_NMI_MASK; do_interrupt_x86_hardirq(env, EXCP02_NMI, 1); next_tb = 0; } else if (interrupt_request & CPU_INTERRUPT_MCE) { - env->interrupt_request &= ~CPU_INTERRUPT_MCE; + cpu->interrupt_request &= ~CPU_INTERRUPT_MCE; do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0); next_tb = 0; } else if ((interrupt_request & CPU_INTERRUPT_HARD) && @@ -325,7 +345,8 @@ int cpu_exec(CPUArchState *env) int intno; cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0); - env->interrupt_request &= ~(CPU_INTERRUPT_HARD | CPU_INTERRUPT_VIRQ); + cpu->interrupt_request &= ~(CPU_INTERRUPT_HARD | + CPU_INTERRUPT_VIRQ); intno = cpu_get_pic_interrupt(env); qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing hardware INT=0x%02x\n", intno); do_interrupt_x86_hardirq(env, intno, 1); @@ -343,7 +364,7 @@ int cpu_exec(CPUArchState *env) intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector)); qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing virtual hardware INT=0x%02x\n", intno); do_interrupt_x86_hardirq(env, intno, 1); - env->interrupt_request &= ~CPU_INTERRUPT_VIRQ; + cpu->interrupt_request &= ~CPU_INTERRUPT_VIRQ; next_tb = 0; #endif } @@ -354,15 +375,16 @@ int cpu_exec(CPUArchState *env) } if (interrupt_request & CPU_INTERRUPT_HARD) { ppc_hw_interrupt(env); - if (env->pending_interrupts == 0) - env->interrupt_request &= ~CPU_INTERRUPT_HARD; + if (env->pending_interrupts == 0) { + cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; + } next_tb = 0; } #elif defined(TARGET_LM32) if ((interrupt_request & CPU_INTERRUPT_HARD) && (env->ie & IE_IE)) { env->exception_index = EXCP_IRQ; - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } #elif defined(TARGET_MICROBLAZE) @@ -371,7 +393,7 @@ int cpu_exec(CPUArchState *env) && !(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP)) && !(env->iflags & (D_FLAG | IMM_FLAG))) { env->exception_index = EXCP_IRQ; - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } #elif defined(TARGET_MIPS) @@ -380,7 +402,7 @@ int cpu_exec(CPUArchState *env) /* Raise it */ env->exception_index = EXCP_EXT_INTERRUPT; env->error_code = 0; - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } #elif defined(TARGET_OPENRISC) @@ -396,7 +418,7 @@ int cpu_exec(CPUArchState *env) } if (idx >= 0) { env->exception_index = idx; - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } } @@ -411,7 +433,7 @@ int cpu_exec(CPUArchState *env) cpu_pil_allowed(env, pil)) || type != TT_EXTINT) { env->exception_index = env->interrupt_index; - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } } @@ -420,7 +442,7 @@ int cpu_exec(CPUArchState *env) if (interrupt_request & CPU_INTERRUPT_FIQ && !(env->uncached_cpsr & CPSR_F)) { env->exception_index = EXCP_FIQ; - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } /* ARMv7-M interrupt return works by loading a magic value @@ -436,19 +458,19 @@ int cpu_exec(CPUArchState *env) && ((IS_M(env) && env->regs[15] < 0xfffffff0) || !(env->uncached_cpsr & CPSR_I))) { env->exception_index = EXCP_IRQ; - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } #elif defined(TARGET_UNICORE32) if (interrupt_request & CPU_INTERRUPT_HARD && !(env->uncached_asr & ASR_I)) { env->exception_index = UC32_EXCP_INTR; - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } #elif defined(TARGET_SH4) if (interrupt_request & CPU_INTERRUPT_HARD) { - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } #elif defined(TARGET_ALPHA) @@ -479,7 +501,7 @@ int cpu_exec(CPUArchState *env) if (idx >= 0) { env->exception_index = idx; env->error_code = 0; - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } } @@ -488,7 +510,7 @@ int cpu_exec(CPUArchState *env) && (env->pregs[PR_CCS] & I_FLAG) && !env->locked_irq) { env->exception_index = EXCP_IRQ; - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } if (interrupt_request & CPU_INTERRUPT_NMI) { @@ -500,7 +522,7 @@ int cpu_exec(CPUArchState *env) } if ((env->pregs[PR_CCS] & m_flag_archval)) { env->exception_index = EXCP_NMI; - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } } @@ -520,27 +542,27 @@ int cpu_exec(CPUArchState *env) #elif defined(TARGET_S390X) && !defined(CONFIG_USER_ONLY) if ((interrupt_request & CPU_INTERRUPT_HARD) && (env->psw.mask & PSW_MASK_EXT)) { - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } #elif defined(TARGET_XTENSA) if (interrupt_request & CPU_INTERRUPT_HARD) { env->exception_index = EXC_IRQ; - do_interrupt(env); + cc->do_interrupt(cpu); next_tb = 0; } #endif /* Don't use the cached interrupt_request value, do_interrupt may have updated the EXITTB flag. */ - if (env->interrupt_request & CPU_INTERRUPT_EXITTB) { - env->interrupt_request &= ~CPU_INTERRUPT_EXITTB; + if (cpu->interrupt_request & CPU_INTERRUPT_EXITTB) { + cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB; /* ensure that no TB jump will be modified as the program flow was changed */ next_tb = 0; } } - if (unlikely(env->exit_request)) { - env->exit_request = 0; + if (unlikely(cpu->exit_request)) { + cpu->exit_request = 0; env->exception_index = EXCP_INTERRUPT; cpu_loop_exit(env); } @@ -563,16 +585,16 @@ int cpu_exec(CPUArchState *env) #endif } #endif /* DEBUG_DISAS || CONFIG_DEBUG_EXEC */ - spin_lock(&tb_lock); + spin_lock(&tcg_ctx.tb_ctx.tb_lock); tb = tb_find_fast(env); /* Note: we do it here to avoid a gcc bug on Mac OS X when doing it in tb_find_slow */ - if (tb_invalidated_flag) { + if (tcg_ctx.tb_ctx.tb_invalidated_flag) { /* as some TB could have been invalidated because of memory exceptions while generating the code, we must recompute the hash index here */ next_tb = 0; - tb_invalidated_flag = 0; + tcg_ctx.tb_ctx.tb_invalidated_flag = 0; } #ifdef CONFIG_DEBUG_EXEC qemu_log_mask(CPU_LOG_EXEC, "Trace %p [" TARGET_FMT_lx "] %s\n", @@ -583,26 +605,38 @@ int cpu_exec(CPUArchState *env) spans two pages, we cannot safely do a direct jump. */ if (next_tb != 0 && tb->page_addr[1] == -1) { - tb_add_jump((TranslationBlock *)(next_tb & ~3), next_tb & 3, tb); + tb_add_jump((TranslationBlock *)(next_tb & ~TB_EXIT_MASK), + next_tb & TB_EXIT_MASK, tb); } - spin_unlock(&tb_lock); + spin_unlock(&tcg_ctx.tb_ctx.tb_lock); /* cpu_interrupt might be called while translating the TB, but before it is linked into a potentially infinite loop and becomes env->current_tb. Avoid starting execution if there is a pending interrupt. */ - env->current_tb = tb; + cpu->current_tb = tb; barrier(); - if (likely(!env->exit_request)) { + if (likely(!cpu->exit_request)) { tc_ptr = tb->tc_ptr; /* execute the generated code */ - next_tb = tcg_qemu_tb_exec(env, tc_ptr); - if ((next_tb & 3) == 2) { + next_tb = cpu_tb_exec(cpu, tc_ptr); + switch (next_tb & TB_EXIT_MASK) { + case TB_EXIT_REQUESTED: + /* Something asked us to stop executing + * chained TBs; just continue round the main + * loop. Whatever requested the exit will also + * have set something else (eg exit_request or + * interrupt_request) which we will handle + * next time around the loop. + */ + tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK); + next_tb = 0; + break; + case TB_EXIT_ICOUNT_EXPIRED: + { /* Instruction counter expired. */ int insns_left; - tb = (TranslationBlock *)(next_tb & ~3); - /* Restore PC. */ - cpu_pc_from_tb(env, tb); + tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK); insns_left = env->icount_decr.u32; if (env->icount_extra && insns_left >= 0) { /* Refill decrementer and continue execution. */ @@ -623,9 +657,13 @@ int cpu_exec(CPUArchState *env) next_tb = 0; cpu_loop_exit(env); } + break; + } + default: + break; } } - env->current_tb = NULL; + cpu->current_tb = NULL; /* reset soft MMU for next block (it can currently only be set by a memory fault) */ } /* for(;;) */ diff --git a/cpus.c b/cpus.c index 4a7782a541..e919dd7fb6 100644 --- a/cpus.c +++ b/cpus.c @@ -72,7 +72,7 @@ static bool cpu_thread_is_idle(CPUArchState *env) if (cpu->stopped || !runstate_is_running()) { return true; } - if (!env->halted || qemu_cpu_has_work(cpu) || + if (!cpu->halted || qemu_cpu_has_work(cpu) || kvm_async_interrupts_enabled()) { return false; } @@ -390,13 +390,15 @@ void hw_error(const char *fmt, ...) { va_list ap; CPUArchState *env; + CPUState *cpu; va_start(ap, fmt); fprintf(stderr, "qemu: hardware error: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); - for(env = first_cpu; env != NULL; env = env->next_cpu) { - fprintf(stderr, "CPU #%d:\n", env->cpu_index); + for (env = first_cpu; env != NULL; env = env->next_cpu) { + cpu = ENV_GET_CPU(env); + fprintf(stderr, "CPU #%d:\n", cpu->cpu_index); cpu_dump_state(env, stderr, fprintf, CPU_DUMP_FPU); } va_end(ap); @@ -515,7 +517,7 @@ static void qemu_init_sigbus(void) prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0); } -static void qemu_kvm_eat_signals(CPUArchState *env) +static void qemu_kvm_eat_signals(CPUState *cpu) { struct timespec ts = { 0, 0 }; siginfo_t siginfo; @@ -536,7 +538,7 @@ static void qemu_kvm_eat_signals(CPUArchState *env) switch (r) { case SIGBUS: - if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr)) { + if (kvm_on_sigbus_vcpu(cpu, siginfo.si_code, siginfo.si_addr)) { sigbus_reraise(); } break; @@ -558,7 +560,7 @@ static void qemu_init_sigbus(void) { } -static void qemu_kvm_eat_signals(CPUArchState *env) +static void qemu_kvm_eat_signals(CPUState *cpu) { } #endif /* !CONFIG_LINUX */ @@ -725,7 +727,7 @@ static void qemu_kvm_wait_io_event(CPUArchState *env) qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex); } - qemu_kvm_eat_signals(env); + qemu_kvm_eat_signals(cpu); qemu_wait_io_event_common(cpu); } @@ -740,7 +742,7 @@ static void *qemu_kvm_cpu_thread_fn(void *arg) cpu->thread_id = qemu_get_thread_id(); cpu_single_env = env; - r = kvm_init_vcpu(env); + r = kvm_init_vcpu(cpu); if (r < 0) { fprintf(stderr, "kvm_init_vcpu failed: %s\n", strerror(-r)); exit(1); @@ -1041,8 +1043,8 @@ void qemu_init_vcpu(void *_env) CPUArchState *env = _env; CPUState *cpu = ENV_GET_CPU(env); - env->nr_cores = smp_cores; - env->nr_threads = smp_threads; + cpu->nr_cores = smp_cores; + cpu->nr_threads = smp_threads; cpu->stopped = true; if (kvm_enabled()) { qemu_kvm_start_vcpu(env); @@ -1160,38 +1162,19 @@ static void tcg_exec_all(void) void set_numa_modes(void) { CPUArchState *env; + CPUState *cpu; int i; for (env = first_cpu; env != NULL; env = env->next_cpu) { + cpu = ENV_GET_CPU(env); for (i = 0; i < nb_numa_nodes; i++) { - if (test_bit(env->cpu_index, node_cpumask[i])) { - env->numa_node = i; + if (test_bit(cpu->cpu_index, node_cpumask[i])) { + cpu->numa_node = i; } } } } -void set_cpu_log(const char *optarg) -{ - int mask; - const CPULogItem *item; - - mask = cpu_str_to_log_mask(optarg); - if (!mask) { - printf("Log items (comma separated):\n"); - for (item = cpu_log_items; item->mask != 0; item++) { - printf("%-10s %s\n", item->name, item->help); - } - exit(1); - } - cpu_set_log(mask); -} - -void set_cpu_log_filename(const char *optarg) -{ - cpu_set_log_filename(optarg); -} - void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg) { /* XXX: implement xxx_cpu_list for targets that still miss it */ @@ -1213,9 +1196,9 @@ CpuInfoList *qmp_query_cpus(Error **errp) info = g_malloc0(sizeof(*info)); info->value = g_malloc0(sizeof(*info->value)); - info->value->CPU = env->cpu_index; + info->value->CPU = cpu->cpu_index; info->value->current = (env == first_cpu); - info->value->halted = env->halted; + info->value->halted = cpu->halted; info->value->thread_id = cpu->thread_id; #if defined(TARGET_I386) info->value->has_pc = true; @@ -1251,23 +1234,20 @@ void qmp_memsave(int64_t addr, int64_t size, const char *filename, FILE *f; uint32_t l; CPUArchState *env; + CPUState *cpu; uint8_t buf[1024]; if (!has_cpu) { cpu_index = 0; } - for (env = first_cpu; env; env = env->next_cpu) { - if (cpu_index == env->cpu_index) { - break; - } - } - - if (env == NULL) { + cpu = qemu_get_cpu(cpu_index); + if (cpu == NULL) { error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", "a CPU number"); return; } + env = cpu->env_ptr; f = fopen(filename, "wb"); if (!f) { @@ -1329,7 +1309,7 @@ void qmp_inject_nmi(Error **errp) for (env = first_cpu; env != NULL; env = env->next_cpu) { if (!env->apic_state) { - cpu_interrupt(env, CPU_INTERRUPT_NMI); + cpu_interrupt(CPU(x86_env_get_cpu(env)), CPU_INTERRUPT_NMI); } else { apic_deliver_nmi(env->apic_state); } diff --git a/cputlb.c b/cputlb.c index 88239c494a..aba7e44e1e 100644 --- a/cputlb.c +++ b/cputlb.c @@ -54,6 +54,7 @@ static const CPUTLBEntry s_cputlb_empty_entry = { */ void tlb_flush(CPUArchState *env, int flush_global) { + CPUState *cpu = ENV_GET_CPU(env); int i; #if defined(DEBUG_TLB) @@ -61,7 +62,7 @@ void tlb_flush(CPUArchState *env, int flush_global) #endif /* must reset current TB so that interrupts cannot modify the links while we are modifying them */ - env->current_tb = NULL; + cpu->current_tb = NULL; for (i = 0; i < CPU_TLB_SIZE; i++) { int mmu_idx; @@ -92,6 +93,7 @@ static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) void tlb_flush_page(CPUArchState *env, target_ulong addr) { + CPUState *cpu = ENV_GET_CPU(env); int i; int mmu_idx; @@ -110,7 +112,7 @@ void tlb_flush_page(CPUArchState *env, target_ulong addr) } /* must reset current TB so that interrupts cannot modify the links while we are modifying them */ - env->current_tb = NULL; + cpu->current_tb = NULL; addr &= TARGET_PAGE_MASK; i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); diff --git a/default-configs/alpha-softmmu.mak b/default-configs/alpha-softmmu.mak index 501dd413b7..2dbee94c89 100644 --- a/default-configs/alpha-softmmu.mak +++ b/default-configs/alpha-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for alpha-softmmu include pci.mak +include usb.mak CONFIG_SERIAL=y CONFIG_I8254=y CONFIG_PCKBD=y diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 2f1a5c9943..68b204547c 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for arm-softmmu include pci.mak +include usb.mak CONFIG_GDBSTUB_XML=y CONFIG_VGA=y CONFIG_ISA_MMIO=y @@ -46,3 +47,5 @@ CONFIG_XGMAC=y CONFIG_VERSATILE_PCI=y CONFIG_VERSATILE_I2C=y + +CONFIG_SDHCI=y diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 2c78175ae7..df9e126c1f 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for i386-softmmu include pci.mak +include usb.mak CONFIG_VGA=y CONFIG_VGA_PCI=y CONFIG_VGA_ISA=y @@ -25,3 +26,5 @@ CONFIG_HPET=y CONFIG_APPLESMC=y CONFIG_I8259=y CONFIG_PFLASH_CFI01=y +CONFIG_TPM_TIS=y +CONFIG_TPM_PASSTHROUGH=y diff --git a/default-configs/m68k-softmmu.mak b/default-configs/m68k-softmmu.mak index 3e2ec3716c..778ea82a10 100644 --- a/default-configs/m68k-softmmu.mak +++ b/default-configs/m68k-softmmu.mak @@ -1,5 +1,6 @@ # Default configuration for m68k-softmmu include pci.mak +include usb.mak CONFIG_GDBSTUB_XML=y CONFIG_PTIMER=y diff --git a/default-configs/mips-softmmu.mak b/default-configs/mips-softmmu.mak index a271b1c6da..4f04a33732 100644 --- a/default-configs/mips-softmmu.mak +++ b/default-configs/mips-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for mips-softmmu include pci.mak +include usb.mak CONFIG_ISA_MMIO=y CONFIG_ESP=y CONFIG_VGA=y diff --git a/default-configs/mips64-softmmu.mak b/default-configs/mips64-softmmu.mak index 0510bb6a53..a5b6c3c36a 100644 --- a/default-configs/mips64-softmmu.mak +++ b/default-configs/mips64-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for mips64-softmmu include pci.mak +include usb.mak CONFIG_ISA_MMIO=y CONFIG_ESP=y CONFIG_VGA=y diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/mips64el-softmmu.mak index ed3bed3b8d..a0e6de8e68 100644 --- a/default-configs/mips64el-softmmu.mak +++ b/default-configs/mips64el-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for mips64el-softmmu include pci.mak +include usb.mak CONFIG_ISA_MMIO=y CONFIG_ESP=y CONFIG_VGA=y diff --git a/default-configs/mipsel-softmmu.mak b/default-configs/mipsel-softmmu.mak index fa3a2cacdd..753dd76a21 100644 --- a/default-configs/mipsel-softmmu.mak +++ b/default-configs/mipsel-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for mipsel-softmmu include pci.mak +include usb.mak CONFIG_ISA_MMIO=y CONFIG_ESP=y CONFIG_VGA=y diff --git a/default-configs/pci.mak b/default-configs/pci.mak index ae9d1eb487..ee2d18d5f2 100644 --- a/default-configs/pci.mak +++ b/default-configs/pci.mak @@ -21,3 +21,4 @@ CONFIG_ESP=y CONFIG_ESP_PCI=y CONFIG_SERIAL=y CONFIG_SERIAL_PCI=y +CONFIG_IPACK=y diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index d0fde7b93d..c209a8da65 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for ppc-softmmu include pci.mak +include usb.mak CONFIG_GDBSTUB_XML=y CONFIG_ISA_MMIO=y CONFIG_ESCC=y @@ -8,6 +9,7 @@ CONFIG_M48T59=y CONFIG_VGA=y CONFIG_VGA_PCI=y CONFIG_SERIAL=y +CONFIG_PARALLEL=y CONFIG_I8254=y CONFIG_PCKBD=y CONFIG_FDC=y @@ -16,6 +18,7 @@ CONFIG_I82374=y CONFIG_OPENPIC=y CONFIG_PREP_PCI=y CONFIG_I82378=y +CONFIG_PC87312=y CONFIG_MACIO=y CONFIG_PCSPK=y CONFIG_CUDA=y @@ -37,3 +40,4 @@ CONFIG_PFLASH_CFI02=y CONFIG_PTIMER=y CONFIG_I8259=y CONFIG_XILINX=y +CONFIG_E500=$(CONFIG_FDT) diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index e4265b4978..8d490bd72e 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for ppc64-softmmu include pci.mak +include usb.mak CONFIG_GDBSTUB_XML=y CONFIG_ISA_MMIO=y CONFIG_ESCC=y @@ -8,13 +9,18 @@ CONFIG_M48T59=y CONFIG_VGA=y CONFIG_VGA_PCI=y CONFIG_SERIAL=y +CONFIG_PARALLEL=y CONFIG_I8254=y CONFIG_PCKBD=y CONFIG_FDC=y CONFIG_DMA=y +CONFIG_I82374=y CONFIG_OPENPIC=y CONFIG_PREP_PCI=y +CONFIG_I82378=y +CONFIG_PC87312=y CONFIG_MACIO=y +CONFIG_PCSPK=y CONFIG_CUDA=y CONFIG_ADB=y CONFIG_MAC_NVRAM=y @@ -34,3 +40,5 @@ CONFIG_PFLASH_CFI02=y CONFIG_PTIMER=y CONFIG_I8259=y CONFIG_XILINX=y +CONFIG_PSERIES=$(CONFIG_FDT) +CONFIG_E500=$(CONFIG_FDT) diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak index aaa9cdc1f7..7f13421d93 100644 --- a/default-configs/ppcemb-softmmu.mak +++ b/default-configs/ppcemb-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for ppcemb-softmmu include pci.mak +include usb.mak CONFIG_GDBSTUB_XML=y CONFIG_ISA_MMIO=y CONFIG_ESCC=y @@ -34,3 +35,4 @@ CONFIG_PFLASH_CFI02=y CONFIG_PTIMER=y CONFIG_I8259=y CONFIG_XILINX=y +CONFIG_E500=$(CONFIG_FDT) diff --git a/default-configs/sh4-softmmu.mak b/default-configs/sh4-softmmu.mak index 5c69acc5f5..e08b2ee106 100644 --- a/default-configs/sh4-softmmu.mak +++ b/default-configs/sh4-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for sh4-softmmu include pci.mak +include usb.mak CONFIG_SERIAL=y CONFIG_PTIMER=y CONFIG_PFLASH_CFI02=y diff --git a/default-configs/sh4eb-softmmu.mak b/default-configs/sh4eb-softmmu.mak index 7cdc122201..3a8453552b 100644 --- a/default-configs/sh4eb-softmmu.mak +++ b/default-configs/sh4eb-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for sh4eb-softmmu include pci.mak +include usb.mak CONFIG_SERIAL=y CONFIG_PTIMER=y CONFIG_PFLASH_CFI02=y diff --git a/default-configs/sparc64-softmmu.mak b/default-configs/sparc64-softmmu.mak index 03e8b42712..2145b6b29f 100644 --- a/default-configs/sparc64-softmmu.mak +++ b/default-configs/sparc64-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for sparc64-softmmu include pci.mak +include usb.mak CONFIG_ISA_MMIO=y CONFIG_M48T59=y CONFIG_PTIMER=y diff --git a/default-configs/usb.mak b/default-configs/usb.mak new file mode 100644 index 0000000000..1bf9075e85 --- /dev/null +++ b/default-configs/usb.mak @@ -0,0 +1,8 @@ +CONFIG_USB_TABLET_WACOM=y +CONFIG_USB_STORAGE_BOT=y +CONFIG_USB_STORAGE_UAS=y +CONFIG_USB_SMARTCARD=y +CONFIG_USB_AUDIO=y +CONFIG_USB_SERIAL=y +CONFIG_USB_NETWORK=y +CONFIG_USB_BLUETOOTH=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 233a8564eb..ab3cd5fc35 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for x86_64-softmmu include pci.mak +include usb.mak CONFIG_VGA=y CONFIG_VGA_PCI=y CONFIG_VGA_ISA=y @@ -25,3 +26,5 @@ CONFIG_HPET=y CONFIG_APPLESMC=y CONFIG_I8259=y CONFIG_PFLASH_CFI01=y +CONFIG_TPM_TIS=y +CONFIG_TPM_PASSTHROUGH=y diff --git a/hw/device-hotplug.c b/device-hotplug.c similarity index 88% rename from hw/device-hotplug.c rename to device-hotplug.c index 88da145a89..103d34ac45 100644 --- a/hw/device-hotplug.c +++ b/device-hotplug.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "boards.h" +#include "hw/hw.h" +#include "hw/boards.h" #include "sysemu/blockdev.h" #include "qemu/config-file.h" #include "sysemu/sysemu.h" @@ -47,15 +47,6 @@ DriveInfo *add_init_drive(const char *optstr) return dinfo; } -#if !defined(TARGET_I386) -int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo) -{ - /* On non-x86 we don't do PCI hotplug */ - monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); - return -1; -} -#endif - void drive_hot_add(Monitor *mon, const QDict *qdict) { DriveInfo *dinfo = NULL; diff --git a/disas/Makefile.objs b/disas/Makefile.objs index 9134429845..ed75f9a99b 100644 --- a/disas/Makefile.objs +++ b/disas/Makefile.objs @@ -1,16 +1,18 @@ -universal-obj-$(CONFIG_ALPHA_DIS) += alpha.o -universal-obj-$(CONFIG_ARM_DIS) += arm.o -universal-obj-$(CONFIG_CRIS_DIS) += cris.o -universal-obj-$(CONFIG_HPPA_DIS) += hppa.o -universal-obj-$(CONFIG_I386_DIS) += i386.o -universal-obj-$(CONFIG_IA64_DIS) += ia64.o -universal-obj-$(CONFIG_M68K_DIS) += m68k.o -universal-obj-$(CONFIG_MICROBLAZE_DIS) += microblaze.o -universal-obj-$(CONFIG_MIPS_DIS) += mips.o -universal-obj-$(CONFIG_PPC_DIS) += ppc.o -universal-obj-$(CONFIG_S390_DIS) += s390.o -universal-obj-$(CONFIG_SH4_DIS) += sh4.o -universal-obj-$(CONFIG_SPARC_DIS) += sparc.o -universal-obj-$(CONFIG_LM32_DIS) += lm32.o +common-obj-$(CONFIG_ALPHA_DIS) += alpha.o +common-obj-$(CONFIG_ARM_DIS) += arm.o +common-obj-$(CONFIG_CRIS_DIS) += cris.o +common-obj-$(CONFIG_HPPA_DIS) += hppa.o +common-obj-$(CONFIG_I386_DIS) += i386.o +common-obj-$(CONFIG_IA64_DIS) += ia64.o +common-obj-$(CONFIG_M68K_DIS) += m68k.o +common-obj-$(CONFIG_MICROBLAZE_DIS) += microblaze.o +common-obj-$(CONFIG_MIPS_DIS) += mips.o +common-obj-$(CONFIG_PPC_DIS) += ppc.o +common-obj-$(CONFIG_S390_DIS) += s390.o +common-obj-$(CONFIG_SH4_DIS) += sh4.o +common-obj-$(CONFIG_SPARC_DIS) += sparc.o +common-obj-$(CONFIG_LM32_DIS) += lm32.o -universal-obj-$(CONFIG_TCI_DIS) += tci.o +# TODO: As long as the TCG interpreter and its generated code depend +# on the QEMU target, we cannot compile the disassembler here. +#common-obj-$(CONFIG_TCI_DIS) += tci.o diff --git a/disas/i386.c b/disas/i386.c index 3b006b13e0..73cc06f1c3 100644 --- a/disas/i386.c +++ b/disas/i386.c @@ -226,7 +226,7 @@ struct dis_private { bfd_byte the_buffer[MAX_MNEM_SIZE]; bfd_vma insn_start; int orig_sizeflag; - jmp_buf bailout; + sigjmp_buf bailout; }; enum address_mode @@ -303,7 +303,7 @@ fetch_data2(struct disassemble_info *info, bfd_byte *addr) STATUS. */ if (priv->max_fetched == priv->the_buffer) (*info->memory_error_func) (status, start, info); - longjmp (priv->bailout, 1); + siglongjmp(priv->bailout, 1); } else priv->max_fetched = addr; @@ -3661,7 +3661,7 @@ print_insn (bfd_vma pc, disassemble_info *info) start_codep = priv.the_buffer; codep = priv.the_buffer; - if (setjmp (priv.bailout) != 0) + if (sigsetjmp(priv.bailout, 0) != 0) { const char *name; @@ -4720,7 +4720,8 @@ print_operand_value (char *buf, size_t bufsize, int hex, bfd_vma disp) buf[0] = '0'; buf[1] = 'x'; snprintf_vma (tmp, sizeof(tmp), disp); - for (i = 0; tmp[i] == '0' && tmp[i + 1]; i++); + for (i = 0; tmp[i] == '0' && tmp[i + 1]; i++) { + } pstrcpy (buf + 2, bufsize - 2, tmp + i); } else diff --git a/disas/m68k.c b/disas/m68k.c index c950241f79..cc0db96cae 100644 --- a/disas/m68k.c +++ b/disas/m68k.c @@ -624,7 +624,7 @@ struct private bfd_byte *max_fetched; bfd_byte the_buffer[MAXLEN]; bfd_vma insn_start; - jmp_buf bailout; + sigjmp_buf bailout; }; /* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive) @@ -644,7 +644,7 @@ fetch_data2(struct disassemble_info *info, bfd_byte *addr) if (status != 0) { (*info->memory_error_func) (status, start, info); - longjmp (priv->bailout, 1); + siglongjmp(priv->bailout, 1); } else priv->max_fetched = addr; @@ -1912,9 +1912,10 @@ print_insn_m68k (bfd_vma memaddr, disassemble_info *info) priv.max_fetched = priv.the_buffer; priv.insn_start = memaddr; - if (setjmp (priv.bailout) != 0) - /* Error return. */ - return -1; + if (sigsetjmp(priv.bailout, 0) != 0) { + /* Error return. */ + return -1; + } switch (info->mach) { diff --git a/disas/s390.c b/disas/s390.c index 0859dfa19f..25499ba419 100644 --- a/disas/s390.c +++ b/disas/s390.c @@ -589,6 +589,16 @@ static const struct s390_operand s390_operands[] = { 4, 32, S390_OPERAND_CCODE }, #define I8_32 46 /* 8 bit signed value starting at 32 */ { 8, 32, S390_OPERAND_SIGNED }, +#define U8_24 47 /* 8 bit unsigned value starting at 24 */ + { 8, 24, 0 }, +#define U8_32 48 /* 8 bit unsigned value starting at 32 */ + { 8, 32, 0 }, +#define I16_32 49 + { 16, 32, S390_OPERAND_SIGNED }, +#define M4_16 50 /* 4-bit condition-code starting at 12 */ + { 4, 16, S390_OPERAND_CCODE }, +#define I8_16 51 + { 8, 16, S390_OPERAND_SIGNED }, /* QEMU-END */ }; @@ -663,7 +673,9 @@ static const struct s390_operand s390_operands[] = This is just a workaround for existing code e.g. glibc. */ #define INSTR_RRE_RR_OPT 4, { R_24,RO_28,0,0,0,0 } /* efpc, sfpc */ #define INSTR_RRF_F0FF 4, { F_16,F_24,F_28,0,0,0 } /* e.g. madbr */ -#define INSTR_RRF_F0FF2 4, { F_24,F_16,F_28,0,0,0 } /* e.g. cpsdr */ +/* QEMU-MOD */ +#define INSTR_RRF_F0FF2 4, { F_24,F_28,F_16,0,0,0 } /* e.g. cpsdr */ +/* QEMU-END */ #define INSTR_RRF_F0FR 4, { F_24,F_16,R_28,0,0,0 } /* e.g. iedtr */ #define INSTR_RRF_FUFF 4, { F_24,F_16,F_28,U4_20,0,0 } /* e.g. didbr */ #define INSTR_RRF_RURR 4, { R_24,R_28,R_16,U4_20,0,0 } /* e.g. .insn */ @@ -801,11 +813,35 @@ static const struct s390_operand s390_operands[] = #define MASK_SSF_RRDRD { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } /* QEMU-ADD: */ -#define INSTR_RIE_MRRP 6, { M4_32,R_8,R_12,J16_16,0,0 } /* e.g. crj */ +#define INSTR_RIE_MRRP 6, { M4_32, R_8, R_12, J16_16, 0, 0 } /* e.g. crj */ #define MASK_RIE_MRRP { 0xff, 0x00, 0x00, 0x00, 0x0f, 0xff } -#define INSTR_RIE_MRIP 6, { M4_12,R_8,I8_32,J16_16,0,0 } /* e.g. cij */ +#define INSTR_RIE_MRIP 6, { M4_12, R_8, I8_32, J16_16, 0, 0 } /* e.g. cij */ #define MASK_RIE_MRIP { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } + +#define INSTR_RIE_RRIII 6, { R_8, R_12, U8_16, U8_24, U8_32, 0 } /* risbg */ +#define MASK_RIE_RRIII { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define INSTR_RIE_MRI 6, { M4_32, R_8, I16_16, 0, 0, 0 } /* e.g. cit */ +#define MASK_RIE_MRI { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define INSTR_RIE_MRU 6, { M4_32, R_8, U16_16, 0, 0, 0 } /* e.g. clfit */ +#define MASK_RIE_MRU { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +#define INSTR_RIE_RRI 6, { R_8, R_12, I16_16, 0, 0, 0 } +#define MASK_RIE_RRI { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } + +#define INSTR_RXY_URRD 6, { U8_8, D20_20, X_12, B_16, 0, 0 } +#define MASK_RXY_URRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } + +#define INSTR_SIL_DRI 6, { D_20, B_16, I16_32, 0, 0, 0 } +#define MASK_SIL_DRI { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } + +#define INSTR_RSY_MRRD 6, { M4_12, R_8, D20_20, B_16, 0, 0 } +#define MASK_SRY_MRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } + +#define INSTR_RRF_MRR 6, { M4_16, R_24, R_28, 0, 0, 0 } +#define MASK_RRF_MRR { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } + +#define INSTR_SIY_DRI 6, { D20_20, B_16, I8_16, 0, 0, 0 } +#define MASK_SIY_DRI { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } /* QEMU-END */ /* The opcode formats table (blueprints for .insn pseudo mnemonic). */ @@ -926,6 +962,30 @@ static const struct s390_opcode s390_opcodes[] = { "ldeb", OP48(0xed0000000004LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0}, { "brxlg", OP48(0xec0000000045LL), MASK_RIE_RRP, INSTR_RIE_RRP, 2, 2}, { "brxhg", OP48(0xec0000000044LL), MASK_RIE_RRP, INSTR_RIE_RRP, 2, 2}, +/* QEMU-ADD: */ + { "crj", OP48(0xec0000000076LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, + { "cgrj", OP48(0xec0000000064LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, + { "clrj", OP48(0xec0000000077LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, + { "clgrj", OP48(0xec0000000065LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, + { "cij", OP48(0xec000000007eLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, + { "cgij", OP48(0xec000000007cLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, + { "clij", OP48(0xec000000007fLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, + { "clgij", OP48(0xec000000007dLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, + { "risbg", OP48(0xec0000000055LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, + { "risbhg", OP48(0xec000000005dLL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, + { "risblg", OP48(0xec0000000051LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, + { "rnsbg", OP48(0xec0000000054LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, + { "rosbg", OP48(0xec0000000056LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, + { "rxsbg", OP48(0xec0000000057LL), MASK_RIE_RRIII, INSTR_RIE_RRIII, 3, 6}, + { "cit", OP48(0xec0000000072LL), MASK_RIE_MRI, INSTR_RIE_MRI, 3, 6}, + { "cgit", OP48(0xec0000000070LL), MASK_RIE_MRI, INSTR_RIE_MRI, 3, 6}, + { "clfit", OP48(0xec0000000073LL), MASK_RIE_MRU, INSTR_RIE_MRU, 3, 6}, + { "clgit", OP48(0xec0000000071LL), MASK_RIE_MRU, INSTR_RIE_MRU, 3, 6}, + { "ahik", OP48(0xec00000000d8LL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6}, + { "aghik", OP48(0xec00000000d9LL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6}, + { "alhsik", OP48(0xec00000000daLL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6}, + { "alghsik", OP48(0xec00000000dbLL), MASK_RIE_RRI, INSTR_RIE_RRI, 3, 6}, +/* QEMU-END */ { "tp", OP48(0xeb00000000c0LL), MASK_RSL_R0RD, INSTR_RSL_R0RD, 3, 0}, { "stamy", OP48(0xeb000000009bLL), MASK_RSY_AARD, INSTR_RSY_AARD, 2, 3}, { "lamy", OP48(0xeb000000009aLL), MASK_RSY_AARD, INSTR_RSY_AARD, 2, 3}, @@ -985,6 +1045,20 @@ static const struct s390_opcode s390_opcodes[] = { "srag", OP48(0xeb000000000aLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, { "lmg", OP48(0xeb0000000004LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3}, { "lmg", OP48(0xeb0000000004LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2}, +/* QEMU-ADD: */ + { "loc", OP48(0xeb00000000f2LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6}, + { "locg", OP48(0xeb00000000e2LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6}, + { "stoc", OP48(0xeb00000000f3LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6}, + { "stocg", OP48(0xeb00000000e3LL), MASK_SRY_MRRD, INSTR_RSY_MRRD, 3, 6}, + { "srak", OP48(0xeb00000000dcLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6}, + { "slak", OP48(0xeb00000000ddLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6}, + { "srlk", OP48(0xeb00000000deLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6}, + { "sllk", OP48(0xeb00000000dfLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 6}, + { "asi", OP48(0xeb000000006aLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6}, + { "alsi", OP48(0xeb000000006eLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6}, + { "agsi", OP48(0xeb000000007aLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6}, + { "algsi", OP48(0xeb000000007eLL), MASK_SIY_DRI, INSTR_SIY_DRI, 3, 6}, +/* QEMU-END */ { "unpka", OP8(0xeaLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, { "pka", OP8(0xe9LL), MASK_SS_L2RDRD, INSTR_SS_L2RDRD, 3, 0}, { "mvcin", OP8(0xe8LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, @@ -993,6 +1067,17 @@ static const struct s390_opcode s390_opcodes[] = { "tprot", OP16(0xe501LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0}, { "strag", OP48(0xe50000000002LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 2, 2}, { "lasp", OP16(0xe500LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0}, +/* QEMU-ADD: */ + { "mvhhi", OP16(0xe544LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "mvghi", OP16(0xe548LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "mvhi", OP16(0xe54cLL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "chhsi", OP16(0xe554LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "clhhsi", OP16(0xe555LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "cghsi", OP16(0xe558LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "clghsi", OP16(0xe559LL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "chsi", OP16(0xe55cLL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, + { "clfhsi", OP16(0xe55dLL), MASK_SIL_DRI, INSTR_SIL_DRI, 3, 6}, +/* QEMU-END */ { "slb", OP48(0xe30000000099LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, { "slb", OP48(0xe30000000099LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2}, { "alc", OP48(0xe30000000098LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3}, @@ -1116,6 +1201,9 @@ static const struct s390_opcode s390_opcodes[] = { "lrag", OP48(0xe30000000003LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3}, { "lrag", OP48(0xe30000000003LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2}, { "ltg", OP48(0xe30000000002LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4}, +/* QEMU-ADD: */ + { "pfd", OP48(0xe30000000036LL), MASK_RXY_URRD, INSTR_RXY_URRD, 3, 6}, +/* QEMU-END */ { "unpku", OP8(0xe2LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, { "pku", OP8(0xe1LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, { "edmk", OP8(0xdfLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0}, @@ -1135,6 +1223,32 @@ static const struct s390_opcode s390_opcodes[] = { "csst", OP16(0xc802LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 5}, { "ectg", OP16(0xc801LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 5}, { "mvcos", OP16(0xc800LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 4}, +/* QEMU-ADD: */ + { "exrl", OP16(0xc600ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "pfdrl", OP16(0xc602ll), MASK_RIL_UP, INSTR_RIL_UP, 3, 6}, + { "cghrl", OP16(0xc604ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "chrl", OP16(0xc605ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "clghrl", OP16(0xc606ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "clhrl", OP16(0xc607ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "cgrl", OP16(0xc608ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "clgrl", OP16(0xc60all), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "cgfrl", OP16(0xc60cll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "crl", OP16(0xc60dll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "clgfrl", OP16(0xc60ell), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "clrl", OP16(0xc60fll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + + { "llhrl", OP16(0xc400ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "lghrl", OP16(0xc404ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "lhrl", OP16(0xc405ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "llghrl", OP16(0xc406ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "sthrl", OP16(0xc407ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "lgrl", OP16(0xc408ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "stgrl", OP16(0xc40bll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "lgfrl", OP16(0xc40cll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "lrl", OP16(0xc40dll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "llgfrl", OP16(0xc40ell), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "strl", OP16(0xc40fll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, +/* QEMU-END */ { "clfi", OP16(0xc20fLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, { "clgfi", OP16(0xc20eLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, { "cfi", OP16(0xc20dLL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4}, @@ -1265,6 +1379,29 @@ static const struct s390_opcode s390_opcodes[] = { "ltgr", OP16(0xb902LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, { "lngr", OP16(0xb901LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, { "lpgr", OP16(0xb900LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2}, +/* QEMU-ADD: */ + { "crt", OP16(0xb972LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6}, + { "cgrt", OP16(0xb960LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6}, + { "clrt", OP16(0xb973LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6}, + { "clgrt", OP16(0xb961LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 6}, + { "locr", OP16(0xb9f2LL), MASK_RRF_MRR, INSTR_RRF_MRR, 3, 6}, + { "locgr", OP16(0xb9e2LL), MASK_RRF_MRR, INSTR_RRF_MRR, 3, 6}, + { "popcnt", OP16(0xb9e1LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 6}, + { "ngrk", OP16(0xb9e4LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "ogrk", OP16(0xb9e6LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "xgrk", OP16(0xb9e7LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "agrk", OP16(0xb9e8LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "sgrk", OP16(0xb9e9LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "algrk", OP16(0xb9eaLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "slgrk", OP16(0xb9ebLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "nrk", OP16(0xb9f4LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "ork", OP16(0xb9f6LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "xrk", OP16(0xb9f7LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "ark", OP16(0xb9f8LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "srk", OP16(0xb9f9LL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "alrk", OP16(0xb9faLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, + { "slrk", OP16(0xb9fbLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 3, 6}, +/* QEMU-END */ { "lctl", OP8(0xb7LL), MASK_RS_CCRD, INSTR_RS_CCRD, 3, 0}, { "stctl", OP8(0xb6LL), MASK_RS_CCRD, INSTR_RS_CCRD, 3, 0}, { "rrxtr", OP16(0xb3ffLL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5}, @@ -1426,6 +1563,20 @@ static const struct s390_opcode s390_opcodes[] = { "ltebr", OP16(0xb302LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, { "lnebr", OP16(0xb301LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, { "lpebr", OP16(0xb300LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0}, +/* QEMU-ADD: */ + { "clfebr", OP16(0xb39cLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "clfdbr", OP16(0xb39dLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "clfxbr", OP16(0xb39eLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "clgebr", OP16(0xb3acLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "clgdbr", OP16(0xb3adLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "clgxbr", OP16(0xb3aeLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "celfbr", OP16(0xb390LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "cdlfbr", OP16(0xb391LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "cxlfbr", OP16(0xb392LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "celgbr", OP16(0xb3a0LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "cdlgbr", OP16(0xb3a1LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, + { "cxlgbr", OP16(0xb3a2LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 3, 6}, +/* QEMU-END */ { "trap4", OP16(0xb2ffLL), MASK_S_RD, INSTR_S_RD, 3, 0}, { "lfas", OP16(0xb2bdLL), MASK_S_RD, INSTR_S_RD, 2, 5}, { "srnmt", OP16(0xb2b9LL), MASK_S_RD, INSTR_S_RD, 2, 5}, @@ -1774,22 +1925,6 @@ static const struct s390_opcode s390_opcodes[] = { "sckpf", OP16(0x0107LL), MASK_E, INSTR_E, 3, 0}, { "upt", OP16(0x0102LL), MASK_E, INSTR_E, 3, 0}, { "pr", OP16(0x0101LL), MASK_E, INSTR_E, 3, 0}, - -/* QEMU-ADD: */ - { "crj", OP48(0xec0000000076LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, - { "cgrj", OP48(0xec0000000064LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, - { "clrj", OP48(0xec0000000077LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, - { "clgrj", OP48(0xec0000000065LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, - - { "cij", OP48(0xec000000007eLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, - { "cgij", OP48(0xec000000007cLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, - { "clij", OP48(0xec000000007fLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, - { "clgij", OP48(0xec000000007dLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, - - { "lrl", OP16(0xc40dll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "lgrl", OP16(0xc408ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, - { "lgfrl", OP16(0xc40cll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, -/* QEMU-END */ }; static const int s390_num_opcodes = diff --git a/docs/migration.txt b/docs/migration.txt index f3ddd2f1a8..0719a55002 100644 --- a/docs/migration.txt +++ b/docs/migration.txt @@ -55,10 +55,7 @@ QEMUFile with: QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, QEMUFileGetBufferFunc *get_buffer, - QEMUFileCloseFunc *close, - QEMUFileRateLimit *rate_limit, - QEMUFileSetRateLimit *set_rate_limit, - QEMUFileGetRateLimit *get_rate_limit); + QEMUFileCloseFunc *close); The functions have the following functionality: @@ -80,24 +77,9 @@ Close a file and return an error code. typedef int (QEMUFileCloseFunc)(void *opaque); -Called to determine if the file has exceeded its bandwidth allocation. The -bandwidth capping is a soft limit, not a hard limit. - -typedef int (QEMUFileRateLimit)(void *opaque); - -Called to change the current bandwidth allocation. This function must return -the new actual bandwidth. It should be new_rate if everything goes OK, and -the old rate otherwise. - -typedef size_t (QEMUFileSetRateLimit)(void *opaque, size_t new_rate); -typedef size_t (QEMUFileGetRateLimit)(void *opaque); - You can use any internal state that you need using the opaque void * pointer that is passed to all functions. -The rate limiting functions are used to limit the bandwidth used by -QEMU migration. - The important functions for us are put_buffer()/get_buffer() that allow to write/read a buffer into the QEMUFile. diff --git a/docs/q35-chipset.cfg b/docs/q35-chipset.cfg new file mode 100644 index 0000000000..1b6efc0f2c --- /dev/null +++ b/docs/q35-chipset.cfg @@ -0,0 +1,129 @@ +################################################################ +# +# qemu -M q35 creates a bare machine with just the very essential +# chipset devices being present: +# +# 00.0 - Host bridge +# 1f.0 - ISA bridge / LPC +# 1f.2 - SATA (AHCI) controller +# 1f.3 - SMBus controller +# +# This config file documents the other devices and how they are +# created. You can simply use "-readconfig $thisfile" to create +# them all. Here is a overview: +# +# 19.0 - Ethernet controller (not created, our e1000 emulation +# doesn't emulate the ich9 device). +# 1a.* - USB Controller #2 (ehci + uhci companions) +# 1b.0 - HD Audio Controller +# 1c.* - PCI Express Ports +# 1d.* - USB Controller #1 (ehci + uhci companions, +# "qemu -M q35 -usb" creates these too) +# 1e.0 - PCI Bridge +# + +[device "ich9-ehci-2"] + driver = "ich9-usb-ehci2" + multifunction = "on" + bus = "pcie.0" + addr = "1a.7" + +[device "ich9-uhci-4"] + driver = "ich9-usb-uhci4" + multifunction = "on" + bus = "pcie.0" + addr = "1a.0" + masterbus = "ich9-ehci-2.0" + firstport = "0" + +[device "ich9-uhci-5"] + driver = "ich9-usb-uhci5" + multifunction = "on" + bus = "pcie.0" + addr = "1a.1" + masterbus = "ich9-ehci-2.0" + firstport = "2" + +[device "ich9-uhci-6"] + driver = "ich9-usb-uhci6" + multifunction = "on" + bus = "pcie.0" + addr = "1a.2" + masterbus = "ich9-ehci-2.0" + firstport = "4" + + +[device "ich9-hda-audio"] + driver = "ich9-intel-hda" + bus = "pcie.0" + addr = "1b.0" + + +[device "ich9-pcie-port-1"] + driver = "ioh3420" + multifunction = "on" + bus = "pcie.0" + addr = "1c.0" + port = "1" + chassis = "1" + +[device "ich9-pcie-port-2"] + driver = "ioh3420" + multifunction = "on" + bus = "pcie.0" + addr = "1c.1" + port = "2" + chassis = "2" + +[device "ich9-pcie-port-3"] + driver = "ioh3420" + multifunction = "on" + bus = "pcie.0" + addr = "1c.2" + port = "3" + chassis = "3" + +[device "ich9-pcie-port-4"] + driver = "ioh3420" + multifunction = "on" + bus = "pcie.0" + addr = "1c.3" + port = "4" + chassis = "4" + + +[device "ich9-ehci-1"] + driver = "ich9-usb-ehci1" + multifunction = "on" + bus = "pcie.0" + addr = "1d.7" + +[device "ich9-uhci-1"] + driver = "ich9-usb-uhci1" + multifunction = "on" + bus = "pcie.0" + addr = "1d.0" + masterbus = "ich9-ehci-1.0" + firstport = "0" + +[device "ich9-uhci-2"] + driver = "ich9-usb-uhci2" + multifunction = "on" + bus = "pcie.0" + addr = "1d.1" + masterbus = "ich9-ehci-1.0" + firstport = "2" + +[device "ich9-uhci-3"] + driver = "ich9-usb-uhci3" + multifunction = "on" + bus = "pcie.0" + addr = "1d.2" + masterbus = "ich9-ehci-1.0" + firstport = "4" + + +[device "ich9-pci-bridge"] + driver = "i82801b11-bridge" + bus = "pcie.0" + addr = "1e.0" diff --git a/docs/specs/pci-ids.txt b/docs/specs/pci-ids.txt new file mode 100644 index 0000000000..3c65e1a6ef --- /dev/null +++ b/docs/specs/pci-ids.txt @@ -0,0 +1,50 @@ + +PCI IDs for qemu +================ + +Red Hat, Inc. donates a part of its device ID range to qemu, to be used for +virtual devices. The vendor IDs are 1af4 (formerly Qumranet ID) and 1b36. + +Contact Gerd Hoffmann to get a device ID assigned +for your devices. + +1af4 vendor ID +-------------- + +The 1000 -> 10ff device ID range is used as follows for virtio-pci devices. +Note that this allocation separate from the virtio device IDs, which are +maintained as part of the virtio specification. + +1af4:1000 network device +1af4:1001 block device +1af4:1002 balloon device +1af4:1003 console device +1af4:1004 SCSI host bus adapter device +1af4:1005 entropy generator device +1af4:1009 9p filesystem device + +1af4:10f0 Available for experimental usage without registration. Must get + to official ID when the code leaves the test lab (i.e. when seeking +1af4:10ff upstream merge or shipping a distro/product) to avoid conflicts. + +1af4:1100 Used as PCI Subsystem ID for existing hardware devices emulated + by qemu. + +1af4:1110 ivshmem device (shared memory, docs/specs/ivshmem_device_spec.txt) + +All other device IDs are reserved. + +1b36 vendor ID +-------------- + +The 0000 -> 00ff device ID range is used as follows for QEMU-specific +PCI devices (other than virtio): + +1b36:0001 PCI-PCI bridge +1b36:0002 PCI serial port (16550A) adapter (docs/specs/pci-serial.txt) +1b36:0003 PCI Dual-port 16550A adapter (docs/specs/pci-serial.txt) +1b36:0004 PCI Quad-port 16550A adapter (docs/specs/pci-serial.txt) + +All these devices are documented in docs/specs. + +The 0100 device ID is used for the QXL video card device. diff --git a/docs/tracing.txt b/docs/tracing.txt index 453cc4a63d..14db3bffb4 100644 --- a/docs/tracing.txt +++ b/docs/tracing.txt @@ -23,7 +23,7 @@ for debugging, profiling, and observing execution. 4. Pretty-print the binary trace file: - ./simpletrace.py trace-events trace-* + ./scripts/simpletrace.py trace-events trace-* == Trace events == @@ -198,7 +198,7 @@ The "simple" backend produces binary trace files that can be formatted with the simpletrace.py script. The script takes the "trace-events" file and the binary trace: - ./simpletrace.py trace-events trace-12345 + ./scripts/simpletrace.py trace-events trace-12345 You must ensure that the same "trace-events" file was used to build QEMU, otherwise trace event declarations may have changed and output will not be diff --git a/docs/usb-storage.txt b/docs/usb-storage.txt index e58e849d4d..fa93111cf6 100644 --- a/docs/usb-storage.txt +++ b/docs/usb-storage.txt @@ -2,7 +2,7 @@ qemu usb storage emulation -------------------------- -QEMU has two emulations for usb storage devices. +QEMU has three devices for usb storage emulation. Number one emulates the classic bulk-only transport protocol which is used by 99% of the usb sticks on the marked today and is called @@ -31,6 +31,15 @@ with tree logical units: -device scsi-cd,bus=uas.0,scsi-id=0,lun=5,drive=uas-cdrom +Number three emulates the classic bulk-only transport protocol too. +It's called "usb-bot". It shares most code with "usb-storage", and +the guest will not be able to see the difference. The qemu command +line interface is simliar to usb-uas though, i.e. no automatic scsi +disk creation. It also features support for up to 16 LUNs. The LUN +numbers must be continous, i.e. for three devices you must use 0+1+2. +The 0+1+5 numbering from the "usb-uas" example isn't going to work +with "usb-bot". + enjoy, Gerd diff --git a/docs/virtio-balloon-stats.txt b/docs/virtio-balloon-stats.txt new file mode 100644 index 0000000000..f74612f468 --- /dev/null +++ b/docs/virtio-balloon-stats.txt @@ -0,0 +1,104 @@ +virtio balloon memory statistics +================================ + +The virtio balloon driver supports guest memory statistics reporting. These +statistics are available to QEMU users as QOM (QEMU Object Model) device +properties via a polling mechanism. + +Before querying the available stats, clients first have to enable polling. +This is done by writing a time interval value (in seconds) to the +guest-stats-polling-interval property. This value can be: + + > 0 enables polling in the specified interval. If polling is already + enabled, the polling time interval is changed to the new value + + 0 disables polling. Previous polled statistics are still valid and + can be queried. + +Once polling is enabled, the virtio-balloon device in QEMU will start +polling the guest's balloon driver for new stats in the specified time +interval. + +To retrieve those stats, clients have to query the guest-stats property, +which will return a dictionary containing: + + o A key named 'stats', containing all available stats. If the guest + doesn't support a particular stat, or if it couldn't be retrieved, + its value will be -1. Currently, the following stats are supported: + + - stat-swap-in + - stat-swap-out + - stat-major-faults + - stat-minor-faults + - stat-free-memory + - stat-total-memory + + o A key named last-update, which contains the last stats update + timestamp in seconds. Since this timestamp is generated by the host, + a buggy guest can't influence its value + +It's also important to note the following: + + - Previously polled statistics remain available even if the polling is + later disabled + + - As noted above, if a guest doesn't support a particular stat its value + will always be -1. However, it's also possible that a guest temporarily + couldn't update one or even all stats. If this happens, just wait for + the next update + + - Polling can be enabled even if the guest doesn't have stats support + or the balloon driver wasn't loaded in the guest. If this is the case + and stats are queried, an error will be returned + + - The polling timer is only re-armed when the guest responds to the + statistics request. This means that if a (buggy) guest doesn't ever + respond to the request the timer will never be re-armed, which has + the same effect as disabling polling + +Here are a few examples. QEMU is started with '-balloon virtio', which +generates '/machine/peripheral-anon/device[1]' as the QOM path for the +balloon device. + +Enable polling with 2 seconds interval: + +{ "execute": "qom-set", + "arguments": { "path": "/machine/peripheral-anon/device[1]", + "property": "guest-stats-polling-interval", "value": 2 } } + +{ "return": {} } + +Change polling to 10 seconds: + +{ "execute": "qom-set", + "arguments": { "path": "/machine/peripheral-anon/device[1]", + "property": "guest-stats-polling-interval", "value": 10 } } + +{ "return": {} } + +Get stats: + +{ "execute": "qom-get", + "arguments": { "path": "/machine/peripheral-anon/device[1]", + "property": "guest-stats" } } +{ + "return": { + "stats": { + "stat-swap-out": 0, + "stat-free-memory": 844943360, + "stat-minor-faults": 219028, + "stat-major-faults": 235, + "stat-total-memory": 1044406272, + "stat-swap-in": 0 + }, + "last-update": 1358529861 + } +} + +Disable polling: + +{ "execute": "qom-set", + "arguments": { "path": "/machine/peripheral-anon/device[1]", + "property": "stats-polling-interval", "value": 0 } } + +{ "return": {} } diff --git a/dump.c b/dump.c index 4ed1fa8622..a25f5096eb 100644 --- a/dump.c +++ b/dump.c @@ -271,11 +271,13 @@ static int write_elf64_note(DumpState *s) static int write_elf64_notes(DumpState *s) { CPUArchState *env; + CPUState *cpu; int ret; int id; for (env = first_cpu; env != NULL; env = env->next_cpu) { - id = cpu_index(env); + cpu = ENV_GET_CPU(env); + id = cpu_index(cpu); ret = cpu_write_elf64_note(fd_write_vmcore, env, id, s); if (ret < 0) { dump_error(s, "dump: failed to write elf notes.\n"); @@ -321,11 +323,13 @@ static int write_elf32_note(DumpState *s) static int write_elf32_notes(DumpState *s) { CPUArchState *env; + CPUState *cpu; int ret; int id; for (env = first_cpu; env != NULL; env = env->next_cpu) { - id = cpu_index(env); + cpu = ENV_GET_CPU(env); + id = cpu_index(cpu); ret = cpu_write_elf32_note(fd_write_vmcore, env, id, s); if (ret < 0) { dump_error(s, "dump: failed to write elf notes.\n"); diff --git a/exec.c b/exec.c index a6923addd4..8a6aac36e3 100644 --- a/exec.c +++ b/exec.c @@ -78,7 +78,7 @@ DEFINE_TLS(CPUArchState *,cpu_single_env); /* 0 = Do not count executed instructions. 1 = Precise instruction counting. 2 = Adaptive rate instruction counting. */ -int use_icount = 0; +int use_icount; #if !defined(CONFIG_USER_ONLY) @@ -219,16 +219,16 @@ void cpu_exec_init_all(void) #endif } -#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY) +#if !defined(CONFIG_USER_ONLY) static int cpu_common_post_load(void *opaque, int version_id) { - CPUArchState *env = opaque; + CPUState *cpu = opaque; /* 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the version_id is increased. */ - env->interrupt_request &= ~0x01; - tlb_flush(env, 1); + cpu->interrupt_request &= ~0x01; + tlb_flush(cpu->env_ptr, 1); return 0; } @@ -240,31 +240,35 @@ static const VMStateDescription vmstate_cpu_common = { .minimum_version_id_old = 1, .post_load = cpu_common_post_load, .fields = (VMStateField []) { - VMSTATE_UINT32(halted, CPUArchState), - VMSTATE_UINT32(interrupt_request, CPUArchState), + VMSTATE_UINT32(halted, CPUState), + VMSTATE_UINT32(interrupt_request, CPUState), VMSTATE_END_OF_LIST() } }; +#else +#define vmstate_cpu_common vmstate_dummy #endif -CPUArchState *qemu_get_cpu(int cpu) +CPUState *qemu_get_cpu(int index) { CPUArchState *env = first_cpu; + CPUState *cpu = NULL; while (env) { - if (env->cpu_index == cpu) + cpu = ENV_GET_CPU(env); + if (cpu->cpu_index == index) { break; + } env = env->next_cpu; } - return env; + return env ? cpu : NULL; } void cpu_exec_init(CPUArchState *env) { -#ifndef CONFIG_USER_ONLY CPUState *cpu = ENV_GET_CPU(env); -#endif + CPUClass *cc = CPU_GET_CLASS(cpu); CPUArchState **penv; int cpu_index; @@ -278,8 +282,8 @@ void cpu_exec_init(CPUArchState *env) penv = &(*penv)->next_cpu; cpu_index++; } - env->cpu_index = cpu_index; - env->numa_node = 0; + cpu->cpu_index = cpu_index; + cpu->numa_node = 0; QTAILQ_INIT(&env->breakpoints); QTAILQ_INIT(&env->watchpoints); #ifndef CONFIG_USER_ONLY @@ -289,11 +293,15 @@ void cpu_exec_init(CPUArchState *env) #if defined(CONFIG_USER_ONLY) cpu_list_unlock(); #endif + vmstate_register(NULL, cpu_index, &vmstate_cpu_common, cpu); #if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY) - vmstate_register(NULL, cpu_index, &vmstate_cpu_common, env); register_savevm(NULL, "cpu", cpu_index, CPU_SAVE_VERSION, cpu_save, cpu_load, env); + assert(cc->vmsd == NULL); #endif + if (cc->vmsd != NULL) { + vmstate_register(NULL, cpu_index, cc->vmsd, cpu); + } } #if defined(TARGET_HAS_ICE) @@ -484,15 +492,12 @@ void cpu_single_step(CPUArchState *env, int enabled) #endif } -void cpu_reset_interrupt(CPUArchState *env, int mask) -{ - env->interrupt_request &= ~mask; -} - void cpu_exit(CPUArchState *env) { - env->exit_request = 1; - cpu_unlink_tb(env); + CPUState *cpu = ENV_GET_CPU(env); + + cpu->exit_request = 1; + cpu->tcg_exit_req = 1; } void cpu_abort(CPUArchState *env, const char *fmt, ...) @@ -531,7 +536,6 @@ CPUArchState *cpu_copy(CPUArchState *env) { CPUArchState *new_env = cpu_init(env->cpu_model_str); CPUArchState *next_cpu = new_env->next_cpu; - int cpu_index = new_env->cpu_index; #if defined(TARGET_HAS_ICE) CPUBreakpoint *bp; CPUWatchpoint *wp; @@ -539,9 +543,8 @@ CPUArchState *cpu_copy(CPUArchState *env) memcpy(new_env, env, sizeof(CPUArchState)); - /* Preserve chaining and index. */ + /* Preserve chaining. */ new_env->next_cpu = next_cpu; - new_env->cpu_index = cpu_index; /* Clone all break/watchpoints. Note: Once we support ptrace with hw-debug register access, make sure @@ -843,6 +846,8 @@ static void *file_ram_alloc(RAMBlock *block, const char *path) { char *filename; + char *sanitized_name; + char *c; void *area; int fd; #ifdef MAP_POPULATE @@ -864,18 +869,25 @@ static void *file_ram_alloc(RAMBlock *block, return NULL; } - if (asprintf(&filename, "%s/qemu_back_mem.XXXXXX", path) == -1) { - return NULL; + /* Make name safe to use with mkstemp by replacing '/' with '_'. */ + sanitized_name = g_strdup(block->mr->name); + for (c = sanitized_name; *c != '\0'; c++) { + if (*c == '/') + *c = '_'; } + filename = g_strdup_printf("%s/qemu_back_mem.%s.XXXXXX", path, + sanitized_name); + g_free(sanitized_name); + fd = mkstemp(filename); if (fd < 0) { perror("unable to create backing store for hugepages"); - free(filename); + g_free(filename); return NULL; } unlink(filename); - free(filename); + g_free(filename); memory = (memory+hpagesize-1) & ~(hpagesize-1); @@ -1466,7 +1478,7 @@ static void check_watchpoint(int offset, int len_mask, int flags) /* We re-entered the check after replacing the TB. Now raise * the debug interrupt so that is will trigger after the * current instruction. */ - cpu_interrupt(env, CPU_INTERRUPT_DEBUG); + cpu_interrupt(ENV_GET_CPU(env), CPU_INTERRUPT_DEBUG); return; } vaddr = (env->mem_io_vaddr & TARGET_PAGE_MASK) + offset; diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 0cfa6b4831..83ccc4b8cd 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1271,11 +1271,18 @@ float64 int64_to_float64( int64 a STATUS_PARAM ) } -float64 uint64_to_float64( uint64 a STATUS_PARAM ) +float64 uint64_to_float64(uint64 a STATUS_PARAM) { - if ( a == 0 ) return float64_zero; - return normalizeRoundAndPackFloat64( 0, 0x43C, a STATUS_VAR ); + int exp = 0x43C; + if (a == 0) { + return float64_zero; + } + if ((int64_t)a < 0) { + shift64RightJamming(a, 1, &a); + exp += 1; + } + return normalizeRoundAndPackFloat64(0, exp, a STATUS_VAR); } /*---------------------------------------------------------------------------- @@ -1332,6 +1339,14 @@ float128 int64_to_float128( int64 a STATUS_PARAM ) } +float128 uint64_to_float128(uint64 a STATUS_PARAM) +{ + if (a == 0) { + return float128_zero; + } + return normalizeRoundAndPackFloat128(0, 0x406E, a, 0 STATUS_VAR); +} + /*---------------------------------------------------------------------------- | Returns the result of converting the single-precision floating-point value | `a' to the 32-bit two's complement integer format. The conversion is @@ -2219,7 +2234,7 @@ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) } } /* Zero plus something non-zero : just return the something */ - return make_float32(float32_val(c) ^ (signflip << 31)); + return packFloat32(cSign ^ signflip, cExp, cSig); } if (aExp == 0) { @@ -3772,7 +3787,7 @@ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) } } /* Zero plus something non-zero : just return the something */ - return make_float64(float64_val(c) ^ ((uint64_t)signflip << 63)); + return packFloat64(cSign ^ signflip, cExp, cSig); } if (aExp == 0) { diff --git a/fsdev/Makefile.objs b/fsdev/Makefile.objs index cb1e2500b9..206289c49f 100644 --- a/fsdev/Makefile.objs +++ b/fsdev/Makefile.objs @@ -1,9 +1,10 @@ ifeq ($(CONFIG_REALLY_VIRTFS),y) common-obj-y = qemu-fsdev.o virtio-9p-marshal.o - -# Toplevel always builds this; targets without virtio will put it in -# common-obj-y -extra-obj-y = qemu-fsdev-dummy.o else common-obj-y = qemu-fsdev-dummy.o endif +common-obj-y += qemu-fsdev-opts.o + +# Toplevel always builds this; targets without virtio will put it in +# common-obj-y +common-obj-$(CONFIG_ALL) += qemu-fsdev-dummy.o diff --git a/fsdev/qemu-fsdev-dummy.c b/fsdev/qemu-fsdev-dummy.c index 4bcf38fe4b..7dc2630a78 100644 --- a/fsdev/qemu-fsdev-dummy.c +++ b/fsdev/qemu-fsdev-dummy.c @@ -20,10 +20,3 @@ int qemu_fsdev_add(QemuOpts *opts) { return 0; } - -static void fsdev_register_config(void) -{ - qemu_add_opts(&qemu_fsdev_opts); - qemu_add_opts(&qemu_virtfs_opts); -} -machine_init(fsdev_register_config); diff --git a/fsdev/qemu-fsdev-opts.c b/fsdev/qemu-fsdev-opts.c new file mode 100644 index 0000000000..6311c7a7e5 --- /dev/null +++ b/fsdev/qemu-fsdev-opts.c @@ -0,0 +1,85 @@ +/* + * Virtio 9p + * + * 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/config-file.h" +#include "qemu/option.h" +#include "qemu/module.h" + +static QemuOptsList qemu_fsdev_opts = { + .name = "fsdev", + .implied_opt_name = "fsdriver", + .head = QTAILQ_HEAD_INITIALIZER(qemu_fsdev_opts.head), + .desc = { + { + .name = "fsdriver", + .type = QEMU_OPT_STRING, + }, { + .name = "path", + .type = QEMU_OPT_STRING, + }, { + .name = "security_model", + .type = QEMU_OPT_STRING, + }, { + .name = "writeout", + .type = QEMU_OPT_STRING, + }, { + .name = "readonly", + .type = QEMU_OPT_BOOL, + + }, { + .name = "socket", + .type = QEMU_OPT_STRING, + }, { + .name = "sock_fd", + .type = QEMU_OPT_NUMBER, + }, + + { /*End of list */ } + }, +}; + +static QemuOptsList qemu_virtfs_opts = { + .name = "virtfs", + .implied_opt_name = "fsdriver", + .head = QTAILQ_HEAD_INITIALIZER(qemu_virtfs_opts.head), + .desc = { + { + .name = "fsdriver", + .type = QEMU_OPT_STRING, + }, { + .name = "path", + .type = QEMU_OPT_STRING, + }, { + .name = "mount_tag", + .type = QEMU_OPT_STRING, + }, { + .name = "security_model", + .type = QEMU_OPT_STRING, + }, { + .name = "writeout", + .type = QEMU_OPT_STRING, + }, { + .name = "readonly", + .type = QEMU_OPT_BOOL, + }, { + .name = "socket", + .type = QEMU_OPT_STRING, + }, { + .name = "sock_fd", + .type = QEMU_OPT_NUMBER, + }, + + { /*End of list */ } + }, +}; + +static void fsdev_register_config(void) +{ + qemu_add_opts(&qemu_fsdev_opts); + qemu_add_opts(&qemu_virtfs_opts); +} +machine_init(fsdev_register_config); diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c index 4cc04d4fde..6eaf36dbfa 100644 --- a/fsdev/qemu-fsdev.c +++ b/fsdev/qemu-fsdev.c @@ -97,11 +97,3 @@ FsDriverEntry *get_fsdev_fsentry(char *id) } return NULL; } - -static void fsdev_register_config(void) -{ - qemu_add_opts(&qemu_fsdev_opts); - qemu_add_opts(&qemu_virtfs_opts); -} -machine_init(fsdev_register_config); - diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c index 6b9afd32d5..36f66163b2 100644 --- a/fsdev/virtfs-proxy-helper.c +++ b/fsdev/virtfs-proxy-helper.c @@ -1039,7 +1039,7 @@ int main(int argc, char **argv) } switch (c) { case 'p': - rpath = strdup(optarg); + rpath = g_strdup(optarg); break; case 'n': is_daemon = false; @@ -1048,7 +1048,7 @@ int main(int argc, char **argv) sock = atoi(optarg); break; case 's': - sock_name = strdup(optarg); + sock_name = g_strdup(optarg); break; case 'u': own_u = atoi(optarg); diff --git a/gdbstub.c b/gdbstub.c index a8dd437ec0..43b7d4d00f 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -40,6 +40,7 @@ #include "cpu.h" #include "qemu/sockets.h" #include "sysemu/kvm.h" +#include "qemu/bitops.h" #ifndef TARGET_CPU_MEMORY_RW_DEBUG static inline int target_memory_rw_debug(CPUArchState *env, target_ulong addr, @@ -1535,27 +1536,34 @@ static int cpu_gdb_write_register(CPUAlphaState *env, uint8_t *mem_buf, int n) } #elif defined (TARGET_S390X) -#define NUM_CORE_REGS S390_NUM_TOTAL_REGS +#define NUM_CORE_REGS S390_NUM_REGS static int cpu_gdb_read_register(CPUS390XState *env, uint8_t *mem_buf, int n) { + uint64_t val; + int cc_op; + switch (n) { - case S390_PSWM_REGNUM: GET_REGL(env->psw.mask); break; - case S390_PSWA_REGNUM: GET_REGL(env->psw.addr); break; - case S390_R0_REGNUM ... S390_R15_REGNUM: - GET_REGL(env->regs[n-S390_R0_REGNUM]); break; - case S390_A0_REGNUM ... S390_A15_REGNUM: - GET_REG32(env->aregs[n-S390_A0_REGNUM]); break; - case S390_FPC_REGNUM: GET_REG32(env->fpc); break; - case S390_F0_REGNUM ... S390_F15_REGNUM: - /* XXX */ - break; - case S390_PC_REGNUM: GET_REGL(env->psw.addr); break; - case S390_CC_REGNUM: - env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, - env->cc_vr); - GET_REG32(env->cc_op); - break; + case S390_PSWM_REGNUM: + cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, env->cc_vr); + val = deposit64(env->psw.mask, 44, 2, cc_op); + GET_REGL(val); + break; + case S390_PSWA_REGNUM: + GET_REGL(env->psw.addr); + break; + case S390_R0_REGNUM ... S390_R15_REGNUM: + GET_REGL(env->regs[n-S390_R0_REGNUM]); + break; + case S390_A0_REGNUM ... S390_A15_REGNUM: + GET_REG32(env->aregs[n-S390_A0_REGNUM]); + break; + case S390_FPC_REGNUM: + GET_REG32(env->fpc); + break; + case S390_F0_REGNUM ... S390_F15_REGNUM: + GET_REG64(env->fregs[n-S390_F0_REGNUM].ll); + break; } return 0; @@ -1570,20 +1578,30 @@ static int cpu_gdb_write_register(CPUS390XState *env, uint8_t *mem_buf, int n) tmp32 = ldl_p(mem_buf); switch (n) { - case S390_PSWM_REGNUM: env->psw.mask = tmpl; break; - case S390_PSWA_REGNUM: env->psw.addr = tmpl; break; - case S390_R0_REGNUM ... S390_R15_REGNUM: - env->regs[n-S390_R0_REGNUM] = tmpl; break; - case S390_A0_REGNUM ... S390_A15_REGNUM: - env->aregs[n-S390_A0_REGNUM] = tmp32; r=4; break; - case S390_FPC_REGNUM: env->fpc = tmp32; r=4; break; - case S390_F0_REGNUM ... S390_F15_REGNUM: - /* XXX */ - break; - case S390_PC_REGNUM: env->psw.addr = tmpl; break; - case S390_CC_REGNUM: env->cc_op = tmp32; r=4; break; + case S390_PSWM_REGNUM: + env->psw.mask = tmpl; + env->cc_op = extract64(tmpl, 44, 2); + break; + case S390_PSWA_REGNUM: + env->psw.addr = tmpl; + break; + case S390_R0_REGNUM ... S390_R15_REGNUM: + env->regs[n-S390_R0_REGNUM] = tmpl; + break; + case S390_A0_REGNUM ... S390_A15_REGNUM: + env->aregs[n-S390_A0_REGNUM] = tmp32; + r = 4; + break; + case S390_FPC_REGNUM: + env->fpc = tmp32; + r = 4; + break; + case S390_F0_REGNUM ... S390_F15_REGNUM: + env->fregs[n-S390_F0_REGNUM].ll = tmpl; + break; + default: + return 0; } - return r; } #elif defined (TARGET_LM32) @@ -2048,9 +2066,11 @@ static void gdb_set_cpu_pc(GDBState *s, target_ulong pc) static CPUArchState *find_cpu(uint32_t thread_id) { CPUArchState *env; + CPUState *cpu; for (env = first_cpu; env != NULL; env = env->next_cpu) { - if (cpu_index(env) == thread_id) { + cpu = ENV_GET_CPU(env); + if (cpu_index(cpu) == thread_id) { return env; } } @@ -2078,7 +2098,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) case '?': /* TODO: Make this return the correct value for user-mode. */ snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP, - cpu_index(s->c_cpu)); + cpu_index(ENV_GET_CPU(s->c_cpu))); put_packet(s, buf); /* Remove all the breakpoints when this query is issued, * because gdb is doing and initial connect and the state @@ -2373,7 +2393,8 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } else if (strcmp(p,"sThreadInfo") == 0) { report_cpuinfo: if (s->query_cpu) { - snprintf(buf, sizeof(buf), "m%x", cpu_index(s->query_cpu)); + snprintf(buf, sizeof(buf), "m%x", + cpu_index(ENV_GET_CPU(s->query_cpu))); put_packet(s, buf); s->query_cpu = s->query_cpu->next_cpu; } else @@ -2383,10 +2404,11 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) thread = strtoull(p+16, (char **)&p, 16); env = find_cpu(thread); if (env != NULL) { + CPUState *cpu = ENV_GET_CPU(env); cpu_synchronize_state(env); len = snprintf((char *)mem_buf, sizeof(mem_buf), - "CPU#%d [%s]", env->cpu_index, - env->halted ? "halted " : "running"); + "CPU#%d [%s]", cpu->cpu_index, + cpu->halted ? "halted " : "running"); memtohex(buf, mem_buf, len); put_packet(s, buf); } @@ -2493,6 +2515,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) { GDBState *s = gdbserver_state; CPUArchState *env = s->c_cpu; + CPUState *cpu = ENV_GET_CPU(env); char buf[256]; const char *type; int ret; @@ -2521,7 +2544,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) } snprintf(buf, sizeof(buf), "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";", - GDB_SIGNAL_TRAP, cpu_index(env), type, + GDB_SIGNAL_TRAP, cpu_index(cpu), type, env->watchpoint_hit->vaddr); env->watchpoint_hit = NULL; goto send_packet; @@ -2554,7 +2577,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) ret = GDB_SIGNAL_UNKNOWN; break; } - snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_index(env)); + snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_index(cpu)); send_packet: put_packet(s, buf); @@ -2818,7 +2841,7 @@ static void gdb_accept(void) GDBState *s; struct sockaddr_in sockaddr; socklen_t len; - int val, fd; + int fd; for(;;) { len = sizeof(sockaddr); @@ -2835,8 +2858,7 @@ static void gdb_accept(void) } /* set short latency */ - val = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); + socket_set_nodelay(fd); s = g_malloc0(sizeof(GDBState)); s->c_cpu = first_cpu; diff --git a/hmp-commands.hx b/hmp-commands.hx index 010b8c9ba5..df44906ef9 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -295,14 +295,14 @@ ETEXI .name = "log", .args_type = "items:s", .params = "item1[,...]", - .help = "activate logging of the specified items to '/tmp/qemu.log'", + .help = "activate logging of the specified items", .mhandler.cmd = do_log, }, STEXI @item log @var{item1}[,...] @findex log -Activate logging of the specified items to @file{/tmp/qemu.log}. +Activate logging of the specified items. ETEXI { @@ -837,6 +837,44 @@ STEXI @item nmi @var{cpu} @findex nmi Inject an NMI on the given CPU (x86 only). + +ETEXI + + { + .name = "ringbuf_write", + .args_type = "device:s,data:s", + .params = "device data", + .help = "Write to a ring buffer character device", + .mhandler.cmd = hmp_ringbuf_write, + }, + +STEXI +@item ringbuf_write @var{device} @var{data} +@findex ringbuf_write +Write @var{data} to ring buffer character device @var{device}. +@var{data} must be a UTF-8 string. + +ETEXI + + { + .name = "ringbuf_read", + .args_type = "device:s,size:i", + .params = "device size", + .help = "Read from a ring buffer character device", + .mhandler.cmd = hmp_ringbuf_read, + }, + +STEXI +@item ringbuf_read @var{device} +@findex ringbuf_read +Read and print up to @var{size} bytes from ring buffer character +device @var{device}. +Certain non-printable characters are printed \uXXXX, where XXXX is the +character code in hexadecimal. Character \ is printed \\. +Bug: can screw up when the buffer contains invalid UTF-8 sequences, +NUL characters, after the ring buffer lost data, and when reading +stops because the size limit is reached. + ETEXI { @@ -1131,7 +1169,7 @@ ETEXI { .name = "netdev_add", .args_type = "netdev:O", - .params = "[user|tap|socket],id=str[,prop=value][,...]", + .params = "[user|tap|socket|hubport],id=str[,prop=value][,...]", .help = "add host network device", .mhandler.cmd = hmp_netdev_add, }, @@ -1482,6 +1520,38 @@ Password is invalidated at the given time. @var{nsec} are the seconds passed since 1970, i.e. unix epoch. @end table +ETEXI + + { + .name = "chardev-add", + .args_type = "args:s", + .params = "args", + .help = "add chardev", + .mhandler.cmd = hmp_chardev_add, + }, + +STEXI +@item chardev_add args +@findex chardev_add + +chardev_add accepts the same parameters as the -chardev command line switch. + +ETEXI + + { + .name = "chardev-remove", + .args_type = "id:s", + .params = "id", + .help = "remove chardev", + .mhandler.cmd = hmp_chardev_remove, + }, + +STEXI +@item chardev_remove id +@findex chardev_remove + +Removes the chardev @var{id}. + ETEXI { @@ -1489,7 +1559,8 @@ ETEXI .args_type = "item:s?", .params = "[subcommand]", .help = "show various information about the system state", - .mhandler.cmd = do_info, + .mhandler.cmd = do_info_help, + .sub_table = info_cmds, }, STEXI @@ -1570,6 +1641,8 @@ show device tree show qdev device model list @item info roms show roms +@item info tpm +show the TPM device @end table ETEXI diff --git a/hmp.c b/hmp.c index 9e9e62450e..b0a861cfbb 100644 --- a/hmp.c +++ b/hmp.c @@ -31,7 +31,7 @@ static void hmp_handle_error(Monitor *mon, Error **errp) } } -void hmp_info_name(Monitor *mon) +void hmp_info_name(Monitor *mon, const QDict *qdict) { NameInfo *info; @@ -42,7 +42,7 @@ void hmp_info_name(Monitor *mon) qapi_free_NameInfo(info); } -void hmp_info_version(Monitor *mon) +void hmp_info_version(Monitor *mon, const QDict *qdict) { VersionInfo *info; @@ -55,7 +55,7 @@ void hmp_info_version(Monitor *mon) qapi_free_VersionInfo(info); } -void hmp_info_kvm(Monitor *mon) +void hmp_info_kvm(Monitor *mon, const QDict *qdict) { KvmInfo *info; @@ -70,7 +70,7 @@ void hmp_info_kvm(Monitor *mon) qapi_free_KvmInfo(info); } -void hmp_info_status(Monitor *mon) +void hmp_info_status(Monitor *mon, const QDict *qdict) { StatusInfo *info; @@ -89,7 +89,7 @@ void hmp_info_status(Monitor *mon) qapi_free_StatusInfo(info); } -void hmp_info_uuid(Monitor *mon) +void hmp_info_uuid(Monitor *mon, const QDict *qdict) { UuidInfo *info; @@ -98,7 +98,7 @@ void hmp_info_uuid(Monitor *mon) qapi_free_UuidInfo(info); } -void hmp_info_chardev(Monitor *mon) +void hmp_info_chardev(Monitor *mon, const QDict *qdict) { ChardevInfoList *char_info, *info; @@ -111,7 +111,7 @@ void hmp_info_chardev(Monitor *mon) qapi_free_ChardevInfoList(char_info); } -void hmp_info_mice(Monitor *mon) +void hmp_info_mice(Monitor *mon, const QDict *qdict) { MouseInfoList *mice_list, *mouse; @@ -131,7 +131,7 @@ void hmp_info_mice(Monitor *mon) qapi_free_MouseInfoList(mice_list); } -void hmp_info_migrate(Monitor *mon) +void hmp_info_migrate(Monitor *mon, const QDict *qdict) { MigrationInfo *info; MigrationCapabilityStatusList *caps, *cap; @@ -209,7 +209,7 @@ void hmp_info_migrate(Monitor *mon) qapi_free_MigrationCapabilityStatusList(caps); } -void hmp_info_migrate_capabilities(Monitor *mon) +void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict) { MigrationCapabilityStatusList *caps, *cap; @@ -228,13 +228,13 @@ void hmp_info_migrate_capabilities(Monitor *mon) qapi_free_MigrationCapabilityStatusList(caps); } -void hmp_info_migrate_cache_size(Monitor *mon) +void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "xbzrel cache size: %" PRId64 " kbytes\n", qmp_query_migrate_cache_size(NULL) >> 10); } -void hmp_info_cpus(Monitor *mon) +void hmp_info_cpus(Monitor *mon, const QDict *qdict) { CpuInfoList *cpu_list, *cpu; @@ -272,7 +272,7 @@ void hmp_info_cpus(Monitor *mon) qapi_free_CpuInfoList(cpu_list); } -void hmp_info_block(Monitor *mon) +void hmp_info_block(Monitor *mon, const QDict *qdict) { BlockInfoList *block_list, *info; @@ -326,7 +326,7 @@ void hmp_info_block(Monitor *mon) qapi_free_BlockInfoList(block_list); } -void hmp_info_blockstats(Monitor *mon) +void hmp_info_blockstats(Monitor *mon, const QDict *qdict) { BlockStatsList *stats_list, *stats; @@ -360,7 +360,7 @@ void hmp_info_blockstats(Monitor *mon) qapi_free_BlockStatsList(stats_list); } -void hmp_info_vnc(Monitor *mon) +void hmp_info_vnc(Monitor *mon, const QDict *qdict) { VncInfo *info; Error *err = NULL; @@ -406,7 +406,7 @@ out: qapi_free_VncInfo(info); } -void hmp_info_spice(Monitor *mon) +void hmp_info_spice(Monitor *mon, const QDict *qdict) { SpiceChannelList *chan; SpiceInfo *info; @@ -453,7 +453,7 @@ out: qapi_free_SpiceInfo(info); } -void hmp_info_balloon(Monitor *mon) +void hmp_info_balloon(Monitor *mon, const QDict *qdict) { BalloonInfo *info; Error *err = NULL; @@ -465,29 +465,7 @@ void hmp_info_balloon(Monitor *mon) return; } - monitor_printf(mon, "balloon: actual=%" PRId64, info->actual >> 20); - if (info->has_mem_swapped_in) { - monitor_printf(mon, " mem_swapped_in=%" PRId64, info->mem_swapped_in); - } - if (info->has_mem_swapped_out) { - monitor_printf(mon, " mem_swapped_out=%" PRId64, info->mem_swapped_out); - } - if (info->has_major_page_faults) { - monitor_printf(mon, " major_page_faults=%" PRId64, - info->major_page_faults); - } - if (info->has_minor_page_faults) { - monitor_printf(mon, " minor_page_faults=%" PRId64, - info->minor_page_faults); - } - if (info->has_free_mem) { - monitor_printf(mon, " free_mem=%" PRId64, info->free_mem); - } - if (info->has_total_mem) { - monitor_printf(mon, " total_mem=%" PRId64, info->total_mem); - } - - monitor_printf(mon, "\n"); + monitor_printf(mon, "balloon: actual=%" PRId64 "\n", info->actual >> 20); qapi_free_BalloonInfo(info); } @@ -570,7 +548,7 @@ static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev) } } -void hmp_info_pci(Monitor *mon) +void hmp_info_pci(Monitor *mon, const QDict *qdict) { PciInfoList *info_list, *info; Error *err = NULL; @@ -593,7 +571,7 @@ void hmp_info_pci(Monitor *mon) qapi_free_PciInfoList(info_list); } -void hmp_info_block_jobs(Monitor *mon) +void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) { BlockJobInfoList *list; Error *err = NULL; @@ -629,6 +607,50 @@ void hmp_info_block_jobs(Monitor *mon) } } +void hmp_info_tpm(Monitor *mon, const QDict *qdict) +{ + TPMInfoList *info_list, *info; + Error *err = NULL; + unsigned int c = 0; + TPMPassthroughOptions *tpo; + + info_list = qmp_query_tpm(&err); + if (err) { + monitor_printf(mon, "TPM device not supported\n"); + error_free(err); + return; + } + + if (info_list) { + monitor_printf(mon, "TPM device:\n"); + } + + for (info = info_list; info; info = info->next) { + TPMInfo *ti = info->value; + monitor_printf(mon, " tpm%d: model=%s\n", + c, TpmModel_lookup[ti->model]); + + monitor_printf(mon, " \\ %s: type=%s", + ti->id, TpmType_lookup[ti->type]); + + switch (ti->tpm_options->kind) { + case TPM_TYPE_OPTIONS_KIND_TPM_PASSTHROUGH_OPTIONS: + tpo = ti->tpm_options->tpm_passthrough_options; + monitor_printf(mon, "%s%s%s%s", + tpo->has_path ? ",path=" : "", + tpo->has_path ? tpo->path : "", + tpo->has_cancel_path ? ",cancel-path=" : "", + tpo->has_cancel_path ? tpo->cancel_path : ""); + break; + case TPM_TYPE_OPTIONS_KIND_MAX: + break; + } + monitor_printf(mon, "\n"); + c++; + } + qapi_free_TPMInfoList(info_list); +} + void hmp_quit(Monitor *mon, const QDict *qdict) { monitor_suspend(mon); @@ -684,6 +706,48 @@ void hmp_pmemsave(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &errp); } +void hmp_ringbuf_write(Monitor *mon, const QDict *qdict) +{ + const char *chardev = qdict_get_str(qdict, "device"); + const char *data = qdict_get_str(qdict, "data"); + Error *errp = NULL; + + qmp_ringbuf_write(chardev, data, false, 0, &errp); + + hmp_handle_error(mon, &errp); +} + +void hmp_ringbuf_read(Monitor *mon, const QDict *qdict) +{ + uint32_t size = qdict_get_int(qdict, "size"); + const char *chardev = qdict_get_str(qdict, "device"); + char *data; + Error *errp = NULL; + int i; + + data = qmp_ringbuf_read(chardev, size, false, 0, &errp); + if (errp) { + monitor_printf(mon, "%s\n", error_get_pretty(errp)); + error_free(errp); + return; + } + + for (i = 0; data[i]; i++) { + unsigned char ch = data[i]; + + if (ch == '\\') { + monitor_printf(mon, "\\\\"); + } else if ((ch < 0x20 && ch != '\n' && ch != '\t') || ch == 0x7F) { + monitor_printf(mon, "\\u%04X", ch); + } else { + monitor_printf(mon, "%c", ch); + } + + } + monitor_printf(mon, "\n"); + g_free(data); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { @@ -796,7 +860,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict) qmp_drive_mirror(device, filename, !!format, format, full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, - true, mode, false, 0, + true, mode, false, 0, false, 0, false, 0, false, 0, false, 0, &errp); hmp_handle_error(mon, &errp); } @@ -880,7 +944,7 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict) qapi_free_MigrationCapabilityStatusList(caps); if (err) { - monitor_printf(mon, "migrate_set_parameter: %s\n", + monitor_printf(mon, "migrate_set_capability: %s\n", error_get_pretty(err)); error_free(err); } @@ -1336,3 +1400,26 @@ void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict) qmp_nbd_server_stop(&errp); hmp_handle_error(mon, &errp); } + +void hmp_chardev_add(Monitor *mon, const QDict *qdict) +{ + const char *args = qdict_get_str(qdict, "args"); + Error *err = NULL; + QemuOpts *opts; + + opts = qemu_opts_parse(qemu_find_opts("chardev"), args, 1); + if (opts == NULL) { + error_setg(&err, "Parsing chardev args failed"); + } else { + qemu_chr_new_from_opts(opts, NULL, &err); + } + hmp_handle_error(mon, &err); +} + +void hmp_chardev_remove(Monitor *mon, const QDict *qdict) +{ + Error *local_err = NULL; + + qmp_chardev_remove(qdict_get_str(qdict, "id"), &local_err); + hmp_handle_error(mon, &local_err); +} diff --git a/hmp.h b/hmp.h index 21f3e05d09..95fe76e218 100644 --- a/hmp.h +++ b/hmp.h @@ -18,24 +18,25 @@ #include "qapi-types.h" #include "qapi/qmp/qdict.h" -void hmp_info_name(Monitor *mon); -void hmp_info_version(Monitor *mon); -void hmp_info_kvm(Monitor *mon); -void hmp_info_status(Monitor *mon); -void hmp_info_uuid(Monitor *mon); -void hmp_info_chardev(Monitor *mon); -void hmp_info_mice(Monitor *mon); -void hmp_info_migrate(Monitor *mon); -void hmp_info_migrate_capabilities(Monitor *mon); -void hmp_info_migrate_cache_size(Monitor *mon); -void hmp_info_cpus(Monitor *mon); -void hmp_info_block(Monitor *mon); -void hmp_info_blockstats(Monitor *mon); -void hmp_info_vnc(Monitor *mon); -void hmp_info_spice(Monitor *mon); -void hmp_info_balloon(Monitor *mon); -void hmp_info_pci(Monitor *mon); -void hmp_info_block_jobs(Monitor *mon); +void hmp_info_name(Monitor *mon, const QDict *qdict); +void hmp_info_version(Monitor *mon, const QDict *qdict); +void hmp_info_kvm(Monitor *mon, const QDict *qdict); +void hmp_info_status(Monitor *mon, const QDict *qdict); +void hmp_info_uuid(Monitor *mon, const QDict *qdict); +void hmp_info_chardev(Monitor *mon, const QDict *qdict); +void hmp_info_mice(Monitor *mon, const QDict *qdict); +void hmp_info_migrate(Monitor *mon, const QDict *qdict); +void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict); +void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict); +void hmp_info_cpus(Monitor *mon, const QDict *qdict); +void hmp_info_block(Monitor *mon, const QDict *qdict); +void hmp_info_blockstats(Monitor *mon, const QDict *qdict); +void hmp_info_vnc(Monitor *mon, const QDict *qdict); +void hmp_info_spice(Monitor *mon, const QDict *qdict); +void hmp_info_balloon(Monitor *mon, const QDict *qdict); +void hmp_info_pci(Monitor *mon, const QDict *qdict); +void hmp_info_block_jobs(Monitor *mon, const QDict *qdict); +void hmp_info_tpm(Monitor *mon, const QDict *qdict); void hmp_quit(Monitor *mon, const QDict *qdict); void hmp_stop(Monitor *mon, const QDict *qdict); void hmp_system_reset(Monitor *mon, const QDict *qdict); @@ -43,6 +44,8 @@ void hmp_system_powerdown(Monitor *mon, const QDict *qdict); void hmp_cpu(Monitor *mon, const QDict *qdict); void hmp_memsave(Monitor *mon, const QDict *qdict); void hmp_pmemsave(Monitor *mon, const QDict *qdict); +void hmp_ringbuf_write(Monitor *mon, const QDict *qdict); +void hmp_ringbuf_read(Monitor *mon, const QDict *qdict); void hmp_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); @@ -80,5 +83,7 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict); void hmp_nbd_server_start(Monitor *mon, const QDict *qdict); void hmp_nbd_server_add(Monitor *mon, const QDict *qdict); void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict); +void hmp_chardev_add(Monitor *mon, const QDict *qdict); +void hmp_chardev_remove(Monitor *mon, const QDict *qdict); #endif diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 6761bce9dc..d321c802f2 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -14,9 +14,9 @@ #include "hw/virtio.h" #include "hw/pc.h" #include "qemu/sockets.h" -#include "hw/virtio-pci.h" #include "virtio-9p.h" #include "fsdev/qemu-fsdev.h" +#include "virtio-9p-device.h" #include "virtio-9p-xattr.h" #include "virtio-9p-coth.h" @@ -85,11 +85,7 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) } s->ctx.export_flags = fse->export_flags; - if (fse->path) { - s->ctx.fs_root = g_strdup(fse->path); - } else { - s->ctx.fs_root = NULL; - } + s->ctx.fs_root = g_strdup(fse->path); s->ctx.exops.get_st_gen = NULL; len = strlen(conf->tag); if (len > MAX_TAG_LEN - 1) { @@ -98,7 +94,7 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) exit(1); } - s->tag = strdup(conf->tag); + s->tag = g_strdup(conf->tag); s->ctx.uid = -1; s->ops = fse->ops; @@ -140,54 +136,3 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) return &s->vdev; } - -static int virtio_9p_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - vdev = virtio_9p_init(&pci_dev->qdev, &proxy->fsconf); - vdev->nvectors = proxy->nvectors; - virtio_init_pci(proxy, vdev); - /* make the actual value visible */ - proxy->nvectors = vdev->nvectors; - return 0; -} - -static Property virtio_9p_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), - DEFINE_PROP_STRING("mount_tag", VirtIOPCIProxy, fsconf.tag), - DEFINE_PROP_STRING("fsdev", VirtIOPCIProxy, fsconf.fsdev_id), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_9p_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_9p_init_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = 0x1009; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = 0x2; - dc->props = virtio_9p_properties; - dc->reset = virtio_pci_reset; -} - -static TypeInfo virtio_9p_info = { - .name = "virtio-9p-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_9p_class_init, -}; - -static void virtio_9p_register_types(void) -{ - type_register_static(&virtio_9p_info); - virtio_9p_set_fd_limit(); -} - -type_init(virtio_9p_register_types) diff --git a/hw/9p.h b/hw/9pfs/virtio-9p-device.h similarity index 85% rename from hw/9p.h rename to hw/9pfs/virtio-9p-device.h index d9951d6bcc..65789db131 100644 --- a/hw/9p.h +++ b/hw/9pfs/virtio-9p-device.h @@ -11,8 +11,8 @@ * */ -#ifndef QEMU_9P_H -#define QEMU_9P_H +#ifndef QEMU_VIRTIO_9P_DEVICE_H +#define QEMU_VIRTIO_9P_DEVICE_H typedef struct V9fsConf { diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c index 113602144c..f1b1c83a22 100644 --- a/hw/9pfs/virtio-9p-local.c +++ b/hw/9pfs/virtio-9p-local.c @@ -46,7 +46,7 @@ static const char *local_mapped_attr_path(FsContext *ctx, const char *path, char *buffer) { char *dir_name; - char *tmp_path = strdup(path); + char *tmp_path = g_strdup(path); char *base_name = basename(tmp_path); /* NULL terminate the directory */ @@ -55,7 +55,7 @@ static const char *local_mapped_attr_path(FsContext *ctx, snprintf(buffer, PATH_MAX, "%s/%s/%s/%s", ctx->fs_root, dir_name, VIRTFS_META_DIR, base_name); - free(tmp_path); + g_free(tmp_path); return buffer; } @@ -130,7 +130,7 @@ static int local_create_mapped_attr_dir(FsContext *ctx, const char *path) { int err; char attr_dir[PATH_MAX]; - char *tmp_path = strdup(path); + char *tmp_path = g_strdup(path); snprintf(attr_dir, PATH_MAX, "%s/%s/%s", ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR); @@ -139,7 +139,7 @@ static int local_create_mapped_attr_dir(FsContext *ctx, const char *path) if (err < 0 && errno == EEXIST) { err = 0; } - free(tmp_path); + g_free(tmp_path); return err; } diff --git a/hw/9pfs/virtio-9p-proxy.c b/hw/9pfs/virtio-9p-proxy.c index d5ad208d00..730027900e 100644 --- a/hw/9pfs/virtio-9p-proxy.c +++ b/hw/9pfs/virtio-9p-proxy.c @@ -13,6 +13,7 @@ #include #include "hw/virtio.h" #include "virtio-9p.h" +#include "qemu/error-report.h" #include "fsdev/qemu-fsdev.h" #include "virtio-9p-proxy.h" @@ -521,7 +522,7 @@ static int v9fs_request(V9fsProxy *proxy, int type, } break; default: - error_report("Invalid type %d\n", type); + error_report("Invalid type %d", type); retval = -EINVAL; break; } diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c index 0aaf0d2de0..5cc4c92012 100644 --- a/hw/9pfs/virtio-9p.c +++ b/hw/9pfs/virtio-9p.c @@ -14,7 +14,6 @@ #include "hw/virtio.h" #include "hw/pc.h" #include "qemu/sockets.h" -#include "hw/virtio-pci.h" #include "virtio-9p.h" #include "fsdev/qemu-fsdev.h" #include "virtio-9p-xattr.h" @@ -327,7 +326,7 @@ static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp) return retval; } -static void put_fid(V9fsPDU *pdu, V9fsFidState *fidp) +static int put_fid(V9fsPDU *pdu, V9fsFidState *fidp) { BUG_ON(!fidp->ref); fidp->ref--; @@ -348,8 +347,9 @@ static void put_fid(V9fsPDU *pdu, V9fsFidState *fidp) pdu->s->migration_blocker = NULL; } } - free_fid(pdu, fidp); + return free_fid(pdu, fidp); } + return 0; } static V9fsFidState *clunk_fid(V9fsState *s, int32_t fid) @@ -1537,9 +1537,10 @@ static void v9fs_clunk(void *opaque) * free the fid. */ fidp->ref++; - err = offset; - - put_fid(pdu, fidp); + err = put_fid(pdu, fidp); + if (!err) { + err = offset; + } out_nofid: complete_pdu(s, pdu, err); } @@ -3101,11 +3102,7 @@ static void v9fs_xattrcreate(void *opaque) xattr_fidp->fs.xattr.flags = flags; v9fs_string_init(&xattr_fidp->fs.xattr.name); v9fs_string_copy(&xattr_fidp->fs.xattr.name, &name); - if (size) { - xattr_fidp->fs.xattr.value = g_malloc(size); - } else { - xattr_fidp->fs.xattr.value = NULL; - } + xattr_fidp->fs.xattr.value = g_malloc(size); err = offset; put_fid(pdu, file_fidp); out_nofid: @@ -3271,7 +3268,7 @@ void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) free_pdu(s, pdu); } -void virtio_9p_set_fd_limit(void) +static void __attribute__((__constructor__)) virtio_9p_set_fd_limit(void) { struct rlimit rlim; if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h index 406fe522db..52b1c6997f 100644 --- a/hw/9pfs/virtio-9p.h +++ b/hw/9pfs/virtio-9p.h @@ -389,7 +389,6 @@ static inline uint8_t v9fs_request_cancelled(V9fsPDU *pdu) } extern void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq); -extern void virtio_9p_set_fd_limit(void); extern void v9fs_reclaim_fd(V9fsPDU *pdu); extern void v9fs_path_init(V9fsPath *path); extern void v9fs_path_free(V9fsPath *path); diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 2778035b1c..eb7eb31a19 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -1,8 +1,15 @@ -common-obj-y = usb/ ide/ pci/ +# core qdev-related obj files, also used by *-user: +common-obj-y += qdev.o qdev-properties.o +# irq.o needed for qdev GPIO handling: +common-obj-y += irq.o + +ifeq ($(CONFIG_SOFTMMU),y) +common-obj-y += usb/ ide/ pci/ common-obj-y += loader.o common-obj-$(CONFIG_VIRTIO) += virtio-console.o common-obj-$(CONFIG_VIRTIO) += virtio-rng.o common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o +common-obj-$(CONFIG_VIRTIO) += virtio-bus.o common-obj-y += fw_cfg.o common-obj-$(CONFIG_PCI) += pci_bridge_dev.o common-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o @@ -31,17 +38,18 @@ common-obj-$(CONFIG_DMA) += dma.o common-obj-$(CONFIG_I82374) += i82374.o common-obj-$(CONFIG_HPET) += hpet.o common-obj-$(CONFIG_APPLESMC) += applesmc.o -common-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o +ifeq ($(CONFIG_USB_SMARTCARD),y) +common-obj-y += ccid-card-passthru.o common-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o +endif common-obj-$(CONFIG_I8259) += i8259_common.o i8259.o -common-obj-y += fifo.o +common-obj-$(CONFIG_SDHCI) += sdhci.o common-obj-y += pam.o -extra-obj-y += pci/ - # PPC devices common-obj-$(CONFIG_PREP_PCI) += prep_pci.o common-obj-$(CONFIG_I82378) += i82378.o +common-obj-$(CONFIG_PC87312) += pc87312.o # Mac shared devices common-obj-$(CONFIG_MACIO) += macio.o common-obj-$(CONFIG_CUDA) += cuda.o @@ -100,6 +108,9 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o # PCI watchdog devices common-obj-$(CONFIG_PCI) += wdt_i6300esb.o +# IndustryPack +common-obj-$(CONFIG_IPACK) += tpci200.o ipoctal232.o ipack.o + # PCI network cards common-obj-$(CONFIG_NE2000_PCI) += ne2000.o common-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o @@ -154,7 +165,6 @@ common-obj-$(CONFIG_SOUND) += $(sound-obj-y) common-obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/ common-obj-y += usb/ -common-obj-y += irq.o common-obj-$(CONFIG_PTIMER) += ptimer.o common-obj-$(CONFIG_MAX7310) += max7310.o common-obj-$(CONFIG_WM8750) += wm8750.o @@ -179,9 +189,8 @@ common-obj-$(CONFIG_SSI_SD) += ssi-sd.o common-obj-$(CONFIG_SD) += sd.o common-obj-y += bt.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o common-obj-y += bt-hci-csr.o -common-obj-y += msmouse.o ps2.o -common-obj-y += qdev.o qdev-properties.o qdev-monitor.o -common-obj-$(CONFIG_BRLAPI) += baum.o +common-obj-y += ps2.o +common-obj-y += qdev-properties-system.o # xen backend driver support common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o @@ -190,14 +199,13 @@ common-obj-$(CONFIG_XEN_BACKEND) += xen_console.o xenfb.o xen_disk.o xen_nic.o # Per-target files # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly +obj-$(CONFIG_VIRTIO) += dataplane/ obj-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o obj-$(CONFIG_VIRTIO) += virtio-serial-bus.o virtio-scsi.o obj-$(CONFIG_SOFTMMU) += vhost_net.o obj-$(CONFIG_VHOST_NET) += vhost.o obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/ obj-$(CONFIG_VGA) += vga.o -obj-$(CONFIG_SOFTMMU) += device-hotplug.o -obj-$(CONFIG_XEN) += xen_domainbuild.o xen_machine_pv.o # Inter-VM PCI shared memory & VFIO PCI device assignment ifeq ($(CONFIG_PCI), y) @@ -205,4 +213,4 @@ obj-$(CONFIG_KVM) += ivshmem.o obj-$(CONFIG_LINUX) += vfio_pci.o endif -$(obj)/baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) +endif diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c index fc0a02ae86..648656d5b4 100644 --- a/hw/a15mpcore.c +++ b/hw/a15mpcore.c @@ -18,7 +18,8 @@ * with this program; if not, see . */ -#include "sysbus.h" +#include "hw/sysbus.h" +#include "sysemu/kvm.h" /* A15MP private memory region. */ @@ -40,13 +41,18 @@ static int a15mp_priv_init(SysBusDevice *dev) { A15MPPrivState *s = FROM_SYSBUS(A15MPPrivState, dev); SysBusDevice *busdev; + const char *gictype = "arm_gic"; - s->gic = qdev_create(NULL, "arm_gic"); + if (kvm_irqchip_in_kernel()) { + gictype = "kvm-arm-gic"; + } + + s->gic = qdev_create(NULL, gictype); qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq); qdev_prop_set_uint32(s->gic, "revision", 2); qdev_init_nofail(s->gic); - busdev = sysbus_from_qdev(s->gic); + busdev = SYS_BUS_DEVICE(s->gic); /* Pass through outbound IRQ lines from the GIC */ sysbus_pass_irq(dev, busdev); @@ -93,7 +99,7 @@ static void a15mp_priv_class_init(ObjectClass *klass, void *data) /* We currently have no savable state */ } -static TypeInfo a15mp_priv_info = { +static const TypeInfo a15mp_priv_info = { .name = "a15mpcore_priv", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(A15MPPrivState), diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c index f802de0824..0a1a10f37a 100644 --- a/hw/a9mpcore.c +++ b/hw/a9mpcore.c @@ -8,135 +8,36 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" +#include "hw/sysbus.h" -/* A9MP private memory region. */ - -typedef struct a9mp_priv_state { +typedef struct A9MPPrivState { SysBusDevice busdev; - uint32_t scu_control; - uint32_t scu_status; - uint32_t old_timer_status[8]; uint32_t num_cpu; - MemoryRegion scu_iomem; MemoryRegion container; DeviceState *mptimer; + DeviceState *wdt; DeviceState *gic; + DeviceState *scu; uint32_t num_irq; -} a9mp_priv_state; - -static uint64_t a9_scu_read(void *opaque, hwaddr offset, - unsigned size) -{ - a9mp_priv_state *s = (a9mp_priv_state *)opaque; - switch (offset) { - case 0x00: /* Control */ - return s->scu_control; - case 0x04: /* Configuration */ - return (((1 << s->num_cpu) - 1) << 4) | (s->num_cpu - 1); - case 0x08: /* CPU Power Status */ - return s->scu_status; - case 0x09: /* CPU status. */ - return s->scu_status >> 8; - case 0x0a: /* CPU status. */ - return s->scu_status >> 16; - case 0x0b: /* CPU status. */ - return s->scu_status >> 24; - case 0x0c: /* Invalidate All Registers In Secure State */ - return 0; - case 0x40: /* Filtering Start Address Register */ - case 0x44: /* Filtering End Address Register */ - /* RAZ/WI, like an implementation with only one AXI master */ - return 0; - case 0x50: /* SCU Access Control Register */ - case 0x54: /* SCU Non-secure Access Control Register */ - /* unimplemented, fall through */ - default: - return 0; - } -} - -static void a9_scu_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - a9mp_priv_state *s = (a9mp_priv_state *)opaque; - uint32_t mask; - uint32_t shift; - switch (size) { - case 1: - mask = 0xff; - break; - case 2: - mask = 0xffff; - break; - case 4: - mask = 0xffffffff; - break; - default: - fprintf(stderr, "Invalid size %u in write to a9 scu register %x\n", - size, (unsigned)offset); - return; - } - - switch (offset) { - case 0x00: /* Control */ - s->scu_control = value & 1; - break; - case 0x4: /* Configuration: RO */ - break; - case 0x08: case 0x09: case 0x0A: case 0x0B: /* Power Control */ - shift = (offset - 0x8) * 8; - s->scu_status &= ~(mask << shift); - s->scu_status |= ((value & mask) << shift); - break; - case 0x0c: /* Invalidate All Registers In Secure State */ - /* no-op as we do not implement caches */ - break; - case 0x40: /* Filtering Start Address Register */ - case 0x44: /* Filtering End Address Register */ - /* RAZ/WI, like an implementation with only one AXI master */ - break; - case 0x50: /* SCU Access Control Register */ - case 0x54: /* SCU Non-secure Access Control Register */ - /* unimplemented, fall through */ - default: - break; - } -} - -static const MemoryRegionOps a9_scu_ops = { - .read = a9_scu_read, - .write = a9_scu_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void a9mp_priv_reset(DeviceState *dev) -{ - a9mp_priv_state *s = FROM_SYSBUS(a9mp_priv_state, sysbus_from_qdev(dev)); - int i; - s->scu_control = 0; - for (i = 0; i < ARRAY_SIZE(s->old_timer_status); i++) { - s->old_timer_status[i] = 0; - } -} +} A9MPPrivState; static void a9mp_priv_set_irq(void *opaque, int irq, int level) { - a9mp_priv_state *s = (a9mp_priv_state *)opaque; + A9MPPrivState *s = (A9MPPrivState *)opaque; qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); } static int a9mp_priv_init(SysBusDevice *dev) { - a9mp_priv_state *s = FROM_SYSBUS(a9mp_priv_state, dev); - SysBusDevice *busdev, *gicbusdev; + A9MPPrivState *s = FROM_SYSBUS(A9MPPrivState, dev); + SysBusDevice *timerbusdev, *wdtbusdev, *gicbusdev, *scubusdev; int i; s->gic = qdev_create(NULL, "arm_gic"); qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq); qdev_init_nofail(s->gic); - gicbusdev = sysbus_from_qdev(s->gic); + gicbusdev = SYS_BUS_DEVICE(s->gic); /* Pass through outbound IRQ lines from the GIC */ sysbus_pass_irq(dev, gicbusdev); @@ -144,10 +45,20 @@ static int a9mp_priv_init(SysBusDevice *dev) /* Pass through inbound GPIO lines to the GIC */ qdev_init_gpio_in(&s->busdev.qdev, a9mp_priv_set_irq, s->num_irq - 32); + s->scu = qdev_create(NULL, "a9-scu"); + qdev_prop_set_uint32(s->scu, "num-cpu", s->num_cpu); + qdev_init_nofail(s->scu); + scubusdev = SYS_BUS_DEVICE(s->scu); + s->mptimer = qdev_create(NULL, "arm_mptimer"); qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu); qdev_init_nofail(s->mptimer); - busdev = sysbus_from_qdev(s->mptimer); + timerbusdev = SYS_BUS_DEVICE(s->mptimer); + + s->wdt = qdev_create(NULL, "arm_mptimer"); + qdev_prop_set_uint32(s->wdt, "num-cpu", s->num_cpu); + qdev_init_nofail(s->wdt); + wdtbusdev = SYS_BUS_DEVICE(s->wdt); /* Memory map (addresses are offsets from PERIPHBASE): * 0x0000-0x00ff -- Snoop Control Unit @@ -161,8 +72,8 @@ static int a9mp_priv_init(SysBusDevice *dev) * We should implement the global timer but don't currently do so. */ memory_region_init(&s->container, "a9mp-priv-container", 0x2000); - memory_region_init_io(&s->scu_iomem, &a9_scu_ops, s, "a9mp-scu", 0x100); - memory_region_add_subregion(&s->container, 0, &s->scu_iomem); + memory_region_add_subregion(&s->container, 0, + sysbus_mmio_get_region(scubusdev, 0)); /* GIC CPU interface */ memory_region_add_subregion(&s->container, 0x100, sysbus_mmio_get_region(gicbusdev, 1)); @@ -170,9 +81,9 @@ static int a9mp_priv_init(SysBusDevice *dev) * memory region, not the "timer/watchdog for core X" ones 11MPcore has. */ memory_region_add_subregion(&s->container, 0x600, - sysbus_mmio_get_region(busdev, 0)); + sysbus_mmio_get_region(timerbusdev, 0)); memory_region_add_subregion(&s->container, 0x620, - sysbus_mmio_get_region(busdev, 1)); + sysbus_mmio_get_region(wdtbusdev, 0)); memory_region_add_subregion(&s->container, 0x1000, sysbus_mmio_get_region(gicbusdev, 0)); @@ -183,35 +94,23 @@ static int a9mp_priv_init(SysBusDevice *dev) */ for (i = 0; i < s->num_cpu; i++) { int ppibase = (s->num_irq - 32) + i * 32; - sysbus_connect_irq(busdev, i * 2, + sysbus_connect_irq(timerbusdev, i, qdev_get_gpio_in(s->gic, ppibase + 29)); - sysbus_connect_irq(busdev, i * 2 + 1, + sysbus_connect_irq(wdtbusdev, i, qdev_get_gpio_in(s->gic, ppibase + 30)); } return 0; } -static const VMStateDescription vmstate_a9mp_priv = { - .name = "a9mpcore_priv", - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(scu_control, a9mp_priv_state), - VMSTATE_UINT32_ARRAY(old_timer_status, a9mp_priv_state, 8), - VMSTATE_UINT32_V(scu_status, a9mp_priv_state, 2), - VMSTATE_END_OF_LIST() - } -}; - static Property a9mp_priv_properties[] = { - DEFINE_PROP_UINT32("num-cpu", a9mp_priv_state, num_cpu, 1), + DEFINE_PROP_UINT32("num-cpu", A9MPPrivState, num_cpu, 1), /* The Cortex-A9MP may have anything from 0 to 224 external interrupt * IRQ lines (with another 32 internal). We default to 64+32, which * is the number provided by the Cortex-A9MP test chip in the * Realview PBX-A9 and Versatile Express A9 development boards. * Other boards may differ and should set this property appropriately. */ - DEFINE_PROP_UINT32("num-irq", a9mp_priv_state, num_irq, 96), + DEFINE_PROP_UINT32("num-irq", A9MPPrivState, num_irq, 96), DEFINE_PROP_END_OF_LIST(), }; @@ -222,14 +121,12 @@ static void a9mp_priv_class_init(ObjectClass *klass, void *data) k->init = a9mp_priv_init; dc->props = a9mp_priv_properties; - dc->vmsd = &vmstate_a9mp_priv; - dc->reset = a9mp_priv_reset; } -static TypeInfo a9mp_priv_info = { +static const TypeInfo a9mp_priv_info = { .name = "a9mpcore_priv", .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(a9mp_priv_state), + .instance_size = sizeof(A9MPPrivState), .class_init = a9mp_priv_class_init, }; diff --git a/hw/a9scu.c b/hw/a9scu.c new file mode 100644 index 0000000000..05897c2fa2 --- /dev/null +++ b/hw/a9scu.c @@ -0,0 +1,164 @@ +/* + * Cortex-A9MPCore Snoop Control Unit (SCU) emulation. + * + * Copyright (c) 2009 CodeSourcery. + * Copyright (c) 2011 Linaro Limited. + * Written by Paul Brook, Peter Maydell. + * + * This code is licensed under the GPL. + */ + +#include "hw/sysbus.h" + +/* A9MP private memory region. */ + +typedef struct A9SCUState { + SysBusDevice busdev; + MemoryRegion iomem; + uint32_t control; + uint32_t status; + uint32_t num_cpu; +} A9SCUState; + +#define TYPE_A9_SCU "a9-scu" +#define A9_SCU(obj) OBJECT_CHECK(A9SCUState, (obj), TYPE_A9_SCU) + +static uint64_t a9_scu_read(void *opaque, hwaddr offset, + unsigned size) +{ + A9SCUState *s = (A9SCUState *)opaque; + switch (offset) { + case 0x00: /* Control */ + return s->control; + case 0x04: /* Configuration */ + return (((1 << s->num_cpu) - 1) << 4) | (s->num_cpu - 1); + case 0x08: /* CPU Power Status */ + return s->status; + case 0x09: /* CPU status. */ + return s->status >> 8; + case 0x0a: /* CPU status. */ + return s->status >> 16; + case 0x0b: /* CPU status. */ + return s->status >> 24; + case 0x0c: /* Invalidate All Registers In Secure State */ + return 0; + case 0x40: /* Filtering Start Address Register */ + case 0x44: /* Filtering End Address Register */ + /* RAZ/WI, like an implementation with only one AXI master */ + return 0; + case 0x50: /* SCU Access Control Register */ + case 0x54: /* SCU Non-secure Access Control Register */ + /* unimplemented, fall through */ + default: + return 0; + } +} + +static void a9_scu_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + A9SCUState *s = (A9SCUState *)opaque; + uint32_t mask; + uint32_t shift; + switch (size) { + case 1: + mask = 0xff; + break; + case 2: + mask = 0xffff; + break; + case 4: + mask = 0xffffffff; + break; + default: + fprintf(stderr, "Invalid size %u in write to a9 scu register %x\n", + size, (unsigned)offset); + return; + } + + switch (offset) { + case 0x00: /* Control */ + s->control = value & 1; + break; + case 0x4: /* Configuration: RO */ + break; + case 0x08: case 0x09: case 0x0A: case 0x0B: /* Power Control */ + shift = (offset - 0x8) * 8; + s->status &= ~(mask << shift); + s->status |= ((value & mask) << shift); + break; + case 0x0c: /* Invalidate All Registers In Secure State */ + /* no-op as we do not implement caches */ + break; + case 0x40: /* Filtering Start Address Register */ + case 0x44: /* Filtering End Address Register */ + /* RAZ/WI, like an implementation with only one AXI master */ + break; + case 0x50: /* SCU Access Control Register */ + case 0x54: /* SCU Non-secure Access Control Register */ + /* unimplemented, fall through */ + default: + break; + } +} + +static const MemoryRegionOps a9_scu_ops = { + .read = a9_scu_read, + .write = a9_scu_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void a9_scu_reset(DeviceState *dev) +{ + A9SCUState *s = A9_SCU(dev); + s->control = 0; +} + +static void a9_scu_realize(DeviceState *dev, Error ** errp) +{ + A9SCUState *s = A9_SCU(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, &a9_scu_ops, s, "a9-scu", 0x100); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription vmstate_a9_scu = { + .name = "a9-scu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(control, A9SCUState), + VMSTATE_UINT32(status, A9SCUState), + VMSTATE_END_OF_LIST() + } +}; + +static Property a9_scu_properties[] = { + DEFINE_PROP_UINT32("num-cpu", A9SCUState, num_cpu, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void a9_scu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = a9_scu_realize; + dc->props = a9_scu_properties; + dc->vmsd = &vmstate_a9_scu; + dc->reset = a9_scu_reset; +} + +static const TypeInfo a9_scu_info = { + .name = TYPE_A9_SCU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(A9SCUState), + .class_init = a9_scu_class_init, +}; + +static void a9mp_register_types(void) +{ + type_register_static(&a9_scu_info); +} + +type_init(a9mp_register_types) diff --git a/hw/ac97.c b/hw/ac97.c index 5096b8b5ad..14c76df56c 100644 --- a/hw/ac97.c +++ b/hw/ac97.c @@ -17,10 +17,10 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "audiodev.h" +#include "hw/hw.h" +#include "hw/audiodev.h" #include "audio/audio.h" -#include "pci/pci.h" +#include "hw/pci/pci.h" #include "sysemu/dma.h" #include "ac97_int.h" @@ -1436,7 +1436,7 @@ static void ac97_class_init (ObjectClass *klass, void *data) dc->props = ac97_properties; } -static TypeInfo ac97_info = { +static const TypeInfo ac97_info = { .name = "AC97", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof (AC97DeviceState), diff --git a/hw/acpi.c b/hw/acpi.c index 97617c4ef5..53e47d5857 100644 --- a/hw/acpi.c +++ b/hw/acpi.c @@ -19,9 +19,9 @@ * GNU GPL, version 2 or (at your option) any later version. */ #include "sysemu/sysemu.h" -#include "hw.h" -#include "pc.h" -#include "acpi.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/acpi.h" #include "monitor/monitor.h" struct acpi_table_header { @@ -104,7 +104,7 @@ int acpi_table_add(const char *t) /* now read in the data files, reallocating buffer as needed */ for (f = strtok(buf, ":"); f; f = strtok(NULL, ":")) { - int fd = open(f, O_RDONLY); + int fd = open(f, O_RDONLY | O_BINARY); if (fd < 0) { fprintf(stderr, "can't open file %s: %s\n", f, strerror(errno)); diff --git a/hw/acpi_ich9.c b/hw/acpi_ich9.c index 37a50e6d7b..29f84ffb45 100644 --- a/hw/acpi_ich9.c +++ b/hw/acpi_ich9.c @@ -23,16 +23,16 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "pc.h" -#include "pci/pci.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/pci/pci.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" -#include "acpi.h" +#include "hw/acpi.h" #include "sysemu/kvm.h" #include "exec/address-spaces.h" -#include "ich9.h" +#include "hw/ich9.h" //#define DEBUG @@ -202,11 +202,13 @@ static void pm_powerdown_req(Notifier *n, void *opaque) acpi_pm1_evt_power_down(&pm->acpi_regs); } -void ich9_pm_init(ICH9LPCPMRegs *pm, qemu_irq sci_irq, qemu_irq cmos_s3) +void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, + qemu_irq sci_irq, qemu_irq cmos_s3) { memory_region_init(&pm->io, "ich9-pm", ICH9_PMIO_SIZE); memory_region_set_enabled(&pm->io, false); - memory_region_add_subregion(get_system_io(), 0, &pm->io); + memory_region_add_subregion(pci_address_space_io(lpc_pci), + 0, &pm->io); acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io); acpi_pm1_evt_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io); diff --git a/hw/acpi_ich9.h b/hw/acpi_ich9.h index bc221d3cbc..91c3aeb7ea 100644 --- a/hw/acpi_ich9.h +++ b/hw/acpi_ich9.h @@ -21,7 +21,7 @@ #ifndef HW_ACPI_ICH9_H #define HW_ACPI_ICH9_H -#include "acpi.h" +#include "hw/acpi.h" typedef struct ICH9LPCPMRegs { /* @@ -30,9 +30,11 @@ typedef struct ICH9LPCPMRegs { * PM1a_CNT_BLK = 2 in FADT so it is defined as uint16_t. */ ACPIREGS acpi_regs; + MemoryRegion io; MemoryRegion io_gpe; MemoryRegion io_smi; + uint32_t smi_en; uint32_t smi_sts; @@ -42,7 +44,7 @@ typedef struct ICH9LPCPMRegs { Notifier powerdown_notifier; } ICH9LPCPMRegs; -void ich9_pm_init(ICH9LPCPMRegs *pm, +void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq, qemu_irq cmos_s3_resume); void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base); extern const VMStateDescription vmstate_ich9_pm; diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index f53b969aa6..7a4b712919 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -18,16 +18,16 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "pc.h" -#include "apm.h" -#include "pm_smbus.h" -#include "pci/pci.h" -#include "acpi.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/apm.h" +#include "hw/pm_smbus.h" +#include "hw/pci/pci.h" +#include "hw/acpi.h" #include "sysemu/sysemu.h" #include "qemu/range.h" #include "exec/ioport.h" -#include "fw_cfg.h" +#include "hw/fw_cfg.h" #include "exec/address-spaces.h" //#define DEBUG @@ -57,6 +57,7 @@ struct pci_status { typedef struct PIIX4PMState { PCIDevice dev; + MemoryRegion io; MemoryRegion io_gpe; MemoryRegion io_pci; @@ -83,7 +84,8 @@ typedef struct PIIX4PMState { uint8_t s4_val; } PIIX4PMState; -static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s); +static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, + PCIBus *bus, PIIX4PMState *s); #define ACPI_ENABLE 0xf1 #define ACPI_DISABLE 0xf0 @@ -233,7 +235,7 @@ static int acpi_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_be16s(f, &s->ar.pm1.evt.en); qemu_get_be16s(f, &s->ar.pm1.cnt.cnt); - ret = vmstate_load_state(f, &vmstate_apm, opaque, 1); + ret = vmstate_load_state(f, &vmstate_apm, &s->apm, 1); if (ret) { return ret; } @@ -251,7 +253,7 @@ static int acpi_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_be16s(f, &temp); } - ret = vmstate_load_state(f, &vmstate_pci_status, opaque, 1); + ret = vmstate_load_state(f, &vmstate_pci_status, &s->pci0_status, 1); return ret; } @@ -406,11 +408,13 @@ static int piix4_pm_initfn(PCIDevice *dev) pci_conf[0xd2] = 0x09; pm_smbus_init(&s->dev.qdev, &s->smb); memory_region_set_enabled(&s->smb.io, pci_conf[0xd2] & 1); - memory_region_add_subregion(get_system_io(), s->smb_io_base, &s->smb.io); + memory_region_add_subregion(pci_address_space_io(dev), + s->smb_io_base, &s->smb.io); memory_region_init(&s->io, "piix4-pm", 64); memory_region_set_enabled(&s->io, false); - memory_region_add_subregion(get_system_io(), 0, &s->io); + memory_region_add_subregion(pci_address_space_io(dev), + 0, &s->io); acpi_pm_tmr_init(&s->ar, pm_tmr_timer, &s->io); acpi_pm1_evt_init(&s->ar, pm_tmr_timer, &s->io); @@ -423,7 +427,8 @@ static int piix4_pm_initfn(PCIDevice *dev) s->machine_ready.notify = piix4_pm_machine_ready; qemu_add_machine_init_done_notifier(&s->machine_ready); qemu_register_reset(piix4_reset, s); - piix4_acpi_system_hot_add_init(dev->bus, s); + + piix4_acpi_system_hot_add_init(pci_address_space_io(dev), dev->bus, s); return 0; } @@ -482,7 +487,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data) dc->props = piix4_pm_properties; } -static TypeInfo piix4_pm_info = { +static const TypeInfo piix4_pm_info = { .name = "PIIX4_PM", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PIIX4PMState), @@ -526,82 +531,73 @@ static const MemoryRegionOps piix4_gpe_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static uint32_t pci_up_read(void *opaque, uint32_t addr) +static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size) { PIIX4PMState *s = opaque; - uint32_t val; + uint32_t val = 0; - /* Manufacture an "up" value to cause a device check on any hotplug - * slot with a device. Extra device checks are harmless. */ - val = s->pci0_slot_device_present & s->pci0_hotplug_enable; + switch (addr) { + case PCI_UP_BASE - PCI_HOTPLUG_ADDR: + /* Manufacture an "up" value to cause a device check on any hotplug + * slot with a device. Extra device checks are harmless. */ + val = s->pci0_slot_device_present & s->pci0_hotplug_enable; + PIIX4_DPRINTF("pci_up_read %x\n", val); + break; + case PCI_DOWN_BASE - PCI_HOTPLUG_ADDR: + val = s->pci0_status.down; + PIIX4_DPRINTF("pci_down_read %x\n", val); + break; + case PCI_EJ_BASE - PCI_HOTPLUG_ADDR: + /* No feature defined yet */ + PIIX4_DPRINTF("pci_features_read %x\n", val); + break; + case PCI_RMV_BASE - PCI_HOTPLUG_ADDR: + val = s->pci0_hotplug_enable; + break; + default: + break; + } - PIIX4_DPRINTF("pci_up_read %x\n", val); return val; } -static uint32_t pci_down_read(void *opaque, uint32_t addr) +static void pci_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) { - PIIX4PMState *s = opaque; - uint32_t val = s->pci0_status.down; - - PIIX4_DPRINTF("pci_down_read %x\n", val); - return val; -} - -static uint32_t pci_features_read(void *opaque, uint32_t addr) -{ - /* No feature defined yet */ - PIIX4_DPRINTF("pci_features_read %x\n", 0); - return 0; -} - -static void pciej_write(void *opaque, uint32_t addr, uint32_t val) -{ - acpi_piix_eject_slot(opaque, val); - - PIIX4_DPRINTF("pciej write %x <== %d\n", addr, val); -} - -static uint32_t pcirmv_read(void *opaque, uint32_t addr) -{ - PIIX4PMState *s = opaque; - - return s->pci0_hotplug_enable; + switch (addr) { + case PCI_EJ_BASE - PCI_HOTPLUG_ADDR: + acpi_piix_eject_slot(opaque, (uint32_t)data); + PIIX4_DPRINTF("pciej write %" HWADDR_PRIx " <== % " PRIu64 "\n", + addr, data); + break; + default: + break; + } } static const MemoryRegionOps piix4_pci_ops = { - .old_portio = (MemoryRegionPortio[]) { - { - .offset = PCI_UP_BASE - PCI_HOTPLUG_ADDR, .len = 4, .size = 4, - .read = pci_up_read, - },{ - .offset = PCI_DOWN_BASE - PCI_HOTPLUG_ADDR, .len = 4, .size = 4, - .read = pci_down_read, - },{ - .offset = PCI_EJ_BASE - PCI_HOTPLUG_ADDR, .len = 4, .size = 4, - .read = pci_features_read, - .write = pciej_write, - },{ - .offset = PCI_RMV_BASE - PCI_HOTPLUG_ADDR, .len = 4, .size = 4, - .read = pcirmv_read, - }, - PORTIO_END_OF_LIST() - }, + .read = pci_read, + .write = pci_write, .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, }; static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, PCIHotplugState state); -static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s) +static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, + PCIBus *bus, PIIX4PMState *s) { memory_region_init_io(&s->io_gpe, &piix4_gpe_ops, s, "apci-gpe0", GPE_LEN); - memory_region_add_subregion(get_system_io(), GPE_BASE, &s->io_gpe); + memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe); memory_region_init_io(&s->io_pci, &piix4_pci_ops, s, "apci-pci-hotplug", PCI_HOTPLUG_SIZE); - memory_region_add_subregion(get_system_io(), PCI_HOTPLUG_ADDR, + memory_region_add_subregion(parent, PCI_HOTPLUG_ADDR, &s->io_pci); pci_bus_hotplug(bus, piix4_device_hotplug, &s->dev.qdev); } diff --git a/hw/adb.c b/hw/adb.c index cc8ad8e057..fd9052c16b 100644 --- a/hw/adb.c +++ b/hw/adb.c @@ -21,8 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "adb.h" +#include "hw/hw.h" +#include "hw/adb.h" #include "ui/console.h" /* debug ADB */ @@ -48,16 +48,21 @@ do { printf("ADB: " fmt , ## __VA_ARGS__); } while (0) #define ADB_CMD_CHANGE_ID_AND_ENABLE 0x00 /* ADB default device IDs (upper 4 bits of ADB command byte) */ -#define ADB_DONGLE 1 -#define ADB_KEYBOARD 2 -#define ADB_MOUSE 3 -#define ADB_TABLET 4 -#define ADB_MODEM 5 -#define ADB_MISC 7 +#define ADB_DEVID_DONGLE 1 +#define ADB_DEVID_KEYBOARD 2 +#define ADB_DEVID_MOUSE 3 +#define ADB_DEVID_TABLET 4 +#define ADB_DEVID_MODEM 5 +#define ADB_DEVID_MISC 7 /* error codes */ #define ADB_RET_NOTPRESENT (-2) +static void adb_device_reset(ADBDevice *d) +{ + qdev_reset_all(DEVICE(d)); +} + int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) { ADBDevice *d; @@ -66,18 +71,17 @@ int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) cmd = buf[0] & 0xf; if (cmd == ADB_BUSRESET) { for(i = 0; i < s->nb_devices; i++) { - d = &s->devices[i]; - if (d->devreset) { - d->devreset(d); - } + d = s->devices[i]; + adb_device_reset(d); } return 0; } devaddr = buf[0] >> 4; for(i = 0; i < s->nb_devices; i++) { - d = &s->devices[i]; + d = s->devices[i]; if (d->devaddr == devaddr) { - return d->devreq(d, obuf, buf, len); + ADBDeviceClass *adc = ADB_DEVICE_GET_CLASS(d); + return adc->devreq(d, obuf, buf, len); } } return ADB_RET_NOTPRESENT; @@ -94,7 +98,7 @@ int adb_poll(ADBBusState *s, uint8_t *obuf) for(i = 0; i < s->nb_devices; i++) { if (s->poll_index >= s->nb_devices) s->poll_index = 0; - d = &s->devices[s->poll_index]; + d = s->devices[s->poll_index]; buf[0] = ADB_READREG | (d->devaddr << 4); olen = adb_request(s, obuf + 1, buf, 1); /* if there is data, we poll again the same device */ @@ -108,32 +112,67 @@ int adb_poll(ADBBusState *s, uint8_t *obuf) return olen; } -static ADBDevice *adb_register_device(ADBBusState *s, int devaddr, - ADBDeviceRequest *devreq, - ADBDeviceReset *devreset, - void *opaque) +static const TypeInfo adb_bus_type_info = { + .name = TYPE_ADB_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(ADBBusState), +}; + +static void adb_device_realizefn(DeviceState *dev, Error **errp) { - ADBDevice *d; - if (s->nb_devices >= MAX_ADB_DEVICES) - return NULL; - d = &s->devices[s->nb_devices++]; - d->bus = s; - d->devaddr = devaddr; - d->devreq = devreq; - d->devreset = devreset; - d->opaque = opaque; - qemu_register_reset((QEMUResetHandler *)devreset, d); - return d; + ADBDevice *d = ADB_DEVICE(dev); + ADBBusState *bus = ADB_BUS(qdev_get_parent_bus(dev)); + + if (bus->nb_devices >= MAX_ADB_DEVICES) { + return; + } + + bus->devices[bus->nb_devices++] = d; } +static void adb_device_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = adb_device_realizefn; + dc->bus_type = TYPE_ADB_BUS; +} + +static const TypeInfo adb_device_type_info = { + .name = TYPE_ADB_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(ADBDevice), + .abstract = true, + .class_init = adb_device_class_init, +}; + /***************************************************************/ /* Keyboard ADB device */ +#define ADB_KEYBOARD(obj) OBJECT_CHECK(KBDState, (obj), TYPE_ADB_KEYBOARD) + typedef struct KBDState { + /*< private >*/ + ADBDevice parent_obj; + /*< public >*/ + uint8_t data[128]; int rptr, wptr, count; } KBDState; +#define ADB_KEYBOARD_CLASS(class) \ + OBJECT_CLASS_CHECK(ADBKeyboardClass, (class), TYPE_ADB_KEYBOARD) +#define ADB_KEYBOARD_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ADBKeyboardClass, (obj), TYPE_ADB_KEYBOARD) + +typedef struct ADBKeyboardClass { + /*< private >*/ + ADBDeviceClass parent_class; + /*< public >*/ + + DeviceRealize parent_realize; +} ADBKeyboardClass; + static const uint8_t pc_to_adb_keycode[256] = { 0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48, 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54, 0, 1, @@ -155,8 +194,7 @@ static const uint8_t pc_to_adb_keycode[256] = { static void adb_kbd_put_keycode(void *opaque, int keycode) { - ADBDevice *d = opaque; - KBDState *s = d->opaque; + KBDState *s = opaque; if (s->count < sizeof(s->data)) { s->data[s->wptr] = keycode; @@ -169,7 +207,7 @@ static void adb_kbd_put_keycode(void *opaque, int keycode) static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf) { static int ext_keycode; - KBDState *s = d->opaque; + KBDState *s = ADB_KEYBOARD(d); int adb_keycode, keycode; int olen; @@ -203,7 +241,7 @@ static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf) static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, const uint8_t *buf, int len) { - KBDState *s = d->opaque; + KBDState *s = ADB_KEYBOARD(d); int cmd, reg, olen; if ((buf[0] & 0x0f) == ADB_FLUSH) { @@ -275,41 +313,90 @@ static const VMStateDescription vmstate_adb_kbd = { } }; -static int adb_kbd_reset(ADBDevice *d) +static void adb_kbd_reset(DeviceState *dev) { - KBDState *s = d->opaque; + ADBDevice *d = ADB_DEVICE(dev); + KBDState *s = ADB_KEYBOARD(dev); d->handler = 1; - d->devaddr = ADB_KEYBOARD; - memset(s, 0, sizeof(KBDState)); - - return 0; + d->devaddr = ADB_DEVID_KEYBOARD; + memset(s->data, 0, sizeof(s->data)); + s->rptr = 0; + s->wptr = 0; + s->count = 0; } -void adb_kbd_init(ADBBusState *bus) +static void adb_kbd_realizefn(DeviceState *dev, Error **errp) { - ADBDevice *d; - KBDState *s; - s = g_malloc0(sizeof(KBDState)); - d = adb_register_device(bus, ADB_KEYBOARD, adb_kbd_request, - adb_kbd_reset, s); + ADBDevice *d = ADB_DEVICE(dev); + ADBKeyboardClass *akc = ADB_KEYBOARD_GET_CLASS(dev); + + akc->parent_realize(dev, errp); + qemu_add_kbd_event_handler(adb_kbd_put_keycode, d); - vmstate_register(NULL, -1, &vmstate_adb_kbd, s); } +static void adb_kbd_initfn(Object *obj) +{ + ADBDevice *d = ADB_DEVICE(obj); + + d->devaddr = ADB_DEVID_KEYBOARD; +} + +static void adb_kbd_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc); + ADBKeyboardClass *akc = ADB_KEYBOARD_CLASS(oc); + + akc->parent_realize = dc->realize; + dc->realize = adb_kbd_realizefn; + + adc->devreq = adb_kbd_request; + dc->reset = adb_kbd_reset; + dc->vmsd = &vmstate_adb_kbd; +} + +static const TypeInfo adb_kbd_type_info = { + .name = TYPE_ADB_KEYBOARD, + .parent = TYPE_ADB_DEVICE, + .instance_size = sizeof(KBDState), + .instance_init = adb_kbd_initfn, + .class_init = adb_kbd_class_init, + .class_size = sizeof(ADBKeyboardClass), +}; + /***************************************************************/ /* Mouse ADB device */ +#define ADB_MOUSE(obj) OBJECT_CHECK(MouseState, (obj), TYPE_ADB_MOUSE) + typedef struct MouseState { + /*< public >*/ + ADBDevice parent_obj; + /*< private >*/ + int buttons_state, last_buttons_state; int dx, dy, dz; } MouseState; +#define ADB_MOUSE_CLASS(class) \ + OBJECT_CLASS_CHECK(ADBMouseClass, (class), TYPE_ADB_MOUSE) +#define ADB_MOUSE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ADBMouseClass, (obj), TYPE_ADB_MOUSE) + +typedef struct ADBMouseClass { + /*< public >*/ + ADBDeviceClass parent_class; + /*< private >*/ + + DeviceRealize parent_realize; +} ADBMouseClass; + static void adb_mouse_event(void *opaque, int dx1, int dy1, int dz1, int buttons_state) { - ADBDevice *d = opaque; - MouseState *s = d->opaque; + MouseState *s = opaque; s->dx += dx1; s->dy += dy1; @@ -320,7 +407,7 @@ static void adb_mouse_event(void *opaque, static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf) { - MouseState *s = d->opaque; + MouseState *s = ADB_MOUSE(d); int dx, dy; if (s->last_buttons_state == s->buttons_state && @@ -359,7 +446,7 @@ static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf) static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, const uint8_t *buf, int len) { - MouseState *s = d->opaque; + MouseState *s = ADB_MOUSE(d); int cmd, reg, olen; if ((buf[0] & 0x0f) == ADB_FLUSH) { @@ -416,15 +503,15 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, return olen; } -static int adb_mouse_reset(ADBDevice *d) +static void adb_mouse_reset(DeviceState *dev) { - MouseState *s = d->opaque; + ADBDevice *d = ADB_DEVICE(dev); + MouseState *s = ADB_MOUSE(dev); d->handler = 2; - d->devaddr = ADB_MOUSE; - memset(s, 0, sizeof(MouseState)); - - return 0; + d->devaddr = ADB_DEVID_MOUSE; + s->last_buttons_state = s->buttons_state = 0; + s->dx = s->dy = s->dz = 0; } static const VMStateDescription vmstate_adb_mouse = { @@ -442,14 +529,53 @@ static const VMStateDescription vmstate_adb_mouse = { } }; -void adb_mouse_init(ADBBusState *bus) +static void adb_mouse_realizefn(DeviceState *dev, Error **errp) { - ADBDevice *d; - MouseState *s; + MouseState *s = ADB_MOUSE(dev); + ADBMouseClass *amc = ADB_MOUSE_GET_CLASS(dev); - s = g_malloc0(sizeof(MouseState)); - d = adb_register_device(bus, ADB_MOUSE, adb_mouse_request, - adb_mouse_reset, s); - qemu_add_mouse_event_handler(adb_mouse_event, d, 0, "QEMU ADB Mouse"); - vmstate_register(NULL, -1, &vmstate_adb_mouse, s); + amc->parent_realize(dev, errp); + + qemu_add_mouse_event_handler(adb_mouse_event, s, 0, "QEMU ADB Mouse"); } + +static void adb_mouse_initfn(Object *obj) +{ + ADBDevice *d = ADB_DEVICE(obj); + + d->devaddr = ADB_DEVID_MOUSE; +} + +static void adb_mouse_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc); + ADBMouseClass *amc = ADB_MOUSE_CLASS(oc); + + amc->parent_realize = dc->realize; + dc->realize = adb_mouse_realizefn; + + adc->devreq = adb_mouse_request; + dc->reset = adb_mouse_reset; + dc->vmsd = &vmstate_adb_mouse; +} + +static const TypeInfo adb_mouse_type_info = { + .name = TYPE_ADB_MOUSE, + .parent = TYPE_ADB_DEVICE, + .instance_size = sizeof(MouseState), + .instance_init = adb_mouse_initfn, + .class_init = adb_mouse_class_init, + .class_size = sizeof(ADBMouseClass), +}; + + +static void adb_register_types(void) +{ + type_register_static(&adb_bus_type_info); + type_register_static(&adb_device_type_info); + type_register_static(&adb_kbd_type_info); + type_register_static(&adb_mouse_type_info); +} + +type_init(adb_register_types) diff --git a/hw/adb.h b/hw/adb.h index 5b27da2dd3..bdfccd4041 100644 --- a/hw/adb.h +++ b/hw/adb.h @@ -26,38 +26,62 @@ #if !defined(__ADB_H__) #define __ADB_H__ +#include "hw/qdev.h" + #define MAX_ADB_DEVICES 16 #define ADB_MAX_OUT_LEN 16 +typedef struct ADBBusState ADBBusState; typedef struct ADBDevice ADBDevice; /* buf = NULL means polling */ typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out, const uint8_t *buf, int len); -typedef int ADBDeviceReset(ADBDevice *d); + +#define TYPE_ADB_DEVICE "adb-device" +#define ADB_DEVICE(obj) OBJECT_CHECK(ADBDevice, (obj), TYPE_ADB_DEVICE) struct ADBDevice { - struct ADBBusState *bus; + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + int devaddr; int handler; - ADBDeviceRequest *devreq; - ADBDeviceReset *devreset; - void *opaque; }; -typedef struct ADBBusState { - ADBDevice devices[MAX_ADB_DEVICES]; +#define ADB_DEVICE_CLASS(cls) \ + OBJECT_CLASS_CHECK(ADBDeviceClass, (cls), TYPE_ADB_DEVICE) +#define ADB_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ADBDeviceClass, (obj), TYPE_ADB_DEVICE) + +typedef struct ADBDeviceClass { + /*< private >*/ + DeviceClass parent_class; + /*< public >*/ + + ADBDeviceRequest *devreq; +} ADBDeviceClass; + +#define TYPE_ADB_BUS "apple-desktop-bus" +#define ADB_BUS(obj) OBJECT_CHECK(ADBBusState, (obj), TYPE_ADB_BUS) + +struct ADBBusState { + /*< private >*/ + BusState parent_obj; + /*< public >*/ + + ADBDevice *devices[MAX_ADB_DEVICES]; int nb_devices; int poll_index; -} ADBBusState; +}; int adb_request(ADBBusState *s, uint8_t *buf_out, const uint8_t *buf, int len); int adb_poll(ADBBusState *s, uint8_t *buf_out); -void adb_kbd_init(ADBBusState *bus); -void adb_mouse_init(ADBBusState *bus); +#define TYPE_ADB_KEYBOARD "adb-keyboard" +#define TYPE_ADB_MOUSE "adb-mouse" -extern ADBBusState adb_bus; #endif /* !defined(__ADB_H__) */ diff --git a/hw/adlib.c b/hw/adlib.c index 07c69fc967..e6bce59512 100644 --- a/hw/adlib.c +++ b/hw/adlib.c @@ -22,10 +22,10 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "audiodev.h" +#include "hw/hw.h" +#include "hw/audiodev.h" #include "audio/audio.h" -#include "isa.h" +#include "hw/isa.h" //#define DEBUG @@ -47,7 +47,7 @@ void YMF262UpdateOneQEMU (int which, INT16 *dst, int length); #define SHIFT 2 #else -#include "fmopl.h" +#include "hw/fmopl.h" #define SHIFT 1 #endif diff --git a/hw/ads7846.c b/hw/ads7846.c index fa137e628e..5da3dc5b2c 100644 --- a/hw/ads7846.c +++ b/hw/ads7846.c @@ -10,7 +10,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "ssi.h" +#include "hw/ssi.h" #include "ui/console.h" typedef struct { @@ -162,7 +162,7 @@ static void ads7846_class_init(ObjectClass *klass, void *data) k->transfer = ads7846_transfer; } -static TypeInfo ads7846_info = { +static const TypeInfo ads7846_info = { .name = "ads7846", .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(ADS7846State), diff --git a/hw/alpha/Makefile.objs b/hw/alpha/Makefile.objs index af1c07fa7c..db868d2ea6 100644 --- a/hw/alpha/Makefile.objs +++ b/hw/alpha/Makefile.objs @@ -1,4 +1,6 @@ obj-y = mc146818rtc.o -obj-y += alpha_pci.o alpha_dp264.o alpha_typhoon.o +obj-y += alpha_typhoon.o obj-y := $(addprefix ../,$(obj-y)) + +obj-y += dp264.o pci.o diff --git a/hw/alpha_dp264.c b/hw/alpha/dp264.c similarity index 89% rename from hw/alpha_dp264.c rename to hw/alpha/dp264.c index e7e52c1f1c..13aaa57b90 100644 --- a/hw/alpha_dp264.c +++ b/hw/alpha/dp264.c @@ -6,16 +6,16 @@ * that we need to emulate as well. */ -#include "hw.h" +#include "hw/hw.h" #include "elf.h" -#include "loader.h" -#include "boards.h" -#include "alpha_sys.h" +#include "hw/loader.h" +#include "hw/boards.h" +#include "hw/alpha_sys.h" #include "sysemu/sysemu.h" -#include "mc146818rtc.h" -#include "ide.h" -#include "i8254.h" -#include "serial.h" +#include "hw/mc146818rtc.h" +#include "hw/ide.h" +#include "hw/i8254.h" +#include "hw/serial.h" #define MAX_IDE_BUS 2 @@ -50,7 +50,7 @@ static void clipper_init(QEMUMachineInitArgs *args) const char *kernel_filename = args->kernel_filename; const char *kernel_cmdline = args->kernel_cmdline; const char *initrd_filename = args->initrd_filename; - CPUAlphaState *cpus[4]; + AlphaCPU *cpus[4]; PCIBus *pci_bus; ISABus *isa_bus; qemu_irq rtc_irq; @@ -62,12 +62,12 @@ static void clipper_init(QEMUMachineInitArgs *args) /* Create up to 4 cpus. */ memset(cpus, 0, sizeof(cpus)); for (i = 0; i < smp_cpus; ++i) { - cpus[i] = cpu_init(cpu_model ? cpu_model : "ev67"); + cpus[i] = cpu_alpha_init(cpu_model ? cpu_model : "ev67"); } - cpus[0]->trap_arg0 = ram_size; - cpus[0]->trap_arg1 = 0; - cpus[0]->trap_arg2 = smp_cpus; + cpus[0]->env.trap_arg0 = ram_size; + cpus[0]->env.trap_arg1 = 0; + cpus[0]->env.trap_arg2 = smp_cpus; /* Init the chipset. */ pci_bus = typhoon_init(ram_size, &isa_bus, &rtc_irq, cpus, @@ -119,9 +119,9 @@ static void clipper_init(QEMUMachineInitArgs *args) /* Start all cpus at the PALcode RESET entry point. */ for (i = 0; i < smp_cpus; ++i) { - cpus[i]->pal_mode = 1; - cpus[i]->pc = palcode_entry; - cpus[i]->palbr = palcode_entry; + cpus[i]->env.pal_mode = 1; + cpus[i]->env.pc = palcode_entry; + cpus[i]->env.palbr = palcode_entry; } /* Load a kernel. */ @@ -136,7 +136,7 @@ static void clipper_init(QEMUMachineInitArgs *args) exit(1); } - cpus[0]->trap_arg1 = kernel_entry; + cpus[0]->env.trap_arg1 = kernel_entry; param_offset = kernel_low - 0x6000; @@ -171,6 +171,7 @@ static QEMUMachine clipper_machine = { .init = clipper_init, .max_cpus = 4, .is_default = 1, + DEFAULT_MACHINE_OPTIONS, }; static void clipper_machine_init(void) diff --git a/hw/alpha_pci.c b/hw/alpha/pci.c similarity index 98% rename from hw/alpha_pci.c rename to hw/alpha/pci.c index 7327d488fd..84628686ad 100644 --- a/hw/alpha_pci.c +++ b/hw/alpha/pci.c @@ -7,7 +7,7 @@ */ #include "config.h" -#include "alpha_sys.h" +#include "hw/alpha_sys.h" #include "qemu/log.h" #include "sysemu/sysemu.h" diff --git a/hw/alpha_sys.h b/hw/alpha_sys.h index 348f55c27e..b4ebd2a9cc 100644 --- a/hw/alpha_sys.h +++ b/hw/alpha_sys.h @@ -3,14 +3,14 @@ #ifndef HW_ALPHA_H #define HW_ALPHA_H 1 -#include "pci/pci.h" -#include "pci/pci_host.h" -#include "ide.h" -#include "pc.h" -#include "irq.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/ide.h" +#include "hw/pc.h" +#include "hw/irq.h" -PCIBus *typhoon_init(ram_addr_t, ISABus **, qemu_irq *, CPUAlphaState *[4], +PCIBus *typhoon_init(ram_addr_t, ISABus **, qemu_irq *, AlphaCPU *[4], pci_map_irq_fn); /* alpha_pci.c. */ diff --git a/hw/alpha_typhoon.c b/hw/alpha_typhoon.c index 9f233d99f5..770dc8cf0d 100644 --- a/hw/alpha_typhoon.c +++ b/hw/alpha_typhoon.c @@ -8,10 +8,10 @@ #include "cpu.h" #include "exec/exec-all.h" -#include "hw.h" -#include "devices.h" +#include "hw/hw.h" +#include "hw/devices.h" #include "sysemu/sysemu.h" -#include "alpha_sys.h" +#include "hw/alpha_sys.h" #include "exec/address-spaces.h" @@ -23,7 +23,7 @@ typedef struct TyphoonCchip { uint64_t drir; uint64_t dim[4]; uint32_t iic[4]; - CPUAlphaState *cpu[4]; + AlphaCPU *cpu[4]; } TyphoonCchip; typedef struct TyphoonWindow { @@ -58,14 +58,15 @@ typedef struct TyphoonState { } TyphoonState; /* Called when one of DRIR or DIM changes. */ -static void cpu_irq_change(CPUAlphaState *env, uint64_t req) +static void cpu_irq_change(AlphaCPU *cpu, uint64_t req) { /* If there are any non-masked interrupts, tell the cpu. */ - if (env) { + if (cpu != NULL) { + CPUState *cs = CPU(cpu); if (req) { - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } } @@ -74,6 +75,7 @@ static uint64_t cchip_read(void *opaque, hwaddr addr, unsigned size) { CPUAlphaState *env = cpu_single_env; TyphoonState *s = opaque; + CPUState *cpu; uint64_t ret = 0; if (addr & 4) { @@ -94,7 +96,8 @@ static uint64_t cchip_read(void *opaque, hwaddr addr, unsigned size) case 0x0080: /* MISC: Miscellaneous Register. */ - ret = s->cchip.misc | (env->cpu_index & 3); + cpu = ENV_GET_CPU(env); + ret = s->cchip.misc | (cpu->cpu_index & 3); break; case 0x00c0: @@ -353,18 +356,19 @@ static void cchip_write(void *opaque, hwaddr addr, if ((newval ^ oldval) & 0xff0) { int i; for (i = 0; i < 4; ++i) { - CPUAlphaState *env = s->cchip.cpu[i]; - if (env) { + AlphaCPU *cpu = s->cchip.cpu[i]; + if (cpu != NULL) { + CPUState *cs = CPU(cpu); /* IPI can be either cleared or set by the write. */ if (newval & (1 << (i + 8))) { - cpu_interrupt(env, CPU_INTERRUPT_SMP); + cpu_interrupt(cs, CPU_INTERRUPT_SMP); } else { - cpu_reset_interrupt(env, CPU_INTERRUPT_SMP); + cpu_reset_interrupt(cs, CPU_INTERRUPT_SMP); } /* ITI can only be cleared by the write. */ if ((newval & (1 << (i + 4))) == 0) { - cpu_reset_interrupt(env, CPU_INTERRUPT_TIMER); + cpu_reset_interrupt(cs, CPU_INTERRUPT_TIMER); } } } @@ -661,8 +665,8 @@ static void typhoon_set_timer_irq(void *opaque, int irq, int level) /* Deliver the interrupt to each CPU, considering each CPU's IIC. */ for (i = 0; i < 4; ++i) { - CPUAlphaState *env = s->cchip.cpu[i]; - if (env) { + AlphaCPU *cpu = s->cchip.cpu[i]; + if (cpu != NULL) { uint32_t iic = s->cchip.iic[i]; /* ??? The verbage in Section 10.2.2.10 isn't 100% clear. @@ -681,7 +685,7 @@ static void typhoon_set_timer_irq(void *opaque, int irq, int level) /* Set the ITI bit for this cpu. */ s->cchip.misc |= 1 << (i + 4); /* And signal the interrupt. */ - cpu_interrupt(env, CPU_INTERRUPT_TIMER); + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_TIMER); } } } @@ -694,12 +698,12 @@ static void typhoon_alarm_timer(void *opaque) /* Set the ITI bit for this cpu. */ s->cchip.misc |= 1 << (cpu + 4); - cpu_interrupt(s->cchip.cpu[cpu], CPU_INTERRUPT_TIMER); + cpu_interrupt(CPU(s->cchip.cpu[cpu]), CPU_INTERRUPT_TIMER); } PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, qemu_irq *p_rtc_irq, - CPUAlphaState *cpus[4], pci_map_irq_fn sys_map_irq) + AlphaCPU *cpus[4], pci_map_irq_fn sys_map_irq) { const uint64_t MB = 1024 * 1024; const uint64_t GB = 1024 * MB; @@ -719,10 +723,10 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, /* Remember the CPUs so that we can deliver interrupts to them. */ for (i = 0; i < 4; i++) { - CPUAlphaState *env = cpus[i]; - s->cchip.cpu[i] = env; - if (env) { - env->alarm_timer = qemu_new_timer_ns(rtc_clock, + AlphaCPU *cpu = cpus[i]; + s->cchip.cpu[i] = cpu; + if (cpu != NULL) { + cpu->alarm_timer = qemu_new_timer_ns(rtc_clock, typhoon_alarm_timer, (void *)((uintptr_t)s + i)); } diff --git a/hw/apb_pci.c b/hw/apb_pci.c index c22e2b0fc3..7992d6f6fd 100644 --- a/hw/apb_pci.c +++ b/hw/apb_pci.c @@ -26,12 +26,12 @@ Ultrasparc PCI host is called the PCI Bus Module (PBM). The APB is the secondary PCI bridge. */ -#include "sysbus.h" -#include "pci/pci.h" -#include "pci/pci_host.h" -#include "pci/pci_bridge.h" -#include "pci/pci_bus.h" -#include "apb_pci.h" +#include "hw/sysbus.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" +#include "hw/apb_pci.h" #include "sysemu/sysemu.h" #include "exec/address-spaces.h" @@ -365,7 +365,7 @@ PCIBus *pci_apb_init(hwaddr special_base, /* Ultrasparc PBM main bus */ dev = qdev_create(NULL, "pbm"); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); /* apb_config */ sysbus_mmio_map(s, 0, special_base); /* PCI configuration space */ @@ -486,7 +486,7 @@ static void pbm_pci_host_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_HOST; } -static TypeInfo pbm_pci_host_info = { +static const TypeInfo pbm_pci_host_info = { .name = "pbm-pci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), @@ -502,7 +502,7 @@ static void pbm_host_class_init(ObjectClass *klass, void *data) dc->reset = pci_pbm_reset; } -static TypeInfo pbm_host_info = { +static const TypeInfo pbm_host_info = { .name = "pbm", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(APBState), @@ -525,7 +525,7 @@ static void pbm_pci_bridge_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pci_device; } -static TypeInfo pbm_pci_bridge_info = { +static const TypeInfo pbm_pci_bridge_info = { .name = "pbm-bridge", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIBridge), diff --git a/hw/apic.c b/hw/apic.c index 81b82f694c..d2395f04dd 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -17,14 +17,14 @@ * License along with this library; if not, see */ #include "qemu/thread.h" -#include "apic_internal.h" -#include "apic.h" -#include "ioapic.h" -#include "pci/msi.h" +#include "hw/apic_internal.h" +#include "hw/apic.h" +#include "hw/ioapic.h" +#include "hw/pci/msi.h" #include "qemu/host-utils.h" #include "trace.h" -#include "pc.h" -#include "apic-msidef.h" +#include "hw/pc.h" +#include "hw/apic-msidef.h" #define MAX_APIC_WORDS 8 @@ -151,15 +151,15 @@ static void apic_local_deliver(APICCommonState *s, int vector) switch ((lvt >> 8) & 7) { case APIC_DM_SMI: - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_SMI); + cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_SMI); break; case APIC_DM_NMI: - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_NMI); + cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_NMI); break; case APIC_DM_EXTINT: - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD); + cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HARD); break; case APIC_DM_FIXED: @@ -187,7 +187,7 @@ void apic_deliver_pic_intr(DeviceState *d, int level) reset_bit(s->irr, lvt & 0xff); /* fall through */ case APIC_DM_EXTINT: - cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_HARD); break; } } @@ -248,20 +248,20 @@ static void apic_bus_deliver(const uint32_t *deliver_bitmask, case APIC_DM_SMI: foreach_apic(apic_iter, deliver_bitmask, - cpu_interrupt(&apic_iter->cpu->env, CPU_INTERRUPT_SMI) + cpu_interrupt(CPU(apic_iter->cpu), CPU_INTERRUPT_SMI) ); return; case APIC_DM_NMI: foreach_apic(apic_iter, deliver_bitmask, - cpu_interrupt(&apic_iter->cpu->env, CPU_INTERRUPT_NMI) + cpu_interrupt(CPU(apic_iter->cpu), CPU_INTERRUPT_NMI) ); return; case APIC_DM_INIT: /* normal INIT IPI sent to processors */ foreach_apic(apic_iter, deliver_bitmask, - cpu_interrupt(&apic_iter->cpu->env, + cpu_interrupt(CPU(apic_iter->cpu), CPU_INTERRUPT_INIT) ); return; @@ -363,15 +363,16 @@ static int apic_irq_pending(APICCommonState *s) /* signal the CPU if an irq is pending */ static void apic_update_irq(APICCommonState *s) { - CPUState *cpu = CPU(s->cpu); + CPUState *cpu; if (!(s->spurious_vec & APIC_SV_ENABLE)) { return; } + cpu = CPU(s->cpu); if (!qemu_cpu_is_self(cpu)) { - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_POLL); + cpu_interrupt(cpu, CPU_INTERRUPT_POLL); } else if (apic_irq_pending(s) > 0) { - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD); + cpu_interrupt(cpu, CPU_INTERRUPT_HARD); } } @@ -478,14 +479,14 @@ static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask, static void apic_startup(APICCommonState *s, int vector_num) { s->sipi_vector = vector_num; - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_SIPI); + cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_SIPI); } void apic_sipi(DeviceState *d) { APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); - cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_SIPI); + cpu_reset_interrupt(CPU(s->cpu), CPU_INTERRUPT_SIPI); if (!s->wait_for_sipi) return; @@ -895,7 +896,7 @@ static void apic_class_init(ObjectClass *klass, void *data) k->post_load = apic_post_load; } -static TypeInfo apic_info = { +static const TypeInfo apic_info = { .name = "apic", .instance_size = sizeof(APICCommonState), .parent = TYPE_APIC_COMMON, diff --git a/hw/apic_common.c b/hw/apic_common.c index 0658be93c1..d0c261602c 100644 --- a/hw/apic_common.c +++ b/hw/apic_common.c @@ -17,8 +17,8 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see */ -#include "apic.h" -#include "apic_internal.h" +#include "hw/apic.h" +#include "hw/apic_internal.h" #include "trace.h" #include "sysemu/kvm.h" @@ -103,7 +103,7 @@ void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip, { APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); - vapic_report_tpr_access(s->vapic, &s->cpu->env, ip, access); + vapic_report_tpr_access(s->vapic, CPU(s->cpu), ip, access); } void apic_report_irq_delivered(int delivered) @@ -385,7 +385,7 @@ static void apic_common_class_init(ObjectClass *klass, void *data) sc->init = apic_init_common; } -static TypeInfo apic_common_type = { +static const TypeInfo apic_common_type = { .name = TYPE_APIC_COMMON, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(APICCommonState), diff --git a/hw/apic_internal.h b/hw/apic_internal.h index dcbbfd41cb..578241f861 100644 --- a/hw/apic_internal.h +++ b/hw/apic_internal.h @@ -21,7 +21,7 @@ #define QEMU_APIC_INTERNAL_H #include "exec/memory.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/timer.h" /* APIC Local Vector Table */ @@ -143,7 +143,7 @@ bool apic_next_timer(APICCommonState *s, int64_t current_time); void apic_enable_tpr_access_reporting(DeviceState *d, bool enable); void apic_enable_vapic(DeviceState *d, hwaddr paddr); -void vapic_report_tpr_access(DeviceState *dev, void *cpu, target_ulong ip, +void vapic_report_tpr_access(DeviceState *dev, CPUState *cpu, target_ulong ip, TPRAccess access); #endif /* !QEMU_APIC_INTERNAL_H */ diff --git a/hw/apm.c b/hw/apm.c index 2e1b1372d2..e2846f99c8 100644 --- a/hw/apm.c +++ b/hw/apm.c @@ -20,9 +20,9 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "apm.h" -#include "hw.h" -#include "pci/pci.h" +#include "hw/apm.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" //#define DEBUG diff --git a/hw/apm.h b/hw/apm.h index 9abb47f99f..3edea5f623 100644 --- a/hw/apm.h +++ b/hw/apm.h @@ -3,7 +3,7 @@ #include #include "qemu-common.h" -#include "hw.h" +#include "hw/hw.h" #include "exec/memory.h" typedef void (*apm_ctrl_changed_t)(uint32_t val, void *arg); diff --git a/hw/applesmc.c b/hw/applesmc.c index c564b60c0a..44b9bacd88 100644 --- a/hw/applesmc.c +++ b/hw/applesmc.c @@ -30,8 +30,8 @@ * */ -#include "hw.h" -#include "isa.h" +#include "hw/hw.h" +#include "hw/isa.h" #include "ui/console.h" #include "qemu/timer.h" @@ -236,7 +236,7 @@ static void qdev_applesmc_class_init(ObjectClass *klass, void *data) dc->props = applesmc_isa_properties; } -static TypeInfo applesmc_isa_info = { +static const TypeInfo applesmc_isa_info = { .name = "isa-applesmc", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(struct AppleSMCStatus), diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 6d049e7de6..f5f7d0e539 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -1,35 +1,36 @@ -obj-y = integratorcp.o versatilepb.o arm_pic.o -obj-y += arm_boot.o -obj-y += xilinx_zynq.o zynq_slcr.o +obj-y += zynq_slcr.o obj-y += xilinx_spips.o obj-y += arm_gic.o arm_gic_common.o -obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o -obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o -obj-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o +obj-y += a9scu.o +obj-y += realview_gic.o arm_sysctl.o arm11mpcore.o a9mpcore.o +obj-y += exynos4210_gic.o exynos4210_combiner.o +obj-y += exynos4210_uart.o exynos4210_pwm.o obj-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o obj-y += exynos4210_rtc.o exynos4210_i2c.o obj-y += arm_mptimer.o a15mpcore.o -obj-y += armv7m.o armv7m_nvic.o stellaris.o stellaris_enet.o -obj-y += highbank.o -obj-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o +obj-y += armv7m_nvic.o stellaris_enet.o +obj-y += pxa2xx_timer.o pxa2xx_dma.o obj-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o -obj-y += gumstix.o -obj-y += zaurus.o ide/microdrive.o spitz.o tosa.o tc6393xb.o -obj-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \ +obj-y += zaurus.o ide/microdrive.o tc6393xb.o +obj-y += omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \ omap_gpio.o omap_intc.o omap_uart.o -obj-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \ +obj-y += omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \ omap_gpmc.o omap_sdrc.o omap_spi.o omap_tap.o omap_l4.o -obj-y += omap_sx1.o palm.o tsc210x.o -obj-y += nseries.o blizzard.o onenand.o cbus.o tusb6010.o usb/hcd-musb.o -obj-y += mst_fpga.o mainstone.o -obj-y += z2.o -obj-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o +obj-y += tsc210x.o +obj-y += blizzard.o onenand.o cbus.o tusb6010.o usb/hcd-musb.o +obj-y += mst_fpga.o +obj-y += bitbang_i2c.o marvell_88w8618_audio.o obj-y += framebuffer.o -obj-y += vexpress.o obj-y += strongarm.o -obj-y += collie.o obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o -obj-y += kzm.o -obj-$(CONFIG_FDT) += ../device_tree.o +obj-$(CONFIG_KVM) += kvm/arm_gic.o obj-y := $(addprefix ../,$(obj-y)) + +obj-y += boot.o collie.o exynos4_boards.o gumstix.o highbank.o +obj-y += integratorcp.o kzm.o mainstone.o musicpal.o nseries.o +obj-y += omap_sx1.o palm.o pic_cpu.o realview.o spitz.o stellaris.o +obj-y += tosa.o versatilepb.o vexpress.o xilinx_zynq.o z2.o + +obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o +obj-y += omap1.o omap2.o diff --git a/hw/armv7m.c b/hw/arm/armv7m.c similarity index 96% rename from hw/armv7m.c rename to hw/arm/armv7m.c index ce2ec9b4dc..1d5bb592c4 100644 --- a/hw/armv7m.c +++ b/hw/arm/armv7m.c @@ -7,9 +7,9 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" -#include "arm-misc.h" -#include "loader.h" +#include "hw/sysbus.h" +#include "hw/arm-misc.h" +#include "hw/loader.h" #include "elf.h" /* Bitbanded IO. Each word corresponds to a single bit. */ @@ -137,12 +137,12 @@ static void armv7m_bitband_init(void) dev = qdev_create(NULL, "ARM,bitband-memory"); qdev_prop_set_uint32(dev, "base", 0x20000000); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x22000000); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x22000000); dev = qdev_create(NULL, "ARM,bitband-memory"); qdev_prop_set_uint32(dev, "base", 0x40000000); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x42000000); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x42000000); } /* Board init. */ @@ -216,7 +216,7 @@ qemu_irq *armv7m_init(MemoryRegion *address_space_mem, env->nvic = nvic; qdev_init_nofail(nvic); cpu_pic = arm_pic_init_cpu(cpu); - sysbus_connect_irq(sysbus_from_qdev(nvic), 0, cpu_pic[ARM_PIC_CPU_IRQ]); + sysbus_connect_irq(SYS_BUS_DEVICE(nvic), 0, cpu_pic[ARM_PIC_CPU_IRQ]); for (i = 0; i < 64; i++) { pic[i] = qdev_get_gpio_in(nvic, i); } @@ -269,7 +269,7 @@ static void bitband_class_init(ObjectClass *klass, void *data) dc->props = bitband_properties; } -static TypeInfo bitband_info = { +static const TypeInfo bitband_info = { .name = "ARM,bitband-memory", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(BitBandState), diff --git a/hw/arm_boot.c b/hw/arm/boot.c similarity index 97% rename from hw/arm_boot.c rename to hw/arm/boot.c index 115f583876..43253fd34a 100644 --- a/hw/arm_boot.c +++ b/hw/arm/boot.c @@ -8,11 +8,11 @@ */ #include "config.h" -#include "hw.h" -#include "arm-misc.h" +#include "hw/hw.h" +#include "hw/arm-misc.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "loader.h" +#include "hw/boards.h" +#include "hw/loader.h" #include "elf.h" #include "sysemu/device_tree.h" #include "qemu/config-file.h" @@ -441,9 +441,12 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) * we point to the kernel args. */ if (info->dtb_filename) { - /* Place the DTB after the initrd in memory */ - hwaddr dtb_start = TARGET_PAGE_ALIGN(info->initrd_start + - initrd_size); + /* Place the DTB after the initrd in memory. Note that some + * kernels will trash anything in the 4K page the initrd + * ends in, so make sure the DTB isn't caught up in that. + */ + hwaddr dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, + 4096); if (load_dtb(dtb_start, info)) { exit(1); } diff --git a/hw/collie.c b/hw/arm/collie.c similarity index 90% rename from hw/collie.c rename to hw/arm/collie.c index 804d61a421..17fddc8d5b 100644 --- a/hw/collie.c +++ b/hw/arm/collie.c @@ -8,13 +8,13 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "sysbus.h" -#include "boards.h" -#include "devices.h" -#include "strongarm.h" -#include "arm-misc.h" -#include "flash.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/boards.h" +#include "hw/devices.h" +#include "hw/strongarm.h" +#include "hw/arm-misc.h" +#include "hw/flash.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" @@ -62,6 +62,7 @@ static QEMUMachine collie_machine = { .name = "collie", .desc = "Collie PDA (SA-1110)", .init = collie_init, + DEFAULT_MACHINE_OPTIONS, }; static void collie_machine_init(void) diff --git a/hw/exynos4210.c b/hw/arm/exynos4210.c similarity index 95% rename from hw/exynos4210.c rename to hw/arm/exynos4210.c index a7b84d61a0..4592514bb2 100644 --- a/hw/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -21,12 +21,13 @@ * */ -#include "boards.h" +#include "hw/boards.h" #include "sysemu/sysemu.h" -#include "sysbus.h" -#include "arm-misc.h" -#include "loader.h" -#include "exynos4210.h" +#include "hw/sysbus.h" +#include "hw/arm-misc.h" +#include "hw/loader.h" +#include "hw/exynos4210.h" +#include "hw/usb/hcd-ehci.h" #define EXYNOS4210_CHIPID_ADDR 0x10000000 @@ -72,6 +73,9 @@ /* Display controllers (FIMD) */ #define EXYNOS4210_FIMD0_BASE_ADDR 0x11C00000 +/* EHCI */ +#define EXYNOS4210_EHCI_BASE_ADDR 0x12580000 + static uint8_t chipid_and_omr[] = { 0x11, 0x02, 0x21, 0x43, 0x09, 0x00, 0x00, 0x00 }; @@ -150,7 +154,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) { gate_irq[i][n] = qdev_get_gpio_in(dev, n); } - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); /* Connect IRQ Gate output to cpu_irq */ sysbus_connect_irq(busdev, 0, cpu_irq[i]); @@ -160,7 +164,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, dev = qdev_create(NULL, "a9mpcore_priv"); qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR); for (n = 0; n < EXYNOS4210_NCPUS; n++) { sysbus_connect_irq(busdev, n, gate_irq[n][0]); @@ -176,7 +180,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, dev = qdev_create(NULL, "exynos4210.gic"); qdev_prop_set_uint32(dev, "num-cpu", EXYNOS4210_NCPUS); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); /* Map CPU interface */ sysbus_mmio_map(busdev, 0, EXYNOS4210_EXT_GIC_CPU_BASE_ADDR); /* Map Distributer interface */ @@ -191,7 +195,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, /* Internal Interrupt Combiner */ dev = qdev_create(NULL, "exynos4210.combiner"); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) { sysbus_connect_irq(busdev, n, s->irqs.int_gic_irq[n]); } @@ -202,7 +206,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, dev = qdev_create(NULL, "exynos4210.combiner"); qdev_prop_set_uint32(dev, "external", 1); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); for (n = 0; n < EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ; n++) { sysbus_connect_irq(busdev, n, s->irqs.ext_gic_irq[n]); } @@ -281,7 +285,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, /* Multi Core Timer */ dev = qdev_create(NULL, "exynos4210.mct"); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); for (n = 0; n < 4; n++) { /* Connect global timer interrupts to Combiner gpio_in */ sysbus_connect_irq(busdev, n, @@ -307,7 +311,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, dev = qdev_create(NULL, "exynos4210.i2c"); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); sysbus_connect_irq(busdev, 0, i2c_irq); sysbus_mmio_map(busdev, 0, addr); s->i2c_if[n] = (i2c_bus *)qdev_get_child_bus(dev, "i2c"); @@ -338,5 +342,8 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, s->irq_table[exynos4210_get_irq(11, 2)], NULL); + sysbus_create_simple(TYPE_EXYNOS4210_EHCI, EXYNOS4210_EHCI_BASE_ADDR, + s->irq_table[exynos4210_get_irq(28, 3)]); + return s; } diff --git a/hw/exynos4_boards.c b/hw/arm/exynos4_boards.c similarity index 96% rename from hw/exynos4_boards.c rename to hw/arm/exynos4_boards.c index b26796847b..473da349bd 100644 --- a/hw/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -22,12 +22,12 @@ */ #include "sysemu/sysemu.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "net/net.h" -#include "arm-misc.h" +#include "hw/arm-misc.h" #include "exec/address-spaces.h" -#include "exynos4210.h" -#include "boards.h" +#include "hw/exynos4210.h" +#include "hw/boards.h" #undef DEBUG @@ -87,7 +87,7 @@ static void lan9215_init(uint32_t base, qemu_irq irq) qdev_set_nic_properties(dev, &nd_table[0]); qdev_prop_set_uint32(dev, "mode_16bit", 1); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, base); sysbus_connect_irq(s, 0, irq); } @@ -150,12 +150,14 @@ static QEMUMachine exynos4_machines[EXYNOS4_NUM_OF_BOARDS] = { .desc = "Samsung NURI board (Exynos4210)", .init = nuri_init, .max_cpus = EXYNOS4210_NCPUS, + DEFAULT_MACHINE_OPTIONS, }, [EXYNOS4_BOARD_SMDKC210] = { .name = "smdkc210", .desc = "Samsung SMDKC210 board (Exynos4210)", .init = smdkc210_init, .max_cpus = EXYNOS4210_NCPUS, + DEFAULT_MACHINE_OPTIONS, }, }; diff --git a/hw/gumstix.c b/hw/arm/gumstix.c similarity index 95% rename from hw/gumstix.c rename to hw/arm/gumstix.c index 6fb068386c..8859b7392f 100644 --- a/hw/gumstix.c +++ b/hw/arm/gumstix.c @@ -34,12 +34,12 @@ * # qemu-system-arm -M verdex -pflash flash -monitor null -nographic -m 289 */ -#include "hw.h" -#include "pxa.h" +#include "hw/hw.h" +#include "hw/pxa.h" #include "net/net.h" -#include "flash.h" -#include "devices.h" -#include "boards.h" +#include "hw/flash.h" +#include "hw/devices.h" +#include "hw/boards.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" @@ -122,12 +122,14 @@ static QEMUMachine connex_machine = { .name = "connex", .desc = "Gumstix Connex (PXA255)", .init = connex_init, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine verdex_machine = { .name = "verdex", .desc = "Gumstix Verdex (PXA270)", .init = verdex_init, + DEFAULT_MACHINE_OPTIONS, }; static void gumstix_machine_init(void) diff --git a/hw/highbank.c b/hw/arm/highbank.c similarity index 91% rename from hw/highbank.c rename to hw/arm/highbank.c index 6005622f3a..a622224dcc 100644 --- a/hw/highbank.c +++ b/hw/arm/highbank.c @@ -17,14 +17,14 @@ * */ -#include "sysbus.h" -#include "arm-misc.h" -#include "devices.h" -#include "loader.h" +#include "hw/sysbus.h" +#include "hw/arm-misc.h" +#include "hw/devices.h" +#include "hw/loader.h" #include "net/net.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "sysbus.h" +#include "hw/boards.h" +#include "hw/sysbus.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" @@ -136,7 +136,7 @@ static VMStateDescription vmstate_highbank_regs = { static void highbank_regs_reset(DeviceState *dev) { - SysBusDevice *sys_dev = sysbus_from_qdev(dev); + SysBusDevice *sys_dev = SYS_BUS_DEVICE(dev); HighbankRegsState *s = FROM_SYSBUS(HighbankRegsState, sys_dev); s->regs[0x40] = 0x05F20121; @@ -168,7 +168,7 @@ static void highbank_regs_class_init(ObjectClass *klass, void *data) dc->reset = highbank_regs_reset; } -static TypeInfo highbank_regs_info = { +static const TypeInfo highbank_regs_info = { .name = "highbank-regs", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(HighbankRegsState), @@ -251,7 +251,7 @@ static void highbank_init(QEMUMachineInitArgs *args) qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); qdev_prop_set_uint32(dev, "num-irq", NIRQ_GIC); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, GIC_BASE_ADDR); for (n = 0; n < smp_cpus; n++) { sysbus_connect_irq(busdev, n, cpu_irq[n]); @@ -263,21 +263,21 @@ static void highbank_init(QEMUMachineInitArgs *args) dev = qdev_create(NULL, "l2x0"); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, 0xfff12000); dev = qdev_create(NULL, "sp804"); qdev_prop_set_uint32(dev, "freq0", 150000000); qdev_prop_set_uint32(dev, "freq1", 150000000); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, 0xfff34000); sysbus_connect_irq(busdev, 0, pic[18]); sysbus_create_simple("pl011", 0xfff36000, pic[20]); dev = qdev_create(NULL, "highbank-regs"); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, 0xfff3c000); sysbus_create_simple("pl061", 0xfff30000, pic[14]); @@ -294,19 +294,19 @@ static void highbank_init(QEMUMachineInitArgs *args) dev = qdev_create(NULL, "xgmac"); qdev_set_nic_properties(dev, &nd_table[0]); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0xfff50000); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[77]); - sysbus_connect_irq(sysbus_from_qdev(dev), 1, pic[78]); - sysbus_connect_irq(sysbus_from_qdev(dev), 2, pic[79]); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xfff50000); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[77]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, pic[78]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, pic[79]); qemu_check_nic_model(&nd_table[1], "xgmac"); dev = qdev_create(NULL, "xgmac"); qdev_set_nic_properties(dev, &nd_table[1]); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0xfff51000); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[80]); - sysbus_connect_irq(sysbus_from_qdev(dev), 1, pic[81]); - sysbus_connect_irq(sysbus_from_qdev(dev), 2, pic[82]); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xfff51000); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[80]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, pic[81]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, pic[82]); } highbank_binfo.ram_size = ram_size; @@ -331,6 +331,7 @@ static QEMUMachine highbank_machine = { .init = highbank_init, .block_default_type = IF_SCSI, .max_cpus = 4, + DEFAULT_MACHINE_OPTIONS, }; static void highbank_machine_init(void) diff --git a/hw/integratorcp.c b/hw/arm/integratorcp.c similarity index 98% rename from hw/integratorcp.c rename to hw/arm/integratorcp.c index 47fc9cb944..e0ba327a55 100644 --- a/hw/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -7,10 +7,10 @@ * This code is licensed under the GPL */ -#include "sysbus.h" -#include "devices.h" -#include "boards.h" -#include "arm-misc.h" +#include "hw/sysbus.h" +#include "hw/devices.h" +#include "hw/boards.h" +#include "hw/arm-misc.h" #include "net/net.h" #include "exec/address-spaces.h" #include "sysemu/sysemu.h" @@ -512,6 +512,7 @@ static QEMUMachine integratorcp_machine = { .desc = "ARM Integrator/CP (ARM926EJ-S)", .init = integratorcp_init, .is_default = 1, + DEFAULT_MACHINE_OPTIONS, }; static void integratorcp_machine_init(void) @@ -535,7 +536,7 @@ static void core_class_init(ObjectClass *klass, void *data) dc->props = core_properties; } -static TypeInfo core_info = { +static const TypeInfo core_info = { .name = "integrator_core", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(integratorcm_state), @@ -549,7 +550,7 @@ static void icp_pic_class_init(ObjectClass *klass, void *data) sdc->init = icp_pic_init; } -static TypeInfo icp_pic_info = { +static const TypeInfo icp_pic_info = { .name = "integrator_pic", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(icp_pic_state), diff --git a/hw/kzm.c b/hw/arm/kzm.c similarity index 96% rename from hw/kzm.c rename to hw/arm/kzm.c index fd00af921e..ec50a319ac 100644 --- a/hw/kzm.c +++ b/hw/arm/kzm.c @@ -13,16 +13,16 @@ * i.MX31 SoC */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "exec/address-spaces.h" -#include "hw.h" -#include "arm-misc.h" -#include "devices.h" +#include "hw/hw.h" +#include "hw/arm-misc.h" +#include "hw/devices.h" #include "net/net.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "serial.h" -#include "imx.h" +#include "hw/boards.h" +#include "hw/serial.h" +#include "hw/imx.h" /* Memory map for Kzm Emulation Baseboard: * 0x00000000-0x00003fff 16k secure ROM IGNORED @@ -146,6 +146,7 @@ static QEMUMachine kzm_machine = { .name = "kzm", .desc = "ARM KZM Emulation Baseboard (ARM1136)", .init = kzm_init, + DEFAULT_MACHINE_OPTIONS, }; static void kzm_machine_init(void) diff --git a/hw/mainstone.c b/hw/arm/mainstone.c similarity index 96% rename from hw/mainstone.c rename to hw/arm/mainstone.c index a5ddbeff9d..aea908f036 100644 --- a/hw/mainstone.c +++ b/hw/arm/mainstone.c @@ -11,15 +11,15 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "pxa.h" -#include "arm-misc.h" +#include "hw/hw.h" +#include "hw/pxa.h" +#include "hw/arm-misc.h" #include "net/net.h" -#include "devices.h" -#include "boards.h" -#include "flash.h" +#include "hw/devices.h" +#include "hw/boards.h" +#include "hw/flash.h" #include "sysemu/blockdev.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "exec/address-spaces.h" /* Device addresses */ @@ -179,6 +179,7 @@ static QEMUMachine mainstone2_machine = { .name = "mainstone", .desc = "Mainstone II (PXA27x)", .init = mainstone_init, + DEFAULT_MACHINE_OPTIONS, }; static void mainstone_machine_init(void) diff --git a/hw/musicpal.c b/hw/arm/musicpal.c similarity index 97% rename from hw/musicpal.c rename to hw/arm/musicpal.c index 77a585eee6..a37dbd7961 100644 --- a/hw/musicpal.c +++ b/hw/arm/musicpal.c @@ -9,19 +9,19 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "sysbus.h" -#include "arm-misc.h" -#include "devices.h" +#include "hw/sysbus.h" +#include "hw/arm-misc.h" +#include "hw/devices.h" #include "net/net.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "serial.h" +#include "hw/boards.h" +#include "hw/serial.h" #include "qemu/timer.h" -#include "ptimer.h" +#include "hw/ptimer.h" #include "block/block.h" -#include "flash.h" +#include "hw/flash.h" #include "ui/console.h" -#include "i2c.h" +#include "hw/i2c.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" #include "ui/pixel_ops.h" @@ -190,7 +190,7 @@ static int eth_can_receive(NetClientState *nc) static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - mv88w8618_eth_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); uint32_t desc_addr; mv88w8618_rx_desc desc; int i; @@ -257,7 +257,7 @@ static void eth_send(mv88w8618_eth_state *s, int queue_index) len = desc.bytes; if (len < 2048) { cpu_physical_memory_read(desc.buffer, buf, len); - qemu_send_packet(&s->nic->nc, buf, len); + qemu_send_packet(qemu_get_queue(s->nic), buf, len); } desc.cmdstat &= ~MP_ETH_TX_OWN; s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index); @@ -369,7 +369,7 @@ static const MemoryRegionOps mv88w8618_eth_ops = { static void eth_cleanup(NetClientState *nc) { - mv88w8618_eth_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + mv88w8618_eth_state *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -428,7 +428,7 @@ static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) dc->props = mv88w8618_eth_properties; } -static TypeInfo mv88w8618_eth_info = { +static const TypeInfo mv88w8618_eth_info = { .name = "mv88w8618_eth", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mv88w8618_eth_state), @@ -643,7 +643,7 @@ static void musicpal_lcd_class_init(ObjectClass *klass, void *data) dc->vmsd = &musicpal_lcd_vmsd; } -static TypeInfo musicpal_lcd_info = { +static const TypeInfo musicpal_lcd_info = { .name = "musicpal_lcd", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(musicpal_lcd_state), @@ -716,7 +716,7 @@ static void mv88w8618_pic_write(void *opaque, hwaddr offset, static void mv88w8618_pic_reset(DeviceState *d) { mv88w8618_pic_state *s = FROM_SYSBUS(mv88w8618_pic_state, - sysbus_from_qdev(d)); + SYS_BUS_DEVICE(d)); s->level = 0; s->enabled = 0; @@ -762,7 +762,7 @@ static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) dc->vmsd = &mv88w8618_pic_vmsd; } -static TypeInfo mv88w8618_pic_info = { +static const TypeInfo mv88w8618_pic_info = { .name = "mv88w8618_pic", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mv88w8618_pic_state), @@ -873,7 +873,7 @@ static void mv88w8618_pit_write(void *opaque, hwaddr offset, static void mv88w8618_pit_reset(DeviceState *d) { mv88w8618_pit_state *s = FROM_SYSBUS(mv88w8618_pit_state, - sysbus_from_qdev(d)); + SYS_BUS_DEVICE(d)); int i; for (i = 0; i < 4; i++) { @@ -939,7 +939,7 @@ static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) dc->vmsd = &mv88w8618_pit_vmsd; } -static TypeInfo mv88w8618_pit_info = { +static const TypeInfo mv88w8618_pit_info = { .name = "mv88w8618_pit", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mv88w8618_pit_state), @@ -1019,7 +1019,7 @@ static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data) dc->vmsd = &mv88w8618_flashcfg_vmsd; } -static TypeInfo mv88w8618_flashcfg_info = { +static const TypeInfo mv88w8618_flashcfg_info = { .name = "mv88w8618_flashcfg", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mv88w8618_flashcfg_state), @@ -1288,7 +1288,7 @@ static const MemoryRegionOps musicpal_gpio_ops = { static void musicpal_gpio_reset(DeviceState *d) { musicpal_gpio_state *s = FROM_SYSBUS(musicpal_gpio_state, - sysbus_from_qdev(d)); + SYS_BUS_DEVICE(d)); s->lcd_brightness = 0; s->out_state = 0; @@ -1341,7 +1341,7 @@ static void musicpal_gpio_class_init(ObjectClass *klass, void *data) dc->vmsd = &musicpal_gpio_vmsd; } -static TypeInfo musicpal_gpio_info = { +static const TypeInfo musicpal_gpio_info = { .name = "musicpal_gpio", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(musicpal_gpio_state), @@ -1495,7 +1495,7 @@ static void musicpal_key_class_init(ObjectClass *klass, void *data) dc->vmsd = &musicpal_key_vmsd; } -static TypeInfo musicpal_key_info = { +static const TypeInfo musicpal_key_info = { .name = "musicpal_key", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(musicpal_key_state), @@ -1607,12 +1607,12 @@ static void musicpal_init(QEMUMachineInitArgs *args) dev = qdev_create(NULL, "mv88w8618_eth"); qdev_set_nic_properties(dev, &nd_table[0]); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, MP_ETH_BASE); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[MP_ETH_IRQ]); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MP_ETH_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[MP_ETH_IRQ]); sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL); - musicpal_misc_init(sysbus_from_qdev(dev)); + musicpal_misc_init(SYS_BUS_DEVICE(dev)); dev = sysbus_create_simple("musicpal_gpio", MP_GPIO_BASE, pic[MP_GPIO_IRQ]); i2c_dev = sysbus_create_simple("gpio_i2c", -1, NULL); @@ -1641,7 +1641,7 @@ static void musicpal_init(QEMUMachineInitArgs *args) wm8750_dev = i2c_create_slave(i2c, "wm8750", MP_WM_ADDR); dev = qdev_create(NULL, "mv88w8618_audio"); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); qdev_prop_set_ptr(dev, "wm8750", wm8750_dev); qdev_init_nofail(dev); sysbus_mmio_map(s, 0, MP_AUDIO_BASE); @@ -1658,6 +1658,7 @@ static QEMUMachine musicpal_machine = { .name = "musicpal", .desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)", .init = musicpal_init, + DEFAULT_MACHINE_OPTIONS, }; static void musicpal_machine_init(void) @@ -1674,7 +1675,7 @@ static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data) sdc->init = mv88w8618_wlan_init; } -static TypeInfo mv88w8618_wlan_info = { +static const TypeInfo mv88w8618_wlan_info = { .name = "mv88w8618_wlan", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysBusDevice), diff --git a/hw/nseries.c b/hw/arm/nseries.c similarity index 98% rename from hw/nseries.c rename to hw/arm/nseries.c index d96b750ccd..c5bf9f95b3 100644 --- a/hw/nseries.c +++ b/hw/arm/nseries.c @@ -20,19 +20,19 @@ #include "qemu-common.h" #include "sysemu/sysemu.h" -#include "omap.h" -#include "arm-misc.h" -#include "irq.h" +#include "hw/omap.h" +#include "hw/arm-misc.h" +#include "hw/irq.h" #include "ui/console.h" -#include "boards.h" -#include "i2c.h" -#include "devices.h" -#include "flash.h" -#include "hw.h" -#include "bt.h" -#include "loader.h" +#include "hw/boards.h" +#include "hw/i2c.h" +#include "hw/devices.h" +#include "hw/flash.h" +#include "hw/hw.h" +#include "hw/bt.h" +#include "hw/loader.h" #include "sysemu/blockdev.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "exec/address-spaces.h" /* Nokia N8x0 support */ @@ -178,10 +178,10 @@ static void n8x0_nand_setup(struct n800_s *s) qdev_prop_set_drive_nofail(s->nand, "drive", dinfo->bdrv); } qdev_init_nofail(s->nand); - sysbus_connect_irq(sysbus_from_qdev(s->nand), 0, + sysbus_connect_irq(SYS_BUS_DEVICE(s->nand), 0, qdev_get_gpio_in(s->mpu->gpio, N8X0_ONENAND_GPIO)); omap_gpmc_attach(s->mpu->gpmc, N8X0_ONENAND_CS, - sysbus_mmio_get_region(sysbus_from_qdev(s->nand), 0)); + sysbus_mmio_get_region(SYS_BUS_DEVICE(s->nand), 0)); otp_region = onenand_raw_otp(s->nand); memcpy(otp_region + 0x000, n8x0_cal_wlan_mac, sizeof(n8x0_cal_wlan_mac)); @@ -783,7 +783,7 @@ static void n8x0_usb_setup(struct n800_s *s) { SysBusDevice *dev; s->usb = qdev_create(NULL, "tusb6010"); - dev = sysbus_from_qdev(s->usb); + dev = SYS_BUS_DEVICE(s->usb); qdev_init_nofail(s->usb); sysbus_connect_irq(dev, 0, qdev_get_gpio_in(s->mpu->gpio, N8X0_TUSB_INT_GPIO)); @@ -1411,12 +1411,14 @@ static QEMUMachine n800_machine = { .name = "n800", .desc = "Nokia N800 tablet aka. RX-34 (OMAP2420)", .init = n800_init, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine n810_machine = { .name = "n810", .desc = "Nokia N810 tablet aka. RX-44 (OMAP2420)", .init = n810_init, + DEFAULT_MACHINE_OPTIONS, }; static void nseries_machine_init(void) diff --git a/hw/omap1.c b/hw/arm/omap1.c similarity index 99% rename from hw/omap1.c rename to hw/arm/omap1.c index 8536e96687..3245c62e68 100644 --- a/hw/omap1.c +++ b/hw/arm/omap1.c @@ -16,14 +16,14 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" -#include "arm-misc.h" -#include "omap.h" +#include "hw/hw.h" +#include "hw/arm-misc.h" +#include "hw/omap.h" #include "sysemu/sysemu.h" -#include "soc_dma.h" +#include "hw/soc_dma.h" #include "sysemu/blockdev.h" #include "qemu/range.h" -#include "sysbus.h" +#include "hw/sysbus.h" /* Should signal the TCMI/GPMC */ uint32_t omap_badwidth_read8(void *opaque, hwaddr addr) @@ -529,6 +529,7 @@ static uint64_t omap_ulpd_pm_read(void *opaque, hwaddr addr, case 0x28: /* Reserved */ case 0x2c: /* Reserved */ OMAP_BAD_REG(addr); + /* fall through */ case 0x00: /* COUNTER_32_LSB */ case 0x04: /* COUNTER_32_MSB */ case 0x08: /* COUNTER_HIGH_FREQ_LSB */ @@ -633,6 +634,7 @@ static void omap_ulpd_pm_write(void *opaque, hwaddr addr, case 0x28: /* Reserved */ case 0x2c: /* Reserved */ OMAP_BAD_REG(addr); + /* fall through */ case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */ case 0x38: /* COUNTER_32_FIQ */ case 0x48: /* LOCL_TIME */ @@ -1089,6 +1091,7 @@ static void omap_mpui_write(void *opaque, hwaddr addr, /* Not in OMAP310 */ case 0x14: /* DSP_STATUS */ OMAP_RO_REG(addr); + break; case 0x18: /* DSP_BOOT_CONFIG */ case 0x1c: /* DSP_MPUI_CONFIG */ break; @@ -1520,7 +1523,7 @@ static inline void omap_clkm_idlect1_update(struct omap_mpu_state_s *s, omap_clk clk; if (value & (1 << 11)) { /* SETARM_IDLE */ - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT); + cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HALT); } if (!(value & (1 << 10))) /* WKUP_MODE */ qemu_system_shutdown_request(); /* XXX: disable wakeup from IRQ */ @@ -1718,6 +1721,7 @@ static uint64_t omap_clkdsp_read(void *opaque, hwaddr addr, unsigned size) { struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + CPUState *cpu = CPU(s->cpu); if (size != 2) { return omap_badwidth_read16(opaque, addr); @@ -1734,8 +1738,9 @@ static uint64_t omap_clkdsp_read(void *opaque, hwaddr addr, return s->clkm.dsp_rstct2; case 0x18: /* DSP_SYSST */ + cpu = CPU(s->cpu); return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start | - (s->cpu->env.halted << 6); /* Quite useless... */ + (cpu->halted << 6); /* Quite useless... */ } OMAP_BAD_REG(addr); @@ -2830,7 +2835,7 @@ static void omap_rtc_tick(void *opaque) s->round = 0; } - memcpy(&s->current_tm, localtime(&s->ti), sizeof(s->current_tm)); + localtime_r(&s->ti, &s->current_tm); if ((s->interrupts & 0x08) && s->ti == s->alarm_ti) { s->status |= 0x40; @@ -3751,9 +3756,10 @@ static void omap_setup_dsp_mapping(MemoryRegion *system_memory, void omap_mpu_wakeup(void *opaque, int irq, int req) { struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque; + CPUState *cpu = CPU(mpu->cpu); - if (mpu->cpu->env.halted) { - cpu_interrupt(&mpu->cpu->env, CPU_INTERRUPT_EXITTB); + if (cpu->halted) { + cpu_interrupt(cpu, CPU_INTERRUPT_EXITTB); } } @@ -3859,7 +3865,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, qdev_prop_set_uint32(s->ih[0], "size", 0x100); qdev_prop_set_ptr(s->ih[0], "clk", omap_findclk(s, "arminth_ck")); qdev_init_nofail(s->ih[0]); - busdev = sysbus_from_qdev(s->ih[0]); + busdev = SYS_BUS_DEVICE(s->ih[0]); sysbus_connect_irq(busdev, 0, cpu_irq[ARM_PIC_CPU_IRQ]); sysbus_connect_irq(busdev, 1, cpu_irq[ARM_PIC_CPU_FIQ]); sysbus_mmio_map(busdev, 0, 0xfffecb00); @@ -3867,7 +3873,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, qdev_prop_set_uint32(s->ih[1], "size", 0x800); qdev_prop_set_ptr(s->ih[1], "clk", omap_findclk(s, "arminth_ck")); qdev_init_nofail(s->ih[1]); - busdev = sysbus_from_qdev(s->ih[1]); + busdev = SYS_BUS_DEVICE(s->ih[1]); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(s->ih[0], OMAP_INT_15XX_IH2_IRQ)); /* The second interrupt controller's FIQ output is not wired up */ @@ -3980,9 +3986,9 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model); qdev_prop_set_ptr(s->gpio, "clk", omap_findclk(s, "arm_gpio_ck")); qdev_init_nofail(s->gpio); - sysbus_connect_irq(sysbus_from_qdev(s->gpio), 0, + sysbus_connect_irq(SYS_BUS_DEVICE(s->gpio), 0, qdev_get_gpio_in(s->ih[0], OMAP_INT_GPIO_BANK1)); - sysbus_mmio_map(sysbus_from_qdev(s->gpio), 0, 0xfffce000); + sysbus_mmio_map(SYS_BUS_DEVICE(s->gpio), 0, 0xfffce000); s->microwire = omap_uwire_init(system_memory, 0xfffb3000, qdev_get_gpio_in(s->ih[1], OMAP_INT_uWireTX), @@ -3998,7 +4004,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, qdev_prop_set_uint8(s->i2c[0], "revision", 0x11); qdev_prop_set_ptr(s->i2c[0], "fclk", omap_findclk(s, "mpuper_ck")); qdev_init_nofail(s->i2c[0]); - busdev = sysbus_from_qdev(s->i2c[0]); + busdev = SYS_BUS_DEVICE(s->i2c[0]); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(s->ih[1], OMAP_INT_I2C)); sysbus_connect_irq(busdev, 1, s->drq[OMAP_DMA_I2C_TX]); sysbus_connect_irq(busdev, 2, s->drq[OMAP_DMA_I2C_RX]); diff --git a/hw/omap2.c b/hw/arm/omap2.c similarity index 99% rename from hw/omap2.c rename to hw/arm/omap2.c index c8358500bc..0a2cd7bab6 100644 --- a/hw/omap2.c +++ b/hw/arm/omap2.c @@ -19,15 +19,15 @@ */ #include "sysemu/blockdev.h" -#include "hw.h" -#include "arm-misc.h" -#include "omap.h" +#include "hw/hw.h" +#include "hw/arm-misc.h" +#include "hw/omap.h" #include "sysemu/sysemu.h" #include "qemu/timer.h" #include "char/char.h" -#include "flash.h" -#include "soc_dma.h" -#include "sysbus.h" +#include "hw/flash.h" +#include "hw/soc_dma.h" +#include "hw/sysbus.h" #include "audio/audio.h" /* Enhanced Audio Controller (CODEC only) */ @@ -2283,7 +2283,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, qdev_prop_set_ptr(s->ih[0], "fclk", omap_findclk(s, "mpu_intc_fclk")); qdev_prop_set_ptr(s->ih[0], "iclk", omap_findclk(s, "mpu_intc_iclk")); qdev_init_nofail(s->ih[0]); - busdev = sysbus_from_qdev(s->ih[0]); + busdev = SYS_BUS_DEVICE(s->ih[0]); sysbus_connect_irq(busdev, 0, cpu_irq[ARM_PIC_CPU_IRQ]); sysbus_connect_irq(busdev, 1, cpu_irq[ARM_PIC_CPU_FIQ]); sysbus_mmio_map(busdev, 0, 0x480fe000); @@ -2398,7 +2398,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, qdev_prop_set_ptr(s->i2c[0], "iclk", omap_findclk(s, "i2c1.iclk")); qdev_prop_set_ptr(s->i2c[0], "fclk", omap_findclk(s, "i2c1.fclk")); qdev_init_nofail(s->i2c[0]); - busdev = sysbus_from_qdev(s->i2c[0]); + busdev = SYS_BUS_DEVICE(s->i2c[0]); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C1_IRQ)); sysbus_connect_irq(busdev, 1, s->drq[OMAP24XX_DMA_I2C1_TX]); @@ -2410,7 +2410,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, qdev_prop_set_ptr(s->i2c[1], "iclk", omap_findclk(s, "i2c2.iclk")); qdev_prop_set_ptr(s->i2c[1], "fclk", omap_findclk(s, "i2c2.fclk")); qdev_init_nofail(s->i2c[1]); - busdev = sysbus_from_qdev(s->i2c[1]); + busdev = SYS_BUS_DEVICE(s->i2c[1]); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C2_IRQ)); sysbus_connect_irq(busdev, 1, s->drq[OMAP24XX_DMA_I2C2_TX]); @@ -2428,7 +2428,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, qdev_prop_set_ptr(s->gpio, "fclk4", omap_findclk(s, "gpio5_dbclk")); } qdev_init_nofail(s->gpio); - busdev = sysbus_from_qdev(s->gpio); + busdev = SYS_BUS_DEVICE(s->gpio); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK1)); sysbus_connect_irq(busdev, 3, diff --git a/hw/omap_sx1.c b/hw/arm/omap_sx1.c similarity index 97% rename from hw/omap_sx1.c rename to hw/arm/omap_sx1.c index 0f03121505..85982334bd 100644 --- a/hw/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -25,12 +25,12 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "ui/console.h" -#include "omap.h" -#include "boards.h" -#include "arm-misc.h" -#include "flash.h" +#include "hw/omap.h" +#include "hw/boards.h" +#include "hw/arm-misc.h" +#include "hw/flash.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" @@ -219,12 +219,14 @@ static QEMUMachine sx1_machine_v2 = { .name = "sx1", .desc = "Siemens SX1 (OMAP310) V2", .init = sx1_init_v2, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine sx1_machine_v1 = { .name = "sx1-v1", .desc = "Siemens SX1 (OMAP310) V1", .init = sx1_init_v1, + DEFAULT_MACHINE_OPTIONS, }; static void sx1_machine_init(void) diff --git a/hw/palm.c b/hw/arm/palm.c similarity index 98% rename from hw/palm.c rename to hw/arm/palm.c index 5219e37394..91bc74af24 100644 --- a/hw/palm.c +++ b/hw/arm/palm.c @@ -16,15 +16,15 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "audio/audio.h" #include "sysemu/sysemu.h" #include "ui/console.h" -#include "omap.h" -#include "boards.h" -#include "arm-misc.h" -#include "devices.h" -#include "loader.h" +#include "hw/omap.h" +#include "hw/boards.h" +#include "hw/arm-misc.h" +#include "hw/devices.h" +#include "hw/loader.h" #include "exec/address-spaces.h" static uint32_t static_readb(void *opaque, hwaddr offset) @@ -280,6 +280,7 @@ static QEMUMachine palmte_machine = { .name = "cheetah", .desc = "Palm Tungsten|E aka. Cheetah PDA (OMAP310)", .init = palmte_init, + DEFAULT_MACHINE_OPTIONS, }; static void palmte_machine_init(void) diff --git a/hw/arm/pic_cpu.c b/hw/arm/pic_cpu.c new file mode 100644 index 0000000000..3a3f06566b --- /dev/null +++ b/hw/arm/pic_cpu.c @@ -0,0 +1,68 @@ +/* + * Generic ARM Programmable Interrupt Controller support. + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the LGPL + */ + +#include "hw/hw.h" +#include "hw/arm-misc.h" +#include "sysemu/kvm.h" + +/* Input 0 is IRQ and input 1 is FIQ. */ +static void arm_pic_cpu_handler(void *opaque, int irq, int level) +{ + ARMCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + + switch (irq) { + case ARM_PIC_CPU_IRQ: + if (level) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + break; + case ARM_PIC_CPU_FIQ: + if (level) { + cpu_interrupt(cs, CPU_INTERRUPT_FIQ); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_FIQ); + } + break; + default: + hw_error("arm_pic_cpu_handler: Bad interrupt line %d\n", irq); + } +} + +static void kvm_arm_pic_cpu_handler(void *opaque, int irq, int level) +{ +#ifdef CONFIG_KVM + ARMCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + int kvm_irq = KVM_ARM_IRQ_TYPE_CPU << KVM_ARM_IRQ_TYPE_SHIFT; + + switch (irq) { + case ARM_PIC_CPU_IRQ: + kvm_irq |= KVM_ARM_IRQ_CPU_IRQ; + break; + case ARM_PIC_CPU_FIQ: + kvm_irq |= KVM_ARM_IRQ_CPU_FIQ; + break; + default: + hw_error("kvm_arm_pic_cpu_handler: Bad interrupt line %d\n", irq); + } + kvm_irq |= cs->cpu_index << KVM_ARM_IRQ_VCPU_SHIFT; + kvm_set_irq(kvm_state, kvm_irq, level ? 1 : 0); +#endif +} + +qemu_irq *arm_pic_init_cpu(ARMCPU *cpu) +{ + if (kvm_enabled()) { + return qemu_allocate_irqs(kvm_arm_pic_cpu_handler, cpu, 2); + } + return qemu_allocate_irqs(arm_pic_cpu_handler, cpu, 2); +} diff --git a/hw/pxa2xx.c b/hw/arm/pxa2xx.c similarity index 98% rename from hw/pxa2xx.c rename to hw/arm/pxa2xx.c index 3c51bc82aa..7467cca4f7 100644 --- a/hw/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -7,12 +7,12 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" -#include "pxa.h" +#include "hw/sysbus.h" +#include "hw/pxa.h" #include "sysemu/sysemu.h" -#include "serial.h" -#include "i2c.h" -#include "ssi.h" +#include "hw/serial.h" +#include "hw/i2c.h" +#include "hw/ssi.h" #include "char/char.h" #include "sysemu/blockdev.h" @@ -263,14 +263,14 @@ static int pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri, case 1: /* Idle */ if (!(s->cm_regs[CCCR >> 2] & (1 << 31))) { /* CPDIS */ - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT); + cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HALT); break; } /* Fall through. */ case 2: /* Deep-Idle */ - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HALT); + cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HALT); s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ goto message; @@ -301,7 +301,8 @@ static int pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri, #endif /* Suspend */ - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT); + cpu_interrupt(CPU(arm_env_get_cpu(cpu_single_env)), + CPU_INTERRUPT_HALT); goto message; @@ -343,23 +344,23 @@ static int pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri, } static const ARMCPRegInfo pxa_cp_reginfo[] = { - /* cp14 crn==1: perf registers */ - { .name = "CPPMNC", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, + /* cp14 crm==1: perf registers */ + { .name = "CPPMNC", .cp = 14, .crn = 0, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .readfn = pxa2xx_cppmnc_read, .writefn = pxa2xx_cppmnc_write }, { .name = "CPCCNT", .cp = 14, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .readfn = pxa2xx_cpccnt_read, .writefn = arm_cp_write_ignore }, - { .name = "CPINTEN", .cp = 14, .crn = 1, .crm = 4, .opc1 = 0, .opc2 = 0, + { .name = "CPINTEN", .cp = 14, .crn = 4, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPFLAG", .cp = 14, .crn = 1, .crm = 5, .opc1 = 0, .opc2 = 0, + { .name = "CPFLAG", .cp = 14, .crn = 5, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPEVTSEL", .cp = 14, .crn = 1, .crm = 8, .opc1 = 0, .opc2 = 0, + { .name = "CPEVTSEL", .cp = 14, .crn = 8, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* cp14 crn==2: performance count registers */ - { .name = "CPPMN0", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, + /* cp14 crm==2: performance count registers */ + { .name = "CPPMN0", .cp = 14, .crn = 0, .crm = 2, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPPMN1", .cp = 14, .crn = 2, .crm = 1, .opc1 = 0, .opc2 = 0, + { .name = "CPPMN1", .cp = 14, .crn = 1, .crm = 2, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "CPPMN2", .cp = 14, .crn = 2, .crm = 2, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -1194,7 +1195,7 @@ static void pxa2xx_rtc_sysbus_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pxa2xx_rtc_regs; } -static TypeInfo pxa2xx_rtc_sysbus_info = { +static const TypeInfo pxa2xx_rtc_sysbus_info = { .name = "pxa2xx_rtc", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxRTCState), @@ -1442,7 +1443,7 @@ static void pxa2xx_i2c_slave_class_init(ObjectClass *klass, void *data) k->send = pxa2xx_i2c_tx; } -static TypeInfo pxa2xx_i2c_slave_info = { +static const TypeInfo pxa2xx_i2c_slave_info = { .name = "pxa2xx-i2c-slave", .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(PXA2xxI2CSlaveState), @@ -1456,7 +1457,7 @@ PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base, SysBusDevice *i2c_dev; PXA2xxI2CState *s; - i2c_dev = sysbus_from_qdev(qdev_create(NULL, "pxa2xx_i2c")); + i2c_dev = SYS_BUS_DEVICE(qdev_create(NULL, "pxa2xx_i2c")); qdev_prop_set_uint32(&i2c_dev->qdev, "size", region_size + 1); qdev_prop_set_uint32(&i2c_dev->qdev, "offset", base & region_size); @@ -1468,7 +1469,7 @@ PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base, s = FROM_SYSBUS(PXA2xxI2CState, i2c_dev); /* FIXME: Should the slave device really be on a separate bus? */ dev = i2c_create_slave(i2c_init_bus(NULL, "dummy"), "pxa2xx-i2c-slave", 0); - s->slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, I2C_SLAVE_FROM_QDEV(dev)); + s->slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, I2C_SLAVE(dev)); s->slave->host = s; return s; @@ -1510,7 +1511,7 @@ static void pxa2xx_i2c_class_init(ObjectClass *klass, void *data) dc->props = pxa2xx_i2c_properties; } -static TypeInfo pxa2xx_i2c_info = { +static const TypeInfo pxa2xx_i2c_info = { .name = "pxa2xx_i2c", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxI2CState), @@ -2045,7 +2046,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, qdev_get_gpio_in(s->pic, PXA27X_PIC_OST_4_11), NULL); - s->gpio = pxa2xx_gpio_init(0x40e00000, &s->cpu->env, s->pic, 121); + s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 121); dinfo = drive_get(IF_SD, 0, 0); if (!dinfo) { @@ -2176,7 +2177,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 3), NULL); - s->gpio = pxa2xx_gpio_init(0x40e00000, &s->cpu->env, s->pic, 85); + s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 85); dinfo = drive_get(IF_SD, 0, 0); if (!dinfo) { @@ -2273,7 +2274,7 @@ static void pxa2xx_ssp_class_init(ObjectClass *klass, void *data) sdc->init = pxa2xx_ssp_init; } -static TypeInfo pxa2xx_ssp_info = { +static const TypeInfo pxa2xx_ssp_info = { .name = "pxa2xx-ssp", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxSSPState), diff --git a/hw/pxa2xx_gpio.c b/hw/arm/pxa2xx_gpio.c similarity index 92% rename from hw/pxa2xx_gpio.c rename to hw/arm/pxa2xx_gpio.c index 7aaf4092df..55ebcd724a 100644 --- a/hw/pxa2xx_gpio.c +++ b/hw/arm/pxa2xx_gpio.c @@ -7,9 +7,9 @@ * This code is licensed under the GPL. */ -#include "hw.h" -#include "sysbus.h" -#include "pxa.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/pxa.h" #define PXA2XX_GPIO_BANKS 4 @@ -93,6 +93,7 @@ static const int pxa2xx_gpio_wake[PXA2XX_GPIO_BANKS] = { static void pxa2xx_gpio_set(void *opaque, int line, int level) { PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque; + CPUState *cpu = CPU(s->cpu); int bank; uint32_t mask; @@ -118,8 +119,8 @@ static void pxa2xx_gpio_set(void *opaque, int line, int level) pxa2xx_gpio_irq_update(s); /* Wake-up GPIOs */ - if (s->cpu->env.halted && (mask & ~s->dir[bank] & pxa2xx_gpio_wake[bank])) { - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_EXITTB); + if (cpu->halted && (mask & ~s->dir[bank] & pxa2xx_gpio_wake[bank])) { + cpu_interrupt(cpu, CPU_INTERRUPT_EXITTB); } } @@ -250,21 +251,22 @@ static const MemoryRegionOps pxa_gpio_ops = { }; DeviceState *pxa2xx_gpio_init(hwaddr base, - CPUARMState *env, DeviceState *pic, int lines) + ARMCPU *cpu, DeviceState *pic, int lines) { + CPUState *cs = CPU(cpu); DeviceState *dev; dev = qdev_create(NULL, "pxa2xx-gpio"); qdev_prop_set_int32(dev, "lines", lines); - qdev_prop_set_int32(dev, "ncpu", env->cpu_index); + qdev_prop_set_int32(dev, "ncpu", cs->cpu_index); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_0)); - sysbus_connect_irq(sysbus_from_qdev(dev), 1, + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_1)); - sysbus_connect_irq(sysbus_from_qdev(dev), 2, + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_X)); return dev; @@ -276,7 +278,7 @@ static int pxa2xx_gpio_initfn(SysBusDevice *dev) s = FROM_SYSBUS(PXA2xxGPIOInfo, dev); - s->cpu = arm_env_get_cpu(qemu_get_cpu(s->ncpu)); + s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu)); qdev_init_gpio_in(&dev->qdev, pxa2xx_gpio_set, s->lines); qdev_init_gpio_out(&dev->qdev, s->handler, s->lines); @@ -296,7 +298,7 @@ static int pxa2xx_gpio_initfn(SysBusDevice *dev) */ void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler) { - PXA2xxGPIOInfo *s = FROM_SYSBUS(PXA2xxGPIOInfo, sysbus_from_qdev(dev)); + PXA2xxGPIOInfo *s = FROM_SYSBUS(PXA2xxGPIOInfo, SYS_BUS_DEVICE(dev)); s->read_notify = handler; } @@ -334,7 +336,7 @@ static void pxa2xx_gpio_class_init(ObjectClass *klass, void *data) dc->props = pxa2xx_gpio_properties; } -static TypeInfo pxa2xx_gpio_info = { +static const TypeInfo pxa2xx_gpio_info = { .name = "pxa2xx-gpio", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxGPIOInfo), diff --git a/hw/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c similarity index 94% rename from hw/pxa2xx_pic.c rename to hw/arm/pxa2xx_pic.c index 70b2b79d07..25e90895e1 100644 --- a/hw/pxa2xx_pic.c +++ b/hw/arm/pxa2xx_pic.c @@ -8,9 +8,9 @@ * This code is licensed under the GPL. */ -#include "hw.h" -#include "pxa.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/pxa.h" +#include "hw/sysbus.h" #define ICIP 0x00 /* Interrupt Controller IRQ Pending register */ #define ICMR 0x04 /* Interrupt Controller Mask register */ @@ -46,12 +46,13 @@ static void pxa2xx_pic_update(void *opaque) { uint32_t mask[2]; PXA2xxPICState *s = (PXA2xxPICState *) opaque; + CPUState *cpu = CPU(s->cpu); - if (s->cpu->env.halted) { + if (cpu->halted) { mask[0] = s->int_pending[0] & (s->int_enabled[0] | s->int_idle); mask[1] = s->int_pending[1] & (s->int_enabled[1] | s->int_idle); if (mask[0] || mask[1]) { - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_EXITTB); + cpu_interrupt(cpu, CPU_INTERRUPT_EXITTB); } } @@ -59,15 +60,15 @@ static void pxa2xx_pic_update(void *opaque) mask[1] = s->int_pending[1] & s->int_enabled[1]; if ((mask[0] & s->is_fiq[0]) || (mask[1] & s->is_fiq[1])) { - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ); + cpu_interrupt(cpu, CPU_INTERRUPT_FIQ); } else { - cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ); + cpu_reset_interrupt(cpu, CPU_INTERRUPT_FIQ); } if ((mask[0] & ~s->is_fiq[0]) || (mask[1] & ~s->is_fiq[1])) { - cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD); + cpu_interrupt(cpu, CPU_INTERRUPT_HARD); } else { - cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); } } @@ -261,7 +262,7 @@ DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) { CPUARMState *env = &cpu->env; DeviceState *dev = qdev_create(NULL, "pxa2xx_pic"); - PXA2xxPICState *s = FROM_SYSBUS(PXA2xxPICState, sysbus_from_qdev(dev)); + PXA2xxPICState *s = FROM_SYSBUS(PXA2xxPICState, SYS_BUS_DEVICE(dev)); s->cpu = cpu; @@ -279,8 +280,8 @@ DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) /* Enable IC memory-mapped registers access. */ memory_region_init_io(&s->iomem, &pxa2xx_pic_ops, s, "pxa2xx-pic", 0x00100000); - sysbus_init_mmio(sysbus_from_qdev(dev), &s->iomem); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); /* Enable IC coprocessor access. */ define_arm_cp_regs_with_opaque(arm_env_get_cpu(env), pxa_pic_cp_reginfo, s); @@ -319,7 +320,7 @@ static void pxa2xx_pic_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pxa2xx_pic_regs; } -static TypeInfo pxa2xx_pic_info = { +static const TypeInfo pxa2xx_pic_info = { .name = "pxa2xx_pic", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxPICState), diff --git a/hw/realview.c b/hw/arm/realview.c similarity index 95% rename from hw/realview.c rename to hw/arm/realview.c index 872b3b468a..5fb490c832 100644 --- a/hw/realview.c +++ b/hw/arm/realview.c @@ -7,15 +7,15 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" -#include "arm-misc.h" -#include "primecell.h" -#include "devices.h" -#include "pci/pci.h" +#include "hw/sysbus.h" +#include "hw/arm-misc.h" +#include "hw/primecell.h" +#include "hw/devices.h" +#include "hw/pci/pci.h" #include "net/net.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "i2c.h" +#include "hw/boards.h" +#include "hw/i2c.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" @@ -140,14 +140,14 @@ static void realview_init(QEMUMachineInitArgs *args, qdev_prop_set_uint32(sysctl, "sys_id", sys_id); qdev_prop_set_uint32(sysctl, "proc_id", proc_id); qdev_init_nofail(sysctl); - sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000); + sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, 0x10000000); if (is_mpcore) { hwaddr periphbase; dev = qdev_create(NULL, is_pb ? "a9mpcore_priv": "realview_mpcore"); qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); if (is_pb) { periphbase = 0x1f000000; } else { @@ -172,8 +172,8 @@ static void realview_init(QEMUMachineInitArgs *args, pl041 = qdev_create(NULL, "pl041"); qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); qdev_init_nofail(pl041); - sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000); - sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[19]); + sysbus_mmio_map(SYS_BUS_DEVICE(pl041), 0, 0x10004000); + sysbus_connect_irq(SYS_BUS_DEVICE(pl041), 0, pic[19]); sysbus_create_simple("pl050_keyboard", 0x10006000, pic[20]); sysbus_create_simple("pl050_mouse", 0x10007000, pic[21]); @@ -215,7 +215,7 @@ static void realview_init(QEMUMachineInitArgs *args, if (!is_pb) { dev = qdev_create(NULL, "realview_pci"); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); qdev_init_nofail(dev); sysbus_mmio_map(busdev, 0, 0x61000000); /* PCI self-config */ sysbus_mmio_map(busdev, 1, 0x62000000); /* PCI config */ @@ -365,6 +365,7 @@ static QEMUMachine realview_eb_machine = { .desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)", .init = realview_eb_init, .block_default_type = IF_SCSI, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine realview_eb_mpcore_machine = { @@ -373,12 +374,14 @@ static QEMUMachine realview_eb_mpcore_machine = { .init = realview_eb_mpcore_init, .block_default_type = IF_SCSI, .max_cpus = 4, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine realview_pb_a8_machine = { .name = "realview-pb-a8", .desc = "ARM RealView Platform Baseboard for Cortex-A8", .init = realview_pb_a8_init, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine realview_pbx_a9_machine = { @@ -387,6 +390,7 @@ static QEMUMachine realview_pbx_a9_machine = { .init = realview_pbx_a9_init, .block_default_type = IF_SCSI, .max_cpus = 4, + DEFAULT_MACHINE_OPTIONS, }; static void realview_machine_init(void) diff --git a/hw/spitz.c b/hw/arm/spitz.c similarity index 98% rename from hw/spitz.c rename to hw/arm/spitz.c index 8e1be7fb21..f5832bea93 100644 --- a/hw/spitz.c +++ b/hw/arm/spitz.c @@ -10,23 +10,23 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "pxa.h" -#include "arm-misc.h" +#include "hw/hw.h" +#include "hw/pxa.h" +#include "hw/arm-misc.h" #include "sysemu/sysemu.h" -#include "pcmcia.h" -#include "i2c.h" -#include "ssi.h" -#include "flash.h" +#include "hw/pcmcia.h" +#include "hw/i2c.h" +#include "hw/ssi.h" +#include "hw/flash.h" #include "qemu/timer.h" -#include "devices.h" -#include "sharpsl.h" +#include "hw/devices.h" +#include "hw/sharpsl.h" #include "ui/console.h" #include "block/block.h" #include "audio/audio.h" -#include "boards.h" +#include "hw/boards.h" #include "sysemu/blockdev.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "exec/address-spaces.h" #undef REG_FMT @@ -156,7 +156,7 @@ static void sl_flash_register(PXA2xxState *cpu, int size) qdev_prop_set_uint8(dev, "chip_id", 0xf1); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, FLASH_BASE); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, FLASH_BASE); } static int sl_nand_init(SysBusDevice *dev) { @@ -459,7 +459,7 @@ static void spitz_keyboard_register(PXA2xxState *cpu) SpitzKeyboardState *s; dev = sysbus_create_simple("spitz-keyboard", -1, NULL); - s = FROM_SYSBUS(SpitzKeyboardState, sysbus_from_qdev(dev)); + s = FROM_SYSBUS(SpitzKeyboardState, SYS_BUS_DEVICE(dev)); for (i = 0; i < SPITZ_KEY_SENSE_NUM; i ++) qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(cpu->gpio, spitz_gpio_key_sense[i])); @@ -959,24 +959,28 @@ static QEMUMachine akitapda_machine = { .name = "akita", .desc = "Akita PDA (PXA270)", .init = akita_init, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine spitzpda_machine = { .name = "spitz", .desc = "Spitz PDA (PXA270)", .init = spitz_init, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine borzoipda_machine = { .name = "borzoi", .desc = "Borzoi PDA (PXA270)", .init = borzoi_init, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine terrierpda_machine = { .name = "terrier", .desc = "Terrier PDA (PXA270)", .init = terrier_init, + DEFAULT_MACHINE_OPTIONS, }; static void spitz_machine_init(void) @@ -1022,7 +1026,7 @@ static void sl_nand_class_init(ObjectClass *klass, void *data) dc->props = sl_nand_properties; } -static TypeInfo sl_nand_info = { +static const TypeInfo sl_nand_info = { .name = "sl-nand", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SLNANDState), @@ -1057,7 +1061,7 @@ static void spitz_keyboard_class_init(ObjectClass *klass, void *data) dc->props = spitz_keyboard_properties; } -static TypeInfo spitz_keyboard_info = { +static const TypeInfo spitz_keyboard_info = { .name = "spitz-keyboard", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SpitzKeyboardState), @@ -1086,7 +1090,7 @@ static void corgi_ssp_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_corgi_ssp_regs; } -static TypeInfo corgi_ssp_info = { +static const TypeInfo corgi_ssp_info = { .name = "corgi-ssp", .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(CorgiSSPState), @@ -1116,7 +1120,7 @@ static void spitz_lcdtg_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_spitz_lcdtg_regs; } -static TypeInfo spitz_lcdtg_info = { +static const TypeInfo spitz_lcdtg_info = { .name = "spitz-lcdtg", .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(SpitzLCDTG), diff --git a/hw/stellaris.c b/hw/arm/stellaris.c similarity index 98% rename from hw/stellaris.c rename to hw/arm/stellaris.c index 26da3c7f60..f4ce7945f3 100644 --- a/hw/stellaris.c +++ b/hw/arm/stellaris.c @@ -7,14 +7,14 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" -#include "ssi.h" -#include "arm-misc.h" -#include "devices.h" +#include "hw/sysbus.h" +#include "hw/ssi.h" +#include "hw/arm-misc.h" +#include "hw/devices.h" #include "qemu/timer.h" -#include "i2c.h" +#include "hw/i2c.h" #include "net/net.h" -#include "boards.h" +#include "hw/boards.h" #include "exec/address-spaces.h" #define GPIO_A 0 @@ -1286,8 +1286,8 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, enet = qdev_create(NULL, "stellaris_enet"); qdev_set_nic_properties(enet, &nd_table[0]); qdev_init_nofail(enet); - sysbus_mmio_map(sysbus_from_qdev(enet), 0, 0x40048000); - sysbus_connect_irq(sysbus_from_qdev(enet), 0, pic[42]); + sysbus_mmio_map(SYS_BUS_DEVICE(enet), 0, 0x40048000); + sysbus_connect_irq(SYS_BUS_DEVICE(enet), 0, pic[42]); } if (board->peripherals & BP_GAMEPAD) { qemu_irq gpad_irq[5]; @@ -1331,12 +1331,14 @@ static QEMUMachine lm3s811evb_machine = { .name = "lm3s811evb", .desc = "Stellaris LM3S811EVB", .init = lm3s811evb_init, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine lm3s6965evb_machine = { .name = "lm3s6965evb", .desc = "Stellaris LM3S6965EVB", .init = lm3s6965evb_init, + DEFAULT_MACHINE_OPTIONS, }; static void stellaris_machine_init(void) @@ -1354,7 +1356,7 @@ static void stellaris_i2c_class_init(ObjectClass *klass, void *data) sdc->init = stellaris_i2c_init; } -static TypeInfo stellaris_i2c_info = { +static const TypeInfo stellaris_i2c_info = { .name = "stellaris-i2c", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(stellaris_i2c_state), @@ -1368,7 +1370,7 @@ static void stellaris_gptm_class_init(ObjectClass *klass, void *data) sdc->init = stellaris_gptm_init; } -static TypeInfo stellaris_gptm_info = { +static const TypeInfo stellaris_gptm_info = { .name = "stellaris-gptm", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(gptm_state), @@ -1382,7 +1384,7 @@ static void stellaris_adc_class_init(ObjectClass *klass, void *data) sdc->init = stellaris_adc_init; } -static TypeInfo stellaris_adc_info = { +static const TypeInfo stellaris_adc_info = { .name = "stellaris-adc", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(stellaris_adc_state), diff --git a/hw/tosa.c b/hw/arm/tosa.c similarity index 96% rename from hw/tosa.c rename to hw/arm/tosa.c index 6ee4693840..747888c64e 100644 --- a/hw/tosa.c +++ b/hw/arm/tosa.c @@ -11,18 +11,18 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "pxa.h" -#include "arm-misc.h" -#include "devices.h" -#include "sharpsl.h" -#include "pcmcia.h" +#include "hw/hw.h" +#include "hw/pxa.h" +#include "hw/arm-misc.h" +#include "hw/devices.h" +#include "hw/sharpsl.h" +#include "hw/pcmcia.h" #include "block/block.h" -#include "boards.h" -#include "i2c.h" -#include "ssi.h" +#include "hw/boards.h" +#include "hw/i2c.h" +#include "hw/ssi.h" #include "sysemu/blockdev.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "exec/address-spaces.h" #define TOSA_RAM 0x04000000 @@ -251,6 +251,7 @@ static QEMUMachine tosapda_machine = { .name = "tosa", .desc = "Tosa PDA (PXA255)", .init = tosa_init, + DEFAULT_MACHINE_OPTIONS, }; static void tosapda_machine_init(void) @@ -270,7 +271,7 @@ static void tosa_dac_class_init(ObjectClass *klass, void *data) k->send = tosa_dac_send; } -static TypeInfo tosa_dac_info = { +static const TypeInfo tosa_dac_info = { .name = "tosa_dac", .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(TosaDACState), @@ -285,7 +286,7 @@ static void tosa_ssp_class_init(ObjectClass *klass, void *data) k->transfer = tosa_ssp_tansfer; } -static TypeInfo tosa_ssp_info = { +static const TypeInfo tosa_ssp_info = { .name = "tosa-ssp", .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(SSISlave), diff --git a/hw/versatilepb.c b/hw/arm/versatilepb.c similarity index 95% rename from hw/versatilepb.c rename to hw/arm/versatilepb.c index 5e89e747a2..baaa265888 100644 --- a/hw/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -7,17 +7,17 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" -#include "arm-misc.h" -#include "devices.h" +#include "hw/sysbus.h" +#include "hw/arm-misc.h" +#include "hw/devices.h" #include "net/net.h" #include "sysemu/sysemu.h" -#include "pci/pci.h" -#include "i2c.h" -#include "boards.h" +#include "hw/pci/pci.h" +#include "hw/i2c.h" +#include "hw/boards.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" -#include "flash.h" +#include "hw/flash.h" #define VERSATILE_FLASH_ADDR 0x34000000 #define VERSATILE_FLASH_SIZE (64 * 1024 * 1024) @@ -203,7 +203,7 @@ static void versatile_init(QEMUMachineInitArgs *args, int board_id) qdev_prop_set_uint32(sysctl, "sys_id", 0x41007004); qdev_prop_set_uint32(sysctl, "proc_id", 0x02000000); qdev_init_nofail(sysctl); - sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000); + sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, 0x10000000); cpu_pic = arm_pic_init_cpu(cpu); dev = sysbus_create_varargs("pl190", 0x10140000, @@ -214,7 +214,7 @@ static void versatile_init(QEMUMachineInitArgs *args, int board_id) } dev = sysbus_create_simple("versatilepb_sic", 0x10003000, NULL); for (n = 0; n < 32; n++) { - sysbus_connect_irq(sysbus_from_qdev(dev), n, pic[n]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), n, pic[n]); sic[n] = qdev_get_gpio_in(dev, n); } @@ -222,7 +222,7 @@ static void versatile_init(QEMUMachineInitArgs *args, int board_id) sysbus_create_simple("pl050_mouse", 0x10007000, sic[4]); dev = qdev_create(NULL, "versatile_pci"); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); qdev_init_nofail(dev); sysbus_mmio_map(busdev, 0, 0x41000000); /* PCI self-config */ sysbus_mmio_map(busdev, 1, 0x42000000); /* PCI config */ @@ -287,8 +287,8 @@ static void versatile_init(QEMUMachineInitArgs *args, int board_id) pl041 = qdev_create(NULL, "pl041"); qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); qdev_init_nofail(pl041); - sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000); - sysbus_connect_irq(sysbus_from_qdev(pl041), 0, sic[24]); + sysbus_mmio_map(SYS_BUS_DEVICE(pl041), 0, 0x10004000); + sysbus_connect_irq(SYS_BUS_DEVICE(pl041), 0, sic[24]); /* Memory map for Versatile/PB: */ /* 0x10000000 System registers. */ @@ -359,6 +359,7 @@ static QEMUMachine versatilepb_machine = { .desc = "ARM Versatile/PB (ARM926EJ-S)", .init = vpb_init, .block_default_type = IF_SCSI, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine versatileab_machine = { @@ -366,6 +367,7 @@ static QEMUMachine versatileab_machine = { .desc = "ARM Versatile/AB (ARM926EJ-S)", .init = vab_init, .block_default_type = IF_SCSI, + DEFAULT_MACHINE_OPTIONS, }; static void versatile_machine_init(void) @@ -386,7 +388,7 @@ static void vpb_sic_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_vpb_sic; } -static TypeInfo vpb_sic_info = { +static const TypeInfo vpb_sic_info = { .name = "versatilepb_sic", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(vpb_sic_state), diff --git a/hw/vexpress.c b/hw/arm/vexpress.c similarity index 97% rename from hw/vexpress.c rename to hw/arm/vexpress.c index 93c3176667..02922c38b3 100644 --- a/hw/vexpress.c +++ b/hw/arm/vexpress.c @@ -21,16 +21,16 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "sysbus.h" -#include "arm-misc.h" -#include "primecell.h" -#include "devices.h" +#include "hw/sysbus.h" +#include "hw/arm-misc.h" +#include "hw/primecell.h" +#include "hw/devices.h" #include "net/net.h" #include "sysemu/sysemu.h" -#include "boards.h" +#include "hw/boards.h" #include "exec/address-spaces.h" #include "sysemu/blockdev.h" -#include "flash.h" +#include "hw/flash.h" #define VEXPRESS_BOARD_ID 0x8e0 #define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024) @@ -211,7 +211,7 @@ static void a9_daughterboard_init(const VEDBoardInfo *daughterboard, dev = qdev_create(NULL, "a9mpcore_priv"); qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, 0x1e000000); for (n = 0; n < smp_cpus; n++) { sysbus_connect_irq(busdev, n, cpu_irq[n]); @@ -271,7 +271,7 @@ static void a15_daughterboard_init(const VEDBoardInfo *daughterboard, cpu_model = "cortex-a15"; } - *proc_id = 0x14000217; + *proc_id = 0x14000237; for (n = 0; n < smp_cpus; n++) { ARMCPU *cpu; @@ -307,7 +307,7 @@ static void a15_daughterboard_init(const VEDBoardInfo *daughterboard, dev = qdev_create(NULL, "a15mpcore_priv"); qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, 0x2c000000); for (n = 0; n < smp_cpus; n++) { sysbus_connect_irq(busdev, n, cpu_irq[n]); @@ -374,7 +374,7 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard, qdev_prop_set_uint32(sysctl, "sys_id", sys_id); qdev_prop_set_uint32(sysctl, "proc_id", proc_id); qdev_init_nofail(sysctl); - sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, map[VE_SYSREGS]); + sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, map[VE_SYSREGS]); /* VE_SP810: not modelled */ /* VE_SERIALPCI: not modelled */ @@ -382,8 +382,8 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard, pl041 = qdev_create(NULL, "pl041"); qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); qdev_init_nofail(pl041); - sysbus_mmio_map(sysbus_from_qdev(pl041), 0, map[VE_PL041]); - sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[11]); + sysbus_mmio_map(SYS_BUS_DEVICE(pl041), 0, map[VE_PL041]); + sysbus_connect_irq(SYS_BUS_DEVICE(pl041), 0, pic[11]); dev = sysbus_create_varargs("pl181", map[VE_MMCI], pic[9], pic[10], NULL); /* Wire up MMC card detect and read-only signals */ @@ -479,6 +479,7 @@ static QEMUMachine vexpress_a9_machine = { .init = vexpress_a9_init, .block_default_type = IF_SCSI, .max_cpus = 4, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine vexpress_a15_machine = { @@ -487,6 +488,7 @@ static QEMUMachine vexpress_a15_machine = { .init = vexpress_a15_init, .block_default_type = IF_SCSI, .max_cpus = 4, + DEFAULT_MACHINE_OPTIONS, }; static void vexpress_machine_init(void) diff --git a/hw/xilinx_zynq.c b/hw/arm/xilinx_zynq.c similarity index 87% rename from hw/xilinx_zynq.c rename to hw/arm/xilinx_zynq.c index da0a7d0aa1..f78c47e43e 100644 --- a/hw/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -15,16 +15,16 @@ * with this program; if not, see . */ -#include "sysbus.h" -#include "arm-misc.h" +#include "hw/sysbus.h" +#include "hw/arm-misc.h" #include "net/net.h" #include "exec/address-spaces.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "flash.h" +#include "hw/boards.h" +#include "hw/flash.h" #include "sysemu/blockdev.h" -#include "loader.h" -#include "ssi.h" +#include "hw/loader.h" +#include "hw/ssi.h" #define NUM_SPI_FLASHES 4 #define NUM_QSPI_FLASHES 2 @@ -46,7 +46,7 @@ static void gem_init(NICInfo *nd, uint32_t base, qemu_irq irq) dev = qdev_create(NULL, "cadence_gem"); qdev_set_nic_properties(dev, nd); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, base); sysbus_connect_irq(s, 0, irq); } @@ -67,7 +67,7 @@ static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq, qdev_prop_set_uint8(dev, "num-ss-bits", num_ss); qdev_prop_set_uint8(dev, "num-busses", num_busses); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, base_addr); if (is_qspi) { sysbus_mmio_map(busdev, 1, 0xFC000000); @@ -82,8 +82,7 @@ static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq, spi = (SSIBus *)qdev_get_child_bus(dev, bus_name); for (j = 0; j < num_ss; ++j) { - flash_dev = ssi_create_slave_no_init(spi, "m25p80"); - qdev_prop_set_string(flash_dev, "partname", "n25q128"); + flash_dev = ssi_create_slave_no_init(spi, "n25q128"); qdev_init_nofail(flash_dev); cs_line = qdev_get_gpio_in(flash_dev, 0); @@ -150,12 +149,12 @@ static void zynq_init(QEMUMachineInitArgs *args) dev = qdev_create(NULL, "xilinx,zynq_slcr"); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0xF8000000); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8000000); dev = qdev_create(NULL, "a9mpcore_priv"); qdev_prop_set_uint32(dev, "num-cpu", 1); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, 0xF8F00000); sysbus_connect_irq(busdev, 0, cpu_irq); @@ -168,7 +167,7 @@ static void zynq_init(QEMUMachineInitArgs *args) zynq_init_spi_flashes(0xE000D000, pic[51-IRQ_OFFSET], true); sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]); - sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[75-IRQ_OFFSET]); + sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[76-IRQ_OFFSET]); sysbus_create_simple("cadence_uart", 0xE0000000, pic[59-IRQ_OFFSET]); sysbus_create_simple("cadence_uart", 0xE0001000, pic[82-IRQ_OFFSET]); @@ -187,6 +186,16 @@ static void zynq_init(QEMUMachineInitArgs *args) } } + dev = qdev_create(NULL, "generic-sdhci"); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0100000); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[56-IRQ_OFFSET]); + + dev = qdev_create(NULL, "generic-sdhci"); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0101000); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[79-IRQ_OFFSET]); + zynq_binfo.ram_size = ram_size; zynq_binfo.kernel_filename = kernel_filename; zynq_binfo.kernel_cmdline = kernel_cmdline; @@ -203,7 +212,8 @@ static QEMUMachine zynq_machine = { .init = zynq_init, .block_default_type = IF_SCSI, .max_cpus = 1, - .no_sdcard = 1 + .no_sdcard = 1, + DEFAULT_MACHINE_OPTIONS, }; static void zynq_machine_init(void) diff --git a/hw/z2.c b/hw/arm/z2.c similarity index 97% rename from hw/z2.c rename to hw/arm/z2.c index 09b03687d1..cbb6d8085e 100644 --- a/hw/z2.c +++ b/hw/arm/z2.c @@ -11,15 +11,15 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "pxa.h" -#include "arm-misc.h" -#include "devices.h" -#include "i2c.h" -#include "ssi.h" -#include "boards.h" +#include "hw/hw.h" +#include "hw/pxa.h" +#include "hw/arm-misc.h" +#include "hw/devices.h" +#include "hw/i2c.h" +#include "hw/ssi.h" +#include "hw/boards.h" #include "sysemu/sysemu.h" -#include "flash.h" +#include "hw/flash.h" #include "sysemu/blockdev.h" #include "ui/console.h" #include "audio/audio.h" @@ -185,7 +185,7 @@ static void zipit_lcd_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_zipit_lcd_state; } -static TypeInfo zipit_lcd_info = { +static const TypeInfo zipit_lcd_info = { .name = "zipit-lcd", .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(ZipitLCD), @@ -288,7 +288,7 @@ static void aer915_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_aer915_state; } -static TypeInfo aer915_info = { +static const TypeInfo aer915_info = { .name = "aer915", .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(AER915State), @@ -373,6 +373,7 @@ static QEMUMachine z2_machine = { .name = "z2", .desc = "Zipit Z2 (PXA27x)", .init = z2_init, + DEFAULT_MACHINE_OPTIONS, }; static void z2_machine_init(void) diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index 093331124a..90dceade71 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -7,12 +7,12 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/timer.h" /* MPCore private memory region. */ -typedef struct mpcore_priv_state { +typedef struct ARM11MPCorePriveState { SysBusDevice busdev; uint32_t scu_control; int iomemtype; @@ -21,16 +21,17 @@ typedef struct mpcore_priv_state { MemoryRegion iomem; MemoryRegion container; DeviceState *mptimer; + DeviceState *wdtimer; DeviceState *gic; uint32_t num_irq; -} mpcore_priv_state; +} ARM11MPCorePriveState; /* Per-CPU private memory mapped IO. */ static uint64_t mpcore_scu_read(void *opaque, hwaddr offset, unsigned size) { - mpcore_priv_state *s = (mpcore_priv_state *)opaque; + ARM11MPCorePriveState *s = (ARM11MPCorePriveState *)opaque; int id; /* SCU */ switch (offset) { @@ -53,7 +54,7 @@ static uint64_t mpcore_scu_read(void *opaque, hwaddr offset, static void mpcore_scu_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { - mpcore_priv_state *s = (mpcore_priv_state *)opaque; + ARM11MPCorePriveState *s = (ARM11MPCorePriveState *)opaque; /* SCU */ switch (offset) { case 0: /* Control register. */ @@ -76,15 +77,16 @@ static const MemoryRegionOps mpcore_scu_ops = { static void mpcore_priv_set_irq(void *opaque, int irq, int level) { - mpcore_priv_state *s = (mpcore_priv_state *)opaque; + ARM11MPCorePriveState *s = (ARM11MPCorePriveState *)opaque; qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); } -static void mpcore_priv_map_setup(mpcore_priv_state *s) +static void mpcore_priv_map_setup(ARM11MPCorePriveState *s) { int i; - SysBusDevice *gicbusdev = sysbus_from_qdev(s->gic); - SysBusDevice *busdev = sysbus_from_qdev(s->mptimer); + SysBusDevice *gicbusdev = SYS_BUS_DEVICE(s->gic); + SysBusDevice *timerbusdev = SYS_BUS_DEVICE(s->mptimer); + SysBusDevice *wdtbusdev = SYS_BUS_DEVICE(s->wdtimer); memory_region_init(&s->container, "mpcode-priv-container", 0x2000); memory_region_init_io(&s->iomem, &mpcore_scu_ops, s, "mpcore-scu", 0x100); memory_region_add_subregion(&s->container, 0, &s->iomem); @@ -99,11 +101,13 @@ static void mpcore_priv_map_setup(mpcore_priv_state *s) /* Add the regions for timer and watchdog for "current CPU" and * for each specific CPU. */ - for (i = 0; i < (s->num_cpu + 1) * 2; i++) { + for (i = 0; i < (s->num_cpu + 1); i++) { /* Timers at 0x600, 0x700, ...; watchdogs at 0x620, 0x720, ... */ - hwaddr offset = 0x600 + (i >> 1) * 0x100 + (i & 1) * 0x20; + hwaddr offset = 0x600 + i * 0x100; memory_region_add_subregion(&s->container, offset, - sysbus_mmio_get_region(busdev, i)); + sysbus_mmio_get_region(timerbusdev, i)); + memory_region_add_subregion(&s->container, offset + 0x20, + sysbus_mmio_get_region(wdtbusdev, i)); } memory_region_add_subregion(&s->container, 0x1000, sysbus_mmio_get_region(gicbusdev, 0)); @@ -112,16 +116,16 @@ static void mpcore_priv_map_setup(mpcore_priv_state *s) */ for (i = 0; i < s->num_cpu; i++) { int ppibase = (s->num_irq - 32) + i * 32; - sysbus_connect_irq(busdev, i * 2, + sysbus_connect_irq(timerbusdev, i, qdev_get_gpio_in(s->gic, ppibase + 29)); - sysbus_connect_irq(busdev, i * 2 + 1, + sysbus_connect_irq(wdtbusdev, i, qdev_get_gpio_in(s->gic, ppibase + 30)); } } static int mpcore_priv_init(SysBusDevice *dev) { - mpcore_priv_state *s = FROM_SYSBUS(mpcore_priv_state, dev); + ARM11MPCorePriveState *s = FROM_SYSBUS(ARM11MPCorePriveState, dev); s->gic = qdev_create(NULL, "arm_gic"); qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); @@ -131,7 +135,7 @@ static int mpcore_priv_init(SysBusDevice *dev) qdev_init_nofail(s->gic); /* Pass through outbound IRQ lines from the GIC */ - sysbus_pass_irq(dev, sysbus_from_qdev(s->gic)); + sysbus_pass_irq(dev, SYS_BUS_DEVICE(s->gic)); /* Pass through inbound GPIO lines to the GIC */ qdev_init_gpio_in(&s->busdev.qdev, mpcore_priv_set_irq, s->num_irq - 32); @@ -139,6 +143,11 @@ static int mpcore_priv_init(SysBusDevice *dev) s->mptimer = qdev_create(NULL, "arm_mptimer"); qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu); qdev_init_nofail(s->mptimer); + + s->wdtimer = qdev_create(NULL, "arm_mptimer"); + qdev_prop_set_uint32(s->wdtimer, "num-cpu", s->num_cpu); + qdev_init_nofail(s->wdtimer); + mpcore_priv_map_setup(s); sysbus_init_mmio(dev, &s->container); return 0; @@ -190,7 +199,7 @@ static int realview_mpcore_init(SysBusDevice *dev) priv = qdev_create(NULL, "arm11mpcore_priv"); qdev_prop_set_uint32(priv, "num-cpu", s->num_cpu); qdev_init_nofail(priv); - s->priv = sysbus_from_qdev(priv); + s->priv = SYS_BUS_DEVICE(priv); sysbus_pass_irq(dev, s->priv); for (i = 0; i < 32; i++) { s->cpuic[i] = qdev_get_gpio_in(priv, i); @@ -222,7 +231,7 @@ static void mpcore_rirq_class_init(ObjectClass *klass, void *data) dc->props = mpcore_rirq_properties; } -static TypeInfo mpcore_rirq_info = { +static const TypeInfo mpcore_rirq_info = { .name = "realview_mpcore", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mpcore_rirq_state), @@ -230,7 +239,7 @@ static TypeInfo mpcore_rirq_info = { }; static Property mpcore_priv_properties[] = { - DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1), + DEFINE_PROP_UINT32("num-cpu", ARM11MPCorePriveState, num_cpu, 1), /* The ARM11 MPCORE TRM says the on-chip controller may have * anything from 0 to 224 external interrupt IRQ lines (with another * 32 internal). We default to 32+32, which is the number provided by @@ -239,7 +248,7 @@ static Property mpcore_priv_properties[] = { * appropriately. Some Linux kernels may not boot if the hardware * has more IRQ lines than the kernel expects. */ - DEFINE_PROP_UINT32("num-irq", mpcore_priv_state, num_irq, 64), + DEFINE_PROP_UINT32("num-irq", ARM11MPCorePriveState, num_irq, 64), DEFINE_PROP_END_OF_LIST(), }; @@ -252,10 +261,10 @@ static void mpcore_priv_class_init(ObjectClass *klass, void *data) dc->props = mpcore_priv_properties; } -static TypeInfo mpcore_priv_info = { +static const TypeInfo mpcore_priv_info = { .name = "arm11mpcore_priv", .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(mpcore_priv_state), + .instance_size = sizeof(ARM11MPCorePriveState), .class_init = mpcore_priv_class_init, }; diff --git a/hw/arm_gic.c b/hw/arm_gic.c index b6062c4241..bcb072bbcf 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -18,8 +18,8 @@ * armv7m_nvic device. */ -#include "sysbus.h" -#include "arm_gic_internal.h" +#include "hw/sysbus.h" +#include "hw/arm_gic_internal.h" //#define DEBUG_GIC @@ -39,7 +39,8 @@ static const uint8_t gic_id[] = { static inline int gic_get_current_cpu(GICState *s) { if (s->num_cpu > 1) { - return cpu_single_env->cpu_index; + CPUState *cpu = ENV_GET_CPU(cpu_single_env); + return cpu->cpu_index; } return 0; } @@ -658,14 +659,18 @@ void gic_init_irqs_and_distributor(GICState *s, int num_irq) memory_region_init_io(&s->iomem, &gic_dist_ops, s, "gic_dist", 0x1000); } -static int arm_gic_init(SysBusDevice *dev) +static void arm_gic_realize(DeviceState *dev, Error **errp) { - /* Device instance init function for the GIC sysbus device */ + /* Device instance realize function for the GIC sysbus device */ int i; - GICState *s = FROM_SYSBUS(GICState, dev); + GICState *s = ARM_GIC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); ARMGICClass *agc = ARM_GIC_GET_CLASS(s); - agc->parent_init(dev); + agc->parent_realize(dev, errp); + if (error_is_set(errp)) { + return; + } gic_init_irqs_and_distributor(s, s->num_irq); @@ -685,25 +690,24 @@ static int arm_gic_init(SysBusDevice *dev) "gic_cpu", 0x100); } /* Distributor */ - sysbus_init_mmio(dev, &s->iomem); + sysbus_init_mmio(sbd, &s->iomem); /* cpu interfaces (one for "current cpu" plus one per cpu) */ for (i = 0; i <= NUM_CPU(s); i++) { - sysbus_init_mmio(dev, &s->cpuiomem[i]); + sysbus_init_mmio(sbd, &s->cpuiomem[i]); } - return 0; } static void arm_gic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); ARMGICClass *agc = ARM_GIC_CLASS(klass); - agc->parent_init = sbc->init; - sbc->init = arm_gic_init; + dc->no_user = 1; + agc->parent_realize = dc->realize; + dc->realize = arm_gic_realize; } -static TypeInfo arm_gic_info = { +static const TypeInfo arm_gic_info = { .name = TYPE_ARM_GIC, .parent = TYPE_ARM_GIC_COMMON, .instance_size = sizeof(GICState), diff --git a/hw/arm_gic_common.c b/hw/arm_gic_common.c index 73ae331807..f2dc8bf555 100644 --- a/hw/arm_gic_common.c +++ b/hw/arm_gic_common.c @@ -18,14 +18,19 @@ * with this program; if not, see . */ -#include "arm_gic_internal.h" +#include "hw/arm_gic_internal.h" static void gic_save(QEMUFile *f, void *opaque) { GICState *s = (GICState *)opaque; + ARMGICCommonClass *c = ARM_GIC_COMMON_GET_CLASS(s); int i; int j; + if (c->pre_save) { + c->pre_save(s); + } + qemu_put_be32(f, s->enabled); for (i = 0; i < s->num_cpu; i++) { qemu_put_be32(f, s->cpu_enabled[i]); @@ -57,6 +62,7 @@ static void gic_save(QEMUFile *f, void *opaque) static int gic_load(QEMUFile *f, void *opaque, int version_id) { GICState *s = (GICState *)opaque; + ARMGICCommonClass *c = ARM_GIC_COMMON_GET_CLASS(s); int i; int j; @@ -91,39 +97,47 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id) s->irq_state[i].trigger = qemu_get_byte(f); } + if (c->post_load) { + c->post_load(s); + } + return 0; } -static int arm_gic_common_init(SysBusDevice *dev) +static void arm_gic_common_realize(DeviceState *dev, Error **errp) { - GICState *s = FROM_SYSBUS(GICState, dev); + GICState *s = ARM_GIC_COMMON(dev); int num_irq = s->num_irq; if (s->num_cpu > NCPU) { - hw_error("requested %u CPUs exceeds GIC maximum %d\n", - s->num_cpu, NCPU); + error_setg(errp, "requested %u CPUs exceeds GIC maximum %d", + s->num_cpu, NCPU); + return; } s->num_irq += GIC_BASE_IRQ; if (s->num_irq > GIC_MAXIRQ) { - hw_error("requested %u interrupt lines exceeds GIC maximum %d\n", - num_irq, GIC_MAXIRQ); + error_setg(errp, + "requested %u interrupt lines exceeds GIC maximum %d", + num_irq, GIC_MAXIRQ); + return; } /* ITLinesNumber is represented as (N / 32) - 1 (see * gic_dist_readb) so this is an implementation imposed * restriction, not an architectural one: */ if (s->num_irq < 32 || (s->num_irq % 32)) { - hw_error("%d interrupt lines unsupported: not divisible by 32\n", - num_irq); + error_setg(errp, + "%d interrupt lines unsupported: not divisible by 32", + num_irq); + return; } register_savevm(NULL, "arm_gic", -1, 3, gic_save, gic_load, s); - return 0; } static void arm_gic_common_reset(DeviceState *dev) { - GICState *s = FROM_SYSBUS(GICState, sysbus_from_qdev(dev)); + GICState *s = FROM_SYSBUS(GICState, SYS_BUS_DEVICE(dev)); int i; memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state)); for (i = 0 ; i < s->num_cpu; i++) { @@ -163,15 +177,15 @@ static Property arm_gic_common_properties[] = { static void arm_gic_common_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); + dc->reset = arm_gic_common_reset; + dc->realize = arm_gic_common_realize; dc->props = arm_gic_common_properties; dc->no_user = 1; - sc->init = arm_gic_common_init; } -static TypeInfo arm_gic_common_type = { +static const TypeInfo arm_gic_common_type = { .name = TYPE_ARM_GIC_COMMON, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(GICState), diff --git a/hw/arm_gic_internal.h b/hw/arm_gic_internal.h index 699352ca8b..3e1928b7eb 100644 --- a/hw/arm_gic_internal.h +++ b/hw/arm_gic_internal.h @@ -21,7 +21,7 @@ #ifndef QEMU_ARM_GIC_INTERNAL_H #define QEMU_ARM_GIC_INTERNAL_H -#include "sysbus.h" +#include "hw/sysbus.h" /* Maximum number of possible interrupts, determined by the GIC architecture */ #define GIC_MAXIRQ 1020 @@ -118,6 +118,8 @@ void gic_init_irqs_and_distributor(GICState *s, int num_irq); typedef struct ARMGICCommonClass { SysBusDeviceClass parent_class; + void (*pre_save)(GICState *s); + void (*post_load)(GICState *s); } ARMGICCommonClass; #define TYPE_ARM_GIC "arm_gic" @@ -130,7 +132,7 @@ typedef struct ARMGICCommonClass { typedef struct ARMGICClass { ARMGICCommonClass parent_class; - int (*parent_init)(SysBusDevice *dev); + DeviceRealize parent_realize; } ARMGICClass; #endif /* !QEMU_ARM_GIC_INTERNAL_H */ diff --git a/hw/arm_l2x0.c b/hw/arm_l2x0.c index 6abf0ee160..eb4427d9c4 100644 --- a/hw/arm_l2x0.c +++ b/hw/arm_l2x0.c @@ -18,7 +18,7 @@ * */ -#include "sysbus.h" +#include "hw/sysbus.h" /* L2C-310 r3p2 */ #define CACHE_ID 0x410000c8 @@ -179,7 +179,7 @@ static void l2x0_class_init(ObjectClass *klass, void *data) dc->reset = l2x0_priv_reset; } -static TypeInfo l2x0_info = { +static const TypeInfo l2x0_info = { .name = "l2x0", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(l2x0_state), diff --git a/hw/arm_mptimer.c b/hw/arm_mptimer.c index 1febaeb7b1..f59a9f11f0 100644 --- a/hw/arm_mptimer.c +++ b/hw/arm_mptimer.c @@ -19,7 +19,7 @@ * with this program; if not, see . */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/timer.h" /* This device implements the per-cpu private timer and watchdog block @@ -38,36 +38,38 @@ typedef struct { QEMUTimer *timer; qemu_irq irq; MemoryRegion iomem; -} timerblock; +} TimerBlock; typedef struct { SysBusDevice busdev; uint32_t num_cpu; - timerblock timerblock[MAX_CPUS * 2]; - MemoryRegion iomem[2]; -} arm_mptimer_state; + TimerBlock timerblock[MAX_CPUS]; + MemoryRegion iomem; +} ARMMPTimerState; -static inline int get_current_cpu(arm_mptimer_state *s) +static inline int get_current_cpu(ARMMPTimerState *s) { - if (cpu_single_env->cpu_index >= s->num_cpu) { + CPUState *cpu_single_cpu = ENV_GET_CPU(cpu_single_env); + + if (cpu_single_cpu->cpu_index >= s->num_cpu) { hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n", - s->num_cpu, cpu_single_env->cpu_index); + s->num_cpu, cpu_single_cpu->cpu_index); } - return cpu_single_env->cpu_index; + return cpu_single_cpu->cpu_index; } -static inline void timerblock_update_irq(timerblock *tb) +static inline void timerblock_update_irq(TimerBlock *tb) { qemu_set_irq(tb->irq, tb->status); } /* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ -static inline uint32_t timerblock_scale(timerblock *tb) +static inline uint32_t timerblock_scale(TimerBlock *tb) { return (((tb->control >> 8) & 0xff) + 1) * 10; } -static void timerblock_reload(timerblock *tb, int restart) +static void timerblock_reload(TimerBlock *tb, int restart) { if (tb->count == 0) { return; @@ -81,7 +83,7 @@ static void timerblock_reload(timerblock *tb, int restart) static void timerblock_tick(void *opaque) { - timerblock *tb = (timerblock *)opaque; + TimerBlock *tb = (TimerBlock *)opaque; tb->status = 1; if (tb->control & 2) { tb->count = tb->load; @@ -95,7 +97,7 @@ static void timerblock_tick(void *opaque) static uint64_t timerblock_read(void *opaque, hwaddr addr, unsigned size) { - timerblock *tb = (timerblock *)opaque; + TimerBlock *tb = (TimerBlock *)opaque; int64_t val; switch (addr) { case 0: /* Load */ @@ -123,7 +125,7 @@ static uint64_t timerblock_read(void *opaque, hwaddr addr, static void timerblock_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - timerblock *tb = (timerblock *)opaque; + TimerBlock *tb = (TimerBlock *)opaque; int64_t old; switch (addr) { case 0: /* Load */ @@ -162,33 +164,17 @@ static void timerblock_write(void *opaque, hwaddr addr, static uint64_t arm_thistimer_read(void *opaque, hwaddr addr, unsigned size) { - arm_mptimer_state *s = (arm_mptimer_state *)opaque; + ARMMPTimerState *s = (ARMMPTimerState *)opaque; int id = get_current_cpu(s); - return timerblock_read(&s->timerblock[id * 2], addr, size); + return timerblock_read(&s->timerblock[id], addr, size); } static void arm_thistimer_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - arm_mptimer_state *s = (arm_mptimer_state *)opaque; + ARMMPTimerState *s = (ARMMPTimerState *)opaque; int id = get_current_cpu(s); - timerblock_write(&s->timerblock[id * 2], addr, value, size); -} - -static uint64_t arm_thiswdog_read(void *opaque, hwaddr addr, - unsigned size) -{ - arm_mptimer_state *s = (arm_mptimer_state *)opaque; - int id = get_current_cpu(s); - return timerblock_read(&s->timerblock[id * 2 + 1], addr, size); -} - -static void arm_thiswdog_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - arm_mptimer_state *s = (arm_mptimer_state *)opaque; - int id = get_current_cpu(s); - timerblock_write(&s->timerblock[id * 2 + 1], addr, value, size); + timerblock_write(&s->timerblock[id], addr, value, size); } static const MemoryRegionOps arm_thistimer_ops = { @@ -201,16 +187,6 @@ static const MemoryRegionOps arm_thistimer_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static const MemoryRegionOps arm_thiswdog_ops = { - .read = arm_thiswdog_read, - .write = arm_thiswdog_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - static const MemoryRegionOps timerblock_ops = { .read = timerblock_read, .write = timerblock_write, @@ -221,7 +197,7 @@ static const MemoryRegionOps timerblock_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void timerblock_reset(timerblock *tb) +static void timerblock_reset(TimerBlock *tb) { tb->count = 0; tb->load = 0; @@ -235,12 +211,9 @@ static void timerblock_reset(timerblock *tb) static void arm_mptimer_reset(DeviceState *dev) { - arm_mptimer_state *s = - FROM_SYSBUS(arm_mptimer_state, sysbus_from_qdev(dev)); + ARMMPTimerState *s = + FROM_SYSBUS(ARMMPTimerState, SYS_BUS_DEVICE(dev)); int i; - /* We reset every timer in the array, not just the ones we're using, - * because vmsave will look at every array element. - */ for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) { timerblock_reset(&s->timerblock[i]); } @@ -248,35 +221,26 @@ static void arm_mptimer_reset(DeviceState *dev) static int arm_mptimer_init(SysBusDevice *dev) { - arm_mptimer_state *s = FROM_SYSBUS(arm_mptimer_state, dev); + ARMMPTimerState *s = FROM_SYSBUS(ARMMPTimerState, dev); int i; if (s->num_cpu < 1 || s->num_cpu > MAX_CPUS) { hw_error("%s: num-cpu must be between 1 and %d\n", __func__, MAX_CPUS); } - /* We implement one timer and one watchdog block per CPU, and - * expose multiple MMIO regions: + /* We implement one timer block per CPU, and expose multiple MMIO regions: * * region 0 is "timer for this core" - * * region 1 is "watchdog for this core" - * * region 2 is "timer for core 0" - * * region 3 is "watchdog for core 0" - * * region 4 is "timer for core 1" - * * region 5 is "watchdog for core 1" + * * region 1 is "timer for core 0" + * * region 2 is "timer for core 1" * and so on. * The outgoing interrupt lines are * * timer for core 0 - * * watchdog for core 0 * * timer for core 1 - * * watchdog for core 1 * and so on. */ - memory_region_init_io(&s->iomem[0], &arm_thistimer_ops, s, + memory_region_init_io(&s->iomem, &arm_thistimer_ops, s, "arm_mptimer_timer", 0x20); - sysbus_init_mmio(dev, &s->iomem[0]); - memory_region_init_io(&s->iomem[1], &arm_thiswdog_ops, s, - "arm_mptimer_wdog", 0x20); - sysbus_init_mmio(dev, &s->iomem[1]); - for (i = 0; i < (s->num_cpu * 2); i++) { - timerblock *tb = &s->timerblock[i]; + sysbus_init_mmio(dev, &s->iomem); + for (i = 0; i < s->num_cpu; i++) { + TimerBlock *tb = &s->timerblock[i]; tb->timer = qemu_new_timer_ns(vm_clock, timerblock_tick, tb); sysbus_init_irq(dev, &tb->irq); memory_region_init_io(&tb->iomem, &timerblock_ops, tb, @@ -292,28 +256,28 @@ static const VMStateDescription vmstate_timerblock = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT32(count, timerblock), - VMSTATE_UINT32(load, timerblock), - VMSTATE_UINT32(control, timerblock), - VMSTATE_UINT32(status, timerblock), - VMSTATE_INT64(tick, timerblock), + VMSTATE_UINT32(count, TimerBlock), + VMSTATE_UINT32(load, TimerBlock), + VMSTATE_UINT32(control, TimerBlock), + VMSTATE_UINT32(status, TimerBlock), + VMSTATE_INT64(tick, TimerBlock), VMSTATE_END_OF_LIST() } }; static const VMStateDescription vmstate_arm_mptimer = { .name = "arm_mptimer", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { - VMSTATE_STRUCT_ARRAY(timerblock, arm_mptimer_state, (MAX_CPUS * 2), - 1, vmstate_timerblock, timerblock), + VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu, + 2, vmstate_timerblock, TimerBlock), VMSTATE_END_OF_LIST() } }; static Property arm_mptimer_properties[] = { - DEFINE_PROP_UINT32("num-cpu", arm_mptimer_state, num_cpu, 0), + DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0), DEFINE_PROP_END_OF_LIST() }; @@ -329,10 +293,10 @@ static void arm_mptimer_class_init(ObjectClass *klass, void *data) dc->props = arm_mptimer_properties; } -static TypeInfo arm_mptimer_info = { +static const TypeInfo arm_mptimer_info = { .name = "arm_mptimer", .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(arm_mptimer_state), + .instance_size = sizeof(ARMMPTimerState), .class_init = arm_mptimer_class_init, }; diff --git a/hw/arm_pic.c b/hw/arm_pic.c deleted file mode 100644 index ffb4d4171a..0000000000 --- a/hw/arm_pic.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Generic ARM Programmable Interrupt Controller support. - * - * Copyright (c) 2006 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the LGPL - */ - -#include "hw.h" -#include "arm-misc.h" - -/* Input 0 is IRQ and input 1 is FIQ. */ -static void arm_pic_cpu_handler(void *opaque, int irq, int level) -{ - ARMCPU *cpu = opaque; - CPUARMState *env = &cpu->env; - - switch (irq) { - case ARM_PIC_CPU_IRQ: - if (level) - cpu_interrupt(env, CPU_INTERRUPT_HARD); - else - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); - break; - case ARM_PIC_CPU_FIQ: - if (level) - cpu_interrupt(env, CPU_INTERRUPT_FIQ); - else - cpu_reset_interrupt(env, CPU_INTERRUPT_FIQ); - break; - default: - hw_error("arm_pic_cpu_handler: Bad interrupt line %d\n", irq); - } -} - -qemu_irq *arm_pic_init_cpu(ARMCPU *cpu) -{ - return qemu_allocate_irqs(arm_pic_cpu_handler, cpu, 2); -} diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c index b733617aa0..a46f8d450e 100644 --- a/hw/arm_sysctl.c +++ b/hw/arm_sysctl.c @@ -7,10 +7,10 @@ * This code is licensed under the GPL. */ -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" -#include "sysbus.h" -#include "primecell.h" +#include "hw/sysbus.h" +#include "hw/primecell.h" #include "sysemu/sysemu.h" #define LOCK_VALUE 0xa05f @@ -75,7 +75,7 @@ static int board_id(arm_sysctl_state *s) static void arm_sysctl_reset(DeviceState *d) { - arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, sysbus_from_qdev(d)); + arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, SYS_BUS_DEVICE(d)); s->leds = 0; s->lockval = 0; @@ -199,6 +199,7 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, switch (offset) { case 0x08: /* LED */ s->leds = val; + break; case 0x0c: /* OSC0 */ case 0x10: /* OSC1 */ case 0x14: /* OSC2 */ @@ -295,6 +296,7 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, /* On VExpress this register is unimplemented and will RAZ/WI */ break; } + break; case 0x54: /* CLCDSER */ case 0x64: /* DMAPSR0 */ case 0x68: /* DMAPSR1 */ @@ -332,6 +334,7 @@ static void arm_sysctl_write(void *opaque, hwaddr offset, default: s->sys_cfgstat |= 2; /* error */ } + s->sys_cfgctrl &= ~(1 << 31); return; case 0xa8: /* SYS_CFGSTAT */ if (board_id(s) != BOARD_ID_VEXPRESS) { @@ -410,7 +413,7 @@ static void arm_sysctl_class_init(ObjectClass *klass, void *data) dc->props = arm_sysctl_properties; } -static TypeInfo arm_sysctl_info = { +static const TypeInfo arm_sysctl_info = { .name = "realview_sysctl", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(arm_sysctl_state), diff --git a/hw/arm_timer.c b/hw/arm_timer.c index 37e28e993c..644987046a 100644 --- a/hw/arm_timer.c +++ b/hw/arm_timer.c @@ -7,11 +7,11 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu-common.h" -#include "qdev.h" -#include "ptimer.h" +#include "hw/qdev.h" +#include "hw/ptimer.h" /* Common timer implementation. */ @@ -361,7 +361,7 @@ static void icp_pit_class_init(ObjectClass *klass, void *data) sdc->init = icp_pit_init; } -static TypeInfo icp_pit_info = { +static const TypeInfo icp_pit_info = { .name = "integrator_pit", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(icp_pit_state), @@ -383,7 +383,7 @@ static void sp804_class_init(ObjectClass *klass, void *data) k->props = sp804_properties; } -static TypeInfo sp804_info = { +static const TypeInfo sp804_info = { .name = "sp804", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(sp804_state), diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 0907e42c0c..d198cfd96e 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -10,11 +10,11 @@ * NVIC. Much of that is also implemented here. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/timer.h" -#include "arm-misc.h" +#include "hw/arm-misc.h" #include "exec/address-spaces.h" -#include "arm_gic_internal.h" +#include "hw/arm_gic_internal.h" typedef struct { GICState gic; @@ -41,7 +41,7 @@ typedef struct NVICClass { /*< private >*/ ARMGICClass parent_class; /*< public >*/ - int (*parent_init)(SysBusDevice *dev); + DeviceRealize parent_realize; void (*parent_reset)(DeviceState *dev); } NVICClass; @@ -465,7 +465,7 @@ static void armv7m_nvic_reset(DeviceState *dev) systick_reset(s); } -static int armv7m_nvic_init(SysBusDevice *dev) +static void armv7m_nvic_realize(DeviceState *dev, Error **errp) { nvic_state *s = NVIC(dev); NVICClass *nc = NVIC_GET_CLASS(s); @@ -475,7 +475,10 @@ static int armv7m_nvic_init(SysBusDevice *dev) /* Tell the common code we're an NVIC */ s->gic.revision = 0xffffffff; s->num_irq = s->gic.num_irq; - nc->parent_init(dev); + nc->parent_realize(dev, errp); + if (error_is_set(errp)) { + return; + } gic_init_irqs_and_distributor(&s->gic, s->num_irq); /* The NVIC and system controller register area looks like this: * 0..0xff : system control registers, including systick @@ -503,7 +506,6 @@ static int armv7m_nvic_init(SysBusDevice *dev) */ memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->container); s->systick.timer = qemu_new_timer_ns(vm_clock, systick_timer_tick, s); - return 0; } static void armv7m_nvic_instance_init(Object *obj) @@ -526,16 +528,15 @@ static void armv7m_nvic_class_init(ObjectClass *klass, void *data) { NVICClass *nc = NVIC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); nc->parent_reset = dc->reset; - nc->parent_init = sdc->init; - sdc->init = armv7m_nvic_init; + nc->parent_realize = dc->realize; dc->vmsd = &vmstate_nvic; dc->reset = armv7m_nvic_reset; + dc->realize = armv7m_nvic_realize; } -static TypeInfo armv7m_nvic_info = { +static const TypeInfo armv7m_nvic_info = { .name = TYPE_NVIC, .parent = TYPE_ARM_GIC_COMMON, .instance_init = armv7m_nvic_instance_init, diff --git a/hw/bitbang_i2c.c b/hw/bitbang_i2c.c index 44ed7f4d61..b8e6d3a103 100644 --- a/hw/bitbang_i2c.c +++ b/hw/bitbang_i2c.c @@ -9,9 +9,9 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "bitbang_i2c.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/bitbang_i2c.h" +#include "hw/sysbus.h" //#define DEBUG_BITBANG_I2C @@ -230,7 +230,7 @@ static void gpio_i2c_class_init(ObjectClass *klass, void *data) dc->desc = "Virtual GPIO to I2C bridge"; } -static TypeInfo gpio_i2c_info = { +static const TypeInfo gpio_i2c_info = { .name = "gpio_i2c", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(GPIOI2CState), diff --git a/hw/bitbang_i2c.h b/hw/bitbang_i2c.h index 519d2dc22f..e86062742c 100644 --- a/hw/bitbang_i2c.h +++ b/hw/bitbang_i2c.h @@ -1,7 +1,7 @@ #ifndef BITBANG_I2C_H #define BITBANG_I2C_H -#include "i2c.h" +#include "hw/i2c.h" typedef struct bitbang_i2c_interface bitbang_i2c_interface; diff --git a/hw/blizzard.c b/hw/blizzard.c index 24bde32e5a..805f4d5558 100644 --- a/hw/blizzard.c +++ b/hw/blizzard.c @@ -20,8 +20,8 @@ #include "qemu-common.h" #include "ui/console.h" -#include "devices.h" -#include "vga_int.h" +#include "hw/devices.h" +#include "hw/vga_int.h" #include "ui/pixel_ops.h" typedef void (*blizzard_fn_t)(uint8_t *, const uint8_t *, unsigned int); @@ -941,15 +941,15 @@ static void blizzard_screen_dump(void *opaque, const char *filename, } #define DEPTH 8 -#include "blizzard_template.h" +#include "hw/blizzard_template.h" #define DEPTH 15 -#include "blizzard_template.h" +#include "hw/blizzard_template.h" #define DEPTH 16 -#include "blizzard_template.h" +#include "hw/blizzard_template.h" #define DEPTH 24 -#include "blizzard_template.h" +#include "hw/blizzard_template.h" #define DEPTH 32 -#include "blizzard_template.h" +#include "hw/blizzard_template.h" void *s1d13745_init(qemu_irq gpio_int) { diff --git a/hw/block-common.c b/hw/block-common.c index e9dd28c76e..65e49565a9 100644 --- a/hw/block-common.c +++ b/hw/block-common.c @@ -18,9 +18,7 @@ void blkconf_serial(BlockConf *conf, char **serial) if (!*serial) { /* try to fall back to value set with legacy -drive serial=... */ dinfo = drive_get_by_blockdev(conf->bs); - if (dinfo->serial) { - *serial = g_strdup(dinfo->serial); - } + *serial = g_strdup(dinfo->serial); } } diff --git a/hw/block-common.h b/hw/block-common.h index 6b36808dfd..f6b153ec20 100644 --- a/hw/block-common.h +++ b/hw/block-common.h @@ -50,7 +50,7 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \ DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1), \ DEFINE_PROP_UINT32("discard_granularity", _state, \ - _conf.discard_granularity, 0) + _conf.discard_granularity, -1) #define DEFINE_BLOCK_CHS_PROPERTIES(_state, _conf) \ DEFINE_PROP_UINT32("cyls", _state, _conf.cyls, 0), \ diff --git a/hw/boards.h b/hw/boards.h index 4540e952f7..425bdc74a8 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -4,7 +4,10 @@ #define HW_BOARDS_H #include "sysemu/blockdev.h" -#include "qdev.h" +#include "hw/qdev.h" + +#define DEFAULT_MACHINE_OPTIONS \ + .boot_order = "cad" typedef struct QEMUMachineInitArgs { ram_addr_t ram_size; @@ -30,11 +33,13 @@ typedef struct QEMUMachine { unsigned int no_serial:1, no_parallel:1, use_virtcon:1, + use_sclp:1, no_floppy:1, no_cdrom:1, no_sdcard:1; int is_default; const char *default_machine_opts; + const char *boot_order; GlobalProperty *compat_props; struct QEMUMachine *next; const char *hw_version; diff --git a/hw/bonito.c b/hw/bonito.c index 0498c9be79..3456e7840e 100644 --- a/hw/bonito.c +++ b/hw/bonito.c @@ -39,11 +39,11 @@ #include -#include "hw.h" -#include "pci/pci.h" -#include "pc.h" -#include "mips.h" -#include "pci/pci_host.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/pc.h" +#include "hw/mips.h" +#include "hw/pci/pci_host.h" #include "sysemu/sysemu.h" #include "exec/address-spaces.h" diff --git a/hw/bt-hci-csr.c b/hw/bt-hci-csr.c index 2070bb940c..e4ada3c731 100644 --- a/hw/bt-hci-csr.c +++ b/hw/bt-hci-csr.c @@ -21,9 +21,9 @@ #include "qemu-common.h" #include "char/char.h" #include "qemu/timer.h" -#include "irq.h" +#include "hw/irq.h" #include "bt/bt.h" -#include "bt.h" +#include "hw/bt.h" struct csrhci_s { int enable; diff --git a/hw/bt-hci.c b/hw/bt-hci.c index 69d2c73862..a76edea2c9 100644 --- a/hw/bt-hci.c +++ b/hw/bt-hci.c @@ -20,9 +20,9 @@ #include "qemu-common.h" #include "qemu/timer.h" -#include "usb.h" +#include "hw/usb.h" #include "bt/bt.h" -#include "bt.h" +#include "hw/bt.h" struct bt_hci_s { uint8_t *(*evt_packet)(void *opaque); diff --git a/hw/bt-hid.c b/hw/bt-hid.c index cfa7c145b8..69ccf9b432 100644 --- a/hw/bt-hid.c +++ b/hw/bt-hid.c @@ -21,8 +21,8 @@ #include "qemu-common.h" #include "qemu/timer.h" #include "ui/console.h" -#include "hid.h" -#include "bt.h" +#include "hw/hid.h" +#include "hw/bt.h" enum hid_transaction_req { BT_HANDSHAKE = 0x0, diff --git a/hw/bt-l2cap.c b/hw/bt-l2cap.c index ba061c0da3..521587a112 100644 --- a/hw/bt-l2cap.c +++ b/hw/bt-l2cap.c @@ -19,7 +19,7 @@ #include "qemu-common.h" #include "qemu/timer.h" -#include "bt.h" +#include "hw/bt.h" #define L2CAP_CID_MAX 0x100 /* Between 0x40 and 0x10000 */ diff --git a/hw/bt-sdp.c b/hw/bt-sdp.c index c0431d1a40..218e075df7 100644 --- a/hw/bt-sdp.c +++ b/hw/bt-sdp.c @@ -18,7 +18,7 @@ */ #include "qemu-common.h" -#include "bt.h" +#include "hw/bt.h" struct bt_l2cap_sdp_state_s { struct bt_l2cap_conn_params_s *channel; diff --git a/hw/bt.c b/hw/bt.c index 4f2372d794..24ef4de49d 100644 --- a/hw/bt.c +++ b/hw/bt.c @@ -19,7 +19,7 @@ #include "qemu-common.h" #include "bt/bt.h" -#include "bt.h" +#include "hw/bt.h" /* Slave implementations can ignore this */ static void bt_dummy_lmp_mode_change(struct bt_link_s *link) diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c index 40a239973c..e177057e49 100644 --- a/hw/cadence_gem.c +++ b/hw/cadence_gem.c @@ -24,7 +24,7 @@ #include /* For crc32 */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "net/net.h" #include "net/checksum.h" @@ -389,10 +389,10 @@ static void gem_init_register_masks(GemState *s) */ static void phy_update_link(GemState *s) { - DB_PRINT("down %d\n", s->nic->nc.link_down); + DB_PRINT("down %d\n", qemu_get_queue(s->nic)->link_down); /* Autonegotiation status mirrors link status. */ - if (s->nic->nc.link_down) { + if (qemu_get_queue(s->nic)->link_down) { s->phy_regs[PHY_REG_STATUS] &= ~(PHY_REG_STATUS_ANEGCMPL | PHY_REG_STATUS_LINK); s->phy_regs[PHY_REG_INT_ST] |= PHY_REG_INT_ST_LINKC; @@ -409,7 +409,7 @@ static int gem_can_receive(NetClientState *nc) { GemState *s; - s = DO_UPCAST(NICState, nc, nc)->opaque; + s = qemu_get_nic_opaque(nc); DB_PRINT("\n"); @@ -427,32 +427,9 @@ static int gem_can_receive(NetClientState *nc) */ static void gem_update_int_status(GemState *s) { - uint32_t new_interrupts = 0; - /* Packet transmitted ? */ - if (s->regs[GEM_TXSTATUS] & GEM_TXSTATUS_TXCMPL) { - new_interrupts |= GEM_INT_TXCMPL; - } - /* End of TX ring ? */ - if (s->regs[GEM_TXSTATUS] & GEM_TXSTATUS_USED) { - new_interrupts |= GEM_INT_TXUSED; - } - - /* Frame received ? */ - if (s->regs[GEM_RXSTATUS] & GEM_RXSTATUS_FRMRCVD) { - new_interrupts |= GEM_INT_RXCMPL; - } - /* RX ring full ? */ - if (s->regs[GEM_RXSTATUS] & GEM_RXSTATUS_NOBUF) { - new_interrupts |= GEM_INT_RXUSED; - } - - s->regs[GEM_ISR] |= new_interrupts & ~(s->regs[GEM_IMR]); - if (s->regs[GEM_ISR]) { DB_PRINT("asserting int. (0x%08x)\n", s->regs[GEM_ISR]); qemu_set_irq(s->irq, 1); - } else { - qemu_set_irq(s->irq, 0); } } @@ -612,10 +589,10 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) uint8_t rxbuf[2048]; uint8_t *rxbuf_ptr; - s = DO_UPCAST(NICState, nc, nc)->opaque; + s = qemu_get_nic_opaque(nc); /* Do nothing if receive is not enabled. */ - if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) { + if (!gem_can_receive(nc)) { return -1; } @@ -687,15 +664,17 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) packet_desc_addr = s->rx_desc_addr; while (1) { - DB_PRINT("read descriptor 0x%x\n", packet_desc_addr); + DB_PRINT("read descriptor 0x%x\n", (unsigned)packet_desc_addr); /* read current descriptor */ cpu_physical_memory_read(packet_desc_addr, (uint8_t *)&desc[0], sizeof(desc)); /* Descriptor owned by software ? */ if (rx_desc_get_ownership(desc) == 1) { - DB_PRINT("descriptor 0x%x owned by sw.\n", packet_desc_addr); + DB_PRINT("descriptor 0x%x owned by sw.\n", + (unsigned)packet_desc_addr); s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF; + s->regs[GEM_ISR] |= GEM_INT_RXUSED & ~(s->regs[GEM_IMR]); /* Handle interrupt consequences */ gem_update_int_status(s); return -1; @@ -709,7 +688,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) */ if (rx_desc_get_buffer(desc) == 0) { DB_PRINT("Invalid RX buffer (NULL) for descriptor 0x%x\n", - packet_desc_addr); + (unsigned)packet_desc_addr); break; } @@ -745,11 +724,13 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) s->rx_desc_addr = last_desc_addr; if (rx_desc_get_wrap(desc)) { s->rx_desc_addr = s->regs[GEM_RXQBASE]; + DB_PRINT("wrapping RX descriptor list\n"); } else { + DB_PRINT("incrementing RX descriptor list\n"); s->rx_desc_addr += 8; } - DB_PRINT("set SOF, OWN on descriptor 0x%08x\n", packet_desc_addr); + DB_PRINT("set SOF, OWN on descriptor 0x%08x\n", (unsigned)packet_desc_addr); /* Count it */ gem_receive_updatestats(s, buf, size); @@ -764,6 +745,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) (uint8_t *)&desc[0], sizeof(desc)); s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_FRMRCVD; + s->regs[GEM_ISR] |= GEM_INT_RXCMPL & ~(s->regs[GEM_IMR]); /* Handle interrupt consequences */ gem_update_int_status(s); @@ -861,7 +843,8 @@ static void gem_transmit(GemState *s) */ if ((tx_desc_get_buffer(desc) == 0) || (tx_desc_get_length(desc) == 0)) { - DB_PRINT("Invalid TX descriptor @ 0x%x\n", packet_desc_addr); + DB_PRINT("Invalid TX descriptor @ 0x%x\n", + (unsigned)packet_desc_addr); break; } @@ -892,6 +875,7 @@ static void gem_transmit(GemState *s) DB_PRINT("TX descriptor next: 0x%08x\n", s->tx_desc_addr); s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_TXCMPL; + s->regs[GEM_ISR] |= GEM_INT_TXCMPL & ~(s->regs[GEM_IMR]); /* Handle interrupt consequences */ gem_update_int_status(s); @@ -906,9 +890,10 @@ static void gem_transmit(GemState *s) /* Send the packet somewhere */ if (s->phy_loop) { - gem_receive(&s->nic->nc, tx_packet, total_bytes); + gem_receive(qemu_get_queue(s->nic), tx_packet, total_bytes); } else { - qemu_send_packet(&s->nic->nc, tx_packet, total_bytes); + qemu_send_packet(qemu_get_queue(s->nic), tx_packet, + total_bytes); } /* Prepare for next packet */ @@ -928,6 +913,7 @@ static void gem_transmit(GemState *s) if (tx_desc_get_used(desc)) { s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_USED; + s->regs[GEM_ISR] |= GEM_INT_TXUSED & ~(s->regs[GEM_IMR]); gem_update_int_status(s); } } @@ -959,7 +945,7 @@ static void gem_phy_reset(GemState *s) static void gem_reset(DeviceState *d) { - GemState *s = FROM_SYSBUS(GemState, sysbus_from_qdev(d)); + GemState *s = FROM_SYSBUS(GemState, SYS_BUS_DEVICE(d)); DB_PRINT("\n"); @@ -1031,10 +1017,11 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) offset >>= 2; retval = s->regs[offset]; - DB_PRINT("offset: 0x%04x read: 0x%08x\n", offset*4, retval); + DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset*4, retval); switch (offset) { case GEM_ISR: + DB_PRINT("lowering irq on ISR read\n"); qemu_set_irq(s->irq, 0); break; case GEM_PHYMNTNC: @@ -1073,7 +1060,7 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, GemState *s = (GemState *)opaque; uint32_t readonly; - DB_PRINT("offset: 0x%04x write: 0x%08x ", offset, (unsigned)val); + DB_PRINT("offset: 0x%04x write: 0x%08x ", (unsigned)offset, (unsigned)val); offset >>= 2; /* Squash bits which are read only in write value */ @@ -1098,9 +1085,8 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, /* Reset to start of Q when transmit disabled. */ s->tx_desc_addr = s->regs[GEM_TXQBASE]; } - if (!(val & GEM_NWCTRL_RXENA)) { - /* Reset to start of Q when receive disabled. */ - s->rx_desc_addr = s->regs[GEM_RXQBASE]; + if (val & GEM_NWCTRL_RXENA) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); } break; @@ -1148,7 +1134,7 @@ static const MemoryRegionOps gem_ops = { static void gem_cleanup(NetClientState *nc) { - GemState *s = DO_UPCAST(NICState, nc, nc)->opaque; + GemState *s = qemu_get_nic_opaque(nc); DB_PRINT("\n"); s->nic = NULL; @@ -1157,7 +1143,7 @@ static void gem_cleanup(NetClientState *nc) static void gem_set_link(NetClientState *nc) { DB_PRINT("\n"); - phy_update_link(DO_UPCAST(NICState, nc, nc)->opaque); + phy_update_link(qemu_get_nic_opaque(nc)); } static NetClientInfo net_gem_info = { @@ -1218,7 +1204,7 @@ static void gem_class_init(ObjectClass *klass, void *data) dc->reset = gem_reset; } -static TypeInfo gem_info = { +static const TypeInfo gem_info = { .class_init = gem_class_init, .name = "cadence_gem", .parent = TYPE_SYS_BUS_DEVICE, diff --git a/hw/cadence_ttc.c b/hw/cadence_ttc.c index 9e1cb1f152..ba584f4719 100644 --- a/hw/cadence_ttc.c +++ b/hw/cadence_ttc.c @@ -16,7 +16,7 @@ * with this program; if not, see . */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/timer.h" #ifdef CADENCE_TTC_ERR_DEBUG @@ -302,7 +302,7 @@ static uint64_t cadence_ttc_read(void *opaque, hwaddr offset, { uint32_t ret = cadence_ttc_read_imp(opaque, offset); - DB_PRINT("addr: %08x data: %08x\n", offset, ret); + DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)ret); return ret; } @@ -311,7 +311,7 @@ static void cadence_ttc_write(void *opaque, hwaddr offset, { CadenceTimerState *s = cadence_timer_from_addr(opaque, offset); - DB_PRINT("addr: %08x data %08x\n", offset, (unsigned)value); + DB_PRINT("addr: %08x data %08x\n", (unsigned)offset, (unsigned)value); cadence_timer_sync(s); @@ -474,7 +474,7 @@ static void cadence_ttc_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_cadence_ttc; } -static TypeInfo cadence_ttc_info = { +static const TypeInfo cadence_ttc_info = { .name = "cadence_ttc", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(CadenceTTCState), diff --git a/hw/cadence_uart.c b/hw/cadence_uart.c index 7dd2fe54ed..5426f10018 100644 --- a/hw/cadence_uart.c +++ b/hw/cadence_uart.c @@ -16,7 +16,7 @@ * with this program; if not, see . */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "char/char.h" #include "qemu/timer.h" @@ -343,6 +343,7 @@ static void uart_read_rx_fifo(UartState *s, uint32_t *c) if (!s->rx_count) { s->r[R_SR] |= UART_SR_INTR_REMPTY; } + qemu_chr_accept_input(s->chr); } else { *c = 0; s->r[R_SR] |= UART_SR_INTR_REMPTY; @@ -501,7 +502,7 @@ static void cadence_uart_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_cadence_uart; } -static TypeInfo cadence_uart_info = { +static const TypeInfo cadence_uart_info = { .name = "cadence_uart", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(UartState), diff --git a/hw/cbus.c b/hw/cbus.c index 6fd3905448..29b467b61f 100644 --- a/hw/cbus.c +++ b/hw/cbus.c @@ -21,8 +21,8 @@ */ #include "qemu-common.h" -#include "irq.h" -#include "devices.h" +#include "hw/irq.h" +#include "hw/devices.h" #include "sysemu/sysemu.h" //#define DEBUG diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c index 6fd44695ae..c8f8ba3792 100644 --- a/hw/ccid-card-emulated.c +++ b/hw/ccid-card-emulated.c @@ -587,7 +587,7 @@ static void emulated_class_initfn(ObjectClass *klass, void *data) dc->props = emulated_card_properties; } -static TypeInfo emulated_card_info = { +static const TypeInfo emulated_card_info = { .name = EMULATED_DEV_NAME, .parent = TYPE_CCID_CARD, .instance_size = sizeof(EmulatedState), diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c index 4be05471a9..984bd0bf4c 100644 --- a/hw/ccid-card-passthru.c +++ b/hw/ccid-card-passthru.c @@ -336,7 +336,7 @@ static void passthru_class_initfn(ObjectClass *klass, void *data) dc->props = passthru_card_properties; } -static TypeInfo passthru_card_info = { +static const TypeInfo passthru_card_info = { .name = PASSTHRU_DEV_NAME, .parent = TYPE_CCID_CARD, .instance_size = sizeof(PassthruState), diff --git a/hw/ccid.h b/hw/ccid.h index 6adc745a6d..9334da8acd 100644 --- a/hw/ccid.h +++ b/hw/ccid.h @@ -10,7 +10,7 @@ #ifndef CCID_H #define CCID_H -#include "qdev.h" +#include "hw/qdev.h" typedef struct CCIDCardState CCIDCardState; typedef struct CCIDCardInfo CCIDCardInfo; diff --git a/hw/cdrom.c b/hw/cdrom.c index 3b99535dce..a018eec40a 100644 --- a/hw/cdrom.c +++ b/hw/cdrom.c @@ -26,7 +26,7 @@ here. */ #include "qemu-common.h" -#include "scsi.h" +#include "hw/scsi.h" static void lba_to_msf(uint8_t *buf, int lba) { diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c index 80510bc9af..7babcb67c8 100644 --- a/hw/cirrus_vga.c +++ b/hw/cirrus_vga.c @@ -26,11 +26,11 @@ * Reference: Finn Thogersons' VGADOC4b * available at http://home.worldonline.dk/~finth/ */ -#include "hw.h" -#include "pci/pci.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" #include "ui/console.h" -#include "vga_int.h" -#include "loader.h" +#include "hw/vga_int.h" +#include "hw/loader.h" /* * TODO: @@ -288,63 +288,63 @@ static void cirrus_bitblt_fill_nop(CirrusVGAState *s, #define ROP_NAME 0 #define ROP_FN(d, s) 0 -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME src_and_dst #define ROP_FN(d, s) (s) & (d) -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME src_and_notdst #define ROP_FN(d, s) (s) & (~(d)) -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME notdst #define ROP_FN(d, s) ~(d) -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME src #define ROP_FN(d, s) s -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME 1 #define ROP_FN(d, s) ~0 -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME notsrc_and_dst #define ROP_FN(d, s) (~(s)) & (d) -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME src_xor_dst #define ROP_FN(d, s) (s) ^ (d) -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME src_or_dst #define ROP_FN(d, s) (s) | (d) -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME notsrc_or_notdst #define ROP_FN(d, s) (~(s)) | (~(d)) -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME src_notxor_dst #define ROP_FN(d, s) ~((s) ^ (d)) -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME src_or_notdst #define ROP_FN(d, s) (s) | (~(d)) -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME notsrc #define ROP_FN(d, s) (~(s)) -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME notsrc_or_dst #define ROP_FN(d, s) (~(s)) | (d) -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" #define ROP_NAME notsrc_and_notdst #define ROP_FN(d, s) (~(s)) & (~(d)) -#include "cirrus_vga_rop.h" +#include "hw/cirrus_vga_rop.h" static const cirrus_bitblt_rop_t cirrus_fwd_rop[16] = { cirrus_bitblt_rop_fwd_0, @@ -2165,13 +2165,13 @@ static void cirrus_cursor_invalidate(VGACommonState *s1) } #define DEPTH 8 -#include "cirrus_vga_template.h" +#include "hw/cirrus_vga_template.h" #define DEPTH 16 -#include "cirrus_vga_template.h" +#include "hw/cirrus_vga_template.h" #define DEPTH 32 -#include "cirrus_vga_template.h" +#include "hw/cirrus_vga_template.h" static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y) { @@ -2933,7 +2933,7 @@ static void isa_cirrus_vga_class_init(ObjectClass *klass, void *data) dc->props = isa_vga_cirrus_properties; } -static TypeInfo isa_cirrus_vga_info = { +static const TypeInfo isa_cirrus_vga_info = { .name = "isa-cirrus-vga", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISACirrusVGAState), @@ -3003,7 +3003,7 @@ static void cirrus_vga_class_init(ObjectClass *klass, void *data) dc->props = pci_vga_cirrus_properties; } -static TypeInfo cirrus_vga_info = { +static const TypeInfo cirrus_vga_info = { .name = "cirrus-vga", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCICirrusVGAState), diff --git a/hw/cirrus_vga_rop.h b/hw/cirrus_vga_rop.h index 9c7bb09286..894610cc22 100644 --- a/hw/cirrus_vga_rop.h +++ b/hw/cirrus_vga_rop.h @@ -191,16 +191,16 @@ glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_16)(CirrusVGAState *s, } #define DEPTH 8 -#include "cirrus_vga_rop2.h" +#include "hw/cirrus_vga_rop2.h" #define DEPTH 16 -#include "cirrus_vga_rop2.h" +#include "hw/cirrus_vga_rop2.h" #define DEPTH 24 -#include "cirrus_vga_rop2.h" +#include "hw/cirrus_vga_rop2.h" #define DEPTH 32 -#include "cirrus_vga_rop2.h" +#include "hw/cirrus_vga_rop2.h" #undef ROP_NAME #undef ROP_OP diff --git a/hw/cris/Makefile.objs b/hw/cris/Makefile.objs index aa9298a0ed..a94c62450d 100644 --- a/hw/cris/Makefile.objs +++ b/hw/cris/Makefile.objs @@ -1,8 +1,3 @@ -# Boards -obj-y = cris_pic_cpu.o -obj-y += cris-boot.o -obj-y += axis_dev88.o - # IO blocks obj-y += etraxfs_dma.o obj-y += etraxfs_pic.o @@ -11,3 +6,8 @@ obj-y += etraxfs_timer.o obj-y += etraxfs_ser.o obj-y := $(addprefix ../,$(obj-y)) + +# Boards +obj-y += pic_cpu.o +obj-y += boot.o +obj-y += axis_dev88.o diff --git a/hw/axis_dev88.c b/hw/cris/axis_dev88.c similarity index 98% rename from hw/axis_dev88.c rename to hw/cris/axis_dev88.c index 2ca606b835..eccd423abf 100644 --- a/hw/axis_dev88.c +++ b/hw/cris/axis_dev88.c @@ -22,14 +22,14 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "net/net.h" -#include "flash.h" -#include "boards.h" -#include "etraxfs.h" -#include "loader.h" +#include "hw/flash.h" +#include "hw/boards.h" +#include "hw/etraxfs.h" +#include "hw/loader.h" #include "elf.h" -#include "cris-boot.h" +#include "hw/cris-boot.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" @@ -300,7 +300,7 @@ void axisdev88_init(QEMUMachineInitArgs *args) /* FIXME: Is there a proper way to signal vectors to the CPU core? */ qdev_prop_set_ptr(dev, "interrupt_vector", &env->interrupt_vector); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, 0x3001c000); sysbus_connect_irq(s, 0, cpu_irq[0]); sysbus_connect_irq(s, 1, cpu_irq[1]); @@ -355,6 +355,7 @@ static QEMUMachine axisdev88_machine = { .desc = "AXIS devboard 88", .init = axisdev88_init, .is_default = 1, + DEFAULT_MACHINE_OPTIONS, }; static void axisdev88_machine_init(void) diff --git a/hw/cris-boot.c b/hw/cris/boot.c similarity index 97% rename from hw/cris-boot.c rename to hw/cris/boot.c index b21326fade..c330e22a86 100644 --- a/hw/cris-boot.c +++ b/hw/cris/boot.c @@ -22,10 +22,10 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "loader.h" +#include "hw/hw.h" +#include "hw/loader.h" #include "elf.h" -#include "cris-boot.h" +#include "hw/cris-boot.h" static void main_cpu_reset(void *opaque) { diff --git a/hw/cris_pic_cpu.c b/hw/cris/pic_cpu.c similarity index 81% rename from hw/cris_pic_cpu.c rename to hw/cris/pic_cpu.c index 3da0e86536..85c68c0497 100644 --- a/hw/cris_pic_cpu.c +++ b/hw/cris/pic_cpu.c @@ -22,24 +22,26 @@ * THE SOFTWARE. */ -#include "sysbus.h" -#include "hw.h" -#include "etraxfs.h" +#include "hw/sysbus.h" +#include "hw/hw.h" +#include "hw/etraxfs.h" #define D(x) static void cris_pic_cpu_handler(void *opaque, int irq, int level) { - CPUCRISState *env = (CPUCRISState *)opaque; + CRISCPU *cpu = opaque; + CPUState *cs = CPU(cpu); int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD; - if (level) - cpu_interrupt(env, type); - else - cpu_reset_interrupt(env, type); + if (level) { + cpu_interrupt(cs, type); + } else { + cpu_reset_interrupt(cs, type); + } } qemu_irq *cris_pic_init_cpu(CPUCRISState *env) { - return qemu_allocate_irqs(cris_pic_cpu_handler, env, 2); + return qemu_allocate_irqs(cris_pic_cpu_handler, cris_env_get_cpu(env), 2); } diff --git a/hw/cs4231.c b/hw/cs4231.c index 23570d5b41..2975336057 100644 --- a/hw/cs4231.c +++ b/hw/cs4231.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "trace.h" /* @@ -166,7 +166,7 @@ static void cs4231_class_init(ObjectClass *klass, void *data) dc->props = cs4231_properties; } -static TypeInfo cs4231_info = { +static const TypeInfo cs4231_info = { .name = "SUNW,CS4231", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(CSState), diff --git a/hw/cs4231a.c b/hw/cs4231a.c index 9d528c43b0..f005f25899 100644 --- a/hw/cs4231a.c +++ b/hw/cs4231a.c @@ -21,11 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "audiodev.h" +#include "hw/hw.h" +#include "hw/audiodev.h" #include "audio/audio.h" -#include "isa.h" -#include "qdev.h" +#include "hw/isa.h" +#include "hw/qdev.h" #include "qemu/timer.h" /* @@ -682,7 +682,7 @@ static void cs4231a_class_initfn (ObjectClass *klass, void *data) dc->props = cs4231a_properties; } -static TypeInfo cs4231a_info = { +static const TypeInfo cs4231a_info = { .name = "cs4231a", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof (CSState), diff --git a/hw/cuda.c b/hw/cuda.c index d59e0aeaa9..2ae430d326 100644 --- a/hw/cuda.c +++ b/hw/cuda.c @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "ppc_mac.h" -#include "adb.h" +#include "hw/hw.h" +#include "hw/ppc/mac.h" +#include "hw/adb.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" @@ -108,50 +108,6 @@ /* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */ #define RTC_OFFSET 2082844800 -typedef struct CUDATimer { - int index; - uint16_t latch; - uint16_t counter_value; /* counter value at load time */ - int64_t load_time; - int64_t next_irq_time; - QEMUTimer *timer; -} CUDATimer; - -typedef struct CUDAState { - MemoryRegion mem; - /* cuda registers */ - uint8_t b; /* B-side data */ - uint8_t a; /* A-side data */ - uint8_t dirb; /* B-side direction (1=output) */ - uint8_t dira; /* A-side direction (1=output) */ - uint8_t sr; /* Shift register */ - uint8_t acr; /* Auxiliary control register */ - uint8_t pcr; /* Peripheral control register */ - uint8_t ifr; /* Interrupt flag register */ - uint8_t ier; /* Interrupt enable register */ - uint8_t anh; /* A-side data, no handshake */ - - CUDATimer timers[2]; - - uint32_t tick_offset; - - uint8_t last_b; /* last value of B register */ - uint8_t last_acr; /* last value of B register */ - - int data_in_size; - int data_in_index; - int data_out_index; - - qemu_irq irq; - uint8_t autopoll; - uint8_t data_in[128]; - uint8_t data_out[16]; - QEMUTimer *adb_poll_timer; -} CUDAState; - -static CUDAState cuda_state; -ADBBusState adb_bus; - static void cuda_update(CUDAState *s); static void cuda_receive_packet_from_host(CUDAState *s, const uint8_t *data, int len); @@ -501,7 +457,7 @@ static void cuda_adb_poll(void *opaque) uint8_t obuf[ADB_MAX_OUT_LEN + 2]; int olen; - olen = adb_poll(&adb_bus, obuf + 2); + olen = adb_poll(&s->adb_bus, obuf + 2); if (olen > 0) { obuf[0] = ADB_PACKET; obuf[1] = 0x40; /* polled data */ @@ -597,7 +553,7 @@ static void cuda_receive_packet_from_host(CUDAState *s, { uint8_t obuf[ADB_MAX_OUT_LEN + 2]; int olen; - olen = adb_request(&adb_bus, obuf + 2, data + 1, len - 1); + olen = adb_request(&s->adb_bus, obuf + 2, data + 1, len - 1); if (olen > 0) { obuf[0] = ADB_PACKET; obuf[1] = 0x00; @@ -701,9 +657,9 @@ static const VMStateDescription vmstate_cuda = { } }; -static void cuda_reset(void *opaque) +static void cuda_reset(DeviceState *dev) { - CUDAState *s = opaque; + CUDAState *s = CUDA(dev); s->b = 0; s->a = 0; @@ -728,25 +684,57 @@ static void cuda_reset(void *opaque) set_counter(s, &s->timers[1], 0xffff); } -void cuda_init (MemoryRegion **cuda_mem, qemu_irq irq) +static void cuda_realizefn(DeviceState *dev, Error **errp) { + CUDAState *s = CUDA(dev); struct tm tm; - CUDAState *s = &cuda_state; - s->irq = irq; - - s->timers[0].index = 0; s->timers[0].timer = qemu_new_timer_ns(vm_clock, cuda_timer1, s); - s->timers[1].index = 1; - qemu_get_timedate(&tm, 0); s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; s->adb_poll_timer = qemu_new_timer_ns(vm_clock, cuda_adb_poll, s); - memory_region_init_io(&s->mem, &cuda_ops, s, "cuda", 0x2000); - - *cuda_mem = &s->mem; - vmstate_register(NULL, -1, &vmstate_cuda, s); - qemu_register_reset(cuda_reset, s); } + +static void cuda_initfn(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + CUDAState *s = CUDA(obj); + int i; + + memory_region_init_io(&s->mem, &cuda_ops, s, "cuda", 0x2000); + sysbus_init_mmio(d, &s->mem); + sysbus_init_irq(d, &s->irq); + + for (i = 0; i < ARRAY_SIZE(s->timers); i++) { + s->timers[i].index = i; + } + + qbus_create_inplace((BusState *)&s->adb_bus, TYPE_ADB_BUS, DEVICE(obj), + "adb.0"); +} + +static void cuda_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = cuda_realizefn; + dc->reset = cuda_reset; + dc->vmsd = &vmstate_cuda; +} + +static const TypeInfo cuda_type_info = { + .name = TYPE_CUDA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CUDAState), + .instance_init = cuda_initfn, + .class_init = cuda_class_init, +}; + +static void cuda_register_types(void) +{ + type_register_static(&cuda_type_info); +} + +type_init(cuda_register_types) diff --git a/hw/dataplane/Makefile.objs b/hw/dataplane/Makefile.objs new file mode 100644 index 0000000000..701111ccb9 --- /dev/null +++ b/hw/dataplane/Makefile.objs @@ -0,0 +1 @@ +obj-$(CONFIG_VIRTIO_BLK_DATA_PLANE) += hostmem.o vring.o ioq.o virtio-blk.o diff --git a/hw/dataplane/hostmem.c b/hw/dataplane/hostmem.c new file mode 100644 index 0000000000..380537e06d --- /dev/null +++ b/hw/dataplane/hostmem.c @@ -0,0 +1,176 @@ +/* + * Thread-safe guest to host memory mapping + * + * Copyright 2012 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Hajnoczi + * + * 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 "exec/address-spaces.h" +#include "hostmem.h" + +static int hostmem_lookup_cmp(const void *phys_, const void *region_) +{ + hwaddr phys = *(const hwaddr *)phys_; + const HostMemRegion *region = region_; + + if (phys < region->guest_addr) { + return -1; + } else if (phys >= region->guest_addr + region->size) { + return 1; + } else { + return 0; + } +} + +/** + * Map guest physical address to host pointer + */ +void *hostmem_lookup(HostMem *hostmem, hwaddr phys, hwaddr len, bool is_write) +{ + HostMemRegion *region; + void *host_addr = NULL; + hwaddr offset_within_region; + + qemu_mutex_lock(&hostmem->current_regions_lock); + region = bsearch(&phys, hostmem->current_regions, + hostmem->num_current_regions, + sizeof(hostmem->current_regions[0]), + hostmem_lookup_cmp); + if (!region) { + goto out; + } + if (is_write && region->readonly) { + goto out; + } + offset_within_region = phys - region->guest_addr; + if (len <= region->size - offset_within_region) { + host_addr = region->host_addr + offset_within_region; + } +out: + qemu_mutex_unlock(&hostmem->current_regions_lock); + + return host_addr; +} + +/** + * Install new regions list + */ +static void hostmem_listener_commit(MemoryListener *listener) +{ + HostMem *hostmem = container_of(listener, HostMem, listener); + + qemu_mutex_lock(&hostmem->current_regions_lock); + g_free(hostmem->current_regions); + hostmem->current_regions = hostmem->new_regions; + hostmem->num_current_regions = hostmem->num_new_regions; + qemu_mutex_unlock(&hostmem->current_regions_lock); + + /* Reset new regions list */ + hostmem->new_regions = NULL; + hostmem->num_new_regions = 0; +} + +/** + * Add a MemoryRegionSection to the new regions list + */ +static void hostmem_append_new_region(HostMem *hostmem, + MemoryRegionSection *section) +{ + void *ram_ptr = memory_region_get_ram_ptr(section->mr); + size_t num = hostmem->num_new_regions; + size_t new_size = (num + 1) * sizeof(hostmem->new_regions[0]); + + hostmem->new_regions = g_realloc(hostmem->new_regions, new_size); + hostmem->new_regions[num] = (HostMemRegion){ + .host_addr = ram_ptr + section->offset_within_region, + .guest_addr = section->offset_within_address_space, + .size = section->size, + .readonly = section->readonly, + }; + hostmem->num_new_regions++; +} + +static void hostmem_listener_append_region(MemoryListener *listener, + MemoryRegionSection *section) +{ + HostMem *hostmem = container_of(listener, HostMem, listener); + + /* Ignore non-RAM regions, we may not be able to map them */ + if (!memory_region_is_ram(section->mr)) { + return; + } + + /* Ignore regions with dirty logging, we cannot mark them dirty */ + if (memory_region_is_logging(section->mr)) { + return; + } + + hostmem_append_new_region(hostmem, section); +} + +/* We don't implement most MemoryListener callbacks, use these nop stubs */ +static void hostmem_listener_dummy(MemoryListener *listener) +{ +} + +static void hostmem_listener_section_dummy(MemoryListener *listener, + MemoryRegionSection *section) +{ +} + +static void hostmem_listener_eventfd_dummy(MemoryListener *listener, + MemoryRegionSection *section, + bool match_data, uint64_t data, + EventNotifier *e) +{ +} + +static void hostmem_listener_coalesced_mmio_dummy(MemoryListener *listener, + MemoryRegionSection *section, + hwaddr addr, hwaddr len) +{ +} + +void hostmem_init(HostMem *hostmem) +{ + memset(hostmem, 0, sizeof(*hostmem)); + + qemu_mutex_init(&hostmem->current_regions_lock); + + hostmem->listener = (MemoryListener){ + .begin = hostmem_listener_dummy, + .commit = hostmem_listener_commit, + .region_add = hostmem_listener_append_region, + .region_del = hostmem_listener_section_dummy, + .region_nop = hostmem_listener_append_region, + .log_start = hostmem_listener_section_dummy, + .log_stop = hostmem_listener_section_dummy, + .log_sync = hostmem_listener_section_dummy, + .log_global_start = hostmem_listener_dummy, + .log_global_stop = hostmem_listener_dummy, + .eventfd_add = hostmem_listener_eventfd_dummy, + .eventfd_del = hostmem_listener_eventfd_dummy, + .coalesced_mmio_add = hostmem_listener_coalesced_mmio_dummy, + .coalesced_mmio_del = hostmem_listener_coalesced_mmio_dummy, + .priority = 10, + }; + + memory_listener_register(&hostmem->listener, &address_space_memory); + if (hostmem->num_new_regions > 0) { + hostmem_listener_commit(&hostmem->listener); + } +} + +void hostmem_finalize(HostMem *hostmem) +{ + memory_listener_unregister(&hostmem->listener); + g_free(hostmem->new_regions); + g_free(hostmem->current_regions); + qemu_mutex_destroy(&hostmem->current_regions_lock); +} diff --git a/hw/dataplane/hostmem.h b/hw/dataplane/hostmem.h new file mode 100644 index 0000000000..b2cf09333f --- /dev/null +++ b/hw/dataplane/hostmem.h @@ -0,0 +1,57 @@ +/* + * Thread-safe guest to host memory mapping + * + * Copyright 2012 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef HOSTMEM_H +#define HOSTMEM_H + +#include "exec/memory.h" +#include "qemu/thread.h" + +typedef struct { + void *host_addr; + hwaddr guest_addr; + uint64_t size; + bool readonly; +} HostMemRegion; + +typedef struct { + /* The listener is invoked when regions change and a new list of regions is + * built up completely before they are installed. + */ + MemoryListener listener; + HostMemRegion *new_regions; + size_t num_new_regions; + + /* Current regions are accessed from multiple threads either to lookup + * addresses or to install a new list of regions. The lock protects the + * pointer and the regions. + */ + QemuMutex current_regions_lock; + HostMemRegion *current_regions; + size_t num_current_regions; +} HostMem; + +void hostmem_init(HostMem *hostmem); +void hostmem_finalize(HostMem *hostmem); + +/** + * Map a guest physical address to a pointer + * + * Note that there is map/unmap mechanism here. The caller must ensure that + * mapped memory is no longer used across events like hot memory unplug. This + * can be done with other mechanisms like bdrv_drain_all() that quiesce + * in-flight I/O. + */ +void *hostmem_lookup(HostMem *hostmem, hwaddr phys, hwaddr len, bool is_write); + +#endif /* HOSTMEM_H */ diff --git a/hw/dataplane/ioq.c b/hw/dataplane/ioq.c new file mode 100644 index 0000000000..f709f87ed6 --- /dev/null +++ b/hw/dataplane/ioq.c @@ -0,0 +1,117 @@ +/* + * Linux AIO request queue + * + * Copyright 2012 IBM, Corp. + * Copyright 2012 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Hajnoczi + * + * 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 "ioq.h" + +void ioq_init(IOQueue *ioq, int fd, unsigned int max_reqs) +{ + int rc; + + ioq->fd = fd; + ioq->max_reqs = max_reqs; + + memset(&ioq->io_ctx, 0, sizeof ioq->io_ctx); + rc = io_setup(max_reqs, &ioq->io_ctx); + if (rc != 0) { + fprintf(stderr, "ioq io_setup failed %d\n", rc); + exit(1); + } + + rc = event_notifier_init(&ioq->io_notifier, 0); + if (rc != 0) { + fprintf(stderr, "ioq io event notifier creation failed %d\n", rc); + exit(1); + } + + ioq->freelist = g_malloc0(sizeof ioq->freelist[0] * max_reqs); + ioq->freelist_idx = 0; + + ioq->queue = g_malloc0(sizeof ioq->queue[0] * max_reqs); + ioq->queue_idx = 0; +} + +void ioq_cleanup(IOQueue *ioq) +{ + g_free(ioq->freelist); + g_free(ioq->queue); + + event_notifier_cleanup(&ioq->io_notifier); + io_destroy(ioq->io_ctx); +} + +EventNotifier *ioq_get_notifier(IOQueue *ioq) +{ + return &ioq->io_notifier; +} + +struct iocb *ioq_get_iocb(IOQueue *ioq) +{ + /* Underflow cannot happen since ioq is sized for max_reqs */ + assert(ioq->freelist_idx != 0); + + struct iocb *iocb = ioq->freelist[--ioq->freelist_idx]; + ioq->queue[ioq->queue_idx++] = iocb; + return iocb; +} + +void ioq_put_iocb(IOQueue *ioq, struct iocb *iocb) +{ + /* Overflow cannot happen since ioq is sized for max_reqs */ + assert(ioq->freelist_idx != ioq->max_reqs); + + ioq->freelist[ioq->freelist_idx++] = iocb; +} + +struct iocb *ioq_rdwr(IOQueue *ioq, bool read, struct iovec *iov, + unsigned int count, long long offset) +{ + struct iocb *iocb = ioq_get_iocb(ioq); + + if (read) { + io_prep_preadv(iocb, ioq->fd, iov, count, offset); + } else { + io_prep_pwritev(iocb, ioq->fd, iov, count, offset); + } + io_set_eventfd(iocb, event_notifier_get_fd(&ioq->io_notifier)); + return iocb; +} + +int ioq_submit(IOQueue *ioq) +{ + int rc = io_submit(ioq->io_ctx, ioq->queue_idx, ioq->queue); + ioq->queue_idx = 0; /* reset */ + return rc; +} + +int ioq_run_completion(IOQueue *ioq, IOQueueCompletion *completion, + void *opaque) +{ + struct io_event events[ioq->max_reqs]; + int nevents, i; + + do { + nevents = io_getevents(ioq->io_ctx, 0, ioq->max_reqs, events, NULL); + } while (nevents < 0 && errno == EINTR); + if (nevents < 0) { + return nevents; + } + + for (i = 0; i < nevents; i++) { + ssize_t ret = ((uint64_t)events[i].res2 << 32) | events[i].res; + + completion(events[i].obj, ret, opaque); + ioq_put_iocb(ioq, events[i].obj); + } + return nevents; +} diff --git a/hw/dataplane/ioq.h b/hw/dataplane/ioq.h new file mode 100644 index 0000000000..b49b5de7f4 --- /dev/null +++ b/hw/dataplane/ioq.h @@ -0,0 +1,57 @@ +/* + * Linux AIO request queue + * + * Copyright 2012 IBM, Corp. + * Copyright 2012 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef IOQ_H +#define IOQ_H + +#include +#include "qemu/event_notifier.h" + +typedef struct { + int fd; /* file descriptor */ + unsigned int max_reqs; /* max length of freelist and queue */ + + io_context_t io_ctx; /* Linux AIO context */ + EventNotifier io_notifier; /* Linux AIO eventfd */ + + /* Requests can complete in any order so a free list is necessary to manage + * available iocbs. + */ + struct iocb **freelist; /* free iocbs */ + unsigned int freelist_idx; + + /* Multiple requests are queued up before submitting them all in one go */ + struct iocb **queue; /* queued iocbs */ + unsigned int queue_idx; +} IOQueue; + +void ioq_init(IOQueue *ioq, int fd, unsigned int max_reqs); +void ioq_cleanup(IOQueue *ioq); +EventNotifier *ioq_get_notifier(IOQueue *ioq); +struct iocb *ioq_get_iocb(IOQueue *ioq); +void ioq_put_iocb(IOQueue *ioq, struct iocb *iocb); +struct iocb *ioq_rdwr(IOQueue *ioq, bool read, struct iovec *iov, + unsigned int count, long long offset); +int ioq_submit(IOQueue *ioq); + +static inline unsigned int ioq_num_queued(IOQueue *ioq) +{ + return ioq->queue_idx; +} + +typedef void IOQueueCompletion(struct iocb *iocb, ssize_t ret, void *opaque); +int ioq_run_completion(IOQueue *ioq, IOQueueCompletion *completion, + void *opaque); + +#endif /* IOQ_H */ diff --git a/hw/dataplane/virtio-blk.c b/hw/dataplane/virtio-blk.c new file mode 100644 index 0000000000..1242d61e71 --- /dev/null +++ b/hw/dataplane/virtio-blk.c @@ -0,0 +1,540 @@ +/* + * Dedicated thread for virtio-blk I/O processing + * + * Copyright 2012 IBM, Corp. + * Copyright 2012 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Hajnoczi + * + * 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 "trace.h" +#include "qemu/iov.h" +#include "qemu/thread.h" +#include "qemu/error-report.h" +#include "vring.h" +#include "ioq.h" +#include "migration/migration.h" +#include "block/block.h" +#include "hw/virtio-blk.h" +#include "hw/dataplane/virtio-blk.h" +#include "block/aio.h" + +enum { + SEG_MAX = 126, /* maximum number of I/O segments */ + VRING_MAX = SEG_MAX + 2, /* maximum number of vring descriptors */ + REQ_MAX = VRING_MAX, /* maximum number of requests in the vring, + * is VRING_MAX / 2 with traditional and + * VRING_MAX with indirect descriptors */ +}; + +typedef struct { + struct iocb iocb; /* Linux AIO control block */ + QEMUIOVector *inhdr; /* iovecs for virtio_blk_inhdr */ + unsigned int head; /* vring descriptor index */ + struct iovec *bounce_iov; /* used if guest buffers are unaligned */ + QEMUIOVector *read_qiov; /* for read completion /w bounce buffer */ +} VirtIOBlockRequest; + +struct VirtIOBlockDataPlane { + bool started; + bool stopping; + QEMUBH *start_bh; + QemuThread thread; + + VirtIOBlkConf *blk; + int fd; /* image file descriptor */ + + VirtIODevice *vdev; + Vring vring; /* virtqueue vring */ + EventNotifier *guest_notifier; /* irq */ + + /* Note that these EventNotifiers are assigned by value. This is + * fine as long as you do not call event_notifier_cleanup on them + * (because you don't own the file descriptor or handle; you just + * use it). + */ + AioContext *ctx; + EventNotifier io_notifier; /* Linux AIO completion */ + EventNotifier host_notifier; /* doorbell */ + + IOQueue ioqueue; /* Linux AIO queue (should really be per + dataplane thread) */ + VirtIOBlockRequest requests[REQ_MAX]; /* pool of requests, managed by the + queue */ + + unsigned int num_reqs; + + Error *migration_blocker; +}; + +/* Raise an interrupt to signal guest, if necessary */ +static void notify_guest(VirtIOBlockDataPlane *s) +{ + if (!vring_should_notify(s->vdev, &s->vring)) { + return; + } + + event_notifier_set(s->guest_notifier); +} + +static void complete_request(struct iocb *iocb, ssize_t ret, void *opaque) +{ + VirtIOBlockDataPlane *s = opaque; + VirtIOBlockRequest *req = container_of(iocb, VirtIOBlockRequest, iocb); + struct virtio_blk_inhdr hdr; + int len; + + if (likely(ret >= 0)) { + hdr.status = VIRTIO_BLK_S_OK; + len = ret; + } else { + hdr.status = VIRTIO_BLK_S_IOERR; + len = 0; + } + + trace_virtio_blk_data_plane_complete_request(s, req->head, ret); + + if (req->read_qiov) { + assert(req->bounce_iov); + qemu_iovec_from_buf(req->read_qiov, 0, req->bounce_iov->iov_base, len); + qemu_iovec_destroy(req->read_qiov); + g_slice_free(QEMUIOVector, req->read_qiov); + } + + if (req->bounce_iov) { + qemu_vfree(req->bounce_iov->iov_base); + g_slice_free(struct iovec, req->bounce_iov); + } + + qemu_iovec_from_buf(req->inhdr, 0, &hdr, sizeof(hdr)); + qemu_iovec_destroy(req->inhdr); + g_slice_free(QEMUIOVector, req->inhdr); + + /* According to the virtio specification len should be the number of bytes + * written to, but for virtio-blk it seems to be the number of bytes + * transferred plus the status bytes. + */ + vring_push(&s->vring, req->head, len + sizeof(hdr)); + + s->num_reqs--; +} + +static void complete_request_early(VirtIOBlockDataPlane *s, unsigned int head, + QEMUIOVector *inhdr, unsigned char status) +{ + struct virtio_blk_inhdr hdr = { + .status = status, + }; + + qemu_iovec_from_buf(inhdr, 0, &hdr, sizeof(hdr)); + qemu_iovec_destroy(inhdr); + g_slice_free(QEMUIOVector, inhdr); + + vring_push(&s->vring, head, sizeof(hdr)); + notify_guest(s); +} + +/* Get disk serial number */ +static void do_get_id_cmd(VirtIOBlockDataPlane *s, + struct iovec *iov, unsigned int iov_cnt, + unsigned int head, QEMUIOVector *inhdr) +{ + char id[VIRTIO_BLK_ID_BYTES]; + + /* Serial number not NUL-terminated when shorter than buffer */ + strncpy(id, s->blk->serial ? s->blk->serial : "", sizeof(id)); + iov_from_buf(iov, iov_cnt, 0, id, sizeof(id)); + complete_request_early(s, head, inhdr, VIRTIO_BLK_S_OK); +} + +static int do_rdwr_cmd(VirtIOBlockDataPlane *s, bool read, + struct iovec *iov, unsigned int iov_cnt, + long long offset, unsigned int head, + QEMUIOVector *inhdr) +{ + struct iocb *iocb; + QEMUIOVector qiov; + struct iovec *bounce_iov = NULL; + QEMUIOVector *read_qiov = NULL; + + qemu_iovec_init_external(&qiov, iov, iov_cnt); + if (!bdrv_qiov_is_aligned(s->blk->conf.bs, &qiov)) { + void *bounce_buffer = qemu_blockalign(s->blk->conf.bs, qiov.size); + + if (read) { + /* Need to copy back from bounce buffer on completion */ + read_qiov = g_slice_new(QEMUIOVector); + qemu_iovec_init(read_qiov, iov_cnt); + qemu_iovec_concat_iov(read_qiov, iov, iov_cnt, 0, qiov.size); + } else { + qemu_iovec_to_buf(&qiov, 0, bounce_buffer, qiov.size); + } + + /* Redirect I/O to aligned bounce buffer */ + bounce_iov = g_slice_new(struct iovec); + bounce_iov->iov_base = bounce_buffer; + bounce_iov->iov_len = qiov.size; + iov = bounce_iov; + iov_cnt = 1; + } + + iocb = ioq_rdwr(&s->ioqueue, read, iov, iov_cnt, offset); + + /* Fill in virtio block metadata needed for completion */ + VirtIOBlockRequest *req = container_of(iocb, VirtIOBlockRequest, iocb); + req->head = head; + req->inhdr = inhdr; + req->bounce_iov = bounce_iov; + req->read_qiov = read_qiov; + return 0; +} + +static int process_request(IOQueue *ioq, struct iovec iov[], + unsigned int out_num, unsigned int in_num, + unsigned int head) +{ + VirtIOBlockDataPlane *s = container_of(ioq, VirtIOBlockDataPlane, ioqueue); + struct iovec *in_iov = &iov[out_num]; + struct virtio_blk_outhdr outhdr; + QEMUIOVector *inhdr; + size_t in_size; + + /* Copy in outhdr */ + if (unlikely(iov_to_buf(iov, out_num, 0, &outhdr, + sizeof(outhdr)) != sizeof(outhdr))) { + error_report("virtio-blk request outhdr too short"); + return -EFAULT; + } + iov_discard_front(&iov, &out_num, sizeof(outhdr)); + + /* Grab inhdr for later */ + in_size = iov_size(in_iov, in_num); + if (in_size < sizeof(struct virtio_blk_inhdr)) { + error_report("virtio_blk request inhdr too short"); + return -EFAULT; + } + inhdr = g_slice_new(QEMUIOVector); + qemu_iovec_init(inhdr, 1); + qemu_iovec_concat_iov(inhdr, in_iov, in_num, + in_size - sizeof(struct virtio_blk_inhdr), + sizeof(struct virtio_blk_inhdr)); + iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); + + /* TODO Linux sets the barrier bit even when not advertised! */ + outhdr.type &= ~VIRTIO_BLK_T_BARRIER; + + switch (outhdr.type) { + case VIRTIO_BLK_T_IN: + do_rdwr_cmd(s, true, in_iov, in_num, outhdr.sector * 512, head, inhdr); + return 0; + + case VIRTIO_BLK_T_OUT: + do_rdwr_cmd(s, false, iov, out_num, outhdr.sector * 512, head, inhdr); + return 0; + + case VIRTIO_BLK_T_SCSI_CMD: + /* TODO support SCSI commands */ + complete_request_early(s, head, inhdr, VIRTIO_BLK_S_UNSUPP); + return 0; + + case VIRTIO_BLK_T_FLUSH: + /* TODO fdsync not supported by Linux AIO, do it synchronously here! */ + if (qemu_fdatasync(s->fd) < 0) { + complete_request_early(s, head, inhdr, VIRTIO_BLK_S_IOERR); + } else { + complete_request_early(s, head, inhdr, VIRTIO_BLK_S_OK); + } + return 0; + + case VIRTIO_BLK_T_GET_ID: + do_get_id_cmd(s, in_iov, in_num, head, inhdr); + return 0; + + default: + error_report("virtio-blk unsupported request type %#x", outhdr.type); + qemu_iovec_destroy(inhdr); + g_slice_free(QEMUIOVector, inhdr); + return -EFAULT; + } +} + +static int flush_true(EventNotifier *e) +{ + return true; +} + +static void handle_notify(EventNotifier *e) +{ + VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane, + host_notifier); + + /* There is one array of iovecs into which all new requests are extracted + * from the vring. Requests are read from the vring and the translated + * descriptors are written to the iovecs array. The iovecs do not have to + * persist across handle_notify() calls because the kernel copies the + * iovecs on io_submit(). + * + * Handling io_submit() EAGAIN may require storing the requests across + * handle_notify() calls until the kernel has sufficient resources to + * accept more I/O. This is not implemented yet. + */ + struct iovec iovec[VRING_MAX]; + struct iovec *end = &iovec[VRING_MAX]; + struct iovec *iov = iovec; + + /* When a request is read from the vring, the index of the first descriptor + * (aka head) is returned so that the completed request can be pushed onto + * the vring later. + * + * The number of hypervisor read-only iovecs is out_num. The number of + * hypervisor write-only iovecs is in_num. + */ + int head; + unsigned int out_num = 0, in_num = 0; + unsigned int num_queued; + + event_notifier_test_and_clear(&s->host_notifier); + for (;;) { + /* Disable guest->host notifies to avoid unnecessary vmexits */ + vring_disable_notification(s->vdev, &s->vring); + + for (;;) { + head = vring_pop(s->vdev, &s->vring, iov, end, &out_num, &in_num); + if (head < 0) { + break; /* no more requests */ + } + + trace_virtio_blk_data_plane_process_request(s, out_num, in_num, + head); + + if (process_request(&s->ioqueue, iov, out_num, in_num, head) < 0) { + vring_set_broken(&s->vring); + break; + } + iov += out_num + in_num; + } + + if (likely(head == -EAGAIN)) { /* vring emptied */ + /* Re-enable guest->host notifies and stop processing the vring. + * But if the guest has snuck in more descriptors, keep processing. + */ + if (vring_enable_notification(s->vdev, &s->vring)) { + break; + } + } else { /* head == -ENOBUFS or fatal error, iovecs[] is depleted */ + /* Since there are no iovecs[] left, stop processing for now. Do + * not re-enable guest->host notifies since the I/O completion + * handler knows to check for more vring descriptors anyway. + */ + break; + } + } + + num_queued = ioq_num_queued(&s->ioqueue); + if (num_queued > 0) { + s->num_reqs += num_queued; + + int rc = ioq_submit(&s->ioqueue); + if (unlikely(rc < 0)) { + fprintf(stderr, "ioq_submit failed %d\n", rc); + exit(1); + } + } +} + +static int flush_io(EventNotifier *e) +{ + VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane, + io_notifier); + + return s->num_reqs > 0; +} + +static void handle_io(EventNotifier *e) +{ + VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane, + io_notifier); + + event_notifier_test_and_clear(&s->io_notifier); + if (ioq_run_completion(&s->ioqueue, complete_request, s) > 0) { + notify_guest(s); + } + + /* If there were more requests than iovecs, the vring will not be empty yet + * so check again. There should now be enough resources to process more + * requests. + */ + if (unlikely(vring_more_avail(&s->vring))) { + handle_notify(&s->host_notifier); + } +} + +static void *data_plane_thread(void *opaque) +{ + VirtIOBlockDataPlane *s = opaque; + + do { + aio_poll(s->ctx, true); + } while (!s->stopping || s->num_reqs > 0); + return NULL; +} + +static void start_data_plane_bh(void *opaque) +{ + VirtIOBlockDataPlane *s = opaque; + + qemu_bh_delete(s->start_bh); + s->start_bh = NULL; + qemu_thread_create(&s->thread, data_plane_thread, + s, QEMU_THREAD_JOINABLE); +} + +bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk, + VirtIOBlockDataPlane **dataplane) +{ + VirtIOBlockDataPlane *s; + int fd; + + *dataplane = NULL; + + if (!blk->data_plane) { + return true; + } + + if (blk->scsi) { + error_report("device is incompatible with x-data-plane, use scsi=off"); + return false; + } + + if (blk->config_wce) { + error_report("device is incompatible with x-data-plane, " + "use config-wce=off"); + return false; + } + + fd = raw_get_aio_fd(blk->conf.bs); + if (fd < 0) { + error_report("drive is incompatible with x-data-plane, " + "use format=raw,cache=none,aio=native"); + return false; + } + + s = g_new0(VirtIOBlockDataPlane, 1); + s->vdev = vdev; + s->fd = fd; + s->blk = blk; + + /* Prevent block operations that conflict with data plane thread */ + bdrv_set_in_use(blk->conf.bs, 1); + + error_setg(&s->migration_blocker, + "x-data-plane does not support migration"); + migrate_add_blocker(s->migration_blocker); + + *dataplane = s; + return true; +} + +void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) +{ + if (!s) { + return; + } + + virtio_blk_data_plane_stop(s); + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); + bdrv_set_in_use(s->blk->conf.bs, 0); + g_free(s); +} + +void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) +{ + VirtQueue *vq; + int i; + + if (s->started) { + return; + } + + vq = virtio_get_queue(s->vdev, 0); + if (!vring_setup(&s->vring, s->vdev, 0)) { + return; + } + + s->ctx = aio_context_new(); + + /* Set up guest notifier (irq) */ + if (s->vdev->binding->set_guest_notifiers(s->vdev->binding_opaque, 1, + true) != 0) { + fprintf(stderr, "virtio-blk failed to set guest notifier, " + "ensure -enable-kvm is set\n"); + exit(1); + } + s->guest_notifier = virtio_queue_get_guest_notifier(vq); + + /* Set up virtqueue notify */ + if (s->vdev->binding->set_host_notifier(s->vdev->binding_opaque, + 0, true) != 0) { + fprintf(stderr, "virtio-blk failed to set host notifier\n"); + exit(1); + } + s->host_notifier = *virtio_queue_get_host_notifier(vq); + aio_set_event_notifier(s->ctx, &s->host_notifier, handle_notify, flush_true); + + /* Set up ioqueue */ + ioq_init(&s->ioqueue, s->fd, REQ_MAX); + for (i = 0; i < ARRAY_SIZE(s->requests); i++) { + ioq_put_iocb(&s->ioqueue, &s->requests[i].iocb); + } + s->io_notifier = *ioq_get_notifier(&s->ioqueue); + aio_set_event_notifier(s->ctx, &s->io_notifier, handle_io, flush_io); + + s->started = true; + trace_virtio_blk_data_plane_start(s); + + /* Kick right away to begin processing requests already in vring */ + event_notifier_set(virtio_queue_get_host_notifier(vq)); + + /* Spawn thread in BH so it inherits iothread cpusets */ + s->start_bh = qemu_bh_new(start_data_plane_bh, s); + qemu_bh_schedule(s->start_bh); +} + +void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s) +{ + if (!s->started || s->stopping) { + return; + } + s->stopping = true; + trace_virtio_blk_data_plane_stop(s); + + /* Stop thread or cancel pending thread creation BH */ + if (s->start_bh) { + qemu_bh_delete(s->start_bh); + s->start_bh = NULL; + } else { + aio_notify(s->ctx); + qemu_thread_join(&s->thread); + } + + aio_set_event_notifier(s->ctx, &s->io_notifier, NULL, NULL); + ioq_cleanup(&s->ioqueue); + + aio_set_event_notifier(s->ctx, &s->host_notifier, NULL, NULL); + s->vdev->binding->set_host_notifier(s->vdev->binding_opaque, 0, false); + + aio_context_unref(s->ctx); + + /* Clean up guest notifier (irq) */ + s->vdev->binding->set_guest_notifiers(s->vdev->binding_opaque, 1, false); + + vring_teardown(&s->vring); + s->started = false; + s->stopping = false; +} diff --git a/hw/dataplane/virtio-blk.h b/hw/dataplane/virtio-blk.h new file mode 100644 index 0000000000..1e8fdfe418 --- /dev/null +++ b/hw/dataplane/virtio-blk.h @@ -0,0 +1,29 @@ +/* + * Dedicated thread for virtio-blk I/O processing + * + * Copyright 2012 IBM, Corp. + * Copyright 2012 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef HW_DATAPLANE_VIRTIO_BLK_H +#define HW_DATAPLANE_VIRTIO_BLK_H + +#include "hw/virtio.h" + +typedef struct VirtIOBlockDataPlane VirtIOBlockDataPlane; + +bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *blk, + VirtIOBlockDataPlane **dataplane); +void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s); +void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s); +void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s); +void virtio_blk_data_plane_drain(VirtIOBlockDataPlane *s); + +#endif /* HW_DATAPLANE_VIRTIO_BLK_H */ diff --git a/hw/dataplane/vring.c b/hw/dataplane/vring.c new file mode 100644 index 0000000000..e3b225315f --- /dev/null +++ b/hw/dataplane/vring.c @@ -0,0 +1,363 @@ +/* Copyright 2012 Red Hat, Inc. + * Copyright IBM, Corp. 2012 + * + * Based on Linux 2.6.39 vhost code: + * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2006 Rusty Russell IBM Corporation + * + * Author: Michael S. Tsirkin + * Stefan Hajnoczi + * + * Inspiration, some code, and most witty comments come from + * Documentation/virtual/lguest/lguest.c, by Rusty Russell + * + * This work is licensed under the terms of the GNU GPL, version 2. + */ + +#include "trace.h" +#include "vring.h" +#include "qemu/error-report.h" + +/* Map the guest's vring to host memory */ +bool vring_setup(Vring *vring, VirtIODevice *vdev, int n) +{ + hwaddr vring_addr = virtio_queue_get_ring_addr(vdev, n); + hwaddr vring_size = virtio_queue_get_ring_size(vdev, n); + void *vring_ptr; + + vring->broken = false; + + hostmem_init(&vring->hostmem); + vring_ptr = hostmem_lookup(&vring->hostmem, vring_addr, vring_size, true); + if (!vring_ptr) { + error_report("Failed to map vring " + "addr %#" HWADDR_PRIx " size %" HWADDR_PRIu, + vring_addr, vring_size); + vring->broken = true; + return false; + } + + vring_init(&vring->vr, virtio_queue_get_num(vdev, n), vring_ptr, 4096); + + vring->last_avail_idx = 0; + vring->last_used_idx = 0; + vring->signalled_used = 0; + vring->signalled_used_valid = false; + + trace_vring_setup(virtio_queue_get_ring_addr(vdev, n), + vring->vr.desc, vring->vr.avail, vring->vr.used); + return true; +} + +void vring_teardown(Vring *vring) +{ + hostmem_finalize(&vring->hostmem); +} + +/* Disable guest->host notifies */ +void vring_disable_notification(VirtIODevice *vdev, Vring *vring) +{ + if (!(vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX))) { + vring->vr.used->flags |= VRING_USED_F_NO_NOTIFY; + } +} + +/* Enable guest->host notifies + * + * Return true if the vring is empty, false if there are more requests. + */ +bool vring_enable_notification(VirtIODevice *vdev, Vring *vring) +{ + if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + vring_avail_event(&vring->vr) = vring->vr.avail->idx; + } else { + vring->vr.used->flags &= ~VRING_USED_F_NO_NOTIFY; + } + smp_mb(); /* ensure update is seen before reading avail_idx */ + return !vring_more_avail(vring); +} + +/* This is stolen from linux/drivers/vhost/vhost.c:vhost_notify() */ +bool vring_should_notify(VirtIODevice *vdev, Vring *vring) +{ + uint16_t old, new; + bool v; + /* Flush out used index updates. This is paired + * with the barrier that the Guest executes when enabling + * interrupts. */ + smp_mb(); + + if ((vdev->guest_features & VIRTIO_F_NOTIFY_ON_EMPTY) && + unlikely(vring->vr.avail->idx == vring->last_avail_idx)) { + return true; + } + + if (!(vdev->guest_features & VIRTIO_RING_F_EVENT_IDX)) { + return !(vring->vr.avail->flags & VRING_AVAIL_F_NO_INTERRUPT); + } + old = vring->signalled_used; + v = vring->signalled_used_valid; + new = vring->signalled_used = vring->last_used_idx; + vring->signalled_used_valid = true; + + if (unlikely(!v)) { + return true; + } + + return vring_need_event(vring_used_event(&vring->vr), new, old); +} + +/* This is stolen from linux/drivers/vhost/vhost.c. */ +static int get_indirect(Vring *vring, + struct iovec iov[], struct iovec *iov_end, + unsigned int *out_num, unsigned int *in_num, + struct vring_desc *indirect) +{ + struct vring_desc desc; + unsigned int i = 0, count, found = 0; + + /* Sanity check */ + if (unlikely(indirect->len % sizeof(desc))) { + error_report("Invalid length in indirect descriptor: " + "len %#x not multiple of %#zx", + indirect->len, sizeof(desc)); + vring->broken = true; + return -EFAULT; + } + + count = indirect->len / sizeof(desc); + /* Buffers are chained via a 16 bit next field, so + * we can have at most 2^16 of these. */ + if (unlikely(count > USHRT_MAX + 1)) { + error_report("Indirect buffer length too big: %d", indirect->len); + vring->broken = true; + return -EFAULT; + } + + do { + struct vring_desc *desc_ptr; + + /* Translate indirect descriptor */ + desc_ptr = hostmem_lookup(&vring->hostmem, + indirect->addr + found * sizeof(desc), + sizeof(desc), false); + if (!desc_ptr) { + error_report("Failed to map indirect descriptor " + "addr %#" PRIx64 " len %zu", + (uint64_t)indirect->addr + found * sizeof(desc), + sizeof(desc)); + vring->broken = true; + return -EFAULT; + } + desc = *desc_ptr; + + /* Ensure descriptor has been loaded before accessing fields */ + barrier(); /* read_barrier_depends(); */ + + if (unlikely(++found > count)) { + error_report("Loop detected: last one at %u " + "indirect size %u", i, count); + vring->broken = true; + return -EFAULT; + } + + if (unlikely(desc.flags & VRING_DESC_F_INDIRECT)) { + error_report("Nested indirect descriptor"); + vring->broken = true; + return -EFAULT; + } + + /* Stop for now if there are not enough iovecs available. */ + if (iov >= iov_end) { + return -ENOBUFS; + } + + iov->iov_base = hostmem_lookup(&vring->hostmem, desc.addr, desc.len, + desc.flags & VRING_DESC_F_WRITE); + if (!iov->iov_base) { + error_report("Failed to map indirect descriptor" + "addr %#" PRIx64 " len %u", + (uint64_t)desc.addr, desc.len); + vring->broken = true; + return -EFAULT; + } + iov->iov_len = desc.len; + iov++; + + /* If this is an input descriptor, increment that count. */ + if (desc.flags & VRING_DESC_F_WRITE) { + *in_num += 1; + } else { + /* If it's an output descriptor, they're all supposed + * to come before any input descriptors. */ + if (unlikely(*in_num)) { + error_report("Indirect descriptor " + "has out after in: idx %u", i); + vring->broken = true; + return -EFAULT; + } + *out_num += 1; + } + i = desc.next; + } while (desc.flags & VRING_DESC_F_NEXT); + return 0; +} + +/* This looks in the virtqueue and for the first available buffer, and converts + * it to an iovec for convenient access. Since descriptors consist of some + * number of output then some number of input descriptors, it's actually two + * iovecs, but we pack them into one and note how many of each there were. + * + * This function returns the descriptor number found, or vq->num (which is + * never a valid descriptor number) if none was found. A negative code is + * returned on error. + * + * Stolen from linux/drivers/vhost/vhost.c. + */ +int vring_pop(VirtIODevice *vdev, Vring *vring, + struct iovec iov[], struct iovec *iov_end, + unsigned int *out_num, unsigned int *in_num) +{ + struct vring_desc desc; + unsigned int i, head, found = 0, num = vring->vr.num; + uint16_t avail_idx, last_avail_idx; + + /* If there was a fatal error then refuse operation */ + if (vring->broken) { + return -EFAULT; + } + + /* Check it isn't doing very strange things with descriptor numbers. */ + last_avail_idx = vring->last_avail_idx; + avail_idx = vring->vr.avail->idx; + barrier(); /* load indices now and not again later */ + + if (unlikely((uint16_t)(avail_idx - last_avail_idx) > num)) { + error_report("Guest moved used index from %u to %u", + last_avail_idx, avail_idx); + vring->broken = true; + return -EFAULT; + } + + /* If there's nothing new since last we looked. */ + if (avail_idx == last_avail_idx) { + return -EAGAIN; + } + + /* Only get avail ring entries after they have been exposed by guest. */ + smp_rmb(); + + /* Grab the next descriptor number they're advertising, and increment + * the index we've seen. */ + head = vring->vr.avail->ring[last_avail_idx % num]; + + /* If their number is silly, that's an error. */ + if (unlikely(head >= num)) { + error_report("Guest says index %u > %u is available", head, num); + vring->broken = true; + return -EFAULT; + } + + if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + vring_avail_event(&vring->vr) = vring->vr.avail->idx; + } + + /* When we start there are none of either input nor output. */ + *out_num = *in_num = 0; + + i = head; + do { + if (unlikely(i >= num)) { + error_report("Desc index is %u > %u, head = %u", i, num, head); + vring->broken = true; + return -EFAULT; + } + if (unlikely(++found > num)) { + error_report("Loop detected: last one at %u vq size %u head %u", + i, num, head); + vring->broken = true; + return -EFAULT; + } + desc = vring->vr.desc[i]; + + /* Ensure descriptor is loaded before accessing fields */ + barrier(); + + if (desc.flags & VRING_DESC_F_INDIRECT) { + int ret = get_indirect(vring, iov, iov_end, out_num, in_num, &desc); + if (ret < 0) { + return ret; + } + continue; + } + + /* If there are not enough iovecs left, stop for now. The caller + * should check if there are more descs available once they have dealt + * with the current set. + */ + if (iov >= iov_end) { + return -ENOBUFS; + } + + /* TODO handle non-contiguous memory across region boundaries */ + iov->iov_base = hostmem_lookup(&vring->hostmem, desc.addr, desc.len, + desc.flags & VRING_DESC_F_WRITE); + if (!iov->iov_base) { + error_report("Failed to map vring desc addr %#" PRIx64 " len %u", + (uint64_t)desc.addr, desc.len); + vring->broken = true; + return -EFAULT; + } + iov->iov_len = desc.len; + iov++; + + if (desc.flags & VRING_DESC_F_WRITE) { + /* If this is an input descriptor, + * increment that count. */ + *in_num += 1; + } else { + /* If it's an output descriptor, they're all supposed + * to come before any input descriptors. */ + if (unlikely(*in_num)) { + error_report("Descriptor has out after in: idx %d", i); + vring->broken = true; + return -EFAULT; + } + *out_num += 1; + } + i = desc.next; + } while (desc.flags & VRING_DESC_F_NEXT); + + /* On success, increment avail index. */ + vring->last_avail_idx++; + return head; +} + +/* After we've used one of their buffers, we tell them about it. + * + * Stolen from linux/drivers/vhost/vhost.c. + */ +void vring_push(Vring *vring, unsigned int head, int len) +{ + struct vring_used_elem *used; + uint16_t new; + + /* Don't touch vring if a fatal error occurred */ + if (vring->broken) { + return; + } + + /* The virtqueue contains a ring of used buffers. Get a pointer to the + * next entry in that used ring. */ + used = &vring->vr.used->ring[vring->last_used_idx % vring->vr.num]; + used->id = head; + used->len = len; + + /* Make sure buffer is written before we update index. */ + smp_wmb(); + + new = vring->vr.used->idx = ++vring->last_used_idx; + if (unlikely((int16_t)(new - vring->signalled_used) < (uint16_t)1)) { + vring->signalled_used_valid = false; + } +} diff --git a/hw/dataplane/vring.h b/hw/dataplane/vring.h new file mode 100644 index 0000000000..defb1efcda --- /dev/null +++ b/hw/dataplane/vring.h @@ -0,0 +1,62 @@ +/* Copyright 2012 Red Hat, Inc. and/or its affiliates + * Copyright IBM, Corp. 2012 + * + * Based on Linux 2.6.39 vhost code: + * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2006 Rusty Russell IBM Corporation + * + * Author: Michael S. Tsirkin + * Stefan Hajnoczi + * + * Inspiration, some code, and most witty comments come from + * Documentation/virtual/lguest/lguest.c, by Rusty Russell + * + * This work is licensed under the terms of the GNU GPL, version 2. + */ + +#ifndef VRING_H +#define VRING_H + +#include +#include "qemu-common.h" +#include "hostmem.h" +#include "hw/virtio.h" + +typedef struct { + HostMem hostmem; /* guest memory mapper */ + struct vring vr; /* virtqueue vring mapped to host memory */ + uint16_t last_avail_idx; /* last processed avail ring index */ + uint16_t last_used_idx; /* last processed used ring index */ + uint16_t signalled_used; /* EVENT_IDX state */ + bool signalled_used_valid; + bool broken; /* was there a fatal error? */ +} Vring; + +static inline unsigned int vring_get_num(Vring *vring) +{ + return vring->vr.num; +} + +/* Are there more descriptors available? */ +static inline bool vring_more_avail(Vring *vring) +{ + return vring->vr.avail->idx != vring->last_avail_idx; +} + +/* Fail future vring_pop() and vring_push() calls until reset */ +static inline void vring_set_broken(Vring *vring) +{ + vring->broken = true; +} + +bool vring_setup(Vring *vring, VirtIODevice *vdev, int n); +void vring_teardown(Vring *vring); +void vring_disable_notification(VirtIODevice *vdev, Vring *vring); +bool vring_enable_notification(VirtIODevice *vdev, Vring *vring); +bool vring_should_notify(VirtIODevice *vdev, Vring *vring); +int vring_pop(VirtIODevice *vdev, Vring *vring, + struct iovec iov[], struct iovec *iov_end, + unsigned int *out_num, unsigned int *in_num); +void vring_push(Vring *vring, unsigned int head, int len); + +#endif /* VRING_H */ diff --git a/hw/debugcon.c b/hw/debugcon.c index 14f83f1962..cab7691b12 100644 --- a/hw/debugcon.c +++ b/hw/debugcon.c @@ -24,25 +24,32 @@ * THE SOFTWARE. */ -#include "hw.h" +#include "hw/hw.h" #include "char/char.h" -#include "isa.h" -#include "pc.h" +#include "hw/isa.h" +#include "hw/pc.h" + +#define TYPE_ISA_DEBUGCON_DEVICE "isa-debugcon" +#define ISA_DEBUGCON_DEVICE(obj) \ + OBJECT_CHECK(ISADebugconState, (obj), TYPE_ISA_DEBUGCON_DEVICE) //#define DEBUG_DEBUGCON typedef struct DebugconState { + MemoryRegion io; CharDriverState *chr; uint32_t readback; } DebugconState; typedef struct ISADebugconState { - ISADevice dev; + ISADevice parent_obj; + uint32_t iobase; DebugconState state; } ISADebugconState; -static void debugcon_ioport_write(void *opaque, uint32_t addr, uint32_t val) +static void debugcon_ioport_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) { DebugconState *s = opaque; unsigned char ch = val; @@ -55,7 +62,7 @@ static void debugcon_ioport_write(void *opaque, uint32_t addr, uint32_t val) } -static uint32_t debugcon_ioport_read(void *opaque, uint32_t addr) +static uint64_t debugcon_ioport_read(void *opaque, hwaddr addr, unsigned width) { DebugconState *s = opaque; @@ -66,6 +73,14 @@ static uint32_t debugcon_ioport_read(void *opaque, uint32_t addr) return s->readback; } +static const MemoryRegionOps debugcon_ops = { + .read = debugcon_ioport_read, + .write = debugcon_ioport_write, + .valid.min_access_size = 1, + .valid.max_access_size = 1, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + static void debugcon_init_core(DebugconState *s) { if (!s->chr) { @@ -78,12 +93,14 @@ static void debugcon_init_core(DebugconState *s) static int debugcon_isa_initfn(ISADevice *dev) { - ISADebugconState *isa = DO_UPCAST(ISADebugconState, dev, dev); + ISADebugconState *isa = ISA_DEBUGCON_DEVICE(dev); DebugconState *s = &isa->state; debugcon_init_core(s); - register_ioport_write(isa->iobase, 1, 1, debugcon_ioport_write, s); - register_ioport_read(isa->iobase, 1, 1, debugcon_ioport_read, s); + memory_region_init_io(&s->io, &debugcon_ops, s, + TYPE_ISA_DEBUGCON_DEVICE, 1); + memory_region_add_subregion(isa_address_space_io(dev), + isa->iobase, &s->io); return 0; } @@ -102,8 +119,8 @@ static void debugcon_isa_class_initfn(ObjectClass *klass, void *data) dc->props = debugcon_isa_properties; } -static TypeInfo debugcon_isa_info = { - .name = "isa-debugcon", +static const TypeInfo debugcon_isa_info = { + .name = TYPE_ISA_DEBUGCON_DEVICE, .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISADebugconState), .class_init = debugcon_isa_class_initfn, diff --git a/hw/debugexit.c b/hw/debugexit.c new file mode 100644 index 0000000000..ba67a8fb41 --- /dev/null +++ b/hw/debugexit.c @@ -0,0 +1,75 @@ +/* + * debug exit port emulation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) any later version. + */ + +#include "hw/hw.h" +#include "hw/isa.h" + +#define TYPE_ISA_DEBUG_EXIT_DEVICE "isa-debug-exit" +#define ISA_DEBUG_EXIT_DEVICE(obj) \ + OBJECT_CHECK(ISADebugExitState, (obj), TYPE_ISA_DEBUG_EXIT_DEVICE) + +typedef struct ISADebugExitState { + ISADevice parent_obj; + + uint32_t iobase; + uint32_t iosize; + MemoryRegion io; +} ISADebugExitState; + +static void debug_exit_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + exit((val << 1) | 1); +} + +static const MemoryRegionOps debug_exit_ops = { + .write = debug_exit_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static int debug_exit_initfn(ISADevice *dev) +{ + ISADebugExitState *isa = ISA_DEBUG_EXIT_DEVICE(dev); + + memory_region_init_io(&isa->io, &debug_exit_ops, isa, + TYPE_ISA_DEBUG_EXIT_DEVICE, isa->iosize); + memory_region_add_subregion(isa_address_space_io(dev), + isa->iobase, &isa->io); + return 0; +} + +static Property debug_exit_properties[] = { + DEFINE_PROP_HEX32("iobase", ISADebugExitState, iobase, 0x501), + DEFINE_PROP_HEX32("iosize", ISADebugExitState, iosize, 0x02), + DEFINE_PROP_END_OF_LIST(), +}; + +static void debug_exit_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ISADeviceClass *ic = ISA_DEVICE_CLASS(klass); + ic->init = debug_exit_initfn; + dc->props = debug_exit_properties; +} + +static const TypeInfo debug_exit_info = { + .name = TYPE_ISA_DEBUG_EXIT_DEVICE, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(ISADebugExitState), + .class_init = debug_exit_class_initfn, +}; + +static void debug_exit_register_types(void) +{ + type_register_static(&debug_exit_info); +} + +type_init(debug_exit_register_types) diff --git a/hw/dec_pci.c b/hw/dec_pci.c index ee3f4ca834..64a50924f6 100644 --- a/hw/dec_pci.c +++ b/hw/dec_pci.c @@ -23,12 +23,12 @@ * THE SOFTWARE. */ -#include "dec_pci.h" -#include "sysbus.h" -#include "pci/pci.h" -#include "pci/pci_host.h" -#include "pci/pci_bridge.h" -#include "pci/pci_bus.h" +#include "hw/dec_pci.h" +#include "hw/sysbus.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_bus.h" /* debug DEC */ //#define DEBUG_DEC diff --git a/hw/dma.c b/hw/dma.c index 0634baa552..fd1161ca31 100644 --- a/hw/dma.c +++ b/hw/dma.c @@ -21,8 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "isa.h" +#include "hw/hw.h" +#include "hw/isa.h" #include "qemu/main-loop.h" /* #define DEBUG_DMA */ @@ -201,7 +201,7 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data, iport = (nport >> d->dshift) & 0x0f; switch (iport) { - case 0x01: /* command */ + case 0x00: /* command */ if ((data != 0) && (data & CMD_NOT_SUPPORTED)) { dolog("command %"PRIx64" not supported\n", data); return; @@ -209,7 +209,7 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data, d->command = data; break; - case 0x02: + case 0x01: ichan = data & 3; if (data & 4) { d->status |= 1 << (ichan + 4); @@ -221,7 +221,7 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data, DMA_run(); break; - case 0x03: /* single mask */ + case 0x02: /* single mask */ if (data & 4) d->mask |= 1 << (data & 3); else @@ -229,7 +229,7 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data, DMA_run(); break; - case 0x04: /* mode */ + case 0x03: /* mode */ { ichan = data & 3; #ifdef DEBUG_DMA @@ -248,23 +248,23 @@ static void write_cont(void *opaque, hwaddr nport, uint64_t data, break; } - case 0x05: /* clear flip flop */ + case 0x04: /* clear flip flop */ d->flip_flop = 0; break; - case 0x06: /* reset */ + case 0x05: /* reset */ d->flip_flop = 0; d->mask = ~0; d->status = 0; d->command = 0; break; - case 0x07: /* clear mask for all channels */ + case 0x06: /* clear mask for all channels */ d->mask = 0; DMA_run(); break; - case 0x08: /* write mask for all channels */ + case 0x07: /* write mask for all channels */ d->mask = data; DMA_run(); break; @@ -289,11 +289,11 @@ static uint64_t read_cont(void *opaque, hwaddr nport, unsigned size) iport = (nport >> d->dshift) & 0x0f; switch (iport) { - case 0x08: /* status */ + case 0x00: /* status */ val = d->status; d->status &= 0xf0; break; - case 0x0f: /* mask */ + case 0x01: /* mask */ val = d->mask; break; default: @@ -468,7 +468,7 @@ void DMA_schedule(int nchan) static void dma_reset(void *opaque) { struct dma_cont *d = opaque; - write_cont(d, (0x06 << d->dshift), 0, 1); + write_cont(d, (0x05 << d->dshift), 0, 1); } static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len) diff --git a/hw/dp8393x.c b/hw/dp8393x.c index b5014501df..8b5ca6a4ec 100644 --- a/hw/dp8393x.c +++ b/hw/dp8393x.c @@ -17,10 +17,10 @@ * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" #include "net/net.h" -#include "mips.h" +#include "hw/mips.h" //#define DEBUG_SONIC @@ -339,6 +339,7 @@ static void do_receiver_disable(dp8393xState *s) static void do_transmit_packets(dp8393xState *s) { + NetClientState *nc = qemu_get_queue(s->nic); uint16_t data[12]; int width, size; int tx_len, len; @@ -408,13 +409,13 @@ static void do_transmit_packets(dp8393xState *s) if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) { /* Loopback */ s->regs[SONIC_TCR] |= SONIC_TCR_CRSL; - if (s->nic->nc.info->can_receive(&s->nic->nc)) { + if (nc->info->can_receive(nc)) { s->loopback_packet = 1; - s->nic->nc.info->receive(&s->nic->nc, s->tx_buffer, tx_len); + nc->info->receive(nc, s->tx_buffer, tx_len); } } else { /* Transmit packet */ - qemu_send_packet(&s->nic->nc, s->tx_buffer, tx_len); + qemu_send_packet(nc, s->tx_buffer, tx_len); } s->regs[SONIC_TCR] |= SONIC_TCR_PTX; @@ -675,7 +676,7 @@ static const MemoryRegionOps dp8393x_ops = { static int nic_can_receive(NetClientState *nc) { - dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque; + dp8393xState *s = qemu_get_nic_opaque(nc); if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN)) return 0; @@ -724,7 +725,7 @@ static int receive_filter(dp8393xState *s, const uint8_t * buf, int size) static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) { - dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque; + dp8393xState *s = qemu_get_nic_opaque(nc); uint16_t data[10]; int packet_type; uint32_t available, address; @@ -860,7 +861,7 @@ static void nic_reset(void *opaque) static void nic_cleanup(NetClientState *nc) { - dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque; + dp8393xState *s = qemu_get_nic_opaque(nc); memory_region_del_subregion(s->address_space, &s->mmio); memory_region_destroy(&s->mmio); @@ -899,11 +900,11 @@ void dp83932_init(NICInfo *nd, hwaddr base, int it_shift, s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ s->conf.macaddr = nd->macaddr; - s->conf.peer = nd->netdev; + s->conf.peers.ncs[0] = nd->netdev; s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); qemu_register_reset(nic_reset, s); nic_reset(s); diff --git a/hw/ds1225y.c b/hw/ds1225y.c index 4b3f69bc67..488f1d7241 100644 --- a/hw/ds1225y.c +++ b/hw/ds1225y.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "trace.h" typedef struct { @@ -150,7 +150,7 @@ static void nvram_sysbus_class_init(ObjectClass *klass, void *data) dc->props = nvram_sysbus_properties; } -static TypeInfo nvram_sysbus_info = { +static const TypeInfo nvram_sysbus_info = { .name = "ds1225y", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysBusNvRamState), diff --git a/hw/ds1338.c b/hw/ds1338.c index 1aefa3ba04..ae7ca9f82d 100644 --- a/hw/ds1338.c +++ b/hw/ds1338.c @@ -10,7 +10,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "i2c.h" +#include "hw/i2c.h" /* Size of NVRAM including both the user-accessible area and the * secondary register area. @@ -59,8 +59,8 @@ static void capture_current_time(DS1338State *s) s->nvram[1] = to_bcd(now.tm_min); if (s->nvram[2] & HOURS_12) { int tmp = now.tm_hour; - if (tmp == 0) { - tmp = 24; + if (tmp % 12 == 0) { + tmp += 12; } if (tmp <= 12) { s->nvram[2] = HOURS_12 | to_bcd(tmp); @@ -145,8 +145,8 @@ static int ds1338_send(I2CSlave *i2c, uint8_t data) if (data & HOURS_PM) { tmp += 12; } - if (tmp == 24) { - tmp = 0; + if (tmp % 12 == 0) { + tmp -= 12; } now.tm_hour = tmp; } else { @@ -198,7 +198,7 @@ static int ds1338_init(I2CSlave *i2c) static void ds1338_reset(DeviceState *dev) { - DS1338State *s = FROM_I2C_SLAVE(DS1338State, I2C_SLAVE_FROM_QDEV(dev)); + DS1338State *s = FROM_I2C_SLAVE(DS1338State, I2C_SLAVE(dev)); /* The clock is running and synchronized with the host */ s->offset = 0; @@ -221,7 +221,7 @@ static void ds1338_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_ds1338; } -static TypeInfo ds1338_info = { +static const TypeInfo ds1338_info = { .name = "ds1338", .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(DS1338State), diff --git a/hw/e1000.c b/hw/e1000.c index 92fb00a89f..80b6ee3c1a 100644 --- a/hw/e1000.c +++ b/hw/e1000.c @@ -25,15 +25,15 @@ */ -#include "hw.h" -#include "pci/pci.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" #include "net/net.h" #include "net/checksum.h" -#include "loader.h" +#include "hw/loader.h" #include "sysemu/sysemu.h" #include "sysemu/dma.h" -#include "e1000_hw.h" +#include "hw/e1000_hw.h" #define E1000_DEBUG @@ -61,6 +61,8 @@ static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL); /* this is the size past which hardware will drop packets when setting LPE=0 */ #define MAXIMUM_ETHERNET_VLAN_SIZE 1522 +/* this is the size past which hardware will drop packets when setting LPE=1 */ +#define MAXIMUM_ETHERNET_LPE_SIZE 16384 /* * HW models: @@ -129,6 +131,11 @@ typedef struct E1000State_st { } eecd_state; QEMUTimer *autoneg_timer; + +/* Compatibility flags for migration to/from qemu 1.3.0 and older */ +#define E1000_FLAG_AUTONEG_BIT 0 +#define E1000_FLAG_AUTONEG (1 << E1000_FLAG_AUTONEG_BIT) + uint32_t compat_flags; } E1000State; #define defreg(x) x = (E1000_##x>>2) @@ -163,8 +170,15 @@ e1000_link_up(E1000State *s) static void set_phy_ctrl(E1000State *s, int index, uint16_t val) { + /* + * QEMU 1.3 does not support link auto-negotiation emulation, so if we + * migrate during auto negotiation, after migration the link will be + * down. + */ + if (!(s->compat_flags & E1000_FLAG_AUTONEG)) { + return; + } if ((val & MII_CR_AUTO_NEG_EN) && (val & MII_CR_RESTART_AUTO_NEG)) { - s->nic->nc.link_down = true; e1000_link_down(s); s->phy_reg[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE; DBGOUT(PHY, "Start link auto negotiation\n"); @@ -176,8 +190,9 @@ static void e1000_autoneg_timer(void *opaque) { E1000State *s = opaque; - s->nic->nc.link_down = false; - e1000_link_up(s); + if (!qemu_get_queue(s->nic)->link_down) { + e1000_link_up(s); + } s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; DBGOUT(PHY, "Auto negotiation is completed\n"); } @@ -230,7 +245,17 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val) val |= E1000_ICR_INT_ASSERTED; } s->mac_reg[ICR] = val; + + /* + * Make sure ICR and ICS registers have the same value. + * The spec says that the ICS register is write-only. However in practice, + * on real hardware ICS is readable, and for reads it has the same value as + * ICR (except that ICS does not have the clear on read behaviour of ICR). + * + * The VxWorks PRO/1000 driver uses this behaviour. + */ s->mac_reg[ICS] = val; + qemu_set_irq(s->dev.irq[0], (s->mac_reg[IMS] & s->mac_reg[ICR]) != 0); } @@ -279,7 +304,7 @@ static void e1000_reset(void *opaque) d->rxbuf_min_shift = 1; memset(&d->tx, 0, sizeof d->tx); - if (d->nic->nc.link_down) { + if (qemu_get_queue(d->nic)->link_down) { e1000_link_down(d); } @@ -307,7 +332,7 @@ set_rx_control(E1000State *s, int index, uint32_t val) s->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1; DBGOUT(RX, "RCTL: %d, mac_reg[RCTL] = 0x%x\n", s->mac_reg[RDT], s->mac_reg[RCTL]); - qemu_flush_queued_packets(&s->nic->nc); + qemu_flush_queued_packets(qemu_get_queue(s->nic)); } static void @@ -458,10 +483,11 @@ fcs_len(E1000State *s) static void e1000_send_packet(E1000State *s, const uint8_t *buf, int size) { + NetClientState *nc = qemu_get_queue(s->nic); if (s->phy_reg[PHY_CTRL] & MII_CR_LOOPBACK) { - s->nic->nc.info->receive(&s->nic->nc, buf, size); + nc->info->receive(nc, buf, size); } else { - qemu_send_packet(&s->nic->nc, buf, size); + qemu_send_packet(nc, buf, size); } } @@ -735,7 +761,7 @@ receive_filter(E1000State *s, const uint8_t *buf, int size) static void e1000_set_link_status(NetClientState *nc) { - E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + E1000State *s = qemu_get_nic_opaque(nc); uint32_t old_status = s->mac_reg[STATUS]; if (nc->link_down) { @@ -769,9 +795,10 @@ static bool e1000_has_rxbufs(E1000State *s, size_t total_size) static int e1000_can_receive(NetClientState *nc) { - E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + E1000State *s = qemu_get_nic_opaque(nc); - return (s->mac_reg[RCTL] & E1000_RCTL_EN) && e1000_has_rxbufs(s, 1); + return (s->mac_reg[STATUS] & E1000_STATUS_LU) && + (s->mac_reg[RCTL] & E1000_RCTL_EN) && e1000_has_rxbufs(s, 1); } static uint64_t rx_desc_base(E1000State *s) @@ -785,7 +812,7 @@ static uint64_t rx_desc_base(E1000State *s) static ssize_t e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + E1000State *s = qemu_get_nic_opaque(nc); struct e1000_rx_desc desc; dma_addr_t base; unsigned int n, rdt; @@ -797,8 +824,13 @@ e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size) size_t desc_size; size_t total_size; - if (!(s->mac_reg[RCTL] & E1000_RCTL_EN)) + if (!(s->mac_reg[STATUS] & E1000_STATUS_LU)) { return -1; + } + + if (!(s->mac_reg[RCTL] & E1000_RCTL_EN)) { + return -1; + } /* Pad to minimum Ethernet frame length */ if (size < sizeof(min_buf)) { @@ -809,8 +841,9 @@ e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size) } /* Discard oversized packets if !LPE and !SBP. */ - if (size > MAXIMUM_ETHERNET_VLAN_SIZE - && !(s->mac_reg[RCTL] & E1000_RCTL_LPE) + if ((size > MAXIMUM_ETHERNET_LPE_SIZE || + (size > MAXIMUM_ETHERNET_VLAN_SIZE + && !(s->mac_reg[RCTL] & E1000_RCTL_LPE))) && !(s->mac_reg[RCTL] & E1000_RCTL_SBP)) { return size; } @@ -945,7 +978,7 @@ set_rdt(E1000State *s, int index, uint32_t val) { s->mac_reg[index] = val & 0xffff; if (e1000_has_rxbufs(s, 1)) { - qemu_flush_queued_packets(&s->nic->nc); + qemu_flush_queued_packets(qemu_get_queue(s->nic)); } } @@ -1096,13 +1129,47 @@ static bool is_version_1(void *opaque, int version_id) return version_id == 1; } +static void e1000_pre_save(void *opaque) +{ + E1000State *s = opaque; + NetClientState *nc = qemu_get_queue(s->nic); + + if (!(s->compat_flags & E1000_FLAG_AUTONEG)) { + return; + } + + /* + * If link is down and auto-negotiation is ongoing, complete + * auto-negotiation immediately. This allows is to look at + * MII_SR_AUTONEG_COMPLETE to infer link status on load. + */ + if (nc->link_down && + s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN && + s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG) { + s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE; + } +} + static int e1000_post_load(void *opaque, int version_id) { E1000State *s = opaque; + NetClientState *nc = qemu_get_queue(s->nic); /* nc.link_down can't be migrated, so infer link_down according - * to link status bit in mac_reg[STATUS] */ - s->nic->nc.link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0; + * to link status bit in mac_reg[STATUS]. + * Alternatively, restart link negotiation if it was in progress. */ + nc->link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0; + + if (!(s->compat_flags & E1000_FLAG_AUTONEG)) { + return 0; + } + + if (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN && + s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG && + !(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) { + nc->link_down = false; + qemu_mod_timer(s->autoneg_timer, qemu_get_clock_ms(vm_clock) + 500); + } return 0; } @@ -1112,6 +1179,7 @@ static const VMStateDescription vmstate_e1000 = { .version_id = 2, .minimum_version_id = 1, .minimum_version_id_old = 1, + .pre_save = e1000_pre_save, .post_load = e1000_post_load, .fields = (VMStateField []) { VMSTATE_PCI_DEVICE(dev, E1000State), @@ -1220,7 +1288,7 @@ e1000_mmio_setup(E1000State *d) static void e1000_cleanup(NetClientState *nc) { - E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + E1000State *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -1234,7 +1302,7 @@ pci_e1000_uninit(PCIDevice *dev) qemu_free_timer(d->autoneg_timer); memory_region_destroy(&d->mmio); memory_region_destroy(&d->io); - qemu_del_net_client(&d->nic->nc); + qemu_del_nic(d->nic); } static NetClientInfo net_e1000_info = { @@ -1281,7 +1349,7 @@ static int pci_e1000_init(PCIDevice *pci_dev) d->nic = qemu_new_nic(&net_e1000_info, &d->conf, object_get_typename(OBJECT(d)), d->dev.qdev.id, d); - qemu_format_nic_info_str(&d->nic->nc, macaddr); + qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr); add_boot_device_path(d->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); @@ -1298,6 +1366,8 @@ static void qdev_e1000_reset(DeviceState *dev) static Property e1000_properties[] = { DEFINE_NIC_PROPERTIES(E1000State, conf), + DEFINE_PROP_BIT("autonegotiation", E1000State, + compat_flags, E1000_FLAG_AUTONEG_BIT, true), DEFINE_PROP_END_OF_LIST(), }; @@ -1319,7 +1389,7 @@ static void e1000_class_init(ObjectClass *klass, void *data) dc->props = e1000_properties; } -static TypeInfo e1000_info = { +static const TypeInfo e1000_info = { .name = "e1000", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(E1000State), diff --git a/hw/ecc.c b/hw/ecc.c index 60d1f1d4f2..8c97c33deb 100644 --- a/hw/ecc.c +++ b/hw/ecc.c @@ -11,8 +11,8 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "flash.h" +#include "hw/hw.h" +#include "hw/flash.h" /* * Pre-calculated 256-way 1 byte column parity. Table borrowed from Linux. diff --git a/hw/eccmemctl.c b/hw/eccmemctl.c index 000bd08dee..6f4a407cbf 100644 --- a/hw/eccmemctl.c +++ b/hw/eccmemctl.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "trace.h" /* There are 3 versions of this chip used in SMP sun4m systems: @@ -324,7 +324,7 @@ static void ecc_class_init(ObjectClass *klass, void *data) dc->props = ecc_properties; } -static TypeInfo ecc_info = { +static const TypeInfo ecc_info = { .name = "eccmemctl", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ECCState), diff --git a/hw/eepro100.c b/hw/eepro100.c index 6bbefb505f..68d729c17a 100644 --- a/hw/eepro100.c +++ b/hw/eepro100.c @@ -41,10 +41,10 @@ */ #include /* offsetof */ -#include "hw.h" -#include "pci/pci.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" #include "net/net.h" -#include "eeprom93xx.h" +#include "hw/eeprom93xx.h" #include "sysemu/sysemu.h" #include "sysemu/dma.h" @@ -828,7 +828,7 @@ static void tx_command(EEPRO100State *s) } } TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size))); - qemu_send_packet(&s->nic->nc, buf, size); + qemu_send_packet(qemu_get_queue(s->nic), buf, size); s->statistics.tx_good_frames++; /* Transmit with bad status would raise an CX/TNO interrupt. * (82557 only). Emulation never has bad status. */ @@ -1036,7 +1036,7 @@ static void eepro100_ru_command(EEPRO100State * s, uint8_t val) } set_ru_state(s, ru_ready); s->ru_offset = e100_read_reg4(s, SCBPointer); - qemu_flush_queued_packets(&s->nic->nc); + qemu_flush_queued_packets(qemu_get_queue(s->nic)); TRACE(OTHER, logout("val=0x%02x (rx start)\n", val)); break; case RX_RESUME: @@ -1619,7 +1619,7 @@ static const MemoryRegionOps eepro100_ops = { static int nic_can_receive(NetClientState *nc) { - EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque; + EEPRO100State *s = qemu_get_nic_opaque(nc); TRACE(RXTX, logout("%p\n", s)); return get_ru_state(s) == ru_ready; #if 0 @@ -1633,7 +1633,7 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) * - Magic packets should set bit 30 in power management driver register. * - Interesting packets should set bit 29 in power management driver register. */ - EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque; + EEPRO100State *s = qemu_get_nic_opaque(nc); uint16_t rfd_status = 0xa000; #if defined(CONFIG_PAD_RECEIVED_FRAMES) uint8_t min_buf[60]; @@ -1835,7 +1835,7 @@ static const VMStateDescription vmstate_eepro100 = { static void nic_cleanup(NetClientState *nc) { - EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque; + EEPRO100State *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -1849,7 +1849,7 @@ static void pci_nic_uninit(PCIDevice *pci_dev) memory_region_destroy(&s->flash_bar); vmstate_unregister(&pci_dev->qdev, s->vmstate, s); eeprom93xx_free(&pci_dev->qdev, s->eeprom); - qemu_del_net_client(&s->nic->nc); + qemu_del_nic(s->nic); } static NetClientInfo net_eepro100_info = { @@ -1895,14 +1895,14 @@ static int e100_nic_init(PCIDevice *pci_dev) s->nic = qemu_new_nic(&net_eepro100_info, &s->conf, object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); - TRACE(OTHER, logout("%s\n", s->nic->nc.info_str)); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); + TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str)); qemu_register_reset(nic_reset, s); s->vmstate = g_malloc(sizeof(vmstate_eepro100)); memcpy(s->vmstate, &vmstate_eepro100, sizeof(vmstate_eepro100)); - s->vmstate->name = s->nic->nc.model; + s->vmstate->name = qemu_get_queue(s->nic)->model; vmstate_register(&pci_dev->qdev, -1, s->vmstate, s); add_boot_device_path(s->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); diff --git a/hw/eeprom93xx.c b/hw/eeprom93xx.c index 4c7158d1a5..39f560553d 100644 --- a/hw/eeprom93xx.c +++ b/hw/eeprom93xx.c @@ -35,8 +35,8 @@ * - No emulation of EEPROM timings. */ -#include "hw.h" -#include "eeprom93xx.h" +#include "hw/hw.h" +#include "hw/eeprom93xx.h" /* Debug EEPROM emulation. */ //~ #define DEBUG_EEPROM diff --git a/hw/elf_ops.h b/hw/elf_ops.h index 531a42553b..acc701e3a4 100644 --- a/hw/elf_ops.h +++ b/hw/elf_ops.h @@ -197,7 +197,7 @@ static int glue(load_elf, SZ)(const char *name, int fd, struct elfhdr ehdr; struct elf_phdr *phdr = NULL, *ph; int size, i, total_size; - elf_word mem_size; + elf_word mem_size, file_size; uint64_t addr, low = (uint64_t)-1, high = 0; uint8_t *data = NULL; char label[128]; @@ -252,14 +252,16 @@ static int glue(load_elf, SZ)(const char *name, int fd, for(i = 0; i < ehdr.e_phnum; i++) { ph = &phdr[i]; if (ph->p_type == PT_LOAD) { - mem_size = ph->p_memsz; - /* XXX: avoid allocating */ - data = g_malloc0(mem_size); + mem_size = ph->p_memsz; /* Size of the ROM */ + file_size = ph->p_filesz; /* Size of the allocated data */ + data = g_malloc0(file_size); if (ph->p_filesz > 0) { - if (lseek(fd, ph->p_offset, SEEK_SET) < 0) + if (lseek(fd, ph->p_offset, SEEK_SET) < 0) { goto fail; - if (read(fd, data, ph->p_filesz) != ph->p_filesz) + } + if (read(fd, data, file_size) != file_size) { goto fail; + } } /* address_offset is hack for kernel images that are linked at the wrong physical address. */ @@ -281,7 +283,9 @@ static int glue(load_elf, SZ)(const char *name, int fd, } snprintf(label, sizeof(label), "phdr #%d: %s", i, name); - rom_add_blob_fixed(label, data, mem_size, addr); + + /* rom_add_elf_program() seize the ownership of 'data' */ + rom_add_elf_program(label, data, file_size, mem_size, addr); total_size += mem_size; if (addr < low) @@ -289,7 +293,6 @@ static int glue(load_elf, SZ)(const char *name, int fd, if ((addr + mem_size) > high) high = addr + mem_size; - g_free(data); data = NULL; } } diff --git a/hw/empty_slot.c b/hw/empty_slot.c index 23978eb149..5234a4ddc6 100644 --- a/hw/empty_slot.c +++ b/hw/empty_slot.c @@ -9,9 +9,9 @@ * version. */ -#include "hw.h" -#include "sysbus.h" -#include "empty_slot.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/empty_slot.h" //#define DEBUG_EMPTY_SLOT @@ -56,7 +56,7 @@ void empty_slot_init(hwaddr addr, uint64_t slot_size) EmptySlot *e; dev = qdev_create(NULL, "empty_slot"); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); e = FROM_SYSBUS(EmptySlot, s); e->size = slot_size; @@ -83,7 +83,7 @@ static void empty_slot_class_init(ObjectClass *klass, void *data) k->init = empty_slot_init1; } -static TypeInfo empty_slot_info = { +static const TypeInfo empty_slot_info = { .name = "empty_slot", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(EmptySlot), diff --git a/hw/es1370.c b/hw/es1370.c index 59c3f2329e..e64cf23099 100644 --- a/hw/es1370.c +++ b/hw/es1370.c @@ -26,10 +26,10 @@ /* #define VERBOSE_ES1370 */ #define SILENT_ES1370 -#include "hw.h" -#include "audiodev.h" +#include "hw/hw.h" +#include "hw/audiodev.h" #include "audio/audio.h" -#include "pci/pci.h" +#include "hw/pci/pci.h" #include "sysemu/dma.h" /* Missing stuff: @@ -1073,7 +1073,7 @@ static void es1370_class_init (ObjectClass *klass, void *data) dc->vmsd = &vmstate_es1370; } -static TypeInfo es1370_info = { +static const TypeInfo es1370_info = { .name = "ES1370", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof (ES1370State), diff --git a/hw/escc.c b/hw/escc.c index f09904aae4..baf0219304 100644 --- a/hw/escc.c +++ b/hw/escc.c @@ -22,9 +22,9 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "sysbus.h" -#include "escc.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/escc.h" #include "char/char.h" #include "ui/console.h" #include "trace.h" @@ -700,7 +700,7 @@ MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB, qdev_prop_set_uint32(dev, "chnBtype", ser); qdev_prop_set_uint32(dev, "chnAtype", ser); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_connect_irq(s, 0, irqB); sysbus_connect_irq(s, 1, irqA); if (base) { @@ -861,7 +861,7 @@ void slavio_serial_ms_kbd_init(hwaddr base, qemu_irq irq, qdev_prop_set_uint32(dev, "chnBtype", mouse); qdev_prop_set_uint32(dev, "chnAtype", kbd); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_connect_irq(s, 0, irq); sysbus_connect_irq(s, 1, irq); sysbus_mmio_map(s, 0, base); @@ -923,7 +923,7 @@ static void escc_class_init(ObjectClass *klass, void *data) dc->props = escc_properties; } -static TypeInfo escc_info = { +static const TypeInfo escc_info = { .name = "escc", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SerialState), diff --git a/hw/esp-pci.c b/hw/esp-pci.c index c949e6e0d9..7599b39d8d 100644 --- a/hw/esp-pci.c +++ b/hw/esp-pci.c @@ -23,9 +23,9 @@ * THE SOFTWARE. */ -#include "pci/pci.h" -#include "eeprom93xx.h" -#include "esp.h" +#include "hw/pci/pci.h" +#include "hw/eeprom93xx.h" +#include "hw/esp.h" #include "trace.h" #include "qemu/log.h" diff --git a/hw/esp.c b/hw/esp.c index 0e4e430880..5365eacec0 100644 --- a/hw/esp.c +++ b/hw/esp.c @@ -23,8 +23,8 @@ * THE SOFTWARE. */ -#include "sysbus.h" -#include "esp.h" +#include "hw/sysbus.h" +#include "hw/esp.h" #include "trace.h" #include "qemu/log.h" @@ -633,7 +633,7 @@ void esp_init(hwaddr espaddr, int it_shift, /* XXX for now until rc4030 has been changed to use DMA enable signal */ esp->dma_enabled = 1; qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_connect_irq(s, 0, irq); sysbus_mmio_map(s, 0, espaddr); *reset = qdev_get_gpio_in(dev, 0); diff --git a/hw/esp.h b/hw/esp.h index f15cc7b5bd..830673be8f 100644 --- a/hw/esp.h +++ b/hw/esp.h @@ -1,7 +1,7 @@ #ifndef QEMU_HW_ESP_H #define QEMU_HW_ESP_H -#include "scsi.h" +#include "hw/scsi.h" /* esp.c */ #define ESP_MAX_DEVS 7 diff --git a/hw/etraxfs.h b/hw/etraxfs.h index cc1d7a17a0..0df4fdd2e9 100644 --- a/hw/etraxfs.h +++ b/hw/etraxfs.h @@ -26,7 +26,7 @@ #define HW_EXTRAXFS_H 1 #include "net/net.h" -#include "etraxfs_dma.h" +#include "hw/etraxfs_dma.h" qemu_irq *cris_pic_init_cpu(CPUCRISState *env); @@ -44,7 +44,7 @@ etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr, qdev_prop_set_ptr(dev, "dma_out", dma_out); qdev_prop_set_ptr(dev, "dma_in", dma_in); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); return dev; } diff --git a/hw/etraxfs_dma.c b/hw/etraxfs_dma.c index d41500316f..a84ec1f23c 100644 --- a/hw/etraxfs_dma.c +++ b/hw/etraxfs_dma.c @@ -23,12 +23,12 @@ */ #include #include -#include "hw.h" +#include "hw/hw.h" #include "exec/address-spaces.h" #include "qemu-common.h" #include "sysemu/sysemu.h" -#include "etraxfs_dma.h" +#include "hw/etraxfs_dma.h" #define D(x) diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c index 289a810edc..591bee245c 100644 --- a/hw/etraxfs_eth.c +++ b/hw/etraxfs_eth.c @@ -23,9 +23,9 @@ */ #include -#include "sysbus.h" +#include "hw/sysbus.h" #include "net/net.h" -#include "etraxfs.h" +#include "hw/etraxfs.h" #define D(x) @@ -35,582 +35,593 @@ #define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ #define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ -/* - * The MDIO extensions in the TDK PHY model were reversed engineered from the +/* + * The MDIO extensions in the TDK PHY model were reversed engineered from the * linux driver (PHYID and Diagnostics reg). * TODO: Add friendly names for the register nums. */ struct qemu_phy { - uint32_t regs[32]; + uint32_t regs[32]; - int link; + int link; - unsigned int (*read)(struct qemu_phy *phy, unsigned int req); - void (*write)(struct qemu_phy *phy, unsigned int req, - unsigned int data); + unsigned int (*read)(struct qemu_phy *phy, unsigned int req); + void (*write)(struct qemu_phy *phy, unsigned int req, unsigned int data); }; static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req) { - int regnum; - unsigned r = 0; + int regnum; + unsigned r = 0; - regnum = req & 0x1f; + regnum = req & 0x1f; - switch (regnum) { - case 1: - if (!phy->link) - break; - /* MR1. */ - /* Speeds and modes. */ - r |= (1 << 13) | (1 << 14); - r |= (1 << 11) | (1 << 12); - r |= (1 << 5); /* Autoneg complete. */ - r |= (1 << 3); /* Autoneg able. */ - r |= (1 << 2); /* link. */ - break; - case 5: - /* Link partner ability. - We are kind; always agree with whatever best mode - the guest advertises. */ - r = 1 << 14; /* Success. */ - /* Copy advertised modes. */ - r |= phy->regs[4] & (15 << 5); - /* Autoneg support. */ - r |= 1; - break; - case 18: - { - /* Diagnostics reg. */ - int duplex = 0; - int speed_100 = 0; + switch (regnum) { + case 1: + if (!phy->link) { + break; + } + /* MR1. */ + /* Speeds and modes. */ + r |= (1 << 13) | (1 << 14); + r |= (1 << 11) | (1 << 12); + r |= (1 << 5); /* Autoneg complete. */ + r |= (1 << 3); /* Autoneg able. */ + r |= (1 << 2); /* link. */ + break; + case 5: + /* Link partner ability. + We are kind; always agree with whatever best mode + the guest advertises. */ + r = 1 << 14; /* Success. */ + /* Copy advertised modes. */ + r |= phy->regs[4] & (15 << 5); + /* Autoneg support. */ + r |= 1; + break; + case 18: + { + /* Diagnostics reg. */ + int duplex = 0; + int speed_100 = 0; - if (!phy->link) - break; + if (!phy->link) { + break; + } - /* Are we advertising 100 half or 100 duplex ? */ - speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF); - speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL); + /* Are we advertising 100 half or 100 duplex ? */ + speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF); + speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL); - /* Are we advertising 10 duplex or 100 duplex ? */ - duplex = !!(phy->regs[4] & ADVERTISE_100FULL); - duplex |= !!(phy->regs[4] & ADVERTISE_10FULL); - r = (speed_100 << 10) | (duplex << 11); - } - break; + /* Are we advertising 10 duplex or 100 duplex ? */ + duplex = !!(phy->regs[4] & ADVERTISE_100FULL); + duplex |= !!(phy->regs[4] & ADVERTISE_10FULL); + r = (speed_100 << 10) | (duplex << 11); + } + break; - default: - r = phy->regs[regnum]; - break; - } - D(printf("\n%s %x = reg[%d]\n", __func__, r, regnum)); - return r; + default: + r = phy->regs[regnum]; + break; + } + D(printf("\n%s %x = reg[%d]\n", __func__, r, regnum)); + return r; } -static void +static void tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data) { - int regnum; + int regnum; - regnum = req & 0x1f; - D(printf("%s reg[%d] = %x\n", __func__, regnum, data)); - switch (regnum) { - default: - phy->regs[regnum] = data; - break; - } + regnum = req & 0x1f; + D(printf("%s reg[%d] = %x\n", __func__, regnum, data)); + switch (regnum) { + default: + phy->regs[regnum] = data; + break; + } } -static void +static void tdk_init(struct qemu_phy *phy) { - phy->regs[0] = 0x3100; - /* PHY Id. */ - phy->regs[2] = 0x0300; - phy->regs[3] = 0xe400; - /* Autonegotiation advertisement reg. */ - phy->regs[4] = 0x01E1; - phy->link = 1; + phy->regs[0] = 0x3100; + /* PHY Id. */ + phy->regs[2] = 0x0300; + phy->regs[3] = 0xe400; + /* Autonegotiation advertisement reg. */ + phy->regs[4] = 0x01E1; + phy->link = 1; - phy->read = tdk_read; - phy->write = tdk_write; + phy->read = tdk_read; + phy->write = tdk_write; } struct qemu_mdio { - /* bus. */ - int mdc; - int mdio; + /* bus. */ + int mdc; + int mdio; - /* decoder. */ - enum { - PREAMBLE, - SOF, - OPC, - ADDR, - REQ, - TURNAROUND, - DATA - } state; - unsigned int drive; + /* decoder. */ + enum { + PREAMBLE, + SOF, + OPC, + ADDR, + REQ, + TURNAROUND, + DATA + } state; + unsigned int drive; - unsigned int cnt; - unsigned int addr; - unsigned int opc; - unsigned int req; - unsigned int data; + unsigned int cnt; + unsigned int addr; + unsigned int opc; + unsigned int req; + unsigned int data; - struct qemu_phy *devs[32]; + struct qemu_phy *devs[32]; }; -static void +static void mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) { - bus->devs[addr & 0x1f] = phy; + bus->devs[addr & 0x1f] = phy; } #ifdef USE_THIS_DEAD_CODE -static void +static void mdio_detach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) { - bus->devs[addr & 0x1f] = NULL; + bus->devs[addr & 0x1f] = NULL; } #endif static void mdio_read_req(struct qemu_mdio *bus) { - struct qemu_phy *phy; + struct qemu_phy *phy; - phy = bus->devs[bus->addr]; - if (phy && phy->read) - bus->data = phy->read(phy, bus->req); - else - bus->data = 0xffff; + phy = bus->devs[bus->addr]; + if (phy && phy->read) { + bus->data = phy->read(phy, bus->req); + } else { + bus->data = 0xffff; + } } static void mdio_write_req(struct qemu_mdio *bus) { - struct qemu_phy *phy; + struct qemu_phy *phy; - phy = bus->devs[bus->addr]; - if (phy && phy->write) - phy->write(phy, bus->req, bus->data); + phy = bus->devs[bus->addr]; + if (phy && phy->write) { + phy->write(phy, bus->req, bus->data); + } } static void mdio_cycle(struct qemu_mdio *bus) { - bus->cnt++; + bus->cnt++; - D(printf("mdc=%d mdio=%d state=%d cnt=%d drv=%d\n", - bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive)); + D(printf("mdc=%d mdio=%d state=%d cnt=%d drv=%d\n", + bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive)); #if 0 - if (bus->mdc) - printf("%d", bus->mdio); + if (bus->mdc) { + printf("%d", bus->mdio); + } #endif - switch (bus->state) - { - case PREAMBLE: - if (bus->mdc) { - if (bus->cnt >= (32 * 2) && !bus->mdio) { - bus->cnt = 0; - bus->state = SOF; - bus->data = 0; - } - } - break; - case SOF: - if (bus->mdc) { - if (bus->mdio != 1) - printf("WARNING: no SOF\n"); - if (bus->cnt == 1*2) { - bus->cnt = 0; - bus->opc = 0; - bus->state = OPC; - } - } - break; - case OPC: - if (bus->mdc) { - bus->opc <<= 1; - bus->opc |= bus->mdio & 1; - if (bus->cnt == 2*2) { - bus->cnt = 0; - bus->addr = 0; - bus->state = ADDR; - } - } - break; - case ADDR: - if (bus->mdc) { - bus->addr <<= 1; - bus->addr |= bus->mdio & 1; + switch (bus->state) { + case PREAMBLE: + if (bus->mdc) { + if (bus->cnt >= (32 * 2) && !bus->mdio) { + bus->cnt = 0; + bus->state = SOF; + bus->data = 0; + } + } + break; + case SOF: + if (bus->mdc) { + if (bus->mdio != 1) { + printf("WARNING: no SOF\n"); + } + if (bus->cnt == 1*2) { + bus->cnt = 0; + bus->opc = 0; + bus->state = OPC; + } + } + break; + case OPC: + if (bus->mdc) { + bus->opc <<= 1; + bus->opc |= bus->mdio & 1; + if (bus->cnt == 2*2) { + bus->cnt = 0; + bus->addr = 0; + bus->state = ADDR; + } + } + break; + case ADDR: + if (bus->mdc) { + bus->addr <<= 1; + bus->addr |= bus->mdio & 1; - if (bus->cnt == 5*2) { - bus->cnt = 0; - bus->req = 0; - bus->state = REQ; - } - } - break; - case REQ: - if (bus->mdc) { - bus->req <<= 1; - bus->req |= bus->mdio & 1; - if (bus->cnt == 5*2) { - bus->cnt = 0; - bus->state = TURNAROUND; - } - } - break; - case TURNAROUND: - if (bus->mdc && bus->cnt == 2*2) { - bus->mdio = 0; - bus->cnt = 0; + if (bus->cnt == 5*2) { + bus->cnt = 0; + bus->req = 0; + bus->state = REQ; + } + } + break; + case REQ: + if (bus->mdc) { + bus->req <<= 1; + bus->req |= bus->mdio & 1; + if (bus->cnt == 5*2) { + bus->cnt = 0; + bus->state = TURNAROUND; + } + } + break; + case TURNAROUND: + if (bus->mdc && bus->cnt == 2*2) { + bus->mdio = 0; + bus->cnt = 0; - if (bus->opc == 2) { - bus->drive = 1; - mdio_read_req(bus); - bus->mdio = bus->data & 1; - } - bus->state = DATA; - } - break; - case DATA: - if (!bus->mdc) { - if (bus->drive) { - bus->mdio = !!(bus->data & (1 << 15)); - bus->data <<= 1; - } - } else { - if (!bus->drive) { - bus->data <<= 1; - bus->data |= bus->mdio; - } - if (bus->cnt == 16 * 2) { - bus->cnt = 0; - bus->state = PREAMBLE; - if (!bus->drive) - mdio_write_req(bus); - bus->drive = 0; - } - } - break; - default: - break; - } + if (bus->opc == 2) { + bus->drive = 1; + mdio_read_req(bus); + bus->mdio = bus->data & 1; + } + bus->state = DATA; + } + break; + case DATA: + if (!bus->mdc) { + if (bus->drive) { + bus->mdio = !!(bus->data & (1 << 15)); + bus->data <<= 1; + } + } else { + if (!bus->drive) { + bus->data <<= 1; + bus->data |= bus->mdio; + } + if (bus->cnt == 16 * 2) { + bus->cnt = 0; + bus->state = PREAMBLE; + if (!bus->drive) { + mdio_write_req(bus); + } + bus->drive = 0; + } + } + break; + default: + break; + } } /* ETRAX-FS Ethernet MAC block starts here. */ -#define RW_MA0_LO 0x00 -#define RW_MA0_HI 0x01 -#define RW_MA1_LO 0x02 -#define RW_MA1_HI 0x03 -#define RW_GA_LO 0x04 -#define RW_GA_HI 0x05 -#define RW_GEN_CTRL 0x06 -#define RW_REC_CTRL 0x07 -#define RW_TR_CTRL 0x08 -#define RW_CLR_ERR 0x09 -#define RW_MGM_CTRL 0x0a -#define R_STAT 0x0b -#define FS_ETH_MAX_REGS 0x17 +#define RW_MA0_LO 0x00 +#define RW_MA0_HI 0x01 +#define RW_MA1_LO 0x02 +#define RW_MA1_HI 0x03 +#define RW_GA_LO 0x04 +#define RW_GA_HI 0x05 +#define RW_GEN_CTRL 0x06 +#define RW_REC_CTRL 0x07 +#define RW_TR_CTRL 0x08 +#define RW_CLR_ERR 0x09 +#define RW_MGM_CTRL 0x0a +#define R_STAT 0x0b +#define FS_ETH_MAX_REGS 0x17 struct fs_eth { - SysBusDevice busdev; - MemoryRegion mmio; - NICState *nic; - NICConf conf; + SysBusDevice busdev; + MemoryRegion mmio; + NICState *nic; + NICConf conf; - /* Two addrs in the filter. */ - uint8_t macaddr[2][6]; - uint32_t regs[FS_ETH_MAX_REGS]; + /* Two addrs in the filter. */ + uint8_t macaddr[2][6]; + uint32_t regs[FS_ETH_MAX_REGS]; - union { - void *vdma_out; - struct etraxfs_dma_client *dma_out; - }; - union { - void *vdma_in; - struct etraxfs_dma_client *dma_in; - }; + union { + void *vdma_out; + struct etraxfs_dma_client *dma_out; + }; + union { + void *vdma_in; + struct etraxfs_dma_client *dma_in; + }; - /* MDIO bus. */ - struct qemu_mdio mdio_bus; - unsigned int phyaddr; - int duplex_mismatch; + /* MDIO bus. */ + struct qemu_mdio mdio_bus; + unsigned int phyaddr; + int duplex_mismatch; - /* PHY. */ - struct qemu_phy phy; + /* PHY. */ + struct qemu_phy phy; }; static void eth_validate_duplex(struct fs_eth *eth) { - struct qemu_phy *phy; - unsigned int phy_duplex; - unsigned int mac_duplex; - int new_mm = 0; + struct qemu_phy *phy; + unsigned int phy_duplex; + unsigned int mac_duplex; + int new_mm = 0; - phy = eth->mdio_bus.devs[eth->phyaddr]; - phy_duplex = !!(phy->read(phy, 18) & (1 << 11)); - mac_duplex = !!(eth->regs[RW_REC_CTRL] & 128); + phy = eth->mdio_bus.devs[eth->phyaddr]; + phy_duplex = !!(phy->read(phy, 18) & (1 << 11)); + mac_duplex = !!(eth->regs[RW_REC_CTRL] & 128); - if (mac_duplex != phy_duplex) - new_mm = 1; + if (mac_duplex != phy_duplex) { + new_mm = 1; + } - if (eth->regs[RW_GEN_CTRL] & 1) { - if (new_mm != eth->duplex_mismatch) { - if (new_mm) - printf("HW: WARNING " - "ETH duplex mismatch MAC=%d PHY=%d\n", - mac_duplex, phy_duplex); - else - printf("HW: ETH duplex ok.\n"); - } - eth->duplex_mismatch = new_mm; - } + if (eth->regs[RW_GEN_CTRL] & 1) { + if (new_mm != eth->duplex_mismatch) { + if (new_mm) { + printf("HW: WARNING ETH duplex mismatch MAC=%d PHY=%d\n", + mac_duplex, phy_duplex); + } else { + printf("HW: ETH duplex ok.\n"); + } + } + eth->duplex_mismatch = new_mm; + } } static uint64_t eth_read(void *opaque, hwaddr addr, unsigned int size) { - struct fs_eth *eth = opaque; - uint32_t r = 0; + struct fs_eth *eth = opaque; + uint32_t r = 0; - addr >>= 2; + addr >>= 2; - switch (addr) { - case R_STAT: - r = eth->mdio_bus.mdio & 1; - break; - default: - r = eth->regs[addr]; - D(printf ("%s %x\n", __func__, addr * 4)); - break; - } - return r; + switch (addr) { + case R_STAT: + r = eth->mdio_bus.mdio & 1; + break; + default: + r = eth->regs[addr]; + D(printf("%s %x\n", __func__, addr * 4)); + break; + } + return r; } static void eth_update_ma(struct fs_eth *eth, int ma) { - int reg; - int i = 0; + int reg; + int i = 0; - ma &= 1; + ma &= 1; - reg = RW_MA0_LO; - if (ma) - reg = RW_MA1_LO; + reg = RW_MA0_LO; + if (ma) { + reg = RW_MA1_LO; + } - eth->macaddr[ma][i++] = eth->regs[reg]; - eth->macaddr[ma][i++] = eth->regs[reg] >> 8; - eth->macaddr[ma][i++] = eth->regs[reg] >> 16; - eth->macaddr[ma][i++] = eth->regs[reg] >> 24; - eth->macaddr[ma][i++] = eth->regs[reg + 1]; - eth->macaddr[ma][i] = eth->regs[reg + 1] >> 8; + eth->macaddr[ma][i++] = eth->regs[reg]; + eth->macaddr[ma][i++] = eth->regs[reg] >> 8; + eth->macaddr[ma][i++] = eth->regs[reg] >> 16; + eth->macaddr[ma][i++] = eth->regs[reg] >> 24; + eth->macaddr[ma][i++] = eth->regs[reg + 1]; + eth->macaddr[ma][i] = eth->regs[reg + 1] >> 8; - D(printf("set mac%d=%x.%x.%x.%x.%x.%x\n", ma, - eth->macaddr[ma][0], eth->macaddr[ma][1], - eth->macaddr[ma][2], eth->macaddr[ma][3], - eth->macaddr[ma][4], eth->macaddr[ma][5])); + D(printf("set mac%d=%x.%x.%x.%x.%x.%x\n", ma, + eth->macaddr[ma][0], eth->macaddr[ma][1], + eth->macaddr[ma][2], eth->macaddr[ma][3], + eth->macaddr[ma][4], eth->macaddr[ma][5])); } static void eth_write(void *opaque, hwaddr addr, uint64_t val64, unsigned int size) { - struct fs_eth *eth = opaque; - uint32_t value = val64; + struct fs_eth *eth = opaque; + uint32_t value = val64; - addr >>= 2; - switch (addr) - { - case RW_MA0_LO: - case RW_MA0_HI: - eth->regs[addr] = value; - eth_update_ma(eth, 0); - break; - case RW_MA1_LO: - case RW_MA1_HI: - eth->regs[addr] = value; - eth_update_ma(eth, 1); - break; + addr >>= 2; + switch (addr) { + case RW_MA0_LO: + case RW_MA0_HI: + eth->regs[addr] = value; + eth_update_ma(eth, 0); + break; + case RW_MA1_LO: + case RW_MA1_HI: + eth->regs[addr] = value; + eth_update_ma(eth, 1); + break; - case RW_MGM_CTRL: - /* Attach an MDIO/PHY abstraction. */ - if (value & 2) - eth->mdio_bus.mdio = value & 1; - if (eth->mdio_bus.mdc != (value & 4)) { - mdio_cycle(ð->mdio_bus); - eth_validate_duplex(eth); - } - eth->mdio_bus.mdc = !!(value & 4); - eth->regs[addr] = value; - break; + case RW_MGM_CTRL: + /* Attach an MDIO/PHY abstraction. */ + if (value & 2) { + eth->mdio_bus.mdio = value & 1; + } + if (eth->mdio_bus.mdc != (value & 4)) { + mdio_cycle(ð->mdio_bus); + eth_validate_duplex(eth); + } + eth->mdio_bus.mdc = !!(value & 4); + eth->regs[addr] = value; + break; - case RW_REC_CTRL: - eth->regs[addr] = value; - eth_validate_duplex(eth); - break; + case RW_REC_CTRL: + eth->regs[addr] = value; + eth_validate_duplex(eth); + break; - default: - eth->regs[addr] = value; - D(printf ("%s %x %x\n", - __func__, addr, value)); - break; - } + default: + eth->regs[addr] = value; + D(printf("%s %x %x\n", __func__, addr, value)); + break; + } } /* The ETRAX FS has a groupt address table (GAT) which works like a k=1 bloom - filter dropping group addresses we have not joined. The filter has 64 - bits (m). The has function is a simple nible xor of the group addr. */ + filter dropping group addresses we have not joined. The filter has 64 + bits (m). The has function is a simple nible xor of the group addr. */ static int eth_match_groupaddr(struct fs_eth *eth, const unsigned char *sa) { - unsigned int hsh; - int m_individual = eth->regs[RW_REC_CTRL] & 4; - int match; + unsigned int hsh; + int m_individual = eth->regs[RW_REC_CTRL] & 4; + int match; - /* First bit on the wire of a MAC address signals multicast or - physical address. */ - if (!m_individual && !(sa[0] & 1)) - return 0; + /* First bit on the wire of a MAC address signals multicast or + physical address. */ + if (!m_individual && !(sa[0] & 1)) { + return 0; + } - /* Calculate the hash index for the GA registers. */ - hsh = 0; - hsh ^= (*sa) & 0x3f; - hsh ^= ((*sa) >> 6) & 0x03; - ++sa; - hsh ^= ((*sa) << 2) & 0x03c; - hsh ^= ((*sa) >> 4) & 0xf; - ++sa; - hsh ^= ((*sa) << 4) & 0x30; - hsh ^= ((*sa) >> 2) & 0x3f; - ++sa; - hsh ^= (*sa) & 0x3f; - hsh ^= ((*sa) >> 6) & 0x03; - ++sa; - hsh ^= ((*sa) << 2) & 0x03c; - hsh ^= ((*sa) >> 4) & 0xf; - ++sa; - hsh ^= ((*sa) << 4) & 0x30; - hsh ^= ((*sa) >> 2) & 0x3f; + /* Calculate the hash index for the GA registers. */ + hsh = 0; + hsh ^= (*sa) & 0x3f; + hsh ^= ((*sa) >> 6) & 0x03; + ++sa; + hsh ^= ((*sa) << 2) & 0x03c; + hsh ^= ((*sa) >> 4) & 0xf; + ++sa; + hsh ^= ((*sa) << 4) & 0x30; + hsh ^= ((*sa) >> 2) & 0x3f; + ++sa; + hsh ^= (*sa) & 0x3f; + hsh ^= ((*sa) >> 6) & 0x03; + ++sa; + hsh ^= ((*sa) << 2) & 0x03c; + hsh ^= ((*sa) >> 4) & 0xf; + ++sa; + hsh ^= ((*sa) << 4) & 0x30; + hsh ^= ((*sa) >> 2) & 0x3f; - hsh &= 63; - if (hsh > 31) - match = eth->regs[RW_GA_HI] & (1 << (hsh - 32)); - else - match = eth->regs[RW_GA_LO] & (1 << hsh); - D(printf("hsh=%x ga=%x.%x mtch=%d\n", hsh, - eth->regs[RW_GA_HI], eth->regs[RW_GA_LO], match)); - return match; + hsh &= 63; + if (hsh > 31) { + match = eth->regs[RW_GA_HI] & (1 << (hsh - 32)); + } else { + match = eth->regs[RW_GA_LO] & (1 << hsh); + } + D(printf("hsh=%x ga=%x.%x mtch=%d\n", hsh, + eth->regs[RW_GA_HI], eth->regs[RW_GA_LO], match)); + return match; } static int eth_can_receive(NetClientState *nc) { - return 1; + return 1; } static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque; - int use_ma0 = eth->regs[RW_REC_CTRL] & 1; - int use_ma1 = eth->regs[RW_REC_CTRL] & 2; - int r_bcast = eth->regs[RW_REC_CTRL] & 8; + unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + struct fs_eth *eth = qemu_get_nic_opaque(nc); + int use_ma0 = eth->regs[RW_REC_CTRL] & 1; + int use_ma1 = eth->regs[RW_REC_CTRL] & 2; + int r_bcast = eth->regs[RW_REC_CTRL] & 8; - if (size < 12) - return -1; + if (size < 12) { + return -1; + } - D(printf("%x.%x.%x.%x.%x.%x ma=%d %d bc=%d\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], - use_ma0, use_ma1, r_bcast)); - - /* Does the frame get through the address filters? */ - if ((!use_ma0 || memcmp(buf, eth->macaddr[0], 6)) - && (!use_ma1 || memcmp(buf, eth->macaddr[1], 6)) - && (!r_bcast || memcmp(buf, sa_bcast, 6)) - && !eth_match_groupaddr(eth, buf)) - return size; + D(printf("%x.%x.%x.%x.%x.%x ma=%d %d bc=%d\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], + use_ma0, use_ma1, r_bcast)); - /* FIXME: Find another way to pass on the fake csum. */ - etraxfs_dmac_input(eth->dma_in, (void *)buf, size + 4, 1); + /* Does the frame get through the address filters? */ + if ((!use_ma0 || memcmp(buf, eth->macaddr[0], 6)) + && (!use_ma1 || memcmp(buf, eth->macaddr[1], 6)) + && (!r_bcast || memcmp(buf, sa_bcast, 6)) + && !eth_match_groupaddr(eth, buf)) { + return size; + } + + /* FIXME: Find another way to pass on the fake csum. */ + etraxfs_dmac_input(eth->dma_in, (void *)buf, size + 4, 1); return size; } static int eth_tx_push(void *opaque, unsigned char *buf, int len, bool eop) { - struct fs_eth *eth = opaque; + struct fs_eth *eth = opaque; - D(printf("%s buf=%p len=%d\n", __func__, buf, len)); - qemu_send_packet(ð->nic->nc, buf, len); - return len; + D(printf("%s buf=%p len=%d\n", __func__, buf, len)); + qemu_send_packet(qemu_get_queue(eth->nic), buf, len); + return len; } static void eth_set_link(NetClientState *nc) { - struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque; - D(printf("%s %d\n", __func__, nc->link_down)); - eth->phy.link = !nc->link_down; + struct fs_eth *eth = qemu_get_nic_opaque(nc); + D(printf("%s %d\n", __func__, nc->link_down)); + eth->phy.link = !nc->link_down; } static const MemoryRegionOps eth_ops = { - .read = eth_read, - .write = eth_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } + .read = eth_read, + .write = eth_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } }; static void eth_cleanup(NetClientState *nc) { - struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque; + struct fs_eth *eth = qemu_get_nic_opaque(nc); - /* Disconnect the client. */ - eth->dma_out->client.push = NULL; - eth->dma_out->client.opaque = NULL; - eth->dma_in->client.opaque = NULL; - eth->dma_in->client.pull = NULL; + /* Disconnect the client. */ + eth->dma_out->client.push = NULL; + eth->dma_out->client.opaque = NULL; + eth->dma_in->client.opaque = NULL; + eth->dma_in->client.pull = NULL; g_free(eth); } static NetClientInfo net_etraxfs_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .can_receive = eth_can_receive, - .receive = eth_receive, - .cleanup = eth_cleanup, - .link_status_changed = eth_set_link, + .type = NET_CLIENT_OPTIONS_KIND_NIC, + .size = sizeof(NICState), + .can_receive = eth_can_receive, + .receive = eth_receive, + .cleanup = eth_cleanup, + .link_status_changed = eth_set_link, }; static int fs_eth_init(SysBusDevice *dev) { - struct fs_eth *s = FROM_SYSBUS(typeof(*s), dev); + struct fs_eth *s = FROM_SYSBUS(typeof(*s), dev); - if (!s->dma_out || !s->dma_in) { - hw_error("Unconnected ETRAX-FS Ethernet MAC.\n"); - } + if (!s->dma_out || !s->dma_in) { + hw_error("Unconnected ETRAX-FS Ethernet MAC.\n"); + } - s->dma_out->client.push = eth_tx_push; - s->dma_out->client.opaque = s; - s->dma_in->client.opaque = s; - s->dma_in->client.pull = NULL; + s->dma_out->client.push = eth_tx_push; + s->dma_out->client.opaque = s; + s->dma_in->client.opaque = s; + s->dma_in->client.pull = NULL; - memory_region_init_io(&s->mmio, ð_ops, s, "etraxfs-eth", 0x5c); - sysbus_init_mmio(dev, &s->mmio); + memory_region_init_io(&s->mmio, ð_ops, s, "etraxfs-eth", 0x5c); + sysbus_init_mmio(dev, &s->mmio); - qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf, - object_get_typename(OBJECT(s)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_macaddr_default_if_unset(&s->conf.macaddr); + s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf, + object_get_typename(OBJECT(s)), dev->qdev.id, s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - tdk_init(&s->phy); - mdio_attach(&s->mdio_bus, &s->phy, s->phyaddr); - return 0; + + tdk_init(&s->phy); + mdio_attach(&s->mdio_bus, &s->phy, s->phyaddr); + return 0; } static Property etraxfs_eth_properties[] = { @@ -630,7 +641,7 @@ static void etraxfs_eth_class_init(ObjectClass *klass, void *data) dc->props = etraxfs_eth_properties; } -static TypeInfo etraxfs_eth_info = { +static const TypeInfo etraxfs_eth_info = { .name = "etraxfs-eth", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct fs_eth), diff --git a/hw/etraxfs_pic.c b/hw/etraxfs_pic.c index 62a62a36af..635103c001 100644 --- a/hw/etraxfs_pic.c +++ b/hw/etraxfs_pic.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#include "sysbus.h" -#include "hw.h" +#include "hw/sysbus.h" +#include "hw/hw.h" //#include "pc.h" //#include "etraxfs.h" @@ -165,7 +165,7 @@ static void etraxfs_pic_class_init(ObjectClass *klass, void *data) dc->props = etraxfs_pic_properties; } -static TypeInfo etraxfs_pic_info = { +static const TypeInfo etraxfs_pic_info = { .name = "etraxfs,pic", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct etrax_pic), diff --git a/hw/etraxfs_ser.c b/hw/etraxfs_ser.c index 7bde8004d0..7e24d34230 100644 --- a/hw/etraxfs_ser.c +++ b/hw/etraxfs_ser.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "char/char.h" #include "qemu/log.h" @@ -233,7 +233,7 @@ static void etraxfs_ser_class_init(ObjectClass *klass, void *data) dc->reset = etraxfs_ser_reset; } -static TypeInfo etraxfs_ser_info = { +static const TypeInfo etraxfs_ser_info = { .name = "etraxfs,serial", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct etrax_serial), diff --git a/hw/etraxfs_timer.c b/hw/etraxfs_timer.c index e9273cd95d..3cd9476bb1 100644 --- a/hw/etraxfs_timer.c +++ b/hw/etraxfs_timer.c @@ -21,10 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "qemu/timer.h" -#include "ptimer.h" +#include "hw/ptimer.h" #define D(x) @@ -336,7 +336,7 @@ static void etraxfs_timer_class_init(ObjectClass *klass, void *data) sdc->init = etraxfs_timer_init; } -static TypeInfo etraxfs_timer_info = { +static const TypeInfo etraxfs_timer_info = { .name = "etraxfs,timer", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof (struct etrax_timer), diff --git a/hw/exynos4210_combiner.c b/hw/exynos4210_combiner.c index 84d36ed11f..5818f10132 100644 --- a/hw/exynos4210_combiner.c +++ b/hw/exynos4210_combiner.c @@ -27,9 +27,9 @@ * IRQs are passed to GIC through Combiner. */ -#include "sysbus.h" +#include "hw/sysbus.h" -#include "exynos4210.h" +#include "hw/exynos4210.h" //#define DEBUG_COMBINER @@ -440,7 +440,7 @@ static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_exynos4210_combiner; } -static TypeInfo exynos4210_combiner_info = { +static const TypeInfo exynos4210_combiner_info = { .name = "exynos4210.combiner", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210CombinerState), diff --git a/hw/exynos4210_fimd.c b/hw/exynos4210_fimd.c index 5c29b5d01d..6b31ae33b6 100644 --- a/hw/exynos4210_fimd.c +++ b/hw/exynos4210_fimd.c @@ -24,7 +24,7 @@ #include "qemu-common.h" #include "exec/cpu-all.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "ui/console.h" #include "ui/pixel_ops.h" #include "qemu/bswap.h" @@ -1913,7 +1913,7 @@ static void exynos4210_fimd_class_init(ObjectClass *klass, void *data) k->init = exynos4210_fimd_init; } -static TypeInfo exynos4210_fimd_info = { +static const TypeInfo exynos4210_fimd_info = { .name = "exynos4210.fimd", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210fimdState), diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c index 4fea09873a..807849c574 100644 --- a/hw/exynos4210_gic.c +++ b/hw/exynos4210_gic.c @@ -20,10 +20,10 @@ * with this program; if not, see . */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu-common.h" -#include "irq.h" -#include "exynos4210.h" +#include "hw/irq.h" +#include "hw/exynos4210.h" enum ExtGicId { EXT_GIC_ID_MDMA_LCD0 = 66, @@ -140,7 +140,7 @@ combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = { EXT_GIC_ID_I2C4, EXT_GIC_ID_I2C5, EXT_GIC_ID_I2C6, EXT_GIC_ID_I2C7 }, /* int combiner group 28 */ - { EXT_GIC_ID_SPI0, EXT_GIC_ID_SPI1, EXT_GIC_ID_SPI2 }, + { EXT_GIC_ID_SPI0, EXT_GIC_ID_SPI1, EXT_GIC_ID_SPI2 , EXT_GIC_ID_USB_HOST}, /* int combiner group 29 */ { EXT_GIC_ID_HSMMC0, EXT_GIC_ID_HSMMC1, EXT_GIC_ID_HSMMC2, EXT_GIC_ID_HSMMC3, EXT_GIC_ID_SDMMC }, @@ -290,7 +290,7 @@ static int exynos4210_gic_init(SysBusDevice *dev) qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); qdev_prop_set_uint32(s->gic, "num-irq", EXYNOS4210_GIC_NIRQ); qdev_init_nofail(s->gic); - busdev = sysbus_from_qdev(s->gic); + busdev = SYS_BUS_DEVICE(s->gic); /* Pass through outbound IRQ lines from the GIC */ sysbus_pass_irq(dev, busdev); @@ -346,7 +346,7 @@ static void exynos4210_gic_class_init(ObjectClass *klass, void *data) dc->props = exynos4210_gic_properties; } -static TypeInfo exynos4210_gic_info = { +static const TypeInfo exynos4210_gic_info = { .name = "exynos4210.gic", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210GicState), @@ -447,7 +447,7 @@ static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data) dc->props = exynos4210_irq_gate_properties; } -static TypeInfo exynos4210_irq_gate_info = { +static const TypeInfo exynos4210_irq_gate_info = { .name = "exynos4210.irq_gate", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210IRQGateState), diff --git a/hw/exynos4210_i2c.c b/hw/exynos4210_i2c.c index cefd736092..9e428759a1 100644 --- a/hw/exynos4210_i2c.c +++ b/hw/exynos4210_i2c.c @@ -21,8 +21,8 @@ */ #include "qemu/timer.h" -#include "sysbus.h" -#include "i2c.h" +#include "hw/sysbus.h" +#include "hw/i2c.h" #ifndef EXYNOS4_I2C_DEBUG #define EXYNOS4_I2C_DEBUG 0 diff --git a/hw/exynos4210_mct.c b/hw/exynos4210_mct.c index 41cd142227..862c96212b 100644 --- a/hw/exynos4210_mct.c +++ b/hw/exynos4210_mct.c @@ -52,12 +52,12 @@ * there is no way to avoid frequently events). */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu-common.h" -#include "ptimer.h" +#include "hw/ptimer.h" -#include "exynos4210.h" +#include "hw/exynos4210.h" //#define DEBUG_MCT @@ -1467,7 +1467,7 @@ static void exynos4210_mct_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_exynos4210_mct_state; } -static TypeInfo exynos4210_mct_info = { +static const TypeInfo exynos4210_mct_info = { .name = "exynos4210.mct", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210MCTState), diff --git a/hw/exynos4210_pmu.c b/hw/exynos4210_pmu.c index a22b8f181a..ba5aa8d0e4 100644 --- a/hw/exynos4210_pmu.c +++ b/hw/exynos4210_pmu.c @@ -24,7 +24,7 @@ * uses PMU INFORM5 register as a holding pen. */ -#include "sysbus.h" +#include "hw/sysbus.h" #ifndef DEBUG_PMU #define DEBUG_PMU 0 @@ -484,7 +484,7 @@ static void exynos4210_pmu_class_init(ObjectClass *klass, void *data) dc->vmsd = &exynos4210_pmu_vmstate; } -static TypeInfo exynos4210_pmu_info = { +static const TypeInfo exynos4210_pmu_info = { .name = "exynos4210.pmu", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210PmuState), diff --git a/hw/exynos4210_pwm.c b/hw/exynos4210_pwm.c index 3a3eb8c27a..6d74cd4db5 100644 --- a/hw/exynos4210_pwm.c +++ b/hw/exynos4210_pwm.c @@ -20,12 +20,12 @@ * with this program; if not, see . */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu-common.h" -#include "ptimer.h" +#include "hw/ptimer.h" -#include "exynos4210.h" +#include "hw/exynos4210.h" //#define DEBUG_PWM @@ -407,7 +407,7 @@ static void exynos4210_pwm_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_exynos4210_pwm_state; } -static TypeInfo exynos4210_pwm_info = { +static const TypeInfo exynos4210_pwm_info = { .name = "exynos4210.pwm", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210PWMState), diff --git a/hw/exynos4210_rtc.c b/hw/exynos4210_rtc.c index 5694a6207b..d170ca755a 100644 --- a/hw/exynos4210_rtc.c +++ b/hw/exynos4210_rtc.c @@ -25,16 +25,16 @@ * CLKOUTEN Bit[9] not used */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/timer.h" #include "qemu-common.h" -#include "ptimer.h" +#include "hw/ptimer.h" -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" -#include "exynos4210.h" +#include "hw/exynos4210.h" #define DEBUG_RTC 0 diff --git a/hw/exynos4210_uart.c b/hw/exynos4210_uart.c index 4f23079095..006f3d44fb 100644 --- a/hw/exynos4210_uart.c +++ b/hw/exynos4210_uart.c @@ -19,11 +19,11 @@ * */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "char/char.h" -#include "exynos4210.h" +#include "hw/exynos4210.h" #undef DEBUG_UART #undef DEBUG_UART_EXTEND @@ -615,7 +615,7 @@ DeviceState *exynos4210_uart_create(hwaddr addr, qdev_prop_set_uint32(dev, "rx-size", fifo_size); qdev_prop_set_uint32(dev, "tx-size", fifo_size); - bus = sysbus_from_qdev(dev); + bus = SYS_BUS_DEVICE(dev); qdev_init_nofail(dev); if (addr != (hwaddr)-1) { sysbus_mmio_map(bus, 0, addr); @@ -661,7 +661,7 @@ static void exynos4210_uart_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_exynos4210_uart; } -static TypeInfo exynos4210_uart_info = { +static const TypeInfo exynos4210_uart_info = { .name = "exynos4210.uart", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210UartState), diff --git a/hw/fdc.c b/hw/fdc.c index ddc0cc3819..a4bb1290ef 100644 --- a/hw/fdc.c +++ b/hw/fdc.c @@ -27,13 +27,13 @@ * way. There are changes in DOR register and DMA is not available. */ -#include "hw.h" -#include "fdc.h" +#include "hw/hw.h" +#include "hw/fdc.h" #include "qemu/error-report.h" #include "qemu/timer.h" -#include "isa.h" -#include "sysbus.h" -#include "qdev-addr.h" +#include "hw/isa.h" +#include "hw/sysbus.h" +#include "hw/qdev-addr.h" #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" #include "qemu/log.h" @@ -2210,7 +2210,7 @@ static void isabus_fdc_class_init1(ObjectClass *klass, void *data) dc->props = isa_fdc_properties; } -static TypeInfo isa_fdc_info = { +static const TypeInfo isa_fdc_info = { .name = "isa-fdc", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(FDCtrlISABus), @@ -2244,7 +2244,7 @@ static void sysbus_fdc_class_init(ObjectClass *klass, void *data) dc->props = sysbus_fdc_properties; } -static TypeInfo sysbus_fdc_info = { +static const TypeInfo sysbus_fdc_info = { .name = "sysbus-fdc", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(FDCtrlSysBus), @@ -2267,7 +2267,7 @@ static void sun4m_fdc_class_init(ObjectClass *klass, void *data) dc->props = sun4m_fdc_properties; } -static TypeInfo sun4m_fdc_info = { +static const TypeInfo sun4m_fdc_info = { .name = "SUNW,fdtwo", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(FDCtrlSysBus), diff --git a/hw/fmopl.c b/hw/fmopl.c index f0a023477d..e50ba6c0ec 100644 --- a/hw/fmopl.c +++ b/hw/fmopl.c @@ -39,7 +39,7 @@ #include #include //#include "driver.h" /* use M.A.M.E. */ -#include "fmopl.h" +#include "hw/fmopl.h" #ifndef PI #define PI 3.14159265358979323846 diff --git a/hw/framebuffer.c b/hw/framebuffer.c index 2a870961bc..d341aa0c6b 100644 --- a/hw/framebuffer.c +++ b/hw/framebuffer.c @@ -17,9 +17,9 @@ - Remove all DisplayState knowledge from devices. */ -#include "hw.h" +#include "hw/hw.h" #include "ui/console.h" -#include "framebuffer.h" +#include "hw/framebuffer.h" /* Render an image from a shared memory framebuffer. */ diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c index 26f7125fe2..63a199876c 100644 --- a/hw/fw_cfg.c +++ b/hw/fw_cfg.c @@ -21,24 +21,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "hw/hw.h" #include "sysemu/sysemu.h" -#include "isa.h" -#include "fw_cfg.h" -#include "sysbus.h" +#include "hw/isa.h" +#include "hw/fw_cfg.h" +#include "hw/sysbus.h" +#include "trace.h" #include "qemu/error-report.h" #include "qemu/config-file.h" -/* debug firmware config */ -//#define DEBUG_FW_CFG - -#ifdef DEBUG_FW_CFG -#define FW_CFG_DPRINTF(fmt, ...) \ - do { printf("FW_CFG: " fmt , ## __VA_ARGS__); } while (0) -#else -#define FW_CFG_DPRINTF(fmt, ...) -#endif - #define FW_CFG_SIZE 2 #define FW_CFG_DATA_SIZE 1 @@ -63,16 +54,17 @@ struct FWCfgState { #define JPG_FILE 0 #define BMP_FILE 1 -static char *read_splashfile(char *filename, int *file_sizep, int *file_typep) +static char *read_splashfile(char *filename, size_t *file_sizep, + int *file_typep) { GError *err = NULL; gboolean res; gchar *content; - int file_type = -1; - unsigned int filehead = 0; + int file_type; + unsigned int filehead; int bmp_bpp; - res = g_file_get_contents(filename, &content, (gsize *)file_sizep, &err); + res = g_file_get_contents(filename, &content, file_sizep, &err); if (res == FALSE) { error_report("failed to read splash file '%s'", filename); g_error_free(err); @@ -120,8 +112,8 @@ static void fw_cfg_bootsplash(FWCfgState *s) const char *boot_splash_filename = NULL; char *p; char *filename, *file_data; - int file_size; - int file_type = -1; + size_t file_size; + int file_type; const char *temp; /* get user configuration */ @@ -213,7 +205,7 @@ static void fw_cfg_write(FWCfgState *s, uint8_t value) int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; - FW_CFG_DPRINTF("write %d\n", value); + trace_fw_cfg_write(s, value); if (s->cur_entry & FW_CFG_WRITE_CHANNEL && e->callback && s->cur_offset < e->len) { @@ -238,8 +230,7 @@ static int fw_cfg_select(FWCfgState *s, uint16_t key) ret = 1; } - FW_CFG_DPRINTF("select key %d (%sfound)\n", key, ret ? "" : "not "); - + trace_fw_cfg_select(s, key, ret); return ret; } @@ -254,8 +245,7 @@ static uint8_t fw_cfg_read(FWCfgState *s) else ret = e->data[s->cur_offset++]; - FW_CFG_DPRINTF("read %d\n", ret); - + trace_fw_cfg_read(s, ret); return ret; } @@ -384,85 +374,83 @@ static const VMStateDescription vmstate_fw_cfg = { } }; -int fw_cfg_add_bytes(FWCfgState *s, uint16_t key, uint8_t *data, uint32_t len) +void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len) { int arch = !!(key & FW_CFG_ARCH_LOCAL); key &= FW_CFG_ENTRY_MASK; - if (key >= FW_CFG_MAX_ENTRY) - return 0; + assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX); s->entries[arch][key].data = data; - s->entries[arch][key].len = len; - - return 1; + s->entries[arch][key].len = (uint32_t)len; } -int fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value) +void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value) +{ + size_t sz = strlen(value) + 1; + + return fw_cfg_add_bytes(s, key, g_memdup(value, sz), sz); +} + +void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value) { uint16_t *copy; copy = g_malloc(sizeof(value)); *copy = cpu_to_le16(value); - return fw_cfg_add_bytes(s, key, (uint8_t *)copy, sizeof(value)); + fw_cfg_add_bytes(s, key, copy, sizeof(value)); } -int fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value) +void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value) { uint32_t *copy; copy = g_malloc(sizeof(value)); *copy = cpu_to_le32(value); - return fw_cfg_add_bytes(s, key, (uint8_t *)copy, sizeof(value)); + fw_cfg_add_bytes(s, key, copy, sizeof(value)); } -int fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value) +void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value) { uint64_t *copy; copy = g_malloc(sizeof(value)); *copy = cpu_to_le64(value); - return fw_cfg_add_bytes(s, key, (uint8_t *)copy, sizeof(value)); + fw_cfg_add_bytes(s, key, copy, sizeof(value)); } -int fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback, - void *callback_opaque, uint8_t *data, size_t len) +void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback, + void *callback_opaque, void *data, size_t len) { int arch = !!(key & FW_CFG_ARCH_LOCAL); - if (!(key & FW_CFG_WRITE_CHANNEL)) - return 0; + assert(key & FW_CFG_WRITE_CHANNEL); key &= FW_CFG_ENTRY_MASK; - if (key >= FW_CFG_MAX_ENTRY || len > 65535) - return 0; + assert(key < FW_CFG_MAX_ENTRY && len <= UINT32_MAX); s->entries[arch][key].data = data; - s->entries[arch][key].len = len; + s->entries[arch][key].len = (uint32_t)len; s->entries[arch][key].callback_opaque = callback_opaque; s->entries[arch][key].callback = callback; - - return 1; } -int fw_cfg_add_file(FWCfgState *s, const char *filename, uint8_t *data, - uint32_t len) +void fw_cfg_add_file(FWCfgState *s, const char *filename, + void *data, size_t len) { int i, index; + size_t dsize; if (!s->files) { - int dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS; + dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS; s->files = g_malloc0(dsize); - fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, (uint8_t*)s->files, dsize); + fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize); } index = be32_to_cpu(s->files->count); - if (index == FW_CFG_FILE_SLOTS) { - fprintf(stderr, "fw_cfg: out of file slots\n"); - return 0; - } + assert(index < FW_CFG_FILE_SLOTS); fw_cfg_add_bytes(s, FW_CFG_FILE_FIRST + index, data, len); @@ -470,24 +458,21 @@ int fw_cfg_add_file(FWCfgState *s, const char *filename, uint8_t *data, filename); for (i = 0; i < index; i++) { if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) { - FW_CFG_DPRINTF("%s: skip duplicate: %s\n", __FUNCTION__, - s->files->f[index].name); - return 1; + trace_fw_cfg_add_file_dupe(s, s->files->f[index].name); + return; } } s->files->f[index].size = cpu_to_be32(len); s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index); - FW_CFG_DPRINTF("%s: #%d: %s (%d bytes)\n", __FUNCTION__, - index, s->files->f[index].name, len); + trace_fw_cfg_add_file(s, index, s->files->f[index].name, len); s->files->count = cpu_to_be32(index+1); - return 1; } static void fw_cfg_machine_ready(struct Notifier *n, void *data) { - uint32_t len; + size_t len; FWCfgState *s = container_of(n, FWCfgState, machine_ready); char *bootindex = get_boot_devices_list(&len); @@ -505,7 +490,7 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, qdev_prop_set_uint32(dev, "ctl_iobase", ctl_port); qdev_prop_set_uint32(dev, "data_iobase", data_port); qdev_init_nofail(dev); - d = sysbus_from_qdev(dev); + d = SYS_BUS_DEVICE(dev); s = DO_UPCAST(FWCfgState, busdev.qdev, dev); @@ -515,11 +500,10 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, if (data_addr) { sysbus_mmio_map(d, 1, data_addr); } - fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (uint8_t *)"QEMU", 4); + fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4); fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16); fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC)); fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); - fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu); fw_cfg_bootsplash(s); fw_cfg_reboot(s); @@ -575,7 +559,7 @@ static void fw_cfg_class_init(ObjectClass *klass, void *data) dc->props = fw_cfg_properties; } -static TypeInfo fw_cfg_info = { +static const TypeInfo fw_cfg_info = { .name = "fw_cfg", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(FWCfgState), diff --git a/hw/fw_cfg.h b/hw/fw_cfg.h index 619a39432a..05c8df186f 100644 --- a/hw/fw_cfg.h +++ b/hw/fw_cfg.h @@ -54,14 +54,15 @@ typedef struct FWCfgFiles { typedef void (*FWCfgCallback)(void *opaque, uint8_t *data); typedef struct FWCfgState FWCfgState; -int fw_cfg_add_bytes(FWCfgState *s, uint16_t key, uint8_t *data, uint32_t len); -int fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value); -int fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value); -int fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value); -int fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback, - void *callback_opaque, uint8_t *data, size_t len); -int fw_cfg_add_file(FWCfgState *s, const char *filename, uint8_t *data, - uint32_t len); +void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len); +void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value); +void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value); +void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value); +void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value); +void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback, + void *callback_opaque, void *data, size_t len); +void fw_cfg_add_file(FWCfgState *s, const char *filename, void *data, + size_t len); FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, hwaddr crl_addr, hwaddr data_addr); diff --git a/hw/g364fb.c b/hw/g364fb.c index b46a044607..7b69815bc9 100644 --- a/hw/g364fb.c +++ b/hw/g364fb.c @@ -17,11 +17,11 @@ * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "ui/console.h" #include "ui/pixel_ops.h" #include "trace.h" -#include "sysbus.h" +#include "hw/sysbus.h" typedef struct G364State { /* hardware */ @@ -597,7 +597,7 @@ static void g364fb_sysbus_class_init(ObjectClass *klass, void *data) dc->props = g364fb_sysbus_properties; } -static TypeInfo g364fb_sysbus_info = { +static const TypeInfo g364fb_sysbus_info = { .name = "sysbus-g364", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(G364SysBusState), diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c index 948416632a..11e47d560e 100644 --- a/hw/grackle_pci.c +++ b/hw/grackle_pci.c @@ -23,9 +23,9 @@ * THE SOFTWARE. */ -#include "pci/pci_host.h" -#include "ppc_mac.h" -#include "pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/ppc/mac.h" +#include "hw/pci/pci.h" /* debug Grackle */ //#define DEBUG_GRACKLE diff --git a/hw/grlib.h b/hw/grlib.h index 35c22f5994..470ce72250 100644 --- a/hw/grlib.h +++ b/hw/grlib.h @@ -25,8 +25,8 @@ #ifndef _GRLIB_H_ #define _GRLIB_H_ -#include "qdev.h" -#include "sysbus.h" +#include "hw/qdev.h" +#include "hw/sysbus.h" /* Emulation of GrLib device is base on the GRLIB IP Core User's Manual: * http://www.gaisler.com/products/grlib/grip.pdf @@ -61,7 +61,7 @@ DeviceState *grlib_irqmp_create(hwaddr base, env->irq_manager = dev; - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); *cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq, dev, @@ -91,10 +91,10 @@ DeviceState *grlib_gptimer_create(hwaddr base, return NULL; } - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); for (i = 0; i < nr_timers; i++) { - sysbus_connect_irq(sysbus_from_qdev(dev), i, cpu_irqs[base_irq + i]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, cpu_irqs[base_irq + i]); } return dev; @@ -116,9 +116,9 @@ DeviceState *grlib_apbuart_create(hwaddr base, return NULL; } - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); return dev; } diff --git a/hw/grlib_apbuart.c b/hw/grlib_apbuart.c index 88c46780d1..62f799083c 100644 --- a/hw/grlib_apbuart.c +++ b/hw/grlib_apbuart.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "char/char.h" #include "trace.h" @@ -75,7 +75,6 @@ typedef struct UART { CharDriverState *chr; /* registers */ - uint32_t receive; uint32_t status; uint32_t control; @@ -136,12 +135,14 @@ static void grlib_apbuart_receive(void *opaque, const uint8_t *buf, int size) { UART *uart = opaque; - uart_add_to_fifo(uart, buf, size); + if (uart->control & UART_RECEIVE_ENABLE) { + uart_add_to_fifo(uart, buf, size); - uart->status |= UART_DATA_READY; + uart->status |= UART_DATA_READY; - if (uart->control & UART_RECEIVE_INTERRUPT) { - qemu_irq_pulse(uart->irq); + if (uart->control & UART_RECEIVE_INTERRUPT) { + qemu_irq_pulse(uart->irq); + } } } @@ -193,8 +194,15 @@ static void grlib_apbuart_write(void *opaque, hwaddr addr, switch (addr) { case DATA_OFFSET: case DATA_OFFSET + 3: /* When only one byte write */ - c = value & 0xFF; - qemu_chr_fe_write(uart->chr, &c, 1); + /* Transmit when character device available and transmitter enabled */ + if ((uart->chr) && (uart->control & UART_TRANSMIT_ENABLE)) { + c = value & 0xFF; + qemu_chr_fe_write(uart->chr, &c, 1); + /* Generate interrupt */ + if (uart->control & UART_TRANSMIT_INTERRUPT) { + qemu_irq_pulse(uart->irq); + } + } return; case STATUS_OFFSET: @@ -242,30 +250,44 @@ static int grlib_apbuart_init(SysBusDevice *dev) return 0; } -static Property grlib_gptimer_properties[] = { +static void grlib_apbuart_reset(DeviceState *d) +{ + UART *uart = container_of(d, UART, busdev.qdev); + + /* Transmitter FIFO and shift registers are always empty in QEMU */ + uart->status = UART_TRANSMIT_FIFO_EMPTY | UART_TRANSMIT_SHIFT_EMPTY; + /* Everything is off */ + uart->control = 0; + /* Flush receive FIFO */ + uart->len = 0; + uart->current = 0; +} + +static Property grlib_apbuart_properties[] = { DEFINE_PROP_CHR("chrdev", UART, chr), DEFINE_PROP_END_OF_LIST(), }; -static void grlib_gptimer_class_init(ObjectClass *klass, void *data) +static void grlib_apbuart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); k->init = grlib_apbuart_init; - dc->props = grlib_gptimer_properties; + dc->reset = grlib_apbuart_reset; + dc->props = grlib_apbuart_properties; } -static TypeInfo grlib_gptimer_info = { +static const TypeInfo grlib_apbuart_info = { .name = "grlib,apbuart", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(UART), - .class_init = grlib_gptimer_class_init, + .class_init = grlib_apbuart_class_init, }; -static void grlib_gptimer_register_types(void) +static void grlib_apbuart_register_types(void) { - type_register_static(&grlib_gptimer_info); + type_register_static(&grlib_apbuart_info); } -type_init(grlib_gptimer_register_types) +type_init(grlib_apbuart_register_types) diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c index 252ba893e3..7043a34684 100644 --- a/hw/grlib_gptimer.c +++ b/hw/grlib_gptimer.c @@ -22,9 +22,9 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/timer.h" -#include "ptimer.h" +#include "hw/ptimer.h" #include "trace.h" @@ -389,7 +389,7 @@ static void grlib_gptimer_class_init(ObjectClass *klass, void *data) dc->props = grlib_gptimer_properties; } -static TypeInfo grlib_gptimer_info = { +static const TypeInfo grlib_gptimer_info = { .name = "grlib,gptimer", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(GPTimerUnit), diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c index 23a6a02bc5..7ee469d191 100644 --- a/hw/grlib_irqmp.c +++ b/hw/grlib_irqmp.c @@ -24,10 +24,10 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "cpu.h" -#include "grlib.h" +#include "hw/grlib.h" #include "trace.h" @@ -109,7 +109,7 @@ void grlib_irqmp_ack(DeviceState *dev, int intno) assert(dev != NULL); - sdev = sysbus_from_qdev(dev); + sdev = SYS_BUS_DEVICE(dev); assert(sdev != NULL); irqmp = FROM_SYSBUS(typeof(*irqmp), sdev); @@ -138,7 +138,7 @@ void grlib_irqmp_set_irq(void *opaque, int irq, int level) assert(opaque != NULL); - irqmp = FROM_SYSBUS(typeof(*irqmp), sysbus_from_qdev(opaque)); + irqmp = FROM_SYSBUS(typeof(*irqmp), SYS_BUS_DEVICE(opaque)); assert(irqmp != NULL); s = irqmp->state; @@ -370,7 +370,7 @@ static void grlib_irqmp_class_init(ObjectClass *klass, void *data) dc->props = grlib_irqmp_properties; } -static TypeInfo grlib_irqmp_info = { +static const TypeInfo grlib_irqmp_info = { .name = "grlib,irqmp", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IRQMP), diff --git a/hw/gt64xxx.c b/hw/gt64xxx.c index 977a2c5e69..c73a58a045 100644 --- a/hw/gt64xxx.c +++ b/hw/gt64xxx.c @@ -22,11 +22,11 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "mips.h" -#include "pci/pci.h" -#include "pci/pci_host.h" -#include "pc.h" +#include "hw/hw.h" +#include "hw/mips.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/pc.h" #include "exec/address-spaces.h" //#define DEBUG diff --git a/hw/gus.c b/hw/gus.c index 840d098d6a..d2682249ca 100644 --- a/hw/gus.c +++ b/hw/gus.c @@ -21,12 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "audiodev.h" +#include "hw/hw.h" +#include "hw/audiodev.h" #include "audio/audio.h" -#include "isa.h" -#include "gusemu.h" -#include "gustate.h" +#include "hw/isa.h" +#include "hw/gusemu.h" +#include "hw/gustate.h" #define dolog(...) AUD_log ("audio", __VA_ARGS__) #ifdef DEBUG @@ -317,7 +317,7 @@ static void gus_class_initfn (ObjectClass *klass, void *data) dc->props = gus_properties; } -static TypeInfo gus_info = { +static const TypeInfo gus_info = { .name = "gus", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof (GUSState), diff --git a/hw/gusemu_hal.c b/hw/gusemu_hal.c index 6096690735..0eee617652 100644 --- a/hw/gusemu_hal.c +++ b/hw/gusemu_hal.c @@ -26,8 +26,8 @@ * TODO: check mixer: see 7.20 of sdk for panning pos (applies to all gus models?)? */ -#include "gustate.h" -#include "gusemu.h" +#include "hw/gustate.h" +#include "hw/gusemu.h" #define GUSregb(position) (* (gusptr+(position))) #define GUSregw(position) (*(GUSword *) (gusptr+(position))) diff --git a/hw/gusemu_mixer.c b/hw/gusemu_mixer.c index 6d8d9ced11..816c58a7ed 100644 --- a/hw/gusemu_mixer.c +++ b/hw/gusemu_mixer.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#include "gusemu.h" -#include "gustate.h" +#include "hw/gusemu.h" +#include "hw/gustate.h" #define GUSregb(position) (* (gusptr+(position))) #define GUSregw(position) (*(GUSword *) (gusptr+(position))) diff --git a/hw/hda-audio.c b/hw/hda-audio.c index 92a91b5ab1..6bdd8209fb 100644 --- a/hw/hda-audio.c +++ b/hw/hda-audio.c @@ -17,10 +17,10 @@ * along with this program; if not, see . */ -#include "hw.h" -#include "pci/pci.h" -#include "intel-hda.h" -#include "intel-hda-defs.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/intel-hda.h" +#include "hw/intel-hda-defs.h" #include "audio/audio.h" /* -------------------------------------------------------------------------- */ @@ -1039,7 +1039,7 @@ static void hda_audio_output_class_init(ObjectClass *klass, void *data) dc->props = hda_audio_properties; } -static TypeInfo hda_audio_output_info = { +static const TypeInfo hda_audio_output_info = { .name = "hda-output", .parent = TYPE_HDA_CODEC_DEVICE, .instance_size = sizeof(HDAAudioState), @@ -1060,7 +1060,7 @@ static void hda_audio_duplex_class_init(ObjectClass *klass, void *data) dc->props = hda_audio_properties; } -static TypeInfo hda_audio_duplex_info = { +static const TypeInfo hda_audio_duplex_info = { .name = "hda-duplex", .parent = TYPE_HDA_CODEC_DEVICE, .instance_size = sizeof(HDAAudioState), @@ -1081,7 +1081,7 @@ static void hda_audio_micro_class_init(ObjectClass *klass, void *data) dc->props = hda_audio_properties; } -static TypeInfo hda_audio_micro_info = { +static const TypeInfo hda_audio_micro_info = { .name = "hda-micro", .parent = TYPE_HDA_CODEC_DEVICE, .instance_size = sizeof(HDAAudioState), diff --git a/hw/heathrow_pic.c b/hw/heathrow_pic.c index b9ec8e7b4d..beb9661182 100644 --- a/hw/heathrow_pic.c +++ b/hw/heathrow_pic.c @@ -22,8 +22,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "ppc_mac.h" +#include "hw/hw.h" +#include "hw/ppc/mac.h" /* debug PIC */ //#define DEBUG_PIC diff --git a/hw/hid.c b/hw/hid.c index 0fee3b6ddd..28b34747ff 100644 --- a/hw/hid.c +++ b/hw/hid.c @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "hw/hw.h" #include "ui/console.h" #include "qemu/timer.h" -#include "hid.h" +#include "hw/hid.h" #define HID_USAGE_ERROR_ROLLOVER 0x01 #define HID_USAGE_POSTFAIL 0x02 @@ -71,12 +71,38 @@ static const uint8_t hid_usage_keys[0x100] = { bool hid_has_events(HIDState *hs) { - return hs->n > 0; + return hs->n > 0 || hs->idle_pending; } -void hid_set_next_idle(HIDState *hs, int64_t curtime) +static void hid_idle_timer(void *opaque) { - hs->next_idle_clock = curtime + (get_ticks_per_sec() * hs->idle * 4) / 1000; + HIDState *hs = opaque; + + hs->idle_pending = true; + hs->event(hs); +} + +static void hid_del_idle_timer(HIDState *hs) +{ + if (hs->idle_timer) { + qemu_del_timer(hs->idle_timer); + qemu_free_timer(hs->idle_timer); + hs->idle_timer = NULL; + } +} + +void hid_set_next_idle(HIDState *hs) +{ + if (hs->idle) { + uint64_t expire_time = qemu_get_clock_ns(vm_clock) + + get_ticks_per_sec() * hs->idle * 4 / 1000; + if (!hs->idle_timer) { + hs->idle_timer = qemu_new_timer_ns(vm_clock, hid_idle_timer, hs); + } + qemu_mod_timer_ns(hs->idle_timer, expire_time); + } else { + hid_del_idle_timer(hs); + } } static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons) @@ -232,6 +258,8 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) int index; HIDPointerEvent *e; + hs->idle_pending = false; + hid_pointer_activate(hs); /* When the buffer is empty, return the last event. Relative @@ -319,6 +347,8 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len) { + hs->idle_pending = false; + if (len < 2) { return 0; } @@ -377,6 +407,8 @@ void hid_reset(HIDState *hs) hs->n = 0; hs->protocol = 1; hs->idle = 0; + hs->idle_pending = false; + hid_del_idle_timer(hs); } void hid_free(HIDState *hs) @@ -390,6 +422,7 @@ void hid_free(HIDState *hs) qemu_remove_mouse_event_handler(hs->ptr.eh_entry); break; } + hid_del_idle_timer(hs); } void hid_init(HIDState *hs, int kind, HIDEventFunc event) @@ -412,9 +445,7 @@ static int hid_post_load(void *opaque, int version_id) { HIDState *s = opaque; - if (s->idle) { - hid_set_next_idle(s, qemu_get_clock_ns(vm_clock)); - } + hid_set_next_idle(s); return 0; } diff --git a/hw/hid.h b/hw/hid.h index 100b121663..56c71ed5ae 100644 --- a/hw/hid.h +++ b/hw/hid.h @@ -43,7 +43,8 @@ struct HIDState { int kind; int32_t protocol; uint8_t idle; - int64_t next_idle_clock; + bool idle_pending; + QEMUTimer *idle_timer; HIDEventFunc event; }; @@ -52,7 +53,7 @@ void hid_reset(HIDState *hs); void hid_free(HIDState *hs); bool hid_has_events(HIDState *hs); -void hid_set_next_idle(HIDState *hs, int64_t curtime); +void hid_set_next_idle(HIDState *hs); void hid_pointer_activate(HIDState *hs); int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len); int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len); diff --git a/hw/hpet.c b/hw/hpet.c index 78c0662dfc..6bfbf3a68c 100644 --- a/hw/hpet.c +++ b/hw/hpet.c @@ -24,14 +24,14 @@ * This driver attempts to emulate an HPET device in software. */ -#include "hw.h" -#include "pc.h" +#include "hw/hw.h" +#include "hw/pc.h" #include "ui/console.h" #include "qemu/timer.h" -#include "hpet_emul.h" -#include "sysbus.h" -#include "mc146818rtc.h" -#include "i8254.h" +#include "hw/hpet_emul.h" +#include "hw/sysbus.h" +#include "hw/mc146818rtc.h" +#include "hw/i8254.h" //#define HPET_DEBUG #ifdef HPET_DEBUG @@ -634,7 +634,7 @@ static const MemoryRegionOps hpet_ram_ops = { static void hpet_reset(DeviceState *d) { - HPETState *s = FROM_SYSBUS(HPETState, sysbus_from_qdev(d)); + HPETState *s = FROM_SYSBUS(HPETState, SYS_BUS_DEVICE(d)); int i; for (i = 0; i < s->num_timers; i++) { @@ -657,7 +657,7 @@ static void hpet_reset(DeviceState *d) s->hpet_offset = 0ULL; s->config = 0ULL; hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; - hpet_cfg.hpet[s->hpet_id].address = sysbus_from_qdev(d)->mmio[0].addr; + hpet_cfg.hpet[s->hpet_id].address = SYS_BUS_DEVICE(d)->mmio[0].addr; /* to document that the RTC lowers its output on reset as well */ s->rtc_irq_level = 0; @@ -745,7 +745,7 @@ static void hpet_device_class_init(ObjectClass *klass, void *data) dc->props = hpet_device_properties; } -static TypeInfo hpet_device_info = { +static const TypeInfo hpet_device_info = { .name = "hpet", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(HPETState), diff --git a/hw/hw.h b/hw/hw.h index dfced97bbc..1553e54aa7 100644 --- a/hw/hw.h +++ b/hw/hw.h @@ -9,7 +9,7 @@ #endif #include "exec/ioport.h" -#include "irq.h" +#include "hw/irq.h" #include "block/aio.h" #include "migration/qemu-file.h" #include "migration/vmstate.h" diff --git a/hw/i2c.c b/hw/i2c.c index 296bece119..ad361cc57f 100644 --- a/hw/i2c.c +++ b/hw/i2c.c @@ -7,7 +7,7 @@ * This code is licensed under the LGPL. */ -#include "i2c.h" +#include "hw/i2c.h" struct i2c_bus { @@ -92,7 +92,7 @@ int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv) QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) { DeviceState *qdev = kid->child; - I2CSlave *candidate = I2C_SLAVE_FROM_QDEV(qdev); + I2CSlave *candidate = I2C_SLAVE(qdev); if (candidate->address == address) { slave = candidate; break; @@ -204,7 +204,7 @@ const VMStateDescription vmstate_i2c_slave = { static int i2c_slave_qdev_init(DeviceState *dev) { - I2CSlave *s = I2C_SLAVE_FROM_QDEV(dev); + I2CSlave *s = I2C_SLAVE(dev); I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(s); return sc->init(s); @@ -228,7 +228,7 @@ static void i2c_slave_class_init(ObjectClass *klass, void *data) k->props = i2c_props; } -static TypeInfo i2c_slave_type_info = { +static const TypeInfo i2c_slave_type_info = { .name = TYPE_I2C_SLAVE, .parent = TYPE_DEVICE, .instance_size = sizeof(I2CSlave), diff --git a/hw/i2c.h b/hw/i2c.h index 0f5682b4b0..461392f374 100644 --- a/hw/i2c.h +++ b/hw/i2c.h @@ -1,7 +1,7 @@ #ifndef QEMU_I2C_H #define QEMU_I2C_H -#include "qdev.h" +#include "hw/qdev.h" /* The QEMU I2C implementation only supports simple transfers that complete immediately. It does not support slave devices that need to be able to @@ -59,7 +59,6 @@ void i2c_nack(i2c_bus *bus); int i2c_send(i2c_bus *bus, uint8_t data); int i2c_recv(i2c_bus *bus); -#define I2C_SLAVE_FROM_QDEV(dev) DO_UPCAST(I2CSlave, qdev, dev) #define FROM_I2C_SLAVE(type, dev) DO_UPCAST(type, i2c, dev) DeviceState *i2c_create_slave(i2c_bus *bus, const char *name, uint8_t addr); @@ -73,9 +72,6 @@ void *wm8750_dac_buffer(void *opaque, int samples); void wm8750_dac_commit(void *opaque); void wm8750_set_bclk_in(void *opaque, int new_hz); -/* tmp105.c */ -void tmp105_set(I2CSlave *i2c, int temp); - /* lm832x.c */ void lm832x_key_event(DeviceState *dev, int key, int state); diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index f9b8d5169f..0f78e36fdd 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -1,18 +1,24 @@ -obj-y += mc146818rtc.o pc.o -obj-y += apic_common.o apic.o kvmvapic.o +obj-y += mc146818rtc.o +obj-y += apic_common.o apic.o obj-y += sga.o ioapic_common.o ioapic.o piix_pci.o obj-y += vmport.o -obj-y += pci/pci-hotplug.o smbios.o wdt_ib700.o -obj-y += debugcon.o multiboot.o -obj-y += pc_piix.o +obj-y += pci/pci-hotplug.o wdt_ib700.o +obj-y += debugcon.o debugexit.o obj-y += pc_sysfw.o -obj-y += lpc_ich9.o q35.o pc_q35.o +obj-y += lpc_ich9.o q35.o obj-$(CONFIG_XEN) += xen_platform.o xen_apic.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o obj-y += kvm/ obj-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o +obj-y += pc-testdev.o obj-$(CONFIG_XBOX) += xbox.o xbox_pci.o acpi_xbox.o amd_smbus.o nv2a.o nv2a_vsh.o mcpx_apu.o mcpx_aci.o smbus_xbox_smc.o smbus_cx25871.o smbus_adm1032.o obj-y := $(addprefix ../,$(obj-y)) + +obj-y += multiboot.o smbios.o +obj-y += pc.o pc_piix.o pc_q35.o +obj-$(CONFIG_XEN) += xen_domainbuild.o xen_machine_pv.o + +obj-y += kvmvapic.o diff --git a/hw/kvmvapic.c b/hw/i386/kvmvapic.c similarity index 98% rename from hw/kvmvapic.c rename to hw/i386/kvmvapic.c index 81f4bcfdf6..c151c95c3e 100644 --- a/hw/kvmvapic.c +++ b/hw/i386/kvmvapic.c @@ -11,7 +11,7 @@ #include "sysemu/sysemu.h" #include "sysemu/cpus.h" #include "sysemu/kvm.h" -#include "apic_internal.h" +#include "hw/apic_internal.h" #define APIC_DEFAULT_ADDRESS 0xfee00000 @@ -382,8 +382,10 @@ static void patch_call(VAPICROMState *s, CPUX86State *env, target_ulong ip, cpu_memory_rw_debug(env, ip + 1, (void *)&offset, sizeof(offset), 1); } -static void patch_instruction(VAPICROMState *s, CPUX86State *env, target_ulong ip) +static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) { + CPUState *cs = CPU(cpu); + CPUX86State *env = &cpu->env; VAPICHandlers *handlers; uint8_t opcode[2]; uint32_t imm32; @@ -439,17 +441,18 @@ static void patch_instruction(VAPICROMState *s, CPUX86State *env, target_ulong i resume_all_vcpus(); if (!kvm_enabled()) { - env->current_tb = NULL; + cs->current_tb = NULL; tb_gen_code(env, current_pc, current_cs_base, current_flags, 1); cpu_resume_from_signal(env, NULL); } } -void vapic_report_tpr_access(DeviceState *dev, void *cpu, target_ulong ip, +void vapic_report_tpr_access(DeviceState *dev, CPUState *cs, target_ulong ip, TPRAccess access) { VAPICROMState *s = DO_UPCAST(VAPICROMState, busdev.qdev, dev); - CPUX86State *env = cpu; + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; cpu_synchronize_state(env); @@ -465,7 +468,7 @@ void vapic_report_tpr_access(DeviceState *dev, void *cpu, target_ulong ip, if (vapic_enable(s, env) < 0) { return; } - patch_instruction(s, env, ip); + patch_instruction(s, cpu, ip); } typedef struct VAPICEnableTPRReporting { @@ -804,7 +807,7 @@ static void vapic_class_init(ObjectClass *klass, void *data) sc->init = vapic_init; } -static TypeInfo vapic_type = { +static const TypeInfo vapic_type = { .name = "kvmvapic", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(VAPICROMState), diff --git a/hw/multiboot.c b/hw/i386/multiboot.c similarity index 99% rename from hw/multiboot.c rename to hw/i386/multiboot.c index c4ec2e34a7..3cb228f0ca 100644 --- a/hw/multiboot.c +++ b/hw/i386/multiboot.c @@ -22,10 +22,10 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "fw_cfg.h" -#include "multiboot.h" -#include "loader.h" +#include "hw/hw.h" +#include "hw/fw_cfg.h" +#include "hw/multiboot.h" +#include "hw/loader.h" #include "elf.h" #include "sysemu/sysemu.h" diff --git a/hw/pc.c b/hw/i386/pc.c similarity index 88% rename from hw/pc.c rename to hw/i386/pc.c index 71902e210b..ed7d9badb5 100644 --- a/hw/pc.c +++ b/hw/i386/pc.c @@ -21,29 +21,29 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "pc.h" -#include "serial.h" -#include "apic.h" -#include "fdc.h" -#include "ide.h" -#include "pci/pci.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/serial.h" +#include "hw/apic.h" +#include "hw/fdc.h" +#include "hw/ide.h" +#include "hw/pci/pci.h" #include "monitor/monitor.h" -#include "fw_cfg.h" -#include "hpet_emul.h" -#include "smbios.h" -#include "loader.h" +#include "hw/fw_cfg.h" +#include "hw/hpet_emul.h" +#include "hw/smbios.h" +#include "hw/loader.h" #include "elf.h" -#include "multiboot.h" -#include "mc146818rtc.h" -#include "i8254.h" -#include "pcspk.h" -#include "pci/msi.h" -#include "sysbus.h" +#include "hw/multiboot.h" +#include "hw/mc146818rtc.h" +#include "hw/i8254.h" +#include "hw/pcspk.h" +#include "hw/pci/msi.h" +#include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "kvm_i386.h" -#include "xen.h" +#include "hw/xen.h" #include "sysemu/blockdev.h" #include "hw/block-common.h" #include "ui/qemu-spice.h" @@ -103,6 +103,11 @@ static void ioport80_write(void *opaque, hwaddr addr, uint64_t data, { } +static uint64_t ioport80_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0xffffffffffffffffULL; +} + /* MSDOS compatibility mode FPU exception support */ static qemu_irq ferr_irq; @@ -123,6 +128,11 @@ static void ioportF0_write(void *opaque, hwaddr addr, uint64_t data, qemu_irq_lower(ferr_irq); } +static uint64_t ioportF0_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0xffffffffffffffffULL; +} + /* TSC handling */ uint64_t cpu_get_tsc(CPUX86State *env) { @@ -180,10 +190,12 @@ static void pic_irq_request(void *opaque, int irq, int level) env = env->next_cpu; } } else { - if (level) - cpu_interrupt(env, CPU_INTERRUPT_HARD); - else - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + CPUState *cs = CPU(x86_env_get_cpu(env)); + if (level) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } } } @@ -501,7 +513,7 @@ static void port92_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_port92_isa; } -static TypeInfo port92_info = { +static const TypeInfo port92_info = { .name = "port92", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(Port92State), @@ -517,39 +529,11 @@ type_init(port92_register_types) static void handle_a20_line_change(void *opaque, int irq, int level) { - CPUX86State *cpu = opaque; + X86CPU *cpu = opaque; /* XXX: send to all CPUs ? */ /* XXX: add logic to handle multiple A20 line sources */ - cpu_x86_set_a20(cpu, level); -} - -/***********************************************************/ -/* Bochs BIOS debug ports */ - -static void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val) -{ - static const char shutdown_str[8] = "Shutdown"; - static int shutdown_index = 0; - - switch(addr) { - case 0x8900: - /* same as Bochs power off */ - if (val == shutdown_str[shutdown_index]) { - shutdown_index++; - if (shutdown_index == 8) { - shutdown_index = 0; - qemu_system_shutdown_request(); - } - } else { - shutdown_index = 0; - } - break; - - case 0x501: - case 0x502: - exit((val << 1) | 1); - } + x86_cpu_set_a20(cpu, level); } int e820_add_entry(uint64_t address, uint64_t length, uint32_t type) @@ -569,13 +553,17 @@ int e820_add_entry(uint64_t address, uint64_t length, uint32_t type) return index; } -static const MemoryRegionPortio bochs_bios_portio_list[] = { - { 0x500, 1, 1, .write = bochs_bios_write, }, /* 0x500 */ - { 0x501, 1, 1, .write = bochs_bios_write, }, /* 0x501 */ - { 0x501, 2, 2, .write = bochs_bios_write, }, /* 0x501 */ - { 0x8900, 1, 1, .write = bochs_bios_write, }, /* 0x8900 */ - PORTIO_END_OF_LIST(), -}; +/* Calculates the limit to CPU APIC ID values + * + * This function returns the limit for the APIC ID value, so that all + * CPU APIC IDs are < pc_apic_id_limit(). + * + * This is used for FW_CFG_MAX_CPUS. See comments on bochs_bios_init(). + */ +static unsigned int pc_apic_id_limit(unsigned int max_cpus) +{ + return x86_cpu_apic_id_from_index(max_cpus - 1) + 1; +} static void *bochs_bios_init(void) { @@ -584,48 +572,60 @@ static void *bochs_bios_init(void) size_t smbios_len; uint64_t *numa_fw_cfg; int i, j; - PortioList *bochs_bios_port_list = g_new(PortioList, 1); - - portio_list_init(bochs_bios_port_list, bochs_bios_portio_list, - NULL, "bochs-bios"); - portio_list_add(bochs_bios_port_list, get_system_io(), 0x0); + unsigned int apic_id_limit = pc_apic_id_limit(max_cpus); fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0); - + /* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86: + * + * SeaBIOS needs FW_CFG_MAX_CPUS for CPU hotplug, but the CPU hotplug + * QEMU<->SeaBIOS interface is not based on the "CPU index", but on the APIC + * ID of hotplugged CPUs[1]. This means that FW_CFG_MAX_CPUS is not the + * "maximum number of CPUs", but the "limit to the APIC ID values SeaBIOS + * may see". + * + * So, this means we must not use max_cpus, here, but the maximum possible + * APIC ID value, plus one. + * + * [1] The only kind of "CPU identifier" used between SeaBIOS and QEMU is + * the APIC ID, not the "CPU index" + */ + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)apic_id_limit); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, (uint8_t *)acpi_tables, - acpi_tables_len); + fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, + acpi_tables, acpi_tables_len); fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override()); smbios_table = smbios_get_table(&smbios_len); if (smbios_table) fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, smbios_table, smbios_len); - fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, (uint8_t *)&e820_table, - sizeof(struct e820_table)); + fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, + &e820_table, sizeof(e820_table)); - fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, (uint8_t *)&hpet_cfg, - sizeof(struct hpet_fw_config)); + fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_cfg, sizeof(hpet_cfg)); /* allocate memory for the NUMA channel: one (64bit) word for the number * of nodes, one word for each VCPU->node and one word for each node to * hold the amount of memory. */ - numa_fw_cfg = g_malloc0((1 + max_cpus + nb_numa_nodes) * 8); + numa_fw_cfg = g_new0(uint64_t, 1 + apic_id_limit + nb_numa_nodes); numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes); for (i = 0; i < max_cpus; i++) { + unsigned int apic_id = x86_cpu_apic_id_from_index(i); + assert(apic_id < apic_id_limit); for (j = 0; j < nb_numa_nodes; j++) { if (test_bit(i, node_cpumask[j])) { - numa_fw_cfg[i + 1] = cpu_to_le64(j); + numa_fw_cfg[apic_id + 1] = cpu_to_le64(j); break; } } } for (i = 0; i < nb_numa_nodes; i++) { - numa_fw_cfg[max_cpus + 1 + i] = cpu_to_le64(node_mem[i]); + numa_fw_cfg[apic_id_limit + 1 + i] = cpu_to_le64(node_mem[i]); } - fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, (uint8_t *)numa_fw_cfg, - (1 + max_cpus + nb_numa_nodes) * 8); + fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, numa_fw_cfg, + (1 + apic_id_limit + nb_numa_nodes) * + sizeof(*numa_fw_cfg)); return fw_cfg; } @@ -724,9 +724,7 @@ static void load_linux(void *fw_cfg, fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline)+1); - fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA, - (uint8_t*)strdup(kernel_cmdline), - strlen(kernel_cmdline)+1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); if (protocol >= 0x202) { stl_p(header+0x228, cmdline_addr); @@ -858,10 +856,10 @@ DeviceState *cpu_get_current_apic(void) void pc_acpi_smi_interrupt(void *opaque, int irq, int level) { - CPUX86State *s = opaque; + X86CPU *cpu = opaque; if (level) { - cpu_interrupt(s, CPU_INTERRUPT_SMI); + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_SMI); } } @@ -880,12 +878,34 @@ void pc_cpus_init(const char *cpu_model) for (i = 0; i < smp_cpus; i++) { if (!cpu_x86_init(cpu_model)) { - fprintf(stderr, "Unable to find x86 CPU definition\n"); exit(1); } } } +void pc_acpi_init(const char *default_dsdt) +{ + char *filename = NULL, *arg = NULL; + + if (acpi_tables != NULL) { + /* manually set via -acpitable, leave it alone */ + return; + } + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, default_dsdt); + if (filename == NULL) { + fprintf(stderr, "WARNING: failed to find %s\n", default_dsdt); + return; + } + + arg = g_strdup_printf("file=%s", filename); + if (acpi_table_add(arg) != 0) { + fprintf(stderr, "WARNING: failed to load %s\n", filename); + } + g_free(arg); + g_free(filename); +} + void *pc_memory_init(MemoryRegion *system_memory, const char *kernel_filename, const char *kernel_cmdline, @@ -978,6 +998,7 @@ static void cpu_request_exit(void *opaque, int irq, int level) static const MemoryRegionOps ioport80_io_ops = { .write = ioport80_write, + .read = ioport80_read, .endianness = DEVICE_NATIVE_ENDIAN, .impl = { .min_access_size = 1, @@ -987,6 +1008,7 @@ static const MemoryRegionOps ioport80_io_ops = { static const MemoryRegionOps ioportF0_io_ops = { .write = ioportF0_write, + .read = ioportF0_read, .endianness = DEVICE_NATIVE_ENDIAN, .impl = { .min_access_size = 1, @@ -1028,7 +1050,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, if (hpet) { for (i = 0; i < GSI_NUM_PINS; i++) { - sysbus_connect_irq(sysbus_from_qdev(hpet), i, gsi[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(hpet), i, gsi[i]); } pit_isa_irq = -1; pit_alt_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_PIT_INT); @@ -1064,7 +1086,8 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi, } } - a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2); + a20_line = qemu_allocate_irqs(handle_a20_line_change, + x86_env_get_cpu(first_cpu), 2); i8042 = isa_create_simple(isa_bus, "i8042"); i8042_setup_a20_line(i8042, &a20_line[0]); if (!no_vmport) { @@ -1131,7 +1154,7 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) "ioapic", OBJECT(dev), NULL); } qdev_init_nofail(dev); - d = sysbus_from_qdev(dev); + d = SYS_BUS_DEVICE(dev); sysbus_mmio_map(d, 0, 0xfec00000); for (i = 0; i < IOAPIC_NUM_PINS; i++) { diff --git a/hw/pc_piix.c b/hw/i386/pc_piix.c similarity index 89% rename from hw/pc_piix.c rename to hw/i386/pc_piix.c index 99747a774c..0abc9f11e3 100644 --- a/hw/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -24,23 +24,23 @@ #include -#include "hw.h" -#include "pc.h" -#include "apic.h" -#include "pci/pci.h" -#include "pci/pci_ids.h" -#include "usb.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/apic.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_ids.h" +#include "hw/usb.h" #include "net/net.h" -#include "boards.h" -#include "ide.h" +#include "hw/boards.h" +#include "hw/ide.h" #include "sysemu/kvm.h" -#include "kvm/clock.h" +#include "hw/kvm/clock.h" #include "sysemu/sysemu.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "sysemu/arch_init.h" #include "sysemu/blockdev.h" -#include "smbus.h" -#include "xen.h" +#include "hw/smbus.h" +#include "hw/xen.h" #include "exec/memory.h" #include "exec/address-spaces.h" #include "cpu.h" @@ -87,6 +87,7 @@ static void pc_init1(MemoryRegion *system_memory, void *fw_cfg = NULL; pc_cpus_init(cpu_model); + pc_acpi_init("acpi-dsdt.aml"); if (kvmclock_enabled) { kvmclock_create(); @@ -204,7 +205,8 @@ static void pc_init1(MemoryRegion *system_memory, if (pci_enabled && acpi_enabled) { i2c_bus *smbus; - smi_irq = qemu_allocate_irqs(pc_acpi_smi_interrupt, first_cpu, 1); + smi_irq = qemu_allocate_irqs(pc_acpi_smi_interrupt, + x86_env_get_cpu(first_cpu), 1); /* TODO: Populate SPD eeprom data. */ smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, gsi[9], *smi_irq, @@ -234,10 +236,18 @@ static void pc_init_pci(QEMUMachineInitArgs *args) static void pc_init_pci_1_3(QEMUMachineInitArgs *args) { - enable_kvm_pv_eoi(); + enable_compat_apic_id_mode(); pc_init_pci(args); } +/* PC machine init function for pc-0.14 to pc-1.2 */ +static void pc_init_pci_1_2(QEMUMachineInitArgs *args) +{ + disable_kvm_pv_eoi(); + pc_init_pci_1_3(args); +} + +/* PC init function for pc-0.10 to pc-0.13, and reused by xenfv */ static void pc_init_pci_no_kvmclock(QEMUMachineInitArgs *args) { ram_addr_t ram_size = args->ram_size; @@ -246,6 +256,8 @@ static void pc_init_pci_no_kvmclock(QEMUMachineInitArgs *args) const char *kernel_cmdline = args->kernel_cmdline; const char *initrd_filename = args->initrd_filename; const char *boot_device = args->boot_device; + disable_kvm_pv_eoi(); + enable_compat_apic_id_mode(); pc_init1(get_system_memory(), get_system_io(), ram_size, boot_device, @@ -263,6 +275,8 @@ static void pc_init_isa(QEMUMachineInitArgs *args) const char *boot_device = args->boot_device; if (cpu_model == NULL) cpu_model = "486"; + disable_kvm_pv_eoi(); + enable_compat_apic_id_mode(); pc_init1(get_system_memory(), get_system_io(), ram_size, boot_device, @@ -281,20 +295,46 @@ static void pc_xen_hvm_init(QEMUMachineInitArgs *args) } #endif -static QEMUMachine pc_machine_v1_4 = { - .name = "pc-1.4", +static QEMUMachine pc_i440fx_machine_v1_5 = { + .name = "pc-i440fx-1.5", .alias = "pc", - .desc = "Standard PC", - .init = pc_init_pci_1_3, + .desc = "Standard PC (i440FX + PIIX, 1996)", + .init = pc_init_pci, .max_cpus = 255, .is_default = 1, + DEFAULT_MACHINE_OPTIONS, +}; + +static QEMUMachine pc_i440fx_machine_v1_4 = { + .name = "pc-i440fx-1.4", + .desc = "Standard PC (i440FX + PIIX, 1996)", + .init = pc_init_pci, + .max_cpus = 255, + .compat_props = (GlobalProperty[]) { + PC_COMPAT_1_4, + { /* end of list */ } + }, + DEFAULT_MACHINE_OPTIONS, }; #define PC_COMPAT_1_3 \ + PC_COMPAT_1_4, \ {\ .driver = "usb-tablet",\ .property = "usb_version",\ .value = stringify(1),\ + },{\ + .driver = "virtio-net-pci",\ + .property = "ctrl_mac_addr",\ + .value = "off", \ + },{ \ + .driver = "virtio-net-pci", \ + .property = "mq", \ + .value = "off", \ + }, {\ + .driver = "e1000",\ + .property = "autonegotiation",\ + .value = "off",\ } static QEMUMachine pc_machine_v1_3 = { @@ -306,6 +346,7 @@ static QEMUMachine pc_machine_v1_3 = { PC_COMPAT_1_3, { /* end of list */ } }, + DEFAULT_MACHINE_OPTIONS, }; #define PC_COMPAT_1_2 \ @@ -339,12 +380,13 @@ static QEMUMachine pc_machine_v1_3 = { static QEMUMachine pc_machine_v1_2 = { .name = "pc-1.2", .desc = "Standard PC", - .init = pc_init_pci, + .init = pc_init_pci_1_2, .max_cpus = 255, .compat_props = (GlobalProperty[]) { PC_COMPAT_1_2, { /* end of list */ } }, + DEFAULT_MACHINE_OPTIONS, }; #define PC_COMPAT_1_1 \ @@ -382,12 +424,13 @@ static QEMUMachine pc_machine_v1_2 = { static QEMUMachine pc_machine_v1_1 = { .name = "pc-1.1", .desc = "Standard PC", - .init = pc_init_pci, + .init = pc_init_pci_1_2, .max_cpus = 255, .compat_props = (GlobalProperty[]) { PC_COMPAT_1_1, { /* end of list */ } }, + DEFAULT_MACHINE_OPTIONS, }; #define PC_COMPAT_1_0 \ @@ -417,13 +460,14 @@ static QEMUMachine pc_machine_v1_1 = { static QEMUMachine pc_machine_v1_0 = { .name = "pc-1.0", .desc = "Standard PC", - .init = pc_init_pci, + .init = pc_init_pci_1_2, .max_cpus = 255, .compat_props = (GlobalProperty[]) { PC_COMPAT_1_0, { /* end of list */ } }, .hw_version = "1.0", + DEFAULT_MACHINE_OPTIONS, }; #define PC_COMPAT_0_15 \ @@ -432,13 +476,14 @@ static QEMUMachine pc_machine_v1_0 = { static QEMUMachine pc_machine_v0_15 = { .name = "pc-0.15", .desc = "Standard PC", - .init = pc_init_pci, + .init = pc_init_pci_1_2, .max_cpus = 255, .compat_props = (GlobalProperty[]) { PC_COMPAT_0_15, { /* end of list */ } }, .hw_version = "0.15", + DEFAULT_MACHINE_OPTIONS, }; #define PC_COMPAT_0_14 \ @@ -464,7 +509,7 @@ static QEMUMachine pc_machine_v0_15 = { static QEMUMachine pc_machine_v0_14 = { .name = "pc-0.14", .desc = "Standard PC", - .init = pc_init_pci, + .init = pc_init_pci_1_2, .max_cpus = 255, .compat_props = (GlobalProperty[]) { PC_COMPAT_0_14, @@ -480,6 +525,7 @@ static QEMUMachine pc_machine_v0_14 = { { /* end of list */ } }, .hw_version = "0.14", + DEFAULT_MACHINE_OPTIONS, }; #define PC_COMPAT_0_13 \ @@ -517,6 +563,7 @@ static QEMUMachine pc_machine_v0_13 = { { /* end of list */ } }, .hw_version = "0.13", + DEFAULT_MACHINE_OPTIONS, }; #define PC_COMPAT_0_12 \ @@ -550,6 +597,7 @@ static QEMUMachine pc_machine_v0_12 = { { /* end of list */ } }, .hw_version = "0.12", + DEFAULT_MACHINE_OPTIONS, }; #define PC_COMPAT_0_11 \ @@ -583,6 +631,7 @@ static QEMUMachine pc_machine_v0_11 = { { /* end of list */ } }, .hw_version = "0.11", + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine pc_machine_v0_10 = { @@ -616,6 +665,7 @@ static QEMUMachine pc_machine_v0_10 = { { /* end of list */ } }, .hw_version = "0.10", + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine isapc_machine = { @@ -631,6 +681,7 @@ static QEMUMachine isapc_machine = { }, { /* end of list */ } }, + DEFAULT_MACHINE_OPTIONS, }; #ifdef CONFIG_XEN @@ -640,12 +691,14 @@ static QEMUMachine xenfv_machine = { .init = pc_xen_hvm_init, .max_cpus = HVM_MAX_VCPUS, .default_machine_opts = "accel=xen", + DEFAULT_MACHINE_OPTIONS, }; #endif static void pc_machine_init(void) { - qemu_register_machine(&pc_machine_v1_4); + qemu_register_machine(&pc_i440fx_machine_v1_5); + qemu_register_machine(&pc_i440fx_machine_v1_4); qemu_register_machine(&pc_machine_v1_3); qemu_register_machine(&pc_machine_v1_2); qemu_register_machine(&pc_machine_v1_1); diff --git a/hw/pc_q35.c b/hw/i386/pc_q35.c similarity index 89% rename from hw/pc_q35.c rename to hw/i386/pc_q35.c index c7262d6315..4f5f347309 100644 --- a/hw/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -27,17 +27,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "hw/hw.h" #include "sysemu/arch_init.h" -#include "smbus.h" -#include "boards.h" -#include "mc146818rtc.h" -#include "xen.h" +#include "hw/smbus.h" +#include "hw/boards.h" +#include "hw/mc146818rtc.h" +#include "hw/xen.h" #include "sysemu/kvm.h" -#include "kvm/clock.h" -#include "q35.h" +#include "hw/kvm/clock.h" +#include "hw/q35.h" #include "exec/address-spaces.h" -#include "ich9.h" +#include "hw/ich9.h" #include "hw/ide/pci.h" #include "hw/ide/ahci.h" #include "hw/usb.h" @@ -87,6 +87,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args) qemu_irq *cmos_s3; pc_cpus_init(cpu_model); + pc_acpi_init("q35-acpi-dsdt.aml"); kvmclock_create(); @@ -146,6 +147,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args) ich9_lpc->ioapic = gsi_state->ioapic_irq; pci_bus_irqs(host_bus, ich9_lpc_set_irq, ich9_lpc_map_irq, ich9_lpc, ICH9_LPC_NB_PIRQS); + pci_bus_set_route_irq_fn(host_bus, ich9_route_intx_pin_to_irq); isa_bus = ich9_lpc->isa_bus; /*end early*/ @@ -207,17 +209,31 @@ static void pc_q35_init(QEMUMachineInitArgs *args) } } -static QEMUMachine pc_q35_machine = { - .name = "q35-next", +static QEMUMachine pc_q35_machine_v1_5 = { + .name = "pc-q35-1.5", .alias = "q35", - .desc = "Q35 chipset PC", + .desc = "Standard PC (Q35 + ICH9, 2009)", .init = pc_q35_init, .max_cpus = 255, + DEFAULT_MACHINE_OPTIONS, +}; + +static QEMUMachine pc_q35_machine_v1_4 = { + .name = "pc-q35-1.4", + .desc = "Standard PC (Q35 + ICH9, 2009)", + .init = pc_q35_init, + .max_cpus = 255, + .compat_props = (GlobalProperty[]) { + PC_COMPAT_1_4, + { /* end of list */ } + }, + DEFAULT_MACHINE_OPTIONS, }; static void pc_q35_machine_init(void) { - qemu_register_machine(&pc_q35_machine); + qemu_register_machine(&pc_q35_machine_v1_5); + qemu_register_machine(&pc_q35_machine_v1_4); } machine_init(pc_q35_machine_init); diff --git a/hw/smbios.c b/hw/i386/smbios.c similarity index 99% rename from hw/smbios.c rename to hw/i386/smbios.c index a7b8bfc383..672ee9b0e7 100644 --- a/hw/smbios.c +++ b/hw/i386/smbios.c @@ -14,8 +14,8 @@ */ #include "sysemu/sysemu.h" -#include "smbios.h" -#include "loader.h" +#include "hw/smbios.h" +#include "hw/loader.h" /* * Structures shared with the BIOS diff --git a/hw/xen_domainbuild.c b/hw/i386/xen_domainbuild.c similarity index 99% rename from hw/xen_domainbuild.c rename to hw/i386/xen_domainbuild.c index a4272f0680..d477061545 100644 --- a/hw/xen_domainbuild.c +++ b/hw/i386/xen_domainbuild.c @@ -1,6 +1,6 @@ #include -#include "xen_backend.h" -#include "xen_domainbuild.h" +#include "hw/xen_backend.h" +#include "hw/xen_domainbuild.h" #include "qemu/timer.h" #include "qemu/log.h" diff --git a/hw/xen_machine_pv.c b/hw/i386/xen_machine_pv.c similarity index 94% rename from hw/xen_machine_pv.c rename to hw/i386/xen_machine_pv.c index 9feecd5a27..37ba34e5a9 100644 --- a/hw/xen_machine_pv.c +++ b/hw/i386/xen_machine_pv.c @@ -22,11 +22,11 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "pc.h" -#include "boards.h" -#include "xen_backend.h" -#include "xen_domainbuild.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/boards.h" +#include "hw/xen_backend.h" +#include "hw/xen_domainbuild.h" #include "sysemu/blockdev.h" static void xen_init_pv(QEMUMachineInitArgs *args) @@ -36,7 +36,7 @@ static void xen_init_pv(QEMUMachineInitArgs *args) const char *kernel_cmdline = args->kernel_cmdline; const char *initrd_filename = args->initrd_filename; X86CPU *cpu; - CPUX86State *env; + CPUState *cs; DriveInfo *dinfo; int i; @@ -49,8 +49,8 @@ static void xen_init_pv(QEMUMachineInitArgs *args) #endif } cpu = cpu_x86_init(cpu_model); - env = &cpu->env; - env->halted = 1; + cs = CPU(cpu); + cs->halted = 1; /* Initialize backend core & drivers */ if (xen_be_init() != 0) { @@ -115,6 +115,7 @@ static QEMUMachine xenpv_machine = { .init = xen_init_pv, .max_cpus = 1, .default_machine_opts = "accel=xen", + DEFAULT_MACHINE_OPTIONS, }; static void xenpv_machine_init(void) diff --git a/hw/i82374.c b/hw/i82374.c index 4a922c3f4e..22115e4fcf 100644 --- a/hw/i82374.c +++ b/hw/i82374.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ -#include "isa.h" +#include "hw/isa.h" //#define DEBUG_I82374 @@ -153,7 +153,7 @@ static void i82374_class_init(ObjectClass *klass, void *data) dc->props = i82374_properties; } -static TypeInfo i82374_isa_info = { +static const TypeInfo i82374_isa_info = { .name = "i82374", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISAi82374State), diff --git a/hw/i82378.c b/hw/i82378.c index c6b0b5ec55..6f8c48b9ae 100644 --- a/hw/i82378.c +++ b/hw/i82378.c @@ -17,10 +17,10 @@ * License along with this library; if not, see . */ -#include "pci/pci.h" -#include "pc.h" -#include "i8254.h" -#include "pcspk.h" +#include "hw/pci/pci.h" +#include "hw/pc.h" +#include "hw/i8254.h" +#include "hw/pcspk.h" //#define DEBUG_I82378 @@ -262,7 +262,7 @@ static void pci_i82378_class_init(ObjectClass *klass, void *data) dc->props = i82378_properties; } -static TypeInfo pci_i82378_info = { +static const TypeInfo pci_i82378_info = { .name = "i82378", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIi82378State), diff --git a/hw/i8254.c b/hw/i8254.c index 7c2aa6238d..67bfc6a806 100644 --- a/hw/i8254.c +++ b/hw/i8254.c @@ -21,12 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "pc.h" -#include "isa.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/isa.h" #include "qemu/timer.h" -#include "i8254.h" -#include "i8254_internal.h" +#include "hw/i8254.h" +#include "hw/i8254_internal.h" //#define DEBUG_PIT @@ -347,7 +347,7 @@ static void pit_class_initfn(ObjectClass *klass, void *data) dc->props = pit_properties; } -static TypeInfo pit_info = { +static const TypeInfo pit_info = { .name = "isa-pit", .parent = TYPE_PIT_COMMON, .instance_size = sizeof(PITCommonState), diff --git a/hw/i8254.h b/hw/i8254.h index ba6b598a99..7d4432e722 100644 --- a/hw/i8254.h +++ b/hw/i8254.h @@ -25,8 +25,8 @@ #ifndef HW_I8254_H #define HW_I8254_H -#include "hw.h" -#include "isa.h" +#include "hw/hw.h" +#include "hw/isa.h" #define PIT_FREQ 1193182 diff --git a/hw/i8254_common.c b/hw/i8254_common.c index 08ab8d14bd..c6c0c80c24 100644 --- a/hw/i8254_common.c +++ b/hw/i8254_common.c @@ -22,12 +22,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "pc.h" -#include "isa.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/isa.h" #include "qemu/timer.h" -#include "i8254.h" -#include "i8254_internal.h" +#include "hw/i8254.h" +#include "hw/i8254_internal.h" /* val must be 0 or 1 */ void pit_set_gate(ISADevice *dev, int channel, int val) @@ -294,7 +294,7 @@ static void pit_common_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo pit_common_type = { +static const TypeInfo pit_common_type = { .name = TYPE_PIT_COMMON, .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(PITCommonState), diff --git a/hw/i8254_internal.h b/hw/i8254_internal.h index 686f0c2ba9..30d5b1b950 100644 --- a/hw/i8254_internal.h +++ b/hw/i8254_internal.h @@ -25,9 +25,9 @@ #ifndef QEMU_I8254_INTERNAL_H #define QEMU_I8254_INTERNAL_H -#include "hw.h" -#include "pc.h" -#include "isa.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/isa.h" typedef struct PITChannelState { int count; /* can be 65536 */ diff --git a/hw/i8259.c b/hw/i8259.c index 8fc6339250..1d8275232a 100644 --- a/hw/i8259.c +++ b/hw/i8259.c @@ -21,12 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "pc.h" -#include "isa.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/isa.h" #include "monitor/monitor.h" #include "qemu/timer.h" -#include "i8259_internal.h" +#include "hw/i8259_internal.h" /* debug PIC */ //#define DEBUG_PIC @@ -407,7 +407,7 @@ static void pic_init(PICCommonState *s) qdev_init_gpio_in(&s->dev.qdev, pic_set_irq, 8); } -void pic_info(Monitor *mon) +void pic_info(Monitor *mon, const QDict *qdict) { int i; PICCommonState *s; @@ -425,7 +425,7 @@ void pic_info(Monitor *mon) } } -void irq_info(Monitor *mon) +void irq_info(Monitor *mon, const QDict *qdict) { #ifndef DEBUG_IRQ_COUNT monitor_printf(mon, "irq statistic code not compiled.\n"); @@ -481,7 +481,7 @@ static void i8259_class_init(ObjectClass *klass, void *data) dc->reset = pic_reset; } -static TypeInfo i8259_info = { +static const TypeInfo i8259_info = { .name = "isa-i8259", .instance_size = sizeof(PICCommonState), .parent = TYPE_PIC_COMMON, diff --git a/hw/i8259_common.c b/hw/i8259_common.c index ab3d98b2a1..98052db1fa 100644 --- a/hw/i8259_common.c +++ b/hw/i8259_common.c @@ -22,8 +22,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "pc.h" -#include "i8259_internal.h" +#include "hw/pc.h" +#include "hw/i8259_internal.h" void pic_reset_common(PICCommonState *s) { @@ -144,7 +144,7 @@ static void pic_common_class_init(ObjectClass *klass, void *data) ic->init = pic_init_common; } -static TypeInfo pic_common_type = { +static const TypeInfo pic_common_type = { .name = TYPE_PIC_COMMON, .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(PICCommonState), diff --git a/hw/i8259_internal.h b/hw/i8259_internal.h index 8785b1da3f..2813ec1baa 100644 --- a/hw/i8259_internal.h +++ b/hw/i8259_internal.h @@ -25,9 +25,9 @@ #ifndef QEMU_I8259_INTERNAL_H #define QEMU_I8259_INTERNAL_H -#include "hw.h" -#include "pc.h" -#include "isa.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/isa.h" typedef struct PICCommonState PICCommonState; diff --git a/hw/i82801b11.c b/hw/i82801b11.c index 3dc10000a4..992095c80f 100644 --- a/hw/i82801b11.c +++ b/hw/i82801b11.c @@ -41,8 +41,8 @@ * License along with this library; if not, see */ -#include "pci/pci.h" -#include "ich9.h" +#include "hw/pci/pci.h" +#include "hw/ich9.h" /*****************************************************************************/ diff --git a/hw/ich9.h b/hw/ich9.h index b8d8e6d3df..e7d2df7280 100644 --- a/hw/ich9.h +++ b/hw/ich9.h @@ -1,23 +1,24 @@ #ifndef HW_ICH9_H #define HW_ICH9_H -#include "hw.h" +#include "hw/hw.h" #include "qemu/range.h" -#include "isa.h" -#include "sysbus.h" -#include "pc.h" -#include "apm.h" -#include "ioapic.h" -#include "pci/pci.h" -#include "pci/pcie_host.h" -#include "pci/pci_bridge.h" -#include "acpi.h" -#include "acpi_ich9.h" -#include "pam.h" -#include "pci/pci_bus.h" +#include "hw/isa.h" +#include "hw/sysbus.h" +#include "hw/pc.h" +#include "hw/apm.h" +#include "hw/ioapic.h" +#include "hw/pci/pci.h" +#include "hw/pci/pcie_host.h" +#include "hw/pci/pci_bridge.h" +#include "hw/acpi.h" +#include "hw/acpi_ich9.h" +#include "hw/pam.h" +#include "hw/pci/pci_bus.h" void ich9_lpc_set_irq(void *opaque, int irq_num, int level); int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx); +PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin); void ich9_lpc_pm_init(PCIDevice *pci_lpc, qemu_irq cmos_s3); PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus); i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base); @@ -48,6 +49,15 @@ typedef struct ICH9LPCState { /* 10.1 Chipset Configuration registers(Memory Space) which is pointed by RCBA */ uint8_t chip_config[ICH9_CC_SIZE]; + + /* + * 13.7.5 RST_CNT---Reset Control Register (LPC I/F---D31:F0) + * + * register contents and IO memory region + */ + uint8_t rst_cnt; + MemoryRegion rst_cnt_mem; + /* isa bus */ ISABus *isa_bus; MemoryRegion rbca_mem; @@ -102,6 +112,8 @@ typedef struct ICH9LPCState { #define ICH9_D2P_A2_REVISION 0x92 +/* D31:F0 LPC Processor Interface */ +#define ICH9_RST_CNT_IOPORT 0xCF9 /* D31:F1 LPC controller */ #define ICH9_A2_LPC "ICH9 A2 LPC" diff --git a/hw/ide.h b/hw/ide.h index 7e23cda8e0..35444a39f9 100644 --- a/hw/ide.h +++ b/hw/ide.h @@ -1,8 +1,8 @@ #ifndef HW_IDE_H #define HW_IDE_H -#include "isa.h" -#include "pci/pci.h" +#include "hw/isa.h" +#include "hw/pci/pci.h" #include "exec/memory.h" #define MAX_IDE_DEVS 2 @@ -19,15 +19,8 @@ PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); -/* ide-macio.c */ -MemoryRegion *pmac_ide_init (DriveInfo **hd_table, qemu_irq irq, - void *dbdma, int channel, qemu_irq dma_irq); - /* ide-mmio.c */ -void mmio_ide_init (hwaddr membase, hwaddr membase2, - MemoryRegion *address_space, - qemu_irq irq, int shift, - DriveInfo *hd0, DriveInfo *hd1); +void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1); int ide_get_geometry(BusState *bus, int unit, int16_t *cyls, int8_t *heads, int8_t *secs); diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index d0724499c7..ad0094f532 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -241,7 +241,7 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) if ((pr->cmd & PORT_CMD_FIS_ON) && !s->dev[port].init_d2h_sent) { ahci_init_d2h(&s->dev[port]); - s->dev[port].init_d2h_sent = 1; + s->dev[port].init_d2h_sent = true; } check_cmd(s, port); @@ -494,7 +494,7 @@ static void ahci_reset_port(AHCIState *s, int port) pr->scr_err = 0; pr->scr_act = 0; d->busy_slot = -1; - d->init_d2h_sent = 0; + d->init_d2h_sent = false; ide_state = &s->dev[port].port.ifs[0]; if (!ide_state->bs) { @@ -946,7 +946,7 @@ static int handle_cmd(AHCIState *s, int port, int slot) ide_state->hcyl = 0xeb; debug_print_fis(ide_state->io_buffer, 0x10); ide_state->feature = IDE_FEATURE_DMA; - s->dev[port].done_atapi_packet = 0; + s->dev[port].done_atapi_packet = false; /* XXX send PIO setup FIS */ } @@ -991,7 +991,7 @@ static int ahci_start_transfer(IDEDMA *dma) if (is_atapi && !ad->done_atapi_packet) { /* already prepopulated iobuffer */ - ad->done_atapi_packet = 1; + ad->done_atapi_packet = true; goto out; } @@ -1035,11 +1035,10 @@ out: static void ahci_start_dma(IDEDMA *dma, IDEState *s, BlockDriverCompletionFunc *dma_cb) { +#ifdef DEBUG_AHCI AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); - +#endif DPRINTF(ad->port_no, "\n"); - ad->dma_cb = dma_cb; - ad->dma_status |= BM_STATUS_DMAING; s->io_buffer_offset = 0; dma_cb(s, 0); } @@ -1095,7 +1094,6 @@ static int ahci_dma_set_unit(IDEDMA *dma, int unit) static int ahci_dma_add_status(IDEDMA *dma, int status) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); - ad->dma_status |= status; DPRINTF(ad->port_no, "set status: %x\n", status); if (status & BM_STATUS_INT) { @@ -1114,8 +1112,6 @@ static int ahci_dma_set_inactive(IDEDMA *dma) /* update d2h status */ ahci_write_fis_d2h(ad, NULL); - ad->dma_cb = NULL; - if (!ad->check_bh) { /* maybe we still have something to process, check later */ ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad); @@ -1203,6 +1199,82 @@ void ahci_reset(AHCIState *s) } } +static const VMStateDescription vmstate_ahci_device = { + .name = "ahci port", + .version_id = 1, + .fields = (VMStateField []) { + VMSTATE_IDE_BUS(port, AHCIDevice), + VMSTATE_UINT32(port_state, AHCIDevice), + VMSTATE_UINT32(finished, AHCIDevice), + VMSTATE_UINT32(port_regs.lst_addr, AHCIDevice), + VMSTATE_UINT32(port_regs.lst_addr_hi, AHCIDevice), + VMSTATE_UINT32(port_regs.fis_addr, AHCIDevice), + VMSTATE_UINT32(port_regs.fis_addr_hi, AHCIDevice), + VMSTATE_UINT32(port_regs.irq_stat, AHCIDevice), + VMSTATE_UINT32(port_regs.irq_mask, AHCIDevice), + VMSTATE_UINT32(port_regs.cmd, AHCIDevice), + VMSTATE_UINT32(port_regs.tfdata, AHCIDevice), + VMSTATE_UINT32(port_regs.sig, AHCIDevice), + VMSTATE_UINT32(port_regs.scr_stat, AHCIDevice), + VMSTATE_UINT32(port_regs.scr_ctl, AHCIDevice), + VMSTATE_UINT32(port_regs.scr_err, AHCIDevice), + VMSTATE_UINT32(port_regs.scr_act, AHCIDevice), + VMSTATE_UINT32(port_regs.cmd_issue, AHCIDevice), + VMSTATE_BOOL(done_atapi_packet, AHCIDevice), + VMSTATE_INT32(busy_slot, AHCIDevice), + VMSTATE_BOOL(init_d2h_sent, AHCIDevice), + VMSTATE_END_OF_LIST() + }, +}; + +static int ahci_state_post_load(void *opaque, int version_id) +{ + int i; + struct AHCIDevice *ad; + AHCIState *s = opaque; + + for (i = 0; i < s->ports; i++) { + ad = &s->dev[i]; + AHCIPortRegs *pr = &ad->port_regs; + + map_page(&ad->lst, + ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); + map_page(&ad->res_fis, + ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); + /* + * All pending i/o should be flushed out on a migrate. However, + * we might not have cleared the busy_slot since this is done + * in a bh. Also, issue i/o against any slots that are pending. + */ + if ((ad->busy_slot != -1) && + !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) { + pr->cmd_issue &= ~(1 << ad->busy_slot); + ad->busy_slot = -1; + } + check_cmd(s, i); + } + + return 0; +} + +const VMStateDescription vmstate_ahci = { + .name = "ahci", + .version_id = 1, + .post_load = ahci_state_post_load, + .fields = (VMStateField []) { + VMSTATE_STRUCT_VARRAY_POINTER_INT32(dev, AHCIState, ports, + vmstate_ahci_device, AHCIDevice), + VMSTATE_UINT32(control_regs.cap, AHCIState), + VMSTATE_UINT32(control_regs.ghc, AHCIState), + VMSTATE_UINT32(control_regs.irqstatus, AHCIState), + VMSTATE_UINT32(control_regs.impl, AHCIState), + VMSTATE_UINT32(control_regs.version, AHCIState), + VMSTATE_UINT32(idp_index, AHCIState), + VMSTATE_INT32(ports, AHCIState), + VMSTATE_END_OF_LIST() + }, +}; + typedef struct SysbusAHCIState { SysBusDevice busdev; AHCIState ahci; @@ -1211,7 +1283,11 @@ typedef struct SysbusAHCIState { static const VMStateDescription vmstate_sysbus_ahci = { .name = "sysbus-ahci", - .unmigratable = 1, + .unmigratable = 1, /* Still buggy under I/O load */ + .fields = (VMStateField []) { + VMSTATE_AHCI(ahci, AHCIPCIState), + VMSTATE_END_OF_LIST() + }, }; static void sysbus_ahci_reset(DeviceState *dev) @@ -1247,7 +1323,7 @@ static void sysbus_ahci_class_init(ObjectClass *klass, void *data) dc->reset = sysbus_ahci_reset; } -static TypeInfo sysbus_ahci_info = { +static const TypeInfo sysbus_ahci_info = { .name = "sysbus-ahci", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysbusAHCIState), diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index 1200a56ada..85f37fe99d 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -281,11 +281,9 @@ struct AHCIDevice { QEMUBH *check_bh; uint8_t *lst; uint8_t *res_fis; - int dma_status; - int done_atapi_packet; - int busy_slot; - int init_d2h_sent; - BlockDriverCompletionFunc *dma_cb; + bool done_atapi_packet; + int32_t busy_slot; + bool init_d2h_sent; AHCICmdHdr *cur_cmd; NCQTransferState ncq_tfs[AHCI_MAX_CMDS]; }; @@ -297,7 +295,7 @@ typedef struct AHCIState { MemoryRegion idp; /* Index-Data Pair I/O port space */ unsigned idp_offset; /* Offset of index in I/O port space */ uint32_t idp_index; /* Current IDP index */ - int ports; + int32_t ports; qemu_irq irq; DMAContext *dma; } AHCIState; @@ -307,6 +305,16 @@ typedef struct AHCIPCIState { AHCIState ahci; } AHCIPCIState; +extern const VMStateDescription vmstate_ahci; + +#define VMSTATE_AHCI(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(AHCIState), \ + .vmsd = &vmstate_ahci, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, AHCIState), \ +} + typedef struct NCQFrame { uint8_t fis_type; uint8_t c; diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index ee855b670f..745ef94deb 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -342,7 +342,7 @@ static void cmd646_ide_class_init(ObjectClass *klass, void *data) dc->props = cmd646_ide_properties; } -static TypeInfo cmd646_ide_info = { +static const TypeInfo cmd646_ide_info = { .name = "cmd646-ide", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIIDEState), diff --git a/hw/ide/core.c b/hw/ide/core.c index 1c5466fd77..d950aef6ee 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -334,14 +334,26 @@ typedef struct TrimAIOCB { BlockDriverAIOCB common; QEMUBH *bh; int ret; + QEMUIOVector *qiov; + BlockDriverAIOCB *aiocb; + int i, j; } TrimAIOCB; static void trim_aio_cancel(BlockDriverAIOCB *acb) { TrimAIOCB *iocb = container_of(acb, TrimAIOCB, common); + /* Exit the loop in case bdrv_aio_cancel calls ide_issue_trim_cb again. */ + iocb->j = iocb->qiov->niov - 1; + iocb->i = (iocb->qiov->iov[iocb->j].iov_len / 8) - 1; + + /* Tell ide_issue_trim_cb not to trigger the completion, too. */ qemu_bh_delete(iocb->bh); iocb->bh = NULL; + + if (iocb->aiocb) { + bdrv_aio_cancel(iocb->aiocb); + } qemu_aio_release(iocb); } @@ -358,43 +370,60 @@ static void ide_trim_bh_cb(void *opaque) qemu_bh_delete(iocb->bh); iocb->bh = NULL; - qemu_aio_release(iocb); } +static void ide_issue_trim_cb(void *opaque, int ret) +{ + TrimAIOCB *iocb = opaque; + if (ret >= 0) { + while (iocb->j < iocb->qiov->niov) { + int j = iocb->j; + while (++iocb->i < iocb->qiov->iov[j].iov_len / 8) { + int i = iocb->i; + uint64_t *buffer = iocb->qiov->iov[j].iov_base; + + /* 6-byte LBA + 2-byte range per entry */ + uint64_t entry = le64_to_cpu(buffer[i]); + uint64_t sector = entry & 0x0000ffffffffffffULL; + uint16_t count = entry >> 48; + + if (count == 0) { + continue; + } + + /* Got an entry! Submit and exit. */ + iocb->aiocb = bdrv_aio_discard(iocb->common.bs, sector, count, + ide_issue_trim_cb, opaque); + return; + } + + iocb->j++; + iocb->i = -1; + } + } else { + iocb->ret = ret; + } + + iocb->aiocb = NULL; + if (iocb->bh) { + qemu_bh_schedule(iocb->bh); + } +} + BlockDriverAIOCB *ide_issue_trim(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { TrimAIOCB *iocb; - int i, j, ret; iocb = qemu_aio_get(&trim_aiocb_info, bs, cb, opaque); iocb->bh = qemu_bh_new(ide_trim_bh_cb, iocb); iocb->ret = 0; - - for (j = 0; j < qiov->niov; j++) { - uint64_t *buffer = qiov->iov[j].iov_base; - - for (i = 0; i < qiov->iov[j].iov_len / 8; i++) { - /* 6-byte LBA + 2-byte range per entry */ - uint64_t entry = le64_to_cpu(buffer[i]); - uint64_t sector = entry & 0x0000ffffffffffffULL; - uint16_t count = entry >> 48; - - if (count == 0) { - break; - } - - ret = bdrv_discard(bs, sector, count); - if (!iocb->ret) { - iocb->ret = ret; - } - } - } - - qemu_bh_schedule(iocb->bh); - + iocb->qiov = qiov; + iocb->i = -1; + iocb->j = 0; + ide_issue_trim_cb(iocb, 0); return &iocb->common; } @@ -1130,8 +1159,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) } ide_set_irq(s->bus); break; + case WIN_VERIFY_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_VERIFY: case WIN_VERIFY_ONCE: /* do sector number check ? */ @@ -1139,8 +1170,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; + case WIN_READ_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_READ: case WIN_READ_ONCE: if (s->drive_kind == IDE_CD) { @@ -1154,8 +1187,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->req_nb_sectors = 1; ide_sector_read(s); break; + case WIN_WRITE_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_WRITE: case WIN_WRITE_ONCE: case CFA_WRITE_SECT_WO_ERASE: @@ -1170,8 +1205,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) ide_transfer_start(s, s->io_buffer, 512, ide_sector_write); s->media_changed = 1; break; + case WIN_MULTREAD_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_MULTREAD: if (!s->bs) { goto abort_cmd; @@ -1183,8 +1220,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) s->req_nb_sectors = s->mult_sectors; ide_sector_read(s); break; + case WIN_MULTWRITE_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_MULTWRITE: case CFA_WRITE_MULTI_WO_ERASE: if (!s->bs) { @@ -1203,8 +1242,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write); s->media_changed = 1; break; + case WIN_READDMA_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_READDMA: case WIN_READDMA_ONCE: if (!s->bs) { @@ -1213,8 +1254,10 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) ide_cmd_lba48_transform(s, lba48); ide_sector_start_dma(s, IDE_DMA_READ); break; + case WIN_WRITEDMA_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_WRITEDMA: case WIN_WRITEDMA_ONCE: if (!s->bs) { @@ -1224,14 +1267,17 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val) ide_sector_start_dma(s, IDE_DMA_WRITE); s->media_changed = 1; break; + case WIN_READ_NATIVE_MAX_EXT: - lba48 = 1; + lba48 = 1; + /* fall through */ case WIN_READ_NATIVE_MAX: ide_cmd_lba48_transform(s, lba48); ide_set_sector(s, s->nb_sectors - 1); s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; + case WIN_CHECKPOWERMODE1: case WIN_CHECKPOWERMODE2: s->error = 0; diff --git a/hw/ide/ich.c b/hw/ide/ich.c index de39b3067a..cc30adc701 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -79,9 +79,15 @@ #define ICH9_IDP_INDEX 0x10 #define ICH9_IDP_INDEX_LOG2 0x04 -static const VMStateDescription vmstate_ahci = { - .name = "ahci", - .unmigratable = 1, +static const VMStateDescription vmstate_ich9_ahci = { + .name = "ich9_ahci", + .unmigratable = 1, /* Still buggy under I/O load */ + .version_id = 1, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(card, AHCIPCIState), + VMSTATE_AHCI(ahci, AHCIPCIState), + VMSTATE_END_OF_LIST() + }, }; static void pci_ich9_reset(DeviceState *dev) @@ -152,11 +158,11 @@ static void ich_ahci_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_INTEL_82801IR; k->revision = 0x02; k->class_id = PCI_CLASS_STORAGE_SATA; - dc->vmsd = &vmstate_ahci; + dc->vmsd = &vmstate_ich9_ahci; dc->reset = pci_ich9_reset; } -static TypeInfo ich_ahci_info = { +static const TypeInfo ich_ahci_info = { .name = "ich9-ahci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(AHCIPCIState), diff --git a/hw/ide/isa.c b/hw/ide/isa.c index aa0e7fa22d..fb7bb8201d 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -111,7 +111,7 @@ static void isa_ide_class_initfn(ObjectClass *klass, void *data) dc->props = isa_ide_properties; } -static TypeInfo isa_ide_info = { +static const TypeInfo isa_ide_info = { .name = "isa-ide", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISAIDEState), diff --git a/hw/ide/macio.c b/hw/ide/macio.c index d8f9b4bce1..375c46f9da 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -22,9 +22,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include -#include -#include +#include "hw/hw.h" +#include "hw/ppc/mac.h" +#include "hw/mac_dbdma.h" #include "block/block.h" #include "sysemu/dma.h" @@ -33,12 +33,6 @@ /***********************************************************/ /* MacIO based PowerPC IDE */ -typedef struct MACIOIDEState { - MemoryRegion mem; - IDEBus bus; - BlockDriverAIOCB *aiocb; -} MACIOIDEState; - #define MACIO_PAGE_SIZE 4096 static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) @@ -321,30 +315,70 @@ static const VMStateDescription vmstate_pmac = { } }; -static void pmac_ide_reset(void *opaque) +static void macio_ide_reset(DeviceState *dev) { - MACIOIDEState *d = opaque; + MACIOIDEState *d = MACIO_IDE(dev); ide_bus_reset(&d->bus); } -/* hd_table must contain 4 block drivers */ -/* PowerMac uses memory mapped registers, not I/O. Return the memory - I/O index to access the ide. */ -MemoryRegion *pmac_ide_init (DriveInfo **hd_table, qemu_irq irq, - void *dbdma, int channel, qemu_irq dma_irq) +static void macio_ide_realizefn(DeviceState *dev, Error **errp) { - MACIOIDEState *d; + MACIOIDEState *s = MACIO_IDE(dev); - d = g_malloc0(sizeof(MACIOIDEState)); - ide_init2_with_non_qdev_drives(&d->bus, hd_table[0], hd_table[1], irq); - - if (dbdma) - DBDMA_register_channel(dbdma, channel, dma_irq, pmac_ide_transfer, pmac_ide_flush, d); - - memory_region_init_io(&d->mem, &pmac_ide_ops, d, "pmac-ide", 0x1000); - vmstate_register(NULL, 0, &vmstate_pmac, d); - qemu_register_reset(pmac_ide_reset, d); - - return &d->mem; + ide_init2(&s->bus, s->irq); } + +static void macio_ide_initfn(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + MACIOIDEState *s = MACIO_IDE(obj); + + ide_bus_new(&s->bus, DEVICE(obj), 0); + memory_region_init_io(&s->mem, &pmac_ide_ops, s, "pmac-ide", 0x1000); + sysbus_init_mmio(d, &s->mem); + sysbus_init_irq(d, &s->irq); + sysbus_init_irq(d, &s->dma_irq); +} + +static void macio_ide_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = macio_ide_realizefn; + dc->reset = macio_ide_reset; + dc->vmsd = &vmstate_pmac; +} + +static const TypeInfo macio_ide_type_info = { + .name = TYPE_MACIO_IDE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MACIOIDEState), + .instance_init = macio_ide_initfn, + .class_init = macio_ide_class_init, +}; + +static void macio_ide_register_types(void) +{ + type_register_static(&macio_ide_type_info); +} + +/* hd_table must contain 4 block drivers */ +void macio_ide_init_drives(MACIOIDEState *s, DriveInfo **hd_table) +{ + int i; + + for (i = 0; i < 2; i++) { + if (hd_table[i]) { + ide_create_drive(&s->bus, i, hd_table[i]); + } + } +} + +void macio_ide_register_dma(MACIOIDEState *s, void *dbdma, int channel) +{ + DBDMA_register_channel(dbdma, channel, s->dma_irq, + pmac_ide_transfer, pmac_ide_flush, s); +} + +type_init(macio_ide_register_types) diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index eb59976eda..ce88c3a651 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -22,7 +22,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include +#include "hw/hw.h" +#include "hw/sysbus.h" #include "block/block.h" #include "sysemu/dma.h" @@ -34,15 +35,24 @@ * dedicated ide controller, which is often seen on embedded boards. */ -typedef struct { +#define TYPE_MMIO_IDE "mmio-ide" +#define MMIO_IDE(obj) OBJECT_CHECK(MMIOState, (obj), TYPE_MMIO_IDE) + +typedef struct MMIOIDEState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + IDEBus bus; - int shift; + + uint32_t shift; + qemu_irq irq; MemoryRegion iomem1, iomem2; } MMIOState; -static void mmio_ide_reset(void *opaque) +static void mmio_ide_reset(DeviceState *dev) { - MMIOState *s = opaque; + MMIOState *s = MMIO_IDE(dev); ide_bus_reset(&s->bus); } @@ -107,24 +117,68 @@ static const VMStateDescription vmstate_ide_mmio = { } }; -void mmio_ide_init (hwaddr membase, hwaddr membase2, - MemoryRegion *address_space, - qemu_irq irq, int shift, - DriveInfo *hd0, DriveInfo *hd1) +static void mmio_ide_realizefn(DeviceState *dev, Error **errp) { - MMIOState *s = g_malloc0(sizeof(MMIOState)); + SysBusDevice *d = SYS_BUS_DEVICE(dev); + MMIOState *s = MMIO_IDE(dev); - ide_init2_with_non_qdev_drives(&s->bus, hd0, hd1, irq); - - s->shift = shift; + ide_init2(&s->bus, s->irq); memory_region_init_io(&s->iomem1, &mmio_ide_ops, s, - "ide-mmio.1", 16 << shift); + "ide-mmio.1", 16 << s->shift); memory_region_init_io(&s->iomem2, &mmio_ide_cs_ops, s, - "ide-mmio.2", 2 << shift); - memory_region_add_subregion(address_space, membase, &s->iomem1); - memory_region_add_subregion(address_space, membase2, &s->iomem2); - vmstate_register(NULL, 0, &vmstate_ide_mmio, s); - qemu_register_reset(mmio_ide_reset, s); + "ide-mmio.2", 2 << s->shift); + sysbus_init_mmio(d, &s->iomem1); + sysbus_init_mmio(d, &s->iomem2); } +static void mmio_ide_initfn(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + MMIOState *s = MMIO_IDE(obj); + + ide_bus_new(&s->bus, DEVICE(obj), 0); + sysbus_init_irq(d, &s->irq); +} + +static Property mmio_ide_properties[] = { + DEFINE_PROP_UINT32("shift", MMIOState, shift, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void mmio_ide_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = mmio_ide_realizefn; + dc->reset = mmio_ide_reset; + dc->props = mmio_ide_properties; + dc->vmsd = &vmstate_ide_mmio; +} + +static const TypeInfo mmio_ide_type_info = { + .name = TYPE_MMIO_IDE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MMIOState), + .instance_init = mmio_ide_initfn, + .class_init = mmio_ide_class_init, +}; + +static void mmio_ide_register_types(void) +{ + type_register_static(&mmio_ide_type_info); +} + +void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1) +{ + MMIOState *s = MMIO_IDE(dev); + + if (hd0 != NULL) { + ide_create_drive(&s->bus, 0, hd0); + } + if (hd1 != NULL) { + ide_create_drive(&s->bus, 1, hd1); + } +} + +type_init(mmio_ide_register_types) diff --git a/hw/ide/pci.c b/hw/ide/pci.c index e6226e3197..59fd53992a 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -311,7 +311,6 @@ void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val) if (bm->bus->dma->aiocb) { bdrv_drain_all(); assert(bm->bus->dma->aiocb == NULL); - assert((bm->status & BM_STATUS_DMAING) == 0); } } else { bm->cur_addr = bm->addr; diff --git a/hw/ide/piix.c b/hw/ide/piix.c index df95aec195..4d3e82266c 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -251,7 +251,7 @@ static void piix3_ide_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo piix3_ide_info = { +static const TypeInfo piix3_ide_info = { .name = "piix3-ide", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIIDEState), @@ -271,7 +271,7 @@ static void piix3_ide_xen_class_init(ObjectClass *klass, void *data) dc->unplug = pci_piix3_xen_ide_unplug; } -static TypeInfo piix3_ide_xen_info = { +static const TypeInfo piix3_ide_xen_info = { .name = "piix3-ide-xen", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIIDEState), @@ -292,7 +292,7 @@ static void piix4_ide_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo piix4_ide_info = { +static const TypeInfo piix4_ide_info = { .name = "piix4-ide", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIIDEState), diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index a09841e4b9..125a783b44 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -143,7 +143,10 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus); IDEState *s = bus->ifs + dev->unit; - if (dev->conf.discard_granularity && dev->conf.discard_granularity != 512) { + if (dev->conf.discard_granularity == -1) { + dev->conf.discard_granularity = 512; + } else if (dev->conf.discard_granularity && + dev->conf.discard_granularity != 512) { error_report("discard_granularity must be 512 for ide"); return -1; } @@ -217,7 +220,7 @@ static void ide_hd_class_init(ObjectClass *klass, void *data) dc->props = ide_hd_properties; } -static TypeInfo ide_hd_info = { +static const TypeInfo ide_hd_info = { .name = "ide-hd", .parent = TYPE_IDE_DEVICE, .instance_size = sizeof(IDEDrive), @@ -239,7 +242,7 @@ static void ide_cd_class_init(ObjectClass *klass, void *data) dc->props = ide_cd_properties; } -static TypeInfo ide_cd_info = { +static const TypeInfo ide_cd_info = { .name = "ide-cd", .parent = TYPE_IDE_DEVICE, .instance_size = sizeof(IDEDrive), @@ -261,7 +264,7 @@ static void ide_drive_class_init(ObjectClass *klass, void *data) dc->props = ide_drive_properties; } -static TypeInfo ide_drive_info = { +static const TypeInfo ide_drive_info = { .name = "ide-drive", .parent = TYPE_IDE_DEVICE, .instance_size = sizeof(IDEDrive), @@ -276,7 +279,7 @@ static void ide_device_class_init(ObjectClass *klass, void *data) k->props = ide_props; } -static TypeInfo ide_device_type_info = { +static const TypeInfo ide_device_type_info = { .name = TYPE_IDE_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(IDEDevice), diff --git a/hw/ide/via.c b/hw/ide/via.c index 14acb3ac04..f40c1adc8c 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -226,7 +226,7 @@ static void via_ide_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo via_ide_info = { +static const TypeInfo via_ide_info = { .name = "via-ide", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIIDEState), diff --git a/hw/imx_avic.c b/hw/imx_avic.c index f1f066cf9c..4e280b6ab9 100644 --- a/hw/imx_avic.c +++ b/hw/imx_avic.c @@ -14,8 +14,8 @@ * TODO: implement vectors. */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "qemu/host-utils.h" #define DEBUG_INT 1 diff --git a/hw/imx_ccm.c b/hw/imx_ccm.c index 46962e4df9..ad7aad3397 100644 --- a/hw/imx_ccm.c +++ b/hw/imx_ccm.c @@ -10,10 +10,10 @@ * the CCM. */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "sysemu/sysemu.h" -#include "imx.h" +#include "hw/imx.h" #define CKIH_FREQ 26000000 /* 26MHz crystal input */ #define CKIL_FREQ 32768 /* nominal 32khz clock */ @@ -306,7 +306,7 @@ static void imx_ccm_class_init(ObjectClass *klass, void *data) dc->desc = "i.MX Clock Control Module"; } -static TypeInfo imx_ccm_info = { +static const TypeInfo imx_ccm_info = { .name = "imx_ccm", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IMXCCMState), diff --git a/hw/imx_serial.c b/hw/imx_serial.c index 124dbb2860..746723cd6e 100644 --- a/hw/imx_serial.c +++ b/hw/imx_serial.c @@ -17,11 +17,11 @@ * is a real serial device. */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "char/char.h" -#include "imx.h" +#include "hw/imx.h" //#define DEBUG_SERIAL 1 #ifdef DEBUG_SERIAL @@ -425,7 +425,7 @@ void imx_serial_create(int uart, const hwaddr addr, qemu_irq irq) } qdev_prop_set_chr(dev, "chardev", chr); - bus = sysbus_from_qdev(dev); + bus = SYS_BUS_DEVICE(dev); qdev_init_nofail(dev); if (addr != (hwaddr)-1) { sysbus_mmio_map(bus, 0, addr); @@ -452,7 +452,7 @@ static void imx_serial_class_init(ObjectClass *klass, void *data) dc->props = imx32_serial_properties; } -static TypeInfo imx_serial_info = { +static const TypeInfo imx_serial_info = { .name = "imx-serial", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IMXSerialState), diff --git a/hw/imx_timer.c b/hw/imx_timer.c index e924c747c5..a8c311141e 100644 --- a/hw/imx_timer.c +++ b/hw/imx_timer.c @@ -11,11 +11,11 @@ * */ -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" -#include "ptimer.h" -#include "sysbus.h" -#include "imx.h" +#include "hw/ptimer.h" +#include "hw/sysbus.h" +#include "hw/imx.h" //#define DEBUG_TIMER 1 #ifdef DEBUG_TIMER diff --git a/hw/intel-hda.c b/hw/intel-hda.c index 98ff93679d..728b60fb9a 100644 --- a/hw/intel-hda.c +++ b/hw/intel-hda.c @@ -17,13 +17,13 @@ * along with this program; if not, see . */ -#include "hw.h" -#include "pci/pci.h" -#include "pci/msi.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/pci/msi.h" #include "qemu/timer.h" -#include "audiodev.h" -#include "intel-hda.h" -#include "intel-hda-defs.h" +#include "hw/audiodev.h" +#include "hw/intel-hda.h" +#include "hw/intel-hda-defs.h" #include "sysemu/dma.h" /* --------------------------------------------------------------------- */ @@ -1232,7 +1232,7 @@ static Property intel_hda_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void intel_hda_class_init(ObjectClass *klass, void *data) +static void intel_hda_class_init_common(ObjectClass *klass) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1240,20 +1240,46 @@ static void intel_hda_class_init(ObjectClass *klass, void *data) k->init = intel_hda_init; k->exit = intel_hda_exit; k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = 0x2668; - k->revision = 1; k->class_id = PCI_CLASS_MULTIMEDIA_HD_AUDIO; - dc->desc = "Intel HD Audio Controller"; dc->reset = intel_hda_reset; dc->vmsd = &vmstate_intel_hda; dc->props = intel_hda_properties; } -static TypeInfo intel_hda_info = { +static void intel_hda_class_init_ich6(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + intel_hda_class_init_common(klass); + k->device_id = 0x2668; + k->revision = 1; + dc->desc = "Intel HD Audio Controller (ich6)"; +} + +static void intel_hda_class_init_ich9(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + intel_hda_class_init_common(klass); + k->device_id = 0x293e; + k->revision = 3; + dc->desc = "Intel HD Audio Controller (ich9)"; +} + +static const TypeInfo intel_hda_info_ich6 = { .name = "intel-hda", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(IntelHDAState), - .class_init = intel_hda_class_init, + .class_init = intel_hda_class_init_ich6, +}; + +static const TypeInfo intel_hda_info_ich9 = { + .name = "ich9-intel-hda", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(IntelHDAState), + .class_init = intel_hda_class_init_ich9, }; static void hda_codec_device_class_init(ObjectClass *klass, void *data) @@ -1265,7 +1291,7 @@ static void hda_codec_device_class_init(ObjectClass *klass, void *data) k->props = hda_props; } -static TypeInfo hda_codec_device_type_info = { +static const TypeInfo hda_codec_device_type_info = { .name = TYPE_HDA_CODEC_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(HDACodecDevice), @@ -1277,7 +1303,8 @@ static TypeInfo hda_codec_device_type_info = { static void intel_hda_register_types(void) { type_register_static(&hda_codec_bus_info); - type_register_static(&intel_hda_info); + type_register_static(&intel_hda_info_ich6); + type_register_static(&intel_hda_info_ich9); type_register_static(&hda_codec_device_type_info); } diff --git a/hw/intel-hda.h b/hw/intel-hda.h index 22e0968d50..2544f0a344 100644 --- a/hw/intel-hda.h +++ b/hw/intel-hda.h @@ -1,7 +1,7 @@ #ifndef HW_INTEL_HDA_H #define HW_INTEL_HDA_H -#include "qdev.h" +#include "hw/qdev.h" /* --------------------------------------------------------------------- */ /* hda bus */ diff --git a/hw/ioapic.c b/hw/ioapic.c index 72730951a6..78629fac6c 100644 --- a/hw/ioapic.c +++ b/hw/ioapic.c @@ -20,11 +20,11 @@ * License along with this library; if not, see . */ -#include "hw.h" -#include "pc.h" -#include "apic.h" -#include "ioapic.h" -#include "ioapic_internal.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/apic.h" +#include "hw/ioapic.h" +#include "hw/ioapic_internal.h" //#define DEBUG_IOAPIC @@ -244,7 +244,7 @@ static void ioapic_class_init(ObjectClass *klass, void *data) dc->reset = ioapic_reset_common; } -static TypeInfo ioapic_info = { +static const TypeInfo ioapic_info = { .name = "ioapic", .parent = TYPE_IOAPIC_COMMON, .instance_size = sizeof(IOAPICCommonState), diff --git a/hw/ioapic_common.c b/hw/ioapic_common.c index 653eef2ce1..d4aff29544 100644 --- a/hw/ioapic_common.c +++ b/hw/ioapic_common.c @@ -19,9 +19,9 @@ * License along with this library; if not, see . */ -#include "ioapic.h" -#include "ioapic_internal.h" -#include "sysbus.h" +#include "hw/ioapic.h" +#include "hw/ioapic_internal.h" +#include "hw/sysbus.h" void ioapic_reset_common(DeviceState *dev) { @@ -103,7 +103,7 @@ static void ioapic_common_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo ioapic_common_type = { +static const TypeInfo ioapic_common_type = { .name = TYPE_IOAPIC_COMMON, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IOAPICCommonState), diff --git a/hw/ioapic_internal.h b/hw/ioapic_internal.h index c8447d7f3b..25576c819e 100644 --- a/hw/ioapic_internal.h +++ b/hw/ioapic_internal.h @@ -22,9 +22,9 @@ #ifndef QEMU_IOAPIC_INTERNAL_H #define QEMU_IOAPIC_INTERNAL_H -#include "hw.h" +#include "hw/hw.h" #include "exec/memory.h" -#include "sysbus.h" +#include "hw/sysbus.h" #define MAX_IOAPICS 1 diff --git a/hw/ioh3420.c b/hw/ioh3420.c index d706e195df..43f855427b 100644 --- a/hw/ioh3420.c +++ b/hw/ioh3420.c @@ -20,10 +20,10 @@ * with this program; if not, see . */ -#include "pci/pci_ids.h" -#include "pci/msi.h" -#include "pci/pcie.h" -#include "ioh3420.h" +#include "hw/pci/pci_ids.h" +#include "hw/pci/msi.h" +#include "hw/pci/pcie.h" +#include "hw/ioh3420.h" #define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */ #define PCI_DEVICE_ID_IOH_REV 0x2 @@ -226,7 +226,7 @@ static void ioh3420_class_init(ObjectClass *klass, void *data) dc->props = ioh3420_properties; } -static TypeInfo ioh3420_info = { +static const TypeInfo ioh3420_info = { .name = "ioh3420", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIESlot), diff --git a/hw/ioh3420.h b/hw/ioh3420.h index 046cf2c281..7776e5b02d 100644 --- a/hw/ioh3420.h +++ b/hw/ioh3420.h @@ -1,7 +1,7 @@ #ifndef QEMU_IOH3420_H #define QEMU_IOH3420_H -#include "pci/pcie_port.h" +#include "hw/pci/pcie_port.h" PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction, const char *bus_name, pci_map_irq_fn map_irq, diff --git a/hw/ipack.c b/hw/ipack.c new file mode 100644 index 0000000000..b1f46c10a4 --- /dev/null +++ b/hw/ipack.c @@ -0,0 +1,115 @@ +/* + * QEMU IndustryPack emulation + * + * Copyright (C) 2012 Igalia, S.L. + * Author: Alberto Garcia + * + * This code is licensed under the GNU GPL v2 or (at your option) any + * later version. + */ + +#include "hw/ipack.h" + +IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot) +{ + BusChild *kid; + + QTAILQ_FOREACH(kid, &BUS(bus)->children, sibling) { + DeviceState *qdev = kid->child; + IPackDevice *ip = IPACK_DEVICE(qdev); + if (ip->slot == slot) { + return ip; + } + } + return NULL; +} + +void ipack_bus_new_inplace(IPackBus *bus, DeviceState *parent, + const char *name, uint8_t n_slots, + qemu_irq_handler handler) +{ + qbus_create_inplace(&bus->qbus, TYPE_IPACK_BUS, parent, name); + bus->n_slots = n_slots; + bus->set_irq = handler; +} + +static int ipack_device_dev_init(DeviceState *qdev) +{ + IPackBus *bus = IPACK_BUS(qdev_get_parent_bus(qdev)); + IPackDevice *dev = IPACK_DEVICE(qdev); + IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev); + + if (dev->slot < 0) { + dev->slot = bus->free_slot; + } + if (dev->slot >= bus->n_slots) { + return -1; + } + bus->free_slot = dev->slot + 1; + + dev->irq = qemu_allocate_irqs(bus->set_irq, dev, 2); + + return k->init(dev); +} + +static int ipack_device_dev_exit(DeviceState *qdev) +{ + IPackDevice *dev = IPACK_DEVICE(qdev); + IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev); + + if (k->exit) { + k->exit(dev); + } + + qemu_free_irqs(dev->irq); + + return 0; +} + +static Property ipack_device_props[] = { + DEFINE_PROP_INT32("slot", IPackDevice, slot, -1), + DEFINE_PROP_END_OF_LIST() +}; + +static void ipack_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k = DEVICE_CLASS(klass); + k->bus_type = TYPE_IPACK_BUS; + k->init = ipack_device_dev_init; + k->exit = ipack_device_dev_exit; + k->props = ipack_device_props; +} + +const VMStateDescription vmstate_ipack_device = { + .name = "ipack_device", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(slot, IPackDevice), + VMSTATE_END_OF_LIST() + } +}; + +static const TypeInfo ipack_device_info = { + .name = TYPE_IPACK_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(IPackDevice), + .class_size = sizeof(IPackDeviceClass), + .class_init = ipack_device_class_init, + .abstract = true, +}; + +static const TypeInfo ipack_bus_info = { + .name = TYPE_IPACK_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(IPackBus), +}; + +static void ipack_register_types(void) +{ + type_register_static(&ipack_device_info); + type_register_static(&ipack_bus_info); +} + +type_init(ipack_register_types) diff --git a/hw/ipack.h b/hw/ipack.h new file mode 100644 index 0000000000..f2b7a12e05 --- /dev/null +++ b/hw/ipack.h @@ -0,0 +1,79 @@ +/* + * QEMU IndustryPack emulation + * + * Copyright (C) 2012 Igalia, S.L. + * Author: Alberto Garcia + * + * This code is licensed under the GNU GPL v2 or (at your option) any + * later version. + */ + +#ifndef QEMU_IPACK_H +#define QEMU_IPACK_H + +#include "hw/qdev.h" + +typedef struct IPackBus IPackBus; + +#define TYPE_IPACK_BUS "IndustryPack" +#define IPACK_BUS(obj) OBJECT_CHECK(IPackBus, (obj), TYPE_IPACK_BUS) + +struct IPackBus { + BusState qbus; + /* All fields are private */ + uint8_t n_slots; + uint8_t free_slot; + qemu_irq_handler set_irq; +}; + +typedef struct IPackDevice IPackDevice; +typedef struct IPackDeviceClass IPackDeviceClass; + +#define TYPE_IPACK_DEVICE "ipack-device" +#define IPACK_DEVICE(obj) \ + OBJECT_CHECK(IPackDevice, (obj), TYPE_IPACK_DEVICE) +#define IPACK_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(IPackDeviceClass, (klass), TYPE_IPACK_DEVICE) +#define IPACK_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(IPackDeviceClass, (obj), TYPE_IPACK_DEVICE) + +struct IPackDeviceClass { + DeviceClass parent_class; + + int (*init)(IPackDevice *dev); + int (*exit)(IPackDevice *dev); + + uint16_t (*io_read)(IPackDevice *dev, uint8_t addr); + void (*io_write)(IPackDevice *dev, uint8_t addr, uint16_t val); + + uint16_t (*id_read)(IPackDevice *dev, uint8_t addr); + void (*id_write)(IPackDevice *dev, uint8_t addr, uint16_t val); + + uint16_t (*int_read)(IPackDevice *dev, uint8_t addr); + void (*int_write)(IPackDevice *dev, uint8_t addr, uint16_t val); + + uint16_t (*mem_read16)(IPackDevice *dev, uint32_t addr); + void (*mem_write16)(IPackDevice *dev, uint32_t addr, uint16_t val); + + uint8_t (*mem_read8)(IPackDevice *dev, uint32_t addr); + void (*mem_write8)(IPackDevice *dev, uint32_t addr, uint8_t val); +}; + +struct IPackDevice { + DeviceState qdev; + int32_t slot; + /* IRQ objects for the IndustryPack INT0# and INT1# */ + qemu_irq *irq; +}; + +extern const VMStateDescription vmstate_ipack_device; + +#define VMSTATE_IPACK_DEVICE(_field, _state) \ + VMSTATE_STRUCT(_field, _state, 1, vmstate_ipack_device, IPackDevice) + +IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot); +void ipack_bus_new_inplace(IPackBus *bus, DeviceState *parent, + const char *name, uint8_t n_slots, + qemu_irq_handler handler); + +#endif diff --git a/hw/ipoctal232.c b/hw/ipoctal232.c new file mode 100644 index 0000000000..1da6a99175 --- /dev/null +++ b/hw/ipoctal232.c @@ -0,0 +1,619 @@ +/* + * QEMU GE IP-Octal 232 IndustryPack emulation + * + * Copyright (C) 2012 Igalia, S.L. + * Author: Alberto Garcia + * + * This code is licensed under the GNU GPL v2 or (at your option) any + * later version. + */ + +#include "hw/ipack.h" +#include "qemu/bitops.h" +#include "char/char.h" + +/* #define DEBUG_IPOCTAL */ + +#ifdef DEBUG_IPOCTAL +#define DPRINTF2(fmt, ...) \ + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF2(fmt, ...) do { } while (0) +#endif + +#define DPRINTF(fmt, ...) DPRINTF2("IP-Octal: " fmt, ## __VA_ARGS__) + +#define RX_FIFO_SIZE 3 + +/* The IP-Octal has 8 channels (a-h) + divided into 4 blocks (A-D) */ +#define N_CHANNELS 8 +#define N_BLOCKS 4 + +#define REG_MRa 0x01 +#define REG_MRb 0x11 +#define REG_SRa 0x03 +#define REG_SRb 0x13 +#define REG_CSRa 0x03 +#define REG_CSRb 0x13 +#define REG_CRa 0x05 +#define REG_CRb 0x15 +#define REG_RHRa 0x07 +#define REG_RHRb 0x17 +#define REG_THRa 0x07 +#define REG_THRb 0x17 +#define REG_ACR 0x09 +#define REG_ISR 0x0B +#define REG_IMR 0x0B +#define REG_OPCR 0x1B + +#define CR_ENABLE_RX BIT(0) +#define CR_DISABLE_RX BIT(1) +#define CR_ENABLE_TX BIT(2) +#define CR_DISABLE_TX BIT(3) +#define CR_CMD(cr) ((cr) >> 4) +#define CR_NO_OP 0 +#define CR_RESET_MR 1 +#define CR_RESET_RX 2 +#define CR_RESET_TX 3 +#define CR_RESET_ERR 4 +#define CR_RESET_BRKINT 5 +#define CR_START_BRK 6 +#define CR_STOP_BRK 7 +#define CR_ASSERT_RTSN 8 +#define CR_NEGATE_RTSN 9 +#define CR_TIMEOUT_ON 10 +#define CR_TIMEOUT_OFF 12 + +#define SR_RXRDY BIT(0) +#define SR_FFULL BIT(1) +#define SR_TXRDY BIT(2) +#define SR_TXEMT BIT(3) +#define SR_OVERRUN BIT(4) +#define SR_PARITY BIT(5) +#define SR_FRAMING BIT(6) +#define SR_BREAK BIT(7) + +#define ISR_TXRDYA BIT(0) +#define ISR_RXRDYA BIT(1) +#define ISR_BREAKA BIT(2) +#define ISR_CNTRDY BIT(3) +#define ISR_TXRDYB BIT(4) +#define ISR_RXRDYB BIT(5) +#define ISR_BREAKB BIT(6) +#define ISR_MPICHG BIT(7) +#define ISR_TXRDY(CH) (((CH) & 1) ? BIT(4) : BIT(0)) +#define ISR_RXRDY(CH) (((CH) & 1) ? BIT(5) : BIT(1)) +#define ISR_BREAK(CH) (((CH) & 1) ? BIT(6) : BIT(2)) + +typedef struct IPOctalState IPOctalState; +typedef struct SCC2698Channel SCC2698Channel; +typedef struct SCC2698Block SCC2698Block; + +struct SCC2698Channel { + IPOctalState *ipoctal; + CharDriverState *dev; + char *devpath; + bool rx_enabled; + uint8_t mr[2]; + uint8_t mr_idx; + uint8_t sr; + uint8_t rhr[RX_FIFO_SIZE]; + uint8_t rhr_idx; + uint8_t rx_pending; +}; + +struct SCC2698Block { + uint8_t imr; + uint8_t isr; +}; + +struct IPOctalState { + IPackDevice dev; + SCC2698Channel ch[N_CHANNELS]; + SCC2698Block blk[N_BLOCKS]; + uint8_t irq_vector; +}; + +#define TYPE_IPOCTAL "ipoctal232" + +#define IPOCTAL(obj) \ + OBJECT_CHECK(IPOctalState, (obj), TYPE_IPOCTAL) + +static const VMStateDescription vmstate_scc2698_channel = { + .name = "scc2698_channel", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(rx_enabled, SCC2698Channel), + VMSTATE_UINT8_ARRAY(mr, SCC2698Channel, 2), + VMSTATE_UINT8(mr_idx, SCC2698Channel), + VMSTATE_UINT8(sr, SCC2698Channel), + VMSTATE_UINT8_ARRAY(rhr, SCC2698Channel, RX_FIFO_SIZE), + VMSTATE_UINT8(rhr_idx, SCC2698Channel), + VMSTATE_UINT8(rx_pending, SCC2698Channel), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_scc2698_block = { + .name = "scc2698_block", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(imr, SCC2698Block), + VMSTATE_UINT8(isr, SCC2698Block), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_ipoctal = { + .name = "ipoctal232", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_IPACK_DEVICE(dev, IPOctalState), + VMSTATE_STRUCT_ARRAY(ch, IPOctalState, N_CHANNELS, 1, + vmstate_scc2698_channel, SCC2698Channel), + VMSTATE_STRUCT_ARRAY(blk, IPOctalState, N_BLOCKS, 1, + vmstate_scc2698_block, SCC2698Block), + VMSTATE_UINT8(irq_vector, IPOctalState), + VMSTATE_END_OF_LIST() + } +}; + +/* data[10] is 0x0C, not 0x0B as the doc says */ +static const uint8_t id_prom_data[] = { + 0x49, 0x50, 0x41, 0x43, 0xF0, 0x22, + 0xA1, 0x00, 0x00, 0x00, 0x0C, 0xCC +}; + +static void update_irq(IPOctalState *dev, unsigned block) +{ + /* Blocks A and B interrupt on INT0#, C and D on INT1#. + Thus, to get the status we have to check two blocks. */ + SCC2698Block *blk0 = &dev->blk[block]; + SCC2698Block *blk1 = &dev->blk[block^1]; + unsigned intno = block / 2; + + if ((blk0->isr & blk0->imr) || (blk1->isr & blk1->imr)) { + qemu_irq_raise(dev->dev.irq[intno]); + } else { + qemu_irq_lower(dev->dev.irq[intno]); + } +} + +static void write_cr(IPOctalState *dev, unsigned channel, uint8_t val) +{ + SCC2698Channel *ch = &dev->ch[channel]; + SCC2698Block *blk = &dev->blk[channel / 2]; + + DPRINTF("Write CR%c %u: ", channel + 'a', val); + + /* The lower 4 bits are used to enable and disable Tx and Rx */ + if (val & CR_ENABLE_RX) { + DPRINTF2("Rx on, "); + ch->rx_enabled = true; + } + if (val & CR_DISABLE_RX) { + DPRINTF2("Rx off, "); + ch->rx_enabled = false; + } + if (val & CR_ENABLE_TX) { + DPRINTF2("Tx on, "); + ch->sr |= SR_TXRDY | SR_TXEMT; + blk->isr |= ISR_TXRDY(channel); + } + if (val & CR_DISABLE_TX) { + DPRINTF2("Tx off, "); + ch->sr &= ~(SR_TXRDY | SR_TXEMT); + blk->isr &= ~ISR_TXRDY(channel); + } + + DPRINTF2("cmd: "); + + /* The rest of the bits implement different commands */ + switch (CR_CMD(val)) { + case CR_NO_OP: + DPRINTF2("none"); + break; + case CR_RESET_MR: + DPRINTF2("reset MR"); + ch->mr_idx = 0; + break; + case CR_RESET_RX: + DPRINTF2("reset Rx"); + ch->rx_enabled = false; + ch->rx_pending = 0; + ch->sr &= ~SR_RXRDY; + blk->isr &= ~ISR_RXRDY(channel); + break; + case CR_RESET_TX: + DPRINTF2("reset Tx"); + ch->sr &= ~(SR_TXRDY | SR_TXEMT); + blk->isr &= ~ISR_TXRDY(channel); + break; + case CR_RESET_ERR: + DPRINTF2("reset err"); + ch->sr &= ~(SR_OVERRUN | SR_PARITY | SR_FRAMING | SR_BREAK); + break; + case CR_RESET_BRKINT: + DPRINTF2("reset brk ch int"); + blk->isr &= ~(ISR_BREAKA | ISR_BREAKB); + break; + default: + DPRINTF2("unsupported 0x%x", CR_CMD(val)); + } + + DPRINTF2("\n"); +} + +static uint16_t io_read(IPackDevice *ip, uint8_t addr) +{ + IPOctalState *dev = IPOCTAL(ip); + uint16_t ret = 0; + /* addr[7:6]: block (A-D) + addr[7:5]: channel (a-h) + addr[5:0]: register */ + unsigned block = addr >> 5; + unsigned channel = addr >> 4; + /* Big endian, accessed using 8-bit bytes at odd locations */ + unsigned offset = (addr & 0x1F) ^ 1; + SCC2698Channel *ch = &dev->ch[channel]; + SCC2698Block *blk = &dev->blk[block]; + uint8_t old_isr = blk->isr; + + switch (offset) { + + case REG_MRa: + case REG_MRb: + ret = ch->mr[ch->mr_idx]; + DPRINTF("Read MR%u%c: 0x%x\n", ch->mr_idx + 1, channel + 'a', ret); + ch->mr_idx = 1; + break; + + case REG_SRa: + case REG_SRb: + ret = ch->sr; + DPRINTF("Read SR%c: 0x%x\n", channel + 'a', ret); + break; + + case REG_RHRa: + case REG_RHRb: + ret = ch->rhr[ch->rhr_idx]; + if (ch->rx_pending > 0) { + ch->rx_pending--; + if (ch->rx_pending == 0) { + ch->sr &= ~SR_RXRDY; + blk->isr &= ~ISR_RXRDY(channel); + if (ch->dev) { + qemu_chr_accept_input(ch->dev); + } + } else { + ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE; + } + if (ch->sr & SR_BREAK) { + ch->sr &= ~SR_BREAK; + blk->isr |= ISR_BREAK(channel); + } + } + DPRINTF("Read RHR%c (0x%x)\n", channel + 'a', ret); + break; + + case REG_ISR: + ret = blk->isr; + DPRINTF("Read ISR%c: 0x%x\n", block + 'A', ret); + break; + + default: + DPRINTF("Read unknown/unsupported register 0x%02x\n", offset); + } + + if (old_isr != blk->isr) { + update_irq(dev, block); + } + + return ret; +} + +static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val) +{ + IPOctalState *dev = IPOCTAL(ip); + unsigned reg = val & 0xFF; + /* addr[7:6]: block (A-D) + addr[7:5]: channel (a-h) + addr[5:0]: register */ + unsigned block = addr >> 5; + unsigned channel = addr >> 4; + /* Big endian, accessed using 8-bit bytes at odd locations */ + unsigned offset = (addr & 0x1F) ^ 1; + SCC2698Channel *ch = &dev->ch[channel]; + SCC2698Block *blk = &dev->blk[block]; + uint8_t old_isr = blk->isr; + uint8_t old_imr = blk->imr; + + switch (offset) { + + case REG_MRa: + case REG_MRb: + ch->mr[ch->mr_idx] = reg; + DPRINTF("Write MR%u%c 0x%x\n", ch->mr_idx + 1, channel + 'a', reg); + ch->mr_idx = 1; + break; + + /* Not implemented */ + case REG_CSRa: + case REG_CSRb: + DPRINTF("Write CSR%c: 0x%x\n", channel + 'a', reg); + break; + + case REG_CRa: + case REG_CRb: + write_cr(dev, channel, reg); + break; + + case REG_THRa: + case REG_THRb: + if (ch->sr & SR_TXRDY) { + DPRINTF("Write THR%c (0x%x)\n", channel + 'a', reg); + if (ch->dev) { + uint8_t thr = reg; + qemu_chr_fe_write(ch->dev, &thr, 1); + } + } else { + DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg); + } + break; + + /* Not implemented */ + case REG_ACR: + DPRINTF("Write ACR%c 0x%x\n", block + 'A', val); + break; + + case REG_IMR: + DPRINTF("Write IMR%c 0x%x\n", block + 'A', val); + blk->imr = reg; + break; + + /* Not implemented */ + case REG_OPCR: + DPRINTF("Write OPCR%c 0x%x\n", block + 'A', val); + break; + + default: + DPRINTF("Write unknown/unsupported register 0x%02x %u\n", offset, val); + } + + if (old_isr != blk->isr || old_imr != blk->imr) { + update_irq(dev, block); + } +} + +static uint16_t id_read(IPackDevice *ip, uint8_t addr) +{ + uint16_t ret = 0; + unsigned pos = addr / 2; /* The ID PROM data is stored every other byte */ + + if (pos < ARRAY_SIZE(id_prom_data)) { + ret = id_prom_data[pos]; + } else { + DPRINTF("Attempt to read unavailable PROM data at 0x%x\n", addr); + } + + return ret; +} + +static void id_write(IPackDevice *ip, uint8_t addr, uint16_t val) +{ + IPOctalState *dev = IPOCTAL(ip); + if (addr == 1) { + DPRINTF("Write IRQ vector: %u\n", (unsigned) val); + dev->irq_vector = val; /* Undocumented, but the hw works like that */ + } else { + DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr); + } +} + +static uint16_t int_read(IPackDevice *ip, uint8_t addr) +{ + IPOctalState *dev = IPOCTAL(ip); + /* Read address 0 to ACK INT0# and address 2 to ACK INT1# */ + if (addr != 0 && addr != 2) { + DPRINTF("Attempt to read from 0x%x\n", addr); + return 0; + } else { + /* Update interrupts if necessary */ + update_irq(dev, addr); + return dev->irq_vector; + } +} + +static void int_write(IPackDevice *ip, uint8_t addr, uint16_t val) +{ + DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr); +} + +static uint16_t mem_read16(IPackDevice *ip, uint32_t addr) +{ + DPRINTF("Attempt to read from 0x%x\n", addr); + return 0; +} + +static void mem_write16(IPackDevice *ip, uint32_t addr, uint16_t val) +{ + DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr); +} + +static uint8_t mem_read8(IPackDevice *ip, uint32_t addr) +{ + DPRINTF("Attempt to read from 0x%x\n", addr); + return 0; +} + +static void mem_write8(IPackDevice *ip, uint32_t addr, uint8_t val) +{ + IPOctalState *dev = IPOCTAL(ip); + if (addr == 1) { + DPRINTF("Write IRQ vector: %u\n", (unsigned) val); + dev->irq_vector = val; + } else { + DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr); + } +} + +static int hostdev_can_receive(void *opaque) +{ + SCC2698Channel *ch = opaque; + int available_bytes = RX_FIFO_SIZE - ch->rx_pending; + return ch->rx_enabled ? available_bytes : 0; +} + +static void hostdev_receive(void *opaque, const uint8_t *buf, int size) +{ + SCC2698Channel *ch = opaque; + IPOctalState *dev = ch->ipoctal; + unsigned pos = ch->rhr_idx + ch->rx_pending; + int i; + + assert(size + ch->rx_pending <= RX_FIFO_SIZE); + + /* Copy data to the RxFIFO */ + for (i = 0; i < size; i++) { + pos %= RX_FIFO_SIZE; + ch->rhr[pos++] = buf[i]; + } + + ch->rx_pending += size; + + /* If the RxFIFO was empty raise an interrupt */ + if (!(ch->sr & SR_RXRDY)) { + unsigned block, channel = 0; + /* Find channel number to update the ISR register */ + while (&dev->ch[channel] != ch) { + channel++; + } + block = channel / 2; + dev->blk[block].isr |= ISR_RXRDY(channel); + ch->sr |= SR_RXRDY; + update_irq(dev, block); + } +} + +static void hostdev_event(void *opaque, int event) +{ + SCC2698Channel *ch = opaque; + switch (event) { + case CHR_EVENT_OPENED: + DPRINTF("Device %s opened\n", ch->dev->label); + break; + case CHR_EVENT_BREAK: { + uint8_t zero = 0; + DPRINTF("Device %s received break\n", ch->dev->label); + + if (!(ch->sr & SR_BREAK)) { + IPOctalState *dev = ch->ipoctal; + unsigned block, channel = 0; + + while (&dev->ch[channel] != ch) { + channel++; + } + block = channel / 2; + + ch->sr |= SR_BREAK; + dev->blk[block].isr |= ISR_BREAK(channel); + } + + /* Put a zero character in the buffer */ + hostdev_receive(ch, &zero, 1); + } + break; + default: + DPRINTF("Device %s received event %d\n", ch->dev->label, event); + } +} + +static int ipoctal_init(IPackDevice *ip) +{ + IPOctalState *s = IPOCTAL(ip); + unsigned i; + + for (i = 0; i < N_CHANNELS; i++) { + SCC2698Channel *ch = &s->ch[i]; + ch->ipoctal = s; + + /* Redirect IP-Octal channels to host character devices */ + if (ch->devpath) { + const char chr_name[] = "ipoctal"; + char label[ARRAY_SIZE(chr_name) + 2]; + static int index; + + snprintf(label, sizeof(label), "%s%d", chr_name, index); + + ch->dev = qemu_chr_new(label, ch->devpath, NULL); + + if (ch->dev) { + index++; + qemu_chr_add_handlers(ch->dev, hostdev_can_receive, + hostdev_receive, hostdev_event, ch); + DPRINTF("Redirecting channel %u to %s (%s)\n", + i, ch->devpath, label); + } else { + DPRINTF("Could not redirect channel %u to %s\n", + i, ch->devpath); + } + } + } + + return 0; +} + +static Property ipoctal_properties[] = { + DEFINE_PROP_STRING("serial0", IPOctalState, ch[0].devpath), + DEFINE_PROP_STRING("serial1", IPOctalState, ch[1].devpath), + DEFINE_PROP_STRING("serial2", IPOctalState, ch[2].devpath), + DEFINE_PROP_STRING("serial3", IPOctalState, ch[3].devpath), + DEFINE_PROP_STRING("serial4", IPOctalState, ch[4].devpath), + DEFINE_PROP_STRING("serial5", IPOctalState, ch[5].devpath), + DEFINE_PROP_STRING("serial6", IPOctalState, ch[6].devpath), + DEFINE_PROP_STRING("serial7", IPOctalState, ch[7].devpath), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ipoctal_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IPackDeviceClass *ic = IPACK_DEVICE_CLASS(klass); + + ic->init = ipoctal_init; + ic->io_read = io_read; + ic->io_write = io_write; + ic->id_read = id_read; + ic->id_write = id_write; + ic->int_read = int_read; + ic->int_write = int_write; + ic->mem_read16 = mem_read16; + ic->mem_write16 = mem_write16; + ic->mem_read8 = mem_read8; + ic->mem_write8 = mem_write8; + + dc->desc = "GE IP-Octal 232 8-channel RS-232 IndustryPack"; + dc->props = ipoctal_properties; + dc->vmsd = &vmstate_ipoctal; +} + +static const TypeInfo ipoctal_info = { + .name = TYPE_IPOCTAL, + .parent = TYPE_IPACK_DEVICE, + .instance_size = sizeof(IPOctalState), + .class_init = ipoctal_class_init, +}; + +static void ipoctal_register_types(void) +{ + type_register_static(&ipoctal_info); +} + +type_init(ipoctal_register_types) diff --git a/hw/irq.c b/hw/irq.c index f4e2a7804a..20785428ef 100644 --- a/hw/irq.c +++ b/hw/irq.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ #include "qemu-common.h" -#include "irq.h" +#include "hw/irq.h" struct IRQState { qemu_irq_handler handler; diff --git a/hw/isa-bus.c b/hw/isa-bus.c index 86b0bbd3d1..67ff8fd314 100644 --- a/hw/isa-bus.c +++ b/hw/isa-bus.c @@ -16,11 +16,11 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "monitor/monitor.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "sysemu/sysemu.h" -#include "isa.h" +#include "hw/isa.h" #include "exec/address-spaces.h" static ISABus *isabus; @@ -124,9 +124,6 @@ static int isa_qdev_init(DeviceState *qdev) ISADevice *dev = ISA_DEVICE(qdev); ISADeviceClass *klass = ISA_DEVICE_GET_CLASS(dev); - dev->isairq[0] = -1; - dev->isairq[1] = -1; - if (klass->init) { return klass->init(dev); } @@ -134,6 +131,14 @@ static int isa_qdev_init(DeviceState *qdev) return 0; } +static void isa_device_init(Object *obj) +{ + ISADevice *dev = ISA_DEVICE(obj); + + dev->isairq[0] = -1; + dev->isairq[1] = -1; +} + ISADevice *isa_create(ISABus *bus, const char *name) { DeviceState *dev; @@ -215,7 +220,7 @@ static void isabus_bridge_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo isabus_bridge_info = { +static const TypeInfo isabus_bridge_info = { .name = "isabus-bridge", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysBusDevice), @@ -229,10 +234,11 @@ static void isa_device_class_init(ObjectClass *klass, void *data) k->bus_type = TYPE_ISA_BUS; } -static TypeInfo isa_device_type_info = { +static const TypeInfo isa_device_type_info = { .name = TYPE_ISA_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(ISADevice), + .instance_init = isa_device_init, .abstract = true, .class_size = sizeof(ISADeviceClass), .class_init = isa_device_class_init, diff --git a/hw/isa.h b/hw/isa.h index 62e89d3bcd..82da37c11d 100644 --- a/hw/isa.h +++ b/hw/isa.h @@ -5,7 +5,7 @@ #include "exec/ioport.h" #include "exec/memory.h" -#include "qdev.h" +#include "hw/qdev.h" #define ISA_NUM_IRQS 16 @@ -82,7 +82,7 @@ void isa_register_portio_list(ISADevice *dev, uint16_t start, static inline ISABus *isa_bus_from_device(ISADevice *d) { - return DO_UPCAST(ISABus, qbus, d->qdev.parent_bus); + return ISA_BUS(qdev_get_parent_bus(DEVICE(d))); } extern hwaddr isa_mem_base; diff --git a/hw/isa_mmio.c b/hw/isa_mmio.c index 487cf6a8fb..a7860e7459 100644 --- a/hw/isa_mmio.c +++ b/hw/isa_mmio.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "isa.h" +#include "hw/hw.h" +#include "hw/isa.h" #include "exec/address-spaces.h" static void isa_mmio_writeb (void *opaque, hwaddr addr, diff --git a/hw/ivshmem.c b/hw/ivshmem.c index fcf5d05bae..68a2cf2e69 100644 --- a/hw/ivshmem.c +++ b/hw/ivshmem.c @@ -16,10 +16,10 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "pc.h" -#include "pci/pci.h" -#include "pci/msix.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/pci/pci.h" +#include "hw/pci/msix.h" #include "sysemu/kvm.h" #include "migration/migration.h" #include "qapi/qmp/qerror.h" @@ -29,6 +29,9 @@ #include #include +#define PCI_VENDOR_ID_IVSHMEM PCI_VENDOR_ID_REDHAT_QUMRANET +#define PCI_DEVICE_ID_IVSHMEM 0x1110 + #define IVSHMEM_IOEVENTFD 0 #define IVSHMEM_MSI 1 @@ -800,14 +803,14 @@ static void ivshmem_class_init(ObjectClass *klass, void *data) k->init = pci_ivshmem_init; k->exit = pci_ivshmem_uninit; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = 0x1110; + k->vendor_id = PCI_VENDOR_ID_IVSHMEM; + k->device_id = PCI_DEVICE_ID_IVSHMEM; k->class_id = PCI_CLASS_MEMORY_RAM; dc->reset = ivshmem_reset; dc->props = ivshmem_properties; } -static TypeInfo ivshmem_info = { +static const TypeInfo ivshmem_info = { .name = "ivshmem", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(IVShmemState), diff --git a/hw/jazz_led.c b/hw/jazz_led.c index f4a040631e..a418a7d1b6 100644 --- a/hw/jazz_led.c +++ b/hw/jazz_led.c @@ -26,7 +26,7 @@ #include "ui/console.h" #include "ui/pixel_ops.h" #include "trace.h" -#include "sysbus.h" +#include "hw/sysbus.h" typedef enum { REDRAW_NONE = 0, REDRAW_SEGMENTS = 1, REDRAW_BACKGROUND = 2, @@ -277,7 +277,7 @@ static void jazz_led_class_init(ObjectClass *klass, void *data) dc->reset = jazz_led_reset; } -static TypeInfo jazz_led_info = { +static const TypeInfo jazz_led_info = { .name = "jazz-led", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LedState), diff --git a/hw/kvm/apic.c b/hw/kvm/apic.c index dad2f21c18..d994ea7c97 100644 --- a/hw/kvm/apic.c +++ b/hw/kvm/apic.c @@ -104,7 +104,7 @@ static void kvm_apic_enable_tpr_reporting(APICCommonState *s, bool enable) .enabled = enable }; - kvm_vcpu_ioctl(&s->cpu->env, KVM_TPR_ACCESS_REPORTING, &ctl); + kvm_vcpu_ioctl(CPU(s->cpu), KVM_TPR_ACCESS_REPORTING, &ctl); } static void kvm_apic_vapic_base_update(APICCommonState *s) @@ -114,7 +114,7 @@ static void kvm_apic_vapic_base_update(APICCommonState *s) }; int ret; - ret = kvm_vcpu_ioctl(&s->cpu->env, KVM_SET_VAPIC_ADDR, &vapid_addr); + ret = kvm_vcpu_ioctl(CPU(s->cpu), KVM_SET_VAPIC_ADDR, &vapid_addr); if (ret < 0) { fprintf(stderr, "KVM: setting VAPIC address failed (%s)\n", strerror(-ret)); @@ -125,15 +125,15 @@ static void kvm_apic_vapic_base_update(APICCommonState *s) static void do_inject_external_nmi(void *data) { APICCommonState *s = data; - CPUX86State *env = &s->cpu->env; + CPUState *cpu = CPU(s->cpu); uint32_t lvt; int ret; - cpu_synchronize_state(env); + cpu_synchronize_state(&s->cpu->env); lvt = s->lvt[APIC_LVT_LINT1]; if (!(lvt & APIC_LVT_MASKED) && ((lvt >> 8) & 7) == APIC_DM_NMI) { - ret = kvm_vcpu_ioctl(env, KVM_NMI); + ret = kvm_vcpu_ioctl(cpu, KVM_NMI); if (ret < 0) { fprintf(stderr, "KVM: injection failed, NMI lost (%s)\n", strerror(-ret)); @@ -194,7 +194,7 @@ static void kvm_apic_class_init(ObjectClass *klass, void *data) k->external_nmi = kvm_apic_external_nmi; } -static TypeInfo kvm_apic_info = { +static const TypeInfo kvm_apic_info = { .name = "kvm-apic", .parent = TYPE_APIC_COMMON, .instance_size = sizeof(APICCommonState), diff --git a/hw/kvm/arm_gic.c b/hw/kvm/arm_gic.c new file mode 100644 index 0000000000..22b40b4f84 --- /dev/null +++ b/hw/kvm/arm_gic.c @@ -0,0 +1,167 @@ +/* + * ARM Generic Interrupt Controller using KVM in-kernel support + * + * Copyright (c) 2012 Linaro Limited + * Written by Peter Maydell + * + * 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) 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 . + */ + +#include "hw/sysbus.h" +#include "sysemu/kvm.h" +#include "kvm_arm.h" +#include "hw/arm_gic_internal.h" + +#define TYPE_KVM_ARM_GIC "kvm-arm-gic" +#define KVM_ARM_GIC(obj) \ + OBJECT_CHECK(GICState, (obj), TYPE_KVM_ARM_GIC) +#define KVM_ARM_GIC_CLASS(klass) \ + OBJECT_CLASS_CHECK(KVMARMGICClass, (klass), TYPE_KVM_ARM_GIC) +#define KVM_ARM_GIC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(KVMARMGICClass, (obj), TYPE_KVM_ARM_GIC) + +typedef struct KVMARMGICClass { + ARMGICCommonClass parent_class; + DeviceRealize parent_realize; + void (*parent_reset)(DeviceState *dev); +} KVMARMGICClass; + +static void kvm_arm_gic_set_irq(void *opaque, int irq, int level) +{ + /* Meaning of the 'irq' parameter: + * [0..N-1] : external interrupts + * [N..N+31] : PPI (internal) interrupts for CPU 0 + * [N+32..N+63] : PPI (internal interrupts for CPU 1 + * ... + * Convert this to the kernel's desired encoding, which + * has separate fields in the irq number for type, + * CPU number and interrupt number. + */ + GICState *s = (GICState *)opaque; + int kvm_irq, irqtype, cpu; + + if (irq < (s->num_irq - GIC_INTERNAL)) { + /* External interrupt. The kernel numbers these like the GIC + * hardware, with external interrupt IDs starting after the + * internal ones. + */ + irqtype = KVM_ARM_IRQ_TYPE_SPI; + cpu = 0; + irq += GIC_INTERNAL; + } else { + /* Internal interrupt: decode into (cpu, interrupt id) */ + irqtype = KVM_ARM_IRQ_TYPE_PPI; + irq -= (s->num_irq - GIC_INTERNAL); + cpu = irq / GIC_INTERNAL; + irq %= GIC_INTERNAL; + } + kvm_irq = (irqtype << KVM_ARM_IRQ_TYPE_SHIFT) + | (cpu << KVM_ARM_IRQ_VCPU_SHIFT) | irq; + + kvm_set_irq(kvm_state, kvm_irq, !!level); +} + +static void kvm_arm_gic_put(GICState *s) +{ + /* TODO: there isn't currently a kernel interface to set the GIC state */ +} + +static void kvm_arm_gic_get(GICState *s) +{ + /* TODO: there isn't currently a kernel interface to get the GIC state */ +} + +static void kvm_arm_gic_reset(DeviceState *dev) +{ + GICState *s = ARM_GIC_COMMON(dev); + KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); + + kgc->parent_reset(dev); + kvm_arm_gic_put(s); +} + +static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) +{ + int i; + GICState *s = KVM_ARM_GIC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + KVMARMGICClass *kgc = KVM_ARM_GIC_GET_CLASS(s); + + kgc->parent_realize(dev, errp); + if (error_is_set(errp)) { + return; + } + + i = s->num_irq - GIC_INTERNAL; + /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. + * GPIO array layout is thus: + * [0..N-1] SPIs + * [N..N+31] PPIs for CPU 0 + * [N+32..N+63] PPIs for CPU 1 + * ... + */ + i += (GIC_INTERNAL * s->num_cpu); + qdev_init_gpio_in(dev, kvm_arm_gic_set_irq, i); + /* We never use our outbound IRQ lines but provide them so that + * we maintain the same interface as the non-KVM GIC. + */ + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->parent_irq[i]); + } + /* Distributor */ + memory_region_init_reservation(&s->iomem, "kvm-gic_dist", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + kvm_arm_register_device(&s->iomem, + (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT) + | KVM_VGIC_V2_ADDR_TYPE_DIST); + /* CPU interface for current core. Unlike arm_gic, we don't + * provide the "interface for core #N" memory regions, because + * cores with a VGIC don't have those. + */ + memory_region_init_reservation(&s->cpuiomem[0], "kvm-gic_cpu", 0x1000); + sysbus_init_mmio(sbd, &s->cpuiomem[0]); + kvm_arm_register_device(&s->cpuiomem[0], + (KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT) + | KVM_VGIC_V2_ADDR_TYPE_CPU); +} + +static void kvm_arm_gic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ARMGICCommonClass *agcc = ARM_GIC_COMMON_CLASS(klass); + KVMARMGICClass *kgc = KVM_ARM_GIC_CLASS(klass); + + agcc->pre_save = kvm_arm_gic_get; + agcc->post_load = kvm_arm_gic_put; + kgc->parent_realize = dc->realize; + kgc->parent_reset = dc->reset; + dc->realize = kvm_arm_gic_realize; + dc->reset = kvm_arm_gic_reset; + dc->no_user = 1; +} + +static const TypeInfo kvm_arm_gic_info = { + .name = TYPE_KVM_ARM_GIC, + .parent = TYPE_ARM_GIC_COMMON, + .instance_size = sizeof(GICState), + .class_init = kvm_arm_gic_class_init, + .class_size = sizeof(KVMARMGICClass), +}; + +static void kvm_arm_gic_register_types(void) +{ + type_register_static(&kvm_arm_gic_info); +} + +type_init(kvm_arm_gic_register_types) diff --git a/hw/kvm/clock.c b/hw/kvm/clock.c index 6fcca95ada..fa40e283f7 100644 --- a/hw/kvm/clock.c +++ b/hw/kvm/clock.c @@ -76,7 +76,7 @@ static void kvmclock_vm_state_change(void *opaque, int running, return; } for (penv = first_cpu; penv != NULL; penv = penv->next_cpu) { - ret = kvm_vcpu_ioctl(penv, KVM_KVMCLOCK_CTRL, 0); + ret = kvm_vcpu_ioctl(ENV_GET_CPU(penv), KVM_KVMCLOCK_CTRL, 0); if (ret) { if (ret != -EINVAL) { fprintf(stderr, "%s: %s\n", __func__, strerror(-ret)); @@ -118,7 +118,7 @@ static void kvmclock_class_init(ObjectClass *klass, void *data) dc->vmsd = &kvmclock_vmsd; } -static TypeInfo kvmclock_info = { +static const TypeInfo kvmclock_info = { .name = "kvmclock", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(KVMClockState), diff --git a/hw/kvm/i8254.c b/hw/kvm/i8254.c index 57faf64ab2..04ad649b0e 100644 --- a/hw/kvm/i8254.c +++ b/hw/kvm/i8254.c @@ -302,7 +302,7 @@ static void kvm_pit_class_init(ObjectClass *klass, void *data) dc->props = kvm_pit_properties; } -static TypeInfo kvm_pit_info = { +static const TypeInfo kvm_pit_info = { .name = "kvm-pit", .parent = TYPE_PIT_COMMON, .instance_size = sizeof(KVMPITState), diff --git a/hw/kvm/i8259.c b/hw/kvm/i8259.c index 70e1d185de..5ae8b6819b 100644 --- a/hw/kvm/i8259.c +++ b/hw/kvm/i8259.c @@ -123,7 +123,7 @@ static void kvm_i8259_class_init(ObjectClass *klass, void *data) k->post_load = kvm_pic_put; } -static TypeInfo kvm_i8259_info = { +static const TypeInfo kvm_i8259_info = { .name = "kvm-i8259", .parent = TYPE_PIC_COMMON, .instance_size = sizeof(PICCommonState), diff --git a/hw/kvm/ioapic.c b/hw/kvm/ioapic.c index 30db6230b4..23877d4259 100644 --- a/hw/kvm/ioapic.c +++ b/hw/kvm/ioapic.c @@ -150,7 +150,7 @@ static void kvm_ioapic_class_init(ObjectClass *klass, void *data) dc->props = kvm_ioapic_properties; } -static TypeInfo kvm_ioapic_info = { +static const TypeInfo kvm_ioapic_info = { .name = "kvm-ioapic", .parent = TYPE_IOAPIC_COMMON, .instance_size = sizeof(KVMIOAPICState), diff --git a/hw/kvm/pci-assign.c b/hw/kvm/pci-assign.c index 410b6c6eeb..da64b5b86f 100644 --- a/hw/kvm/pci-assign.c +++ b/hw/kvm/pci-assign.c @@ -46,6 +46,7 @@ #define IORESOURCE_IRQ 0x00000400 #define IORESOURCE_DMA 0x00000800 #define IORESOURCE_PREFETCH 0x00002000 /* No side effects */ +#define IORESOURCE_MEM_64 0x00100000 //#define DEVICE_ASSIGNMENT_DEBUG @@ -442,9 +443,13 @@ static int assigned_dev_register_regions(PCIRegion *io_regions, /* handle memory io regions */ if (cur_region->type & IORESOURCE_MEM) { - int t = cur_region->type & IORESOURCE_PREFETCH - ? PCI_BASE_ADDRESS_MEM_PREFETCH - : PCI_BASE_ADDRESS_SPACE_MEMORY; + int t = PCI_BASE_ADDRESS_SPACE_MEMORY; + if (cur_region->type & IORESOURCE_PREFETCH) { + t |= PCI_BASE_ADDRESS_MEM_PREFETCH; + } + if (cur_region->type & IORESOURCE_MEM_64) { + t |= PCI_BASE_ADDRESS_MEM_TYPE_64; + } /* map physical memory */ pci_dev->v_addrs[i].u.r_virtbase = mmap(NULL, cur_region->size, @@ -632,7 +637,8 @@ again: rp->valid = 0; rp->resource_fd = -1; size = end - start + 1; - flags &= IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; + flags &= IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH + | IORESOURCE_MEM_64; if (size == 0 || (flags & ~IORESOURCE_PREFETCH) == 0) { continue; } @@ -930,8 +936,8 @@ retry: /* Retry with host-side MSI. There might be an IRQ conflict and * either the kernel or the device doesn't support sharing. */ error_report("Host-side INTx sharing not supported, " - "using MSI instead.\n" - "Some devices do not to work properly in this mode."); + "using MSI instead"); + error_printf("Some devices do not work properly in this mode.\n"); dev->features |= ASSIGNED_DEVICE_PREFER_MSI_MASK; goto retry; } @@ -1025,6 +1031,19 @@ static bool assigned_dev_msix_masked(MSIXTableEntry *entry) return (entry->ctrl & cpu_to_le32(0x1)) != 0; } +/* + * When MSI-X is first enabled the vector table typically has all the + * vectors masked, so we can't use that as the obvious test to figure out + * how many vectors to initially enable. Instead we look at the data field + * because this is what worked for pci-assign for a long time. This makes + * sure the physical MSI-X state tracks the guest's view, which is important + * for some VF/PF and PF/fw communication channels. + */ +static bool assigned_dev_msix_skipped(MSIXTableEntry *entry) +{ + return !entry->data; +} + static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev) { AssignedDevice *adev = DO_UPCAST(AssignedDevice, dev, pci_dev); @@ -1035,7 +1054,7 @@ static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev) /* Get the usable entry number for allocating */ for (i = 0; i < adev->msix_max; i++, entry++) { - if (assigned_dev_msix_masked(entry)) { + if (assigned_dev_msix_skipped(entry)) { continue; } entries_nr++; @@ -1064,7 +1083,7 @@ static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev) for (i = 0; i < adev->msix_max; i++, entry++) { adev->msi_virq[i] = -1; - if (assigned_dev_msix_masked(entry)) { + if (assigned_dev_msix_skipped(entry)) { continue; } @@ -1884,10 +1903,10 @@ static void assigned_dev_load_option_rom(AssignedDevice *dev) memset(ptr, 0xff, st.st_size); if (!fread(ptr, 1, st.st_size, fp)) { - error_report("pci-assign: Cannot read from host %s\n" - "\tDevice option ROM contents are probably invalid " - "(check dmesg).\n\tSkip option ROM probe with rombar=0, " - "or load from file with romfile=", rom_file); + error_report("pci-assign: Cannot read from host %s", rom_file); + error_printf("Device option ROM contents are probably invalid " + "(check dmesg).\nSkip option ROM probe with rombar=0, " + "or load from file with romfile=\n"); memory_region_destroy(&dev->dev.rom); goto close_rom; } diff --git a/hw/lan9118.c b/hw/lan9118.c index 5adf91199b..403fb868ae 100644 --- a/hw/lan9118.c +++ b/hw/lan9118.c @@ -10,11 +10,11 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "net/net.h" -#include "devices.h" +#include "hw/devices.h" #include "sysemu/sysemu.h" -#include "ptimer.h" +#include "hw/ptimer.h" /* For crc32 */ #include @@ -341,7 +341,7 @@ static void lan9118_update(lan9118_state *s) static void lan9118_mac_changed(lan9118_state *s) { - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } static void lan9118_reload_eeprom(lan9118_state *s) @@ -373,7 +373,7 @@ static void phy_update_irq(lan9118_state *s) static void phy_update_link(lan9118_state *s) { /* Autonegotiation status mirrors link status. */ - if (s->nic->nc.link_down) { + if (qemu_get_queue(s->nic)->link_down) { s->phy_status &= ~0x0024; s->phy_int |= PHY_INT_DOWN; } else { @@ -386,7 +386,7 @@ static void phy_update_link(lan9118_state *s) static void lan9118_set_link(NetClientState *nc) { - phy_update_link(DO_UPCAST(NICState, nc, nc)->opaque); + phy_update_link(qemu_get_nic_opaque(nc)); } static void phy_reset(lan9118_state *s) @@ -401,7 +401,7 @@ static void phy_reset(lan9118_state *s) static void lan9118_reset(DeviceState *d) { - lan9118_state *s = FROM_SYSBUS(lan9118_state, sysbus_from_qdev(d)); + lan9118_state *s = FROM_SYSBUS(lan9118_state, SYS_BUS_DEVICE(d)); s->irq_cfg &= (IRQ_TYPE | IRQ_POL); s->int_sts = 0; s->int_en = 0; @@ -512,7 +512,7 @@ static int lan9118_filter(lan9118_state *s, const uint8_t *addr) static ssize_t lan9118_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + lan9118_state *s = qemu_get_nic_opaque(nc); int fifo_len; int offset; int src_pos; @@ -657,9 +657,9 @@ static void do_tx_packet(lan9118_state *s) /* FIXME: Honor TX disable, and allow queueing of packets. */ if (s->phy_control & 0x4000) { /* This assumes the receive routine doesn't touch the VLANClient. */ - lan9118_receive(&s->nic->nc, s->txp->data, s->txp->len); + lan9118_receive(qemu_get_queue(s->nic), s->txp->data, s->txp->len); } else { - qemu_send_packet(&s->nic->nc, s->txp->data, s->txp->len); + qemu_send_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len); } s->txp->fifo_used = 0; @@ -1306,7 +1306,7 @@ static const MemoryRegionOps lan9118_16bit_mem_ops = { static void lan9118_cleanup(NetClientState *nc) { - lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + lan9118_state *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -1335,7 +1335,7 @@ static int lan9118_init1(SysBusDevice *dev) s->nic = qemu_new_nic(&net_lan9118_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->eeprom[0] = 0xa5; for (i = 0; i < 6; i++) { s->eeprom[i + 1] = s->conf.macaddr.a[i]; @@ -1368,7 +1368,7 @@ static void lan9118_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_lan9118; } -static TypeInfo lan9118_info = { +static const TypeInfo lan9118_info = { .name = "lan9118", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(lan9118_state), @@ -1391,7 +1391,7 @@ void lan9118_init(NICInfo *nd, uint32_t base, qemu_irq irq) dev = qdev_create(NULL, "lan9118"); qdev_set_nic_properties(dev, nd); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, base); sysbus_connect_irq(s, 0, irq); } diff --git a/hw/lance.c b/hw/lance.c index b7265c0fed..acfffaed31 100644 --- a/hw/lance.c +++ b/hw/lance.c @@ -35,12 +35,12 @@ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "net/net.h" #include "qemu/timer.h" #include "qemu/sockets.h" -#include "sun4m.h" -#include "pcnet.h" +#include "hw/sun4m.h" +#include "hw/pcnet.h" #include "trace.h" typedef struct { @@ -87,7 +87,7 @@ static const MemoryRegionOps lance_mem_ops = { static void lance_cleanup(NetClientState *nc) { - PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque; + PCNetState *d = qemu_get_nic_opaque(nc); pcnet_common_cleanup(d); } @@ -155,7 +155,7 @@ static void lance_class_init(ObjectClass *klass, void *data) dc->props = lance_properties; } -static TypeInfo lance_info = { +static const TypeInfo lance_info = { .name = "lance", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysBusPCNetState), diff --git a/hw/lm32.h b/hw/lm32.h index 4194c9a813..236686ef2b 100644 --- a/hw/lm32.h +++ b/hw/lm32.h @@ -11,7 +11,7 @@ static inline DeviceState *lm32_pic_init(qemu_irq cpu_irq) dev = qdev_create(NULL, "lm32-pic"); qdev_init_nofail(dev); - d = sysbus_from_qdev(dev); + d = SYS_BUS_DEVICE(dev); sysbus_connect_irq(d, 0, cpu_irq); return dev; diff --git a/hw/lm32/Makefile.objs b/hw/lm32/Makefile.objs index 4e1843c11d..4592fe5fc8 100644 --- a/hw/lm32/Makefile.objs +++ b/hw/lm32/Makefile.objs @@ -1,7 +1,3 @@ -# LM32 boards -obj-y += lm32_boards.o -obj-y += milkymist.o - # LM32 peripherals obj-y += lm32_pic.o obj-y += lm32_juart.o @@ -21,3 +17,7 @@ obj-y += milkymist-vgafb.o obj-y += framebuffer.o obj-y := $(addprefix ../,$(obj-y)) + +# LM32 boards +obj-y += lm32_boards.o +obj-y += milkymist.o diff --git a/hw/lm32_boards.c b/hw/lm32/lm32_boards.c similarity index 95% rename from hw/lm32_boards.c rename to hw/lm32/lm32_boards.c index 42e8b6b52a..db92948092 100644 --- a/hw/lm32_boards.c +++ b/hw/lm32/lm32_boards.c @@ -17,16 +17,16 @@ * License along with this library; if not, see . */ -#include "sysbus.h" -#include "hw.h" -#include "flash.h" -#include "devices.h" -#include "boards.h" -#include "loader.h" +#include "hw/sysbus.h" +#include "hw/hw.h" +#include "hw/flash.h" +#include "hw/devices.h" +#include "hw/boards.h" +#include "hw/loader.h" #include "sysemu/blockdev.h" #include "elf.h" -#include "lm32_hwsetup.h" -#include "lm32.h" +#include "hw/lm32_hwsetup.h" +#include "hw/lm32.h" #include "exec/address-spaces.h" typedef struct { @@ -41,12 +41,13 @@ typedef struct { static void cpu_irq_handler(void *opaque, int irq, int level) { - CPULM32State *env = opaque; + LM32CPU *cpu = opaque; + CPUState *cs = CPU(cpu); if (level) { - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } @@ -117,7 +118,7 @@ static void lm32_evr_init(QEMUMachineInitArgs *args) 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1); /* create irq lines */ - cpu_irq = qemu_allocate_irqs(cpu_irq_handler, env, 1); + cpu_irq = qemu_allocate_irqs(cpu_irq_handler, cpu, 1); env->pic_state = lm32_pic_init(*cpu_irq); for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(env->pic_state, i); @@ -287,14 +288,16 @@ static QEMUMachine lm32_evr_machine = { .name = "lm32-evr", .desc = "LatticeMico32 EVR32 eval system", .init = lm32_evr_init, - .is_default = 1 + .is_default = 1, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine lm32_uclinux_machine = { .name = "lm32-uclinux", .desc = "lm32 platform for uClinux and u-boot by Theobroma Systems", .init = lm32_uclinux_init, - .is_default = 0 + .is_default = 0, + DEFAULT_MACHINE_OPTIONS, }; static void lm32_machine_init(void) diff --git a/hw/milkymist.c b/hw/lm32/milkymist.c similarity index 93% rename from hw/milkymist.c rename to hw/lm32/milkymist.c index 0c23b672f3..b347cf964c 100644 --- a/hw/milkymist.c +++ b/hw/lm32/milkymist.c @@ -17,17 +17,17 @@ * License along with this library; if not, see . */ -#include "sysbus.h" -#include "hw.h" -#include "flash.h" +#include "hw/sysbus.h" +#include "hw/hw.h" +#include "hw/flash.h" #include "sysemu/sysemu.h" -#include "devices.h" -#include "boards.h" -#include "loader.h" +#include "hw/devices.h" +#include "hw/boards.h" +#include "hw/loader.h" #include "elf.h" #include "sysemu/blockdev.h" -#include "milkymist-hw.h" -#include "lm32.h" +#include "hw/milkymist-hw.h" +#include "hw/lm32.h" #include "exec/address-spaces.h" #define BIOS_FILENAME "mmone-bios.bin" @@ -46,12 +46,13 @@ typedef struct { static void cpu_irq_handler(void *opaque, int irq, int level) { - CPULM32State *env = opaque; + LM32CPU *cpu = opaque; + CPUState *cs = CPU(cpu); if (level) { - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } @@ -123,7 +124,7 @@ milkymist_init(QEMUMachineInitArgs *args) 0x00, 0x89, 0x00, 0x1d, 1); /* create irq lines */ - cpu_irq = qemu_allocate_irqs(cpu_irq_handler, env, 1); + cpu_irq = qemu_allocate_irqs(cpu_irq_handler, cpu, 1); env->pic_state = lm32_pic_init(*cpu_irq); for (i = 0; i < 32; i++) { irq[i] = qdev_get_gpio_in(env->pic_state, i); @@ -206,7 +207,8 @@ static QEMUMachine milkymist_machine = { .name = "milkymist", .desc = "Milkymist One", .init = milkymist_init, - .is_default = 0 + .is_default = 0, + DEFAULT_MACHINE_OPTIONS, }; static void milkymist_machine_init(void) diff --git a/hw/lm32_hwsetup.h b/hw/lm32_hwsetup.h index 853e9abc7b..3449bd8dfc 100644 --- a/hw/lm32_hwsetup.h +++ b/hw/lm32_hwsetup.h @@ -26,7 +26,7 @@ #define QEMU_HW_LM32_HWSETUP_H #include "qemu-common.h" -#include "loader.h" +#include "hw/loader.h" typedef struct { void *data; diff --git a/hw/lm32_juart.c b/hw/lm32_juart.c index 7c2d202d6a..472e9c25fd 100644 --- a/hw/lm32_juart.c +++ b/hw/lm32_juart.c @@ -17,12 +17,12 @@ * License along with this library; if not, see . */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "trace.h" #include "char/char.h" -#include "lm32_juart.h" +#include "hw/lm32_juart.h" enum { LM32_JUART_MIN_SAVE_VERSION = 0, @@ -144,7 +144,7 @@ static void lm32_juart_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_lm32_juart; } -static TypeInfo lm32_juart_info = { +static const TypeInfo lm32_juart_info = { .name = "lm32-juart", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LM32JuartState), diff --git a/hw/lm32_pic.c b/hw/lm32_pic.c index 42d5602cf0..d17c310d5c 100644 --- a/hw/lm32_pic.c +++ b/hw/lm32_pic.c @@ -19,12 +19,12 @@ #include -#include "hw.h" -#include "pc.h" +#include "hw/hw.h" +#include "hw/pc.h" #include "monitor/monitor.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "trace.h" -#include "lm32_pic.h" +#include "hw/lm32_pic.h" struct LM32PicState { SysBusDevice busdev; @@ -39,7 +39,7 @@ struct LM32PicState { typedef struct LM32PicState LM32PicState; static LM32PicState *pic; -void lm32_do_pic_info(Monitor *mon) +void lm32_do_pic_info(Monitor *mon, const QDict *qdict) { if (pic == NULL) { return; @@ -49,7 +49,7 @@ void lm32_do_pic_info(Monitor *mon) pic->im, pic->ip, pic->irq_state); } -void lm32_irq_info(Monitor *mon) +void lm32_irq_info(Monitor *mon, const QDict *qdict) { int i; uint32_t count; @@ -184,7 +184,7 @@ static void lm32_pic_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_lm32_pic; } -static TypeInfo lm32_pic_info = { +static const TypeInfo lm32_pic_info = { .name = "lm32-pic", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LM32PicState), diff --git a/hw/lm32_pic.h b/hw/lm32_pic.h index 14456f37cb..555680304e 100644 --- a/hw/lm32_pic.h +++ b/hw/lm32_pic.h @@ -8,7 +8,7 @@ uint32_t lm32_pic_get_im(DeviceState *d); void lm32_pic_set_ip(DeviceState *d, uint32_t ip); void lm32_pic_set_im(DeviceState *d, uint32_t im); -void lm32_do_pic_info(Monitor *mon); -void lm32_irq_info(Monitor *mon); +void lm32_do_pic_info(Monitor *mon, const QDict *qdict); +void lm32_irq_info(Monitor *mon, const QDict *qdict); #endif /* QEMU_HW_LM32_PIC_H */ diff --git a/hw/lm32_sys.c b/hw/lm32_sys.c index e3a9db9748..33a3b80ce7 100644 --- a/hw/lm32_sys.c +++ b/hw/lm32_sys.c @@ -28,8 +28,8 @@ * the test is passed or any non-zero value to it if the test is failed. */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "trace.h" #include "qemu/log.h" #include "qemu/error-report.h" @@ -157,7 +157,7 @@ static void lm32_sys_class_init(ObjectClass *klass, void *data) dc->props = lm32_sys_properties; } -static TypeInfo lm32_sys_info = { +static const TypeInfo lm32_sys_info = { .name = "lm32-sys", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LM32SysState), diff --git a/hw/lm32_timer.c b/hw/lm32_timer.c index bd4c346386..e06fac7082 100644 --- a/hw/lm32_timer.c +++ b/hw/lm32_timer.c @@ -21,11 +21,11 @@ * http://www.latticesemi.com/documents/mico32timer.pdf */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "trace.h" #include "qemu/timer.h" -#include "ptimer.h" +#include "hw/ptimer.h" #include "qemu/error-report.h" #define DEFAULT_FREQUENCY (50*1000000) @@ -215,7 +215,7 @@ static void lm32_timer_class_init(ObjectClass *klass, void *data) dc->props = lm32_timer_properties; } -static TypeInfo lm32_timer_info = { +static const TypeInfo lm32_timer_info = { .name = "lm32-timer", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LM32TimerState), diff --git a/hw/lm32_uart.c b/hw/lm32_uart.c index 89605b8e77..02f6f89174 100644 --- a/hw/lm32_uart.c +++ b/hw/lm32_uart.c @@ -22,8 +22,8 @@ */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "trace.h" #include "char/char.h" #include "qemu/error-report.h" @@ -281,7 +281,7 @@ static void lm32_uart_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_lm32_uart; } -static TypeInfo lm32_uart_info = { +static const TypeInfo lm32_uart_info = { .name = "lm32-uart", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(LM32UartState), diff --git a/hw/lm4549.c b/hw/lm4549.c index b3c2d5f25d..67335cba61 100644 --- a/hw/lm4549.c +++ b/hw/lm4549.c @@ -13,9 +13,9 @@ * It supports only one playback voice and no record voice. */ -#include "hw.h" +#include "hw/hw.h" #include "audio/audio.h" -#include "lm4549.h" +#include "hw/lm4549.h" #if 0 #define LM4549_DEBUG 1 diff --git a/hw/lm832x.c b/hw/lm832x.c index 3649e3d249..a064dfd172 100644 --- a/hw/lm832x.c +++ b/hw/lm832x.c @@ -18,8 +18,8 @@ * with this program; if not, see . */ -#include "hw.h" -#include "i2c.h" +#include "hw/hw.h" +#include "hw/i2c.h" #include "qemu/timer.h" #include "ui/console.h" @@ -476,7 +476,7 @@ static int lm8323_init(I2CSlave *i2c) void lm832x_key_event(DeviceState *dev, int key, int state) { - LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, I2C_SLAVE_FROM_QDEV(dev)); + LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, I2C_SLAVE(dev)); if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR)) return; @@ -506,7 +506,7 @@ static void lm8323_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_lm_kbd; } -static TypeInfo lm8323_info = { +static const TypeInfo lm8323_info = { .name = "lm8323", .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(LM823KbdState), diff --git a/hw/loader.c b/hw/loader.c index 3f59fcd14a..6ce66fb5bb 100644 --- a/hw/loader.c +++ b/hw/loader.c @@ -42,13 +42,13 @@ * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "disas/disas.h" #include "monitor/monitor.h" #include "sysemu/sysemu.h" -#include "uboot_image.h" -#include "loader.h" -#include "fw_cfg.h" +#include "hw/uboot_image.h" +#include "hw/loader.h" +#include "hw/fw_cfg.h" #include "exec/memory.h" #include "exec/address-spaces.h" @@ -260,7 +260,7 @@ static void *load_at(int fd, int offset, int size) #define elf_word uint32_t #define elf_sword int32_t #define bswapSZs bswap32s -#include "elf_ops.h" +#include "hw/elf_ops.h" #undef elfhdr #undef elf_phdr @@ -280,7 +280,7 @@ static void *load_at(int fd, int offset, int size) #define elf_sword int64_t #define bswapSZs bswap64s #define SZ 64 -#include "elf_ops.h" +#include "hw/elf_ops.h" /* return < 0 if error, otherwise the number of bytes loaded in memory */ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), @@ -533,7 +533,14 @@ typedef struct Rom Rom; struct Rom { char *name; char *path; + + /* datasize is the amount of memory allocated in "data". If datasize is less + * than romsize, it means that the area from datasize to romsize is filled + * with zeros. + */ size_t romsize; + size_t datasize; + uint8_t *data; int isrom; char *fw_dir; @@ -589,14 +596,15 @@ int rom_add_file(const char *file, const char *fw_dir, rom->fw_dir = g_strdup(fw_dir); rom->fw_file = g_strdup(file); } - rom->addr = addr; - rom->romsize = lseek(fd, 0, SEEK_END); - rom->data = g_malloc0(rom->romsize); + rom->addr = addr; + rom->romsize = lseek(fd, 0, SEEK_END); + rom->datasize = rom->romsize; + rom->data = g_malloc0(rom->datasize); lseek(fd, 0, SEEK_SET); - rc = read(fd, rom->data, rom->romsize); - if (rc != rom->romsize) { + rc = read(fd, rom->data, rom->datasize); + if (rc != rom->datasize) { fprintf(stderr, "rom: file %-20s: read error: rc=%d (expected %zd)\n", - rom->name, rc, rom->romsize); + rom->name, rc, rom->datasize); goto err; } close(fd); @@ -637,16 +645,37 @@ int rom_add_blob(const char *name, const void *blob, size_t len, { Rom *rom; - rom = g_malloc0(sizeof(*rom)); - rom->name = g_strdup(name); - rom->addr = addr; - rom->romsize = len; - rom->data = g_malloc0(rom->romsize); + rom = g_malloc0(sizeof(*rom)); + rom->name = g_strdup(name); + rom->addr = addr; + rom->romsize = len; + rom->datasize = len; + rom->data = g_malloc0(rom->datasize); memcpy(rom->data, blob, len); rom_insert(rom); return 0; } +/* This function is specific for elf program because we don't need to allocate + * all the rom. We just allocate the first part and the rest is just zeros. This + * is why romsize and datasize are different. Also, this function seize the + * memory ownership of "data", so we don't have to allocate and copy the buffer. + */ +int rom_add_elf_program(const char *name, void *data, size_t datasize, + size_t romsize, hwaddr addr) +{ + Rom *rom; + + rom = g_malloc0(sizeof(*rom)); + rom->name = g_strdup(name); + rom->addr = addr; + rom->datasize = datasize; + rom->romsize = romsize; + rom->data = data; + rom_insert(rom); + return 0; +} + int rom_add_vga(const char *file) { return rom_add_file(file, "vgaroms", 0, -1); @@ -668,7 +697,7 @@ static void rom_reset(void *unused) if (rom->data == NULL) { continue; } - cpu_physical_memory_write_rom(rom->addr, rom->data, rom->romsize); + cpu_physical_memory_write_rom(rom->addr, rom->data, rom->datasize); if (rom->isrom) { /* rom needs to be written only once */ g_free(rom->data); @@ -756,13 +785,33 @@ int rom_copy(uint8_t *dest, hwaddr addr, size_t size) d = dest + (rom->addr - addr); s = rom->data; - l = rom->romsize; + l = rom->datasize; if ((d + l) > (dest + size)) { l = dest - d; } memcpy(d, s, l); + + if (rom->romsize > rom->datasize) { + /* If datasize is less than romsize, it means that we didn't + * allocate all the ROM because the trailing data are only zeros. + */ + + d += l; + l = rom->romsize - rom->datasize; + + if ((d + l) > (dest + size)) { + /* Rom size doesn't fit in the destination area. Adjust to avoid + * overflow. + */ + l = dest - d; + } + + if (l > 0) { + memset(d, 0x0, l); + } + } } return (d + l) - dest; @@ -778,7 +827,7 @@ void *rom_ptr(hwaddr addr) return rom->data + (addr - rom->addr); } -void do_info_roms(Monitor *mon) +void do_info_roms(Monitor *mon, const QDict *qdict) { Rom *rom; diff --git a/hw/loader.h b/hw/loader.h index 26480ad8dd..0958f06934 100644 --- a/hw/loader.h +++ b/hw/loader.h @@ -1,5 +1,6 @@ #ifndef LOADER_H #define LOADER_H +#include "qapi/qmp/qdict.h" /* loader.c */ int get_image_size(const char *filename); @@ -26,11 +27,13 @@ int rom_add_file(const char *file, const char *fw_dir, hwaddr addr, int32_t bootindex); int rom_add_blob(const char *name, const void *blob, size_t len, hwaddr addr); +int rom_add_elf_program(const char *name, void *data, size_t datasize, + size_t romsize, hwaddr addr); int rom_load_all(void); void rom_set_fw(void *f); int rom_copy(uint8_t *dest, hwaddr addr, size_t size); void *rom_ptr(hwaddr addr); -void do_info_roms(Monitor *mon); +void do_info_roms(Monitor *mon, const QDict *qdict); #define rom_add_file_fixed(_f, _a, _i) \ rom_add_file(_f, NULL, _a, _i) diff --git a/hw/lpc_ich9.c b/hw/lpc_ich9.c index a068715c04..ff0a3092d5 100644 --- a/hw/lpc_ich9.c +++ b/hw/lpc_ich9.c @@ -28,21 +28,21 @@ * THE SOFTWARE. */ #include "qemu-common.h" -#include "hw.h" +#include "hw/hw.h" #include "qemu/range.h" -#include "isa.h" -#include "sysbus.h" -#include "pc.h" -#include "apm.h" -#include "ioapic.h" -#include "pci/pci.h" -#include "pci/pcie_host.h" -#include "pci/pci_bridge.h" -#include "ich9.h" -#include "acpi.h" -#include "acpi_ich9.h" -#include "pam.h" -#include "pci/pci_bus.h" +#include "hw/isa.h" +#include "hw/sysbus.h" +#include "hw/pc.h" +#include "hw/apm.h" +#include "hw/ioapic.h" +#include "hw/pci/pci.h" +#include "hw/pci/pcie_host.h" +#include "hw/pci/pci_bridge.h" +#include "hw/ich9.h" +#include "hw/acpi.h" +#include "hw/acpi_ich9.h" +#include "hw/pam.h" +#include "hw/pci/pci_bus.h" #include "exec/address-spaces.h" #include "sysemu/sysemu.h" @@ -158,6 +158,7 @@ static void ich9_cc_write(void *opaque, hwaddr addr, ich9_cc_addr_len(&addr, &len); memcpy(lpc->chip_config + addr, &val, len); + pci_bus_fire_intx_routing_notifier(lpc->d.bus); ich9_cc_update(lpc); } @@ -286,6 +287,32 @@ int ich9_lpc_map_irq(PCIDevice *pci_dev, int intx) return lpc->irr[PCI_SLOT(pci_dev->devfn)][intx]; } +PCIINTxRoute ich9_route_intx_pin_to_irq(void *opaque, int pirq_pin) +{ + ICH9LPCState *lpc = opaque; + PCIINTxRoute route; + int pic_irq; + int pic_dis; + + assert(0 <= pirq_pin); + assert(pirq_pin < ICH9_LPC_NB_PIRQS); + + route.mode = PCI_INTX_ENABLED; + ich9_lpc_pic_irq(lpc, pirq_pin, &pic_irq, &pic_dis); + if (!pic_dis) { + if (pic_irq < ICH9_LPC_PIC_NUM_PINS) { + route.irq = pic_irq; + } else { + route.mode = PCI_INTX_DISABLED; + route.irq = -1; + } + } else { + route.irq = ich9_pirq_to_gsi(pirq_pin); + } + + return route; +} + static int ich9_lpc_sci_irq(ICH9LPCState *lpc) { switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] & @@ -336,7 +363,7 @@ void ich9_lpc_pm_init(PCIDevice *lpc_pci, qemu_irq cmos_s3) qemu_irq *sci_irq; sci_irq = qemu_allocate_irqs(ich9_set_sci, lpc, 1); - ich9_pm_init(&lpc->pm, sci_irq[0], cmos_s3); + ich9_pm_init(lpc_pci, &lpc->pm, sci_irq[0], cmos_s3); ich9_lpc_reset(&lpc->d.qdev); } @@ -354,7 +381,7 @@ 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(first_cpu, CPU_INTERRUPT_SMI); + cpu_interrupt(CPU(x86_env_get_cpu(first_cpu)), CPU_INTERRUPT_SMI); } } @@ -405,6 +432,12 @@ static void ich9_lpc_config_write(PCIDevice *d, if (ranges_overlap(addr, len, ICH9_LPC_RCBA, 4)) { ich9_lpc_rcba_update(lpc, rbca_old); } + if (ranges_overlap(addr, len, ICH9_LPC_PIRQA_ROUT, 4)) { + pci_bus_fire_intx_routing_notifier(lpc->d.bus); + } + if (ranges_overlap(addr, len, ICH9_LPC_PIRQE_ROUT, 4)) { + pci_bus_fire_intx_routing_notifier(lpc->d.bus); + } } static void ich9_lpc_reset(DeviceState *qdev) @@ -433,6 +466,7 @@ static void ich9_lpc_reset(DeviceState *qdev) ich9_lpc_rcba_update(lpc, rbca_old); lpc->sci_level = 0; + lpc->rst_cnt = 0; } static const MemoryRegionOps rbca_mmio_ops = { @@ -465,6 +499,32 @@ static void ich9_lpc_machine_ready(Notifier *n, void *opaque) } } +/* reset control */ +static void ich9_rst_cnt_write(void *opaque, hwaddr addr, uint64_t val, + unsigned len) +{ + ICH9LPCState *lpc = opaque; + + if (val & 4) { + qemu_system_reset_request(); + return; + } + lpc->rst_cnt = val & 0xA; /* keep FULL_RST (bit 3) and SYS_RST (bit 1) */ +} + +static uint64_t ich9_rst_cnt_read(void *opaque, hwaddr addr, unsigned len) +{ + ICH9LPCState *lpc = opaque; + + return lpc->rst_cnt; +} + +static const MemoryRegionOps ich9_rst_cnt_ops = { + .read = ich9_rst_cnt_read, + .write = ich9_rst_cnt_write, + .endianness = DEVICE_LITTLE_ENDIAN +}; + static int ich9_lpc_initfn(PCIDevice *d) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); @@ -486,9 +546,32 @@ static int ich9_lpc_initfn(PCIDevice *d) lpc->machine_ready.notify = ich9_lpc_machine_ready; qemu_add_machine_init_done_notifier(&lpc->machine_ready); + memory_region_init_io(&lpc->rst_cnt_mem, &ich9_rst_cnt_ops, lpc, + "lpc-reset-control", 1); + memory_region_add_subregion_overlap(pci_address_space_io(d), + ICH9_RST_CNT_IOPORT, &lpc->rst_cnt_mem, + 1); + return 0; } +static bool ich9_rst_cnt_needed(void *opaque) +{ + ICH9LPCState *lpc = opaque; + + return (lpc->rst_cnt != 0); +} + +static const VMStateDescription vmstate_ich9_rst_cnt = { + .name = "ICH9LPC/rst_cnt", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(rst_cnt, ICH9LPCState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_ich9_lpc = { .name = "ICH9LPC", .version_id = 1, @@ -502,6 +585,13 @@ static const VMStateDescription vmstate_ich9_lpc = { VMSTATE_UINT8_ARRAY(chip_config, ICH9LPCState, ICH9_CC_SIZE), VMSTATE_UINT32(sci_level, ICH9LPCState), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmstate_ich9_rst_cnt, + .needed = ich9_rst_cnt_needed + }, + { 0 } } }; diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 0aafb00b58..5a8bf4d0e9 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -12,9 +12,9 @@ #include -#include "hw.h" -#include "pci/pci.h" -#include "scsi.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/scsi.h" #include "sysemu/dma.h" //#define DEBUG_LSI @@ -1670,12 +1670,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) } if (val & LSI_SCNTL1_RST) { if (!(s->sstat0 & LSI_SSTAT0_RST)) { - BusChild *kid; - - QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { - DeviceState *dev = kid->child; - device_reset(dev); - } + qbus_reset_all(&s->bus.qbus); s->sstat0 |= LSI_SSTAT0_RST; lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0); } @@ -2126,7 +2121,7 @@ static void lsi_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_lsi_scsi; } -static TypeInfo lsi_info = { +static const TypeInfo lsi_info = { .name = "lsi53c895a", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(LSIState), diff --git a/hw/m25p80.c b/hw/m25p80.c index d39265632b..55e9d0d37a 100644 --- a/hw/m25p80.c +++ b/hw/m25p80.c @@ -21,10 +21,10 @@ * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "sysemu/blockdev.h" -#include "ssi.h" -#include "devices.h" +#include "hw/ssi.h" +#include "hw/devices.h" #ifdef M25P80_ERR_DEBUG #define DB_PRINT(...) do { \ @@ -178,12 +178,11 @@ static const FlashPartInfo known_devices[] = { /* Numonyx -- n25q128 */ { INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) }, - - { }, }; typedef enum { NOP = 0, + WRSR = 0x1, WRDI = 0x4, RDSR = 0x5, WREN = 0x6, @@ -235,11 +234,23 @@ typedef struct Flash { int64_t dirty_page; - char *part_name; const FlashPartInfo *pi; } Flash; +typedef struct M25P80Class { + SSISlaveClass parent_class; + FlashPartInfo *pi; +} M25P80Class; + +#define TYPE_M25P80 "m25p80-generic" +#define M25P80(obj) \ + OBJECT_CHECK(Flash, (obj), TYPE_M25P80) +#define M25P80_CLASS(klass) \ + OBJECT_CLASS_CHECK(M25P80Class, (klass), TYPE_M25P80) +#define M25P80_GET_CLASS(obj) \ + OBJECT_GET_CLASS(M25P80Class, (obj), TYPE_M25P80) + static void bdrv_sync_complete(void *opaque, int ret) { /* do nothing. Masters do not directly interact with the backing store, @@ -358,6 +369,8 @@ static void complete_collecting_data(Flash *s) s->cur_addr |= s->data[1] << 8; s->cur_addr |= s->data[2]; + s->state = STATE_IDLE; + switch (s->cmd_in_progress) { case DPP: case QPP: @@ -377,6 +390,11 @@ static void complete_collecting_data(Flash *s) case ERASE_SECTOR: flash_erase(s, s->cur_addr, s->cmd_in_progress); break; + case WRSR: + if (s->write_enable) { + s->write_enable = false; + } + break; default: break; } @@ -441,6 +459,15 @@ static void decode_new_cmd(Flash *s, uint32_t value) s->state = STATE_COLLECTING_DATA; break; + case WRSR: + if (s->write_enable) { + s->needed_bytes = 1; + s->pos = 0; + s->len = 0; + s->state = STATE_COLLECTING_DATA; + } + break; + case WRDI: s->write_enable = false; break; @@ -554,23 +581,9 @@ static int m25p80_init(SSISlave *ss) { DriveInfo *dinfo; Flash *s = FROM_SSI_SLAVE(Flash, ss); - const FlashPartInfo *i; + M25P80Class *mc = M25P80_GET_CLASS(s); - if (!s->part_name) { /* default to actual m25p80 if no partname given */ - s->part_name = (char *)"m25p80"; - } - - i = known_devices; - for (i = known_devices;; i++) { - assert(i); - if (!i->part_name) { - fprintf(stderr, "Unknown SPI flash part: \"%s\"\n", s->part_name); - return 1; - } else if (!strcmp(i->part_name, s->part_name)) { - s->pi = i; - break; - } - } + s->pi = mc->pi; s->size = s->pi->sector_size * s->pi->n_sectors; s->dirty_page = -1; @@ -618,34 +631,42 @@ static const VMStateDescription vmstate_m25p80 = { } }; -static Property m25p80_properties[] = { - DEFINE_PROP_STRING("partname", Flash, part_name), - DEFINE_PROP_END_OF_LIST(), -}; - static void m25p80_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SSISlaveClass *k = SSI_SLAVE_CLASS(klass); + M25P80Class *mc = M25P80_CLASS(klass); k->init = m25p80_init; k->transfer = m25p80_transfer8; k->set_cs = m25p80_cs; k->cs_polarity = SSI_CS_LOW; - dc->props = m25p80_properties; dc->vmsd = &vmstate_m25p80; + mc->pi = data; } static const TypeInfo m25p80_info = { - .name = "m25p80", + .name = TYPE_M25P80, .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(Flash), - .class_init = m25p80_class_init, + .class_size = sizeof(M25P80Class), + .abstract = true, }; static void m25p80_register_types(void) { + int i; + type_register_static(&m25p80_info); + for (i = 0; i < ARRAY_SIZE(known_devices); ++i) { + TypeInfo ti = { + .name = known_devices[i].part_name, + .parent = TYPE_M25P80, + .class_init = m25p80_class_init, + .class_data = (void *)&known_devices[i], + }; + type_register(&ti); + } } type_init(m25p80_register_types) diff --git a/hw/m48t59.c b/hw/m48t59.c index 393c5c049a..39a9d808cd 100644 --- a/hw/m48t59.c +++ b/hw/m48t59.c @@ -21,12 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "nvram.h" +#include "hw/hw.h" +#include "hw/nvram.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" -#include "sysbus.h" -#include "isa.h" +#include "hw/sysbus.h" +#include "hw/isa.h" #include "exec/address-spaces.h" //#define DEBUG_NVRAM @@ -646,7 +646,7 @@ M48t59State *m48t59_init(qemu_irq IRQ, hwaddr mem_base, qdev_prop_set_uint32(dev, "size", size); qdev_prop_set_uint32(dev, "io_base", io_base); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); d = FROM_SYSBUS(M48t59SysBusState, s); state = &d->state; sysbus_connect_irq(s, 0, IRQ); @@ -738,7 +738,7 @@ static void m48t59_init_class_isa1(ObjectClass *klass, void *data) dc->props = m48t59_isa_properties; } -static TypeInfo m48t59_isa_info = { +static const TypeInfo m48t59_isa_info = { .name = "m48t59_isa", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(M48t59ISAState), @@ -762,7 +762,7 @@ static void m48t59_class_init(ObjectClass *klass, void *data) dc->props = m48t59_properties; } -static TypeInfo m48t59_info = { +static const TypeInfo m48t59_info = { .name = "m48t59", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(M48t59SysBusState), diff --git a/hw/m68k/Makefile.objs b/hw/m68k/Makefile.objs index 93b6d25baf..ede32a7c4e 100644 --- a/hw/m68k/Makefile.objs +++ b/hw/m68k/Makefile.objs @@ -1,4 +1,8 @@ -obj-y = an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o -obj-y += dummy_m68k.o +obj-y = mcf_uart.o mcf_fec.o obj-y := $(addprefix ../,$(obj-y)) + +obj-y += an5206.o mcf5208.o +obj-y += dummy_m68k.o + +obj-y += mcf5206.o mcf_intc.o diff --git a/hw/an5206.c b/hw/m68k/an5206.c similarity index 89% rename from hw/an5206.c rename to hw/m68k/an5206.c index dcfe34b3ae..7c21c66cde 100644 --- a/hw/an5206.c +++ b/hw/m68k/an5206.c @@ -6,10 +6,10 @@ * This code is licensed under the GPL */ -#include "hw.h" -#include "mcf.h" -#include "boards.h" -#include "loader.h" +#include "hw/hw.h" +#include "hw/mcf.h" +#include "hw/boards.h" +#include "hw/loader.h" #include "elf.h" #include "exec/address-spaces.h" @@ -24,6 +24,7 @@ static void an5206_init(QEMUMachineInitArgs *args) ram_addr_t ram_size = args->ram_size; const char *cpu_model = args->cpu_model; const char *kernel_filename = args->kernel_filename; + M68kCPU *cpu; CPUM68KState *env; int kernel_size; uint64_t elf_entry; @@ -32,12 +33,14 @@ static void an5206_init(QEMUMachineInitArgs *args) MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *sram = g_new(MemoryRegion, 1); - if (!cpu_model) + if (!cpu_model) { cpu_model = "m5206"; - env = cpu_init(cpu_model); - if (!env) { + } + cpu = cpu_m68k_init(cpu_model); + if (!cpu) { hw_error("Unable to find m68k CPU definition\n"); } + env = &cpu->env; /* Initialize CPU registers. */ env->vbr = 0; @@ -55,7 +58,7 @@ static void an5206_init(QEMUMachineInitArgs *args) vmstate_register_ram_global(sram); memory_region_add_subregion(address_space_mem, AN5206_RAMBAR_ADDR, sram); - mcf5206_init(address_space_mem, AN5206_MBAR_ADDR, env); + mcf5206_init(address_space_mem, AN5206_MBAR_ADDR, cpu); /* Load kernel. */ if (!kernel_filename) { @@ -86,6 +89,7 @@ static QEMUMachine an5206_machine = { .name = "an5206", .desc = "Arnewsh 5206", .init = an5206_init, + DEFAULT_MACHINE_OPTIONS, }; static void an5206_machine_init(void) diff --git a/hw/dummy_m68k.c b/hw/m68k/dummy_m68k.c similarity index 95% rename from hw/dummy_m68k.c rename to hw/m68k/dummy_m68k.c index 7878cc3e15..544d56b59d 100644 --- a/hw/dummy_m68k.c +++ b/hw/m68k/dummy_m68k.c @@ -6,9 +6,9 @@ * This code is licensed under the GPL */ -#include "hw.h" -#include "boards.h" -#include "loader.h" +#include "hw/hw.h" +#include "hw/boards.h" +#include "hw/loader.h" #include "elf.h" #include "exec/address-spaces.h" @@ -73,6 +73,7 @@ static QEMUMachine dummy_m68k_machine = { .name = "dummy", .desc = "Dummy board", .init = dummy_m68k_init, + DEFAULT_MACHINE_OPTIONS, }; static void dummy_m68k_machine_init(void) diff --git a/hw/mcf5206.c b/hw/m68k/mcf5206.c similarity index 98% rename from hw/mcf5206.c rename to hw/m68k/mcf5206.c index fe7a48864f..58cd8d46c9 100644 --- a/hw/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -5,10 +5,10 @@ * * This code is licensed under the GPL */ -#include "hw.h" -#include "mcf.h" +#include "hw/hw.h" +#include "hw/mcf.h" #include "qemu/timer.h" -#include "ptimer.h" +#include "hw/ptimer.h" #include "sysemu/sysemu.h" #include "exec/address-spaces.h" @@ -145,7 +145,7 @@ static m5206_timer_state *m5206_timer_init(qemu_irq irq) /* System Integration Module. */ typedef struct { - CPUM68KState *env; + M68kCPU *cpu; MemoryRegion iomem; m5206_timer_state *timer[2]; void *uart[2]; @@ -226,7 +226,7 @@ static void m5206_mbar_update(m5206_mbar_state *s) level = 0; vector = 0; } - m68k_set_irq_level(s->env, level, vector); + m68k_set_irq_level(s->cpu, level, vector); } static void m5206_mbar_set_irq(void *opaque, int irq, int level) @@ -359,7 +359,7 @@ static void m5206_mbar_write(m5206_mbar_state *s, uint32_t offset, /* Internal peripherals use a variety of register widths. This lookup table allows a single routine to handle all of them. */ -static const int m5206_mbar_width[] = +static const uint8_t m5206_mbar_width[] = { /* 000-040 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, /* 040-080 */ 1, 2, 2, 2, 4, 1, 2, 4, 1, 2, 4, 2, 2, 4, 2, 2, @@ -525,7 +525,7 @@ static const MemoryRegionOps m5206_mbar_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -qemu_irq *mcf5206_init(MemoryRegion *sysmem, uint32_t base, CPUM68KState *env) +qemu_irq *mcf5206_init(MemoryRegion *sysmem, uint32_t base, M68kCPU *cpu) { m5206_mbar_state *s; qemu_irq *pic; @@ -541,7 +541,7 @@ qemu_irq *mcf5206_init(MemoryRegion *sysmem, uint32_t base, CPUM68KState *env) s->timer[1] = m5206_timer_init(pic[10]); s->uart[0] = mcf_uart_init(pic[12], serial_hds[0]); s->uart[1] = mcf_uart_init(pic[13], serial_hds[1]); - s->env = env; + s->cpu = cpu; m5206_mbar_reset(s); return pic; diff --git a/hw/mcf5208.c b/hw/m68k/mcf5208.c similarity index 96% rename from hw/mcf5208.c rename to hw/m68k/mcf5208.c index c1816cc9d1..748bf56983 100644 --- a/hw/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -5,14 +5,14 @@ * * This code is licensed under the GPL */ -#include "hw.h" -#include "mcf.h" +#include "hw/hw.h" +#include "hw/mcf.h" #include "qemu/timer.h" -#include "ptimer.h" +#include "hw/ptimer.h" #include "sysemu/sysemu.h" #include "net/net.h" -#include "boards.h" -#include "loader.h" +#include "hw/boards.h" +#include "hw/loader.h" #include "elf.h" #include "exec/address-spaces.h" @@ -192,6 +192,7 @@ static void mcf5208evb_init(QEMUMachineInitArgs *args) ram_addr_t ram_size = args->ram_size; const char *cpu_model = args->cpu_model; const char *kernel_filename = args->kernel_filename; + M68kCPU *cpu; CPUM68KState *env; int kernel_size; uint64_t elf_entry; @@ -201,13 +202,15 @@ static void mcf5208evb_init(QEMUMachineInitArgs *args) MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *sram = g_new(MemoryRegion, 1); - if (!cpu_model) + if (!cpu_model) { cpu_model = "m5208"; - env = cpu_init(cpu_model); - if (!env) { + } + cpu = cpu_m68k_init(cpu_model); + if (!cpu) { fprintf(stderr, "Unable to find m68k CPU definition\n"); exit(1); } + env = &cpu->env; /* Initialize CPU registers. */ env->vbr = 0; @@ -224,7 +227,7 @@ static void mcf5208evb_init(QEMUMachineInitArgs *args) memory_region_add_subregion(address_space_mem, 0x80000000, sram); /* Internal peripherals. */ - pic = mcf_intc_init(address_space_mem, 0xfc048000, env); + pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu); mcf_uart_mm_init(address_space_mem, 0xfc060000, pic[26], serial_hds[0]); mcf_uart_mm_init(address_space_mem, 0xfc064000, pic[27], serial_hds[1]); @@ -292,6 +295,7 @@ static QEMUMachine mcf5208evb_machine = { .desc = "MCF5206EVB", .init = mcf5208evb_init, .is_default = 1, + DEFAULT_MACHINE_OPTIONS, }; static void mcf5208evb_machine_init(void) diff --git a/hw/mcf_intc.c b/hw/m68k/mcf_intc.c similarity index 95% rename from hw/mcf_intc.c rename to hw/m68k/mcf_intc.c index 3bed3a2e4c..fff27b34aa 100644 --- a/hw/mcf_intc.c +++ b/hw/m68k/mcf_intc.c @@ -5,8 +5,8 @@ * * This code is licensed under the GPL */ -#include "hw.h" -#include "mcf.h" +#include "hw/hw.h" +#include "hw/mcf.h" #include "exec/address-spaces.h" typedef struct { @@ -16,7 +16,7 @@ typedef struct { uint64_t ifr; uint64_t enabled; uint8_t icr[64]; - CPUM68KState *env; + M68kCPU *cpu; int active_vector; } mcf_intc_state; @@ -40,7 +40,7 @@ static void mcf_intc_update(mcf_intc_state *s) } } s->active_vector = ((best == 64) ? 24 : (best + 64)); - m68k_set_irq_level(s->env, best_level, s->active_vector); + m68k_set_irq_level(s->cpu, best_level, s->active_vector); } static uint64_t mcf_intc_read(void *opaque, hwaddr addr, @@ -139,12 +139,12 @@ static const MemoryRegionOps mcf_intc_ops = { qemu_irq *mcf_intc_init(MemoryRegion *sysmem, hwaddr base, - CPUM68KState *env) + M68kCPU *cpu) { mcf_intc_state *s; s = g_malloc0(sizeof(mcf_intc_state)); - s->env = env; + s->cpu = cpu; mcf_intc_reset(s); memory_region_init_io(&s->iomem, &mcf_intc_ops, s, "mcf", 0x100); diff --git a/hw/mac_dbdma.c b/hw/mac_dbdma.c index b894ab21aa..61d2f35c8e 100644 --- a/hw/mac_dbdma.c +++ b/hw/mac_dbdma.c @@ -36,9 +36,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "isa.h" -#include "mac_dbdma.h" +#include "hw/hw.h" +#include "hw/isa.h" +#include "hw/mac_dbdma.h" #include "qemu/main-loop.h" /* debug DBDMA */ @@ -688,6 +688,10 @@ dbdma_control_write(DBDMA_channel *ch) if ((ch->regs[DBDMA_STATUS] & RUN) && !(status & RUN)) { /* RUN is cleared */ status &= ~(ACTIVE|DEAD); + if ((status & FLUSH) && ch->flush) { + ch->flush(&ch->io); + status &= ~FLUSH; + } } DBDMA_DPRINTF(" status 0x%08x\n", status); diff --git a/hw/mac_nvram.c b/hw/mac_nvram.c index 71093c2b10..ed32bde5ab 100644 --- a/hw/mac_nvram.c +++ b/hw/mac_nvram.c @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "firmware_abi.h" +#include "hw/hw.h" +#include "hw/firmware_abi.h" #include "sysemu/sysemu.h" -#include "ppc_mac.h" +#include "hw/ppc/mac.h" /* debug NVR */ //#define DEBUG_NVR @@ -37,37 +37,29 @@ #define NVR_DPRINTF(fmt, ...) #endif -struct MacIONVRAMState { - uint32_t size; - MemoryRegion mem; - unsigned int it_shift; - uint8_t *data; -}; - #define DEF_SYSTEM_SIZE 0xc10 /* Direct access to NVRAM */ -uint32_t macio_nvram_read (void *opaque, uint32_t addr) +uint8_t macio_nvram_read(MacIONVRAMState *s, uint32_t addr) { - MacIONVRAMState *s = opaque; uint32_t ret; - if (addr < s->size) + if (addr < s->size) { ret = s->data[addr]; - else + } else { ret = -1; - NVR_DPRINTF("read addr %04x val %x\n", addr, ret); + } + NVR_DPRINTF("read addr %04" PRIx32 " val %" PRIx8 "\n", addr, ret); return ret; } -void macio_nvram_write (void *opaque, uint32_t addr, uint32_t val) +void macio_nvram_write(MacIONVRAMState *s, uint32_t addr, uint8_t val) { - MacIONVRAMState *s = opaque; - - NVR_DPRINTF("write addr %04x val %x\n", addr, val); - if (addr < s->size) + NVR_DPRINTF("write addr %04" PRIx32 " val %" PRIx8 "\n", addr, val); + if (addr < s->size) { s->data[addr] = val; + } } /* macio style NVRAM device */ @@ -78,7 +70,7 @@ static void macio_nvram_writeb(void *opaque, hwaddr addr, addr = (addr >> s->it_shift) & (s->size - 1); s->data[addr] = value; - NVR_DPRINTF("writeb addr %04x val %x\n", (int)addr, value); + NVR_DPRINTF("writeb addr %04" PHYS_PRIx " val %" PRIx64 "\n", addr, value); } static uint64_t macio_nvram_readb(void *opaque, hwaddr addr, @@ -97,7 +89,7 @@ static uint64_t macio_nvram_readb(void *opaque, hwaddr addr, static const MemoryRegionOps macio_nvram_ops = { .read = macio_nvram_readb, .write = macio_nvram_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_BIG_ENDIAN, }; static const VMStateDescription vmstate_macio_nvram = { @@ -112,32 +104,56 @@ static const VMStateDescription vmstate_macio_nvram = { }; -static void macio_nvram_reset(void *opaque) +static void macio_nvram_reset(DeviceState *dev) { } -MacIONVRAMState *macio_nvram_init (hwaddr size, - unsigned int it_shift) +static void macio_nvram_realizefn(DeviceState *dev, Error **errp) { - MacIONVRAMState *s; + SysBusDevice *d = SYS_BUS_DEVICE(dev); + MacIONVRAMState *s = MACIO_NVRAM(dev); - s = g_malloc0(sizeof(MacIONVRAMState)); - s->data = g_malloc0(size); - s->size = size; - s->it_shift = it_shift; + s->data = g_malloc0(s->size); memory_region_init_io(&s->mem, &macio_nvram_ops, s, "macio-nvram", - size << it_shift); - vmstate_register(NULL, -1, &vmstate_macio_nvram, s); - qemu_register_reset(macio_nvram_reset, s); - - return s; + s->size << s->it_shift); + sysbus_init_mmio(d, &s->mem); } -void macio_nvram_setup_bar(MacIONVRAMState *s, MemoryRegion *bar, - hwaddr mem_base) +static void macio_nvram_unrealizefn(DeviceState *dev, Error **errp) { - memory_region_add_subregion(bar, mem_base, &s->mem); + MacIONVRAMState *s = MACIO_NVRAM(dev); + + g_free(s->data); +} + +static Property macio_nvram_properties[] = { + DEFINE_PROP_UINT32("size", MacIONVRAMState, size, 0), + DEFINE_PROP_UINT32("it_shift", MacIONVRAMState, it_shift, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void macio_nvram_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = macio_nvram_realizefn; + dc->unrealize = macio_nvram_unrealizefn; + dc->reset = macio_nvram_reset; + dc->vmsd = &vmstate_macio_nvram; + dc->props = macio_nvram_properties; +} + +static const TypeInfo macio_nvram_type_info = { + .name = TYPE_MACIO_NVRAM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MacIONVRAMState), + .class_init = macio_nvram_class_init, +}; + +static void macio_nvram_register_types(void) +{ + type_register_static(&macio_nvram_type_info); } /* Set up a system OpenBIOS NVRAM partition */ @@ -176,3 +192,5 @@ void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len) end = len; OpenBIOS_finish_partition(part_header, end - start); } + +type_init(macio_nvram_register_types) diff --git a/hw/macio.c b/hw/macio.c index 362afdc7ec..e91143e331 100644 --- a/hw/macio.c +++ b/hw/macio.c @@ -22,119 +22,284 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "ppc_mac.h" -#include "pci/pci.h" -#include "escc.h" +#include "hw/hw.h" +#include "hw/ppc/mac.h" +#include "hw/pci/pci.h" +#include "hw/mac_dbdma.h" +#include "hw/escc.h" + +#define TYPE_MACIO "macio" +#define MACIO(obj) OBJECT_CHECK(MacIOState, (obj), TYPE_MACIO) typedef struct MacIOState { + /*< private >*/ PCIDevice parent; - int is_oldworld; + /*< public >*/ + MemoryRegion bar; + CUDAState cuda; + void *dbdma; MemoryRegion *pic_mem; - MemoryRegion *dbdma_mem; - MemoryRegion *cuda_mem; MemoryRegion *escc_mem; - void *nvram; - int nb_ide; - MemoryRegion *ide_mem[4]; } MacIOState; +#define OLDWORLD_MACIO(obj) \ + OBJECT_CHECK(OldWorldMacIOState, (obj), TYPE_OLDWORLD_MACIO) + +typedef struct OldWorldMacIOState { + /*< private >*/ + MacIOState parent_obj; + /*< public >*/ + + qemu_irq irqs[3]; + + MacIONVRAMState nvram; + MACIOIDEState ide; +} OldWorldMacIOState; + +#define NEWWORLD_MACIO(obj) \ + OBJECT_CHECK(NewWorldMacIOState, (obj), TYPE_NEWWORLD_MACIO) + +typedef struct NewWorldMacIOState { + /*< private >*/ + MacIOState parent_obj; + /*< public >*/ + qemu_irq irqs[5]; + MACIOIDEState ide[2]; +} NewWorldMacIOState; + static void macio_bar_setup(MacIOState *macio_state) { - int i; MemoryRegion *bar = &macio_state->bar; - memory_region_init(bar, "macio", 0x80000); - if (macio_state->pic_mem) { - if (macio_state->is_oldworld) { - /* Heathrow PIC */ - memory_region_add_subregion(bar, 0x00000, macio_state->pic_mem); - } else { - /* OpenPIC */ - memory_region_add_subregion(bar, 0x40000, macio_state->pic_mem); - } - } - if (macio_state->dbdma_mem) { - memory_region_add_subregion(bar, 0x08000, macio_state->dbdma_mem); - } if (macio_state->escc_mem) { memory_region_add_subregion(bar, 0x13000, macio_state->escc_mem); } - if (macio_state->cuda_mem) { - memory_region_add_subregion(bar, 0x16000, macio_state->cuda_mem); - } - for (i = 0; i < macio_state->nb_ide; i++) { - if (macio_state->ide_mem[i]) { - memory_region_add_subregion(bar, 0x1f000 + (i * 0x1000), - macio_state->ide_mem[i]); - } - } - if (macio_state->nvram != NULL) - macio_nvram_setup_bar(macio_state->nvram, bar, 0x60000); } -static int macio_initfn(PCIDevice *d) +static int macio_common_initfn(PCIDevice *d) { + MacIOState *s = MACIO(d); + SysBusDevice *sysbus_dev; + int ret; + d->config[0x3d] = 0x01; // interrupt on pin 1 + + ret = qdev_init(DEVICE(&s->cuda)); + if (ret < 0) { + return ret; + } + sysbus_dev = SYS_BUS_DEVICE(&s->cuda); + memory_region_add_subregion(&s->bar, 0x16000, + sysbus_mmio_get_region(sysbus_dev, 0)); + + macio_bar_setup(s); + pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar); + return 0; } +static int macio_oldworld_initfn(PCIDevice *d) +{ + MacIOState *s = MACIO(d); + OldWorldMacIOState *os = OLDWORLD_MACIO(d); + SysBusDevice *sysbus_dev; + int ret = macio_common_initfn(d); + if (ret < 0) { + return ret; + } + + sysbus_dev = SYS_BUS_DEVICE(&s->cuda); + sysbus_connect_irq(sysbus_dev, 0, os->irqs[0]); + + ret = qdev_init(DEVICE(&os->nvram)); + if (ret < 0) { + return ret; + } + sysbus_dev = SYS_BUS_DEVICE(&os->nvram); + memory_region_add_subregion(&s->bar, 0x60000, + sysbus_mmio_get_region(sysbus_dev, 0)); + pmac_format_nvram_partition(&os->nvram, os->nvram.size); + + if (s->pic_mem) { + /* Heathrow PIC */ + memory_region_add_subregion(&s->bar, 0x00000, s->pic_mem); + } + + sysbus_dev = SYS_BUS_DEVICE(&os->ide); + sysbus_connect_irq(sysbus_dev, 0, os->irqs[1]); + sysbus_connect_irq(sysbus_dev, 1, os->irqs[2]); + macio_ide_register_dma(&os->ide, s->dbdma, 0x16); + ret = qdev_init(DEVICE(&os->ide)); + if (ret < 0) { + return ret; + } + + return 0; +} + +static void macio_oldworld_init(Object *obj) +{ + MacIOState *s = MACIO(obj); + OldWorldMacIOState *os = OLDWORLD_MACIO(obj); + DeviceState *dev; + + qdev_init_gpio_out(DEVICE(obj), os->irqs, ARRAY_SIZE(os->irqs)); + + object_initialize(&os->nvram, TYPE_MACIO_NVRAM); + dev = DEVICE(&os->nvram); + qdev_prop_set_uint32(dev, "size", 0x2000); + qdev_prop_set_uint32(dev, "it_shift", 4); + + object_initialize(&os->ide, TYPE_MACIO_IDE); + qdev_set_parent_bus(DEVICE(&os->ide), sysbus_get_default()); + memory_region_add_subregion(&s->bar, 0x1f000 + (1 * 0x1000), &os->ide.mem); + object_property_add_child(obj, "ide", OBJECT(&os->ide), NULL); +} + +static int macio_newworld_initfn(PCIDevice *d) +{ + MacIOState *s = MACIO(d); + NewWorldMacIOState *ns = NEWWORLD_MACIO(d); + SysBusDevice *sysbus_dev; + int ret = macio_common_initfn(d); + if (ret < 0) { + return ret; + } + + sysbus_dev = SYS_BUS_DEVICE(&s->cuda); + sysbus_connect_irq(sysbus_dev, 0, ns->irqs[0]); + + if (s->pic_mem) { + /* OpenPIC */ + memory_region_add_subregion(&s->bar, 0x40000, s->pic_mem); + } + + sysbus_dev = SYS_BUS_DEVICE(&ns->ide[0]); + sysbus_connect_irq(sysbus_dev, 0, ns->irqs[1]); + sysbus_connect_irq(sysbus_dev, 1, ns->irqs[2]); + macio_ide_register_dma(&ns->ide[0], s->dbdma, 0x16); + ret = qdev_init(DEVICE(&ns->ide[0])); + if (ret < 0) { + return ret; + } + + sysbus_dev = SYS_BUS_DEVICE(&ns->ide[1]); + sysbus_connect_irq(sysbus_dev, 0, ns->irqs[3]); + sysbus_connect_irq(sysbus_dev, 1, ns->irqs[4]); + macio_ide_register_dma(&ns->ide[1], s->dbdma, 0x1a); + ret = qdev_init(DEVICE(&ns->ide[1])); + if (ret < 0) { + return ret; + } + + return 0; +} + +static void macio_newworld_init(Object *obj) +{ + MacIOState *s = MACIO(obj); + NewWorldMacIOState *ns = NEWWORLD_MACIO(obj); + int i; + gchar *name; + + qdev_init_gpio_out(DEVICE(obj), ns->irqs, ARRAY_SIZE(ns->irqs)); + + for (i = 0; i < 2; i++) { + object_initialize(&ns->ide[i], TYPE_MACIO_IDE); + qdev_set_parent_bus(DEVICE(&ns->ide[i]), sysbus_get_default()); + memory_region_add_subregion(&s->bar, 0x1f000 + ((i + 1) * 0x1000), + &ns->ide[i].mem); + name = g_strdup_printf("ide[%i]", i); + object_property_add_child(obj, name, OBJECT(&ns->ide[i]), NULL); + g_free(name); + } +} + +static void macio_instance_init(Object *obj) +{ + MacIOState *s = MACIO(obj); + MemoryRegion *dbdma_mem; + + memory_region_init(&s->bar, "macio", 0x80000); + + object_initialize(&s->cuda, TYPE_CUDA); + qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); + object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL); + + s->dbdma = DBDMA_init(&dbdma_mem); + memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem); +} + +static void macio_oldworld_class_init(ObjectClass *oc, void *data) +{ + PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); + + pdc->init = macio_oldworld_initfn; + pdc->device_id = PCI_DEVICE_ID_APPLE_343S1201; +} + +static void macio_newworld_class_init(ObjectClass *oc, void *data) +{ + PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); + + pdc->init = macio_newworld_initfn; + pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL; +} + static void macio_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = macio_initfn; k->vendor_id = PCI_VENDOR_ID_APPLE; k->class_id = PCI_CLASS_OTHERS << 8; } -static TypeInfo macio_info = { - .name = "macio", +static const TypeInfo macio_oldworld_type_info = { + .name = TYPE_OLDWORLD_MACIO, + .parent = TYPE_MACIO, + .instance_size = sizeof(OldWorldMacIOState), + .instance_init = macio_oldworld_init, + .class_init = macio_oldworld_class_init, +}; + +static const TypeInfo macio_newworld_type_info = { + .name = TYPE_NEWWORLD_MACIO, + .parent = TYPE_MACIO, + .instance_size = sizeof(NewWorldMacIOState), + .instance_init = macio_newworld_init, + .class_init = macio_newworld_class_init, +}; + +static const TypeInfo macio_type_info = { + .name = TYPE_MACIO, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(MacIOState), + .instance_init = macio_instance_init, + .abstract = true, .class_init = macio_class_init, }; static void macio_register_types(void) { - type_register_static(&macio_info); + type_register_static(&macio_type_info); + type_register_static(&macio_oldworld_type_info); + type_register_static(&macio_newworld_type_info); } type_init(macio_register_types) -void macio_init (PCIBus *bus, int device_id, int is_oldworld, - MemoryRegion *pic_mem, MemoryRegion *dbdma_mem, - MemoryRegion *cuda_mem, void *nvram, - int nb_ide, MemoryRegion **ide_mem, - MemoryRegion *escc_mem) +void macio_init(PCIDevice *d, + MemoryRegion *pic_mem, + MemoryRegion *escc_mem) { - PCIDevice *d; - MacIOState *macio_state; - int i; + MacIOState *macio_state = MACIO(d); - d = pci_create_simple(bus, -1, "macio"); - - macio_state = DO_UPCAST(MacIOState, parent, d); - macio_state->is_oldworld = is_oldworld; macio_state->pic_mem = pic_mem; - macio_state->dbdma_mem = dbdma_mem; - macio_state->cuda_mem = cuda_mem; macio_state->escc_mem = escc_mem; - macio_state->nvram = nvram; - if (nb_ide > 4) - nb_ide = 4; - macio_state->nb_ide = nb_ide; - for (i = 0; i < nb_ide; i++) - macio_state->ide_mem[i] = ide_mem[i]; - for (; i < 4; i++) - macio_state->ide_mem[i] = NULL; /* Note: this code is strongly inspirated from the corresponding code in PearPC */ - pci_config_set_device_id(d->config, device_id); - - macio_bar_setup(macio_state); - pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &macio_state->bar); + qdev_init_nofail(DEVICE(d)); } diff --git a/hw/marvell_88w8618_audio.c b/hw/marvell_88w8618_audio.c index de16cfa090..e042046e4f 100644 --- a/hw/marvell_88w8618_audio.c +++ b/hw/marvell_88w8618_audio.c @@ -9,10 +9,10 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ -#include "sysbus.h" -#include "hw.h" -#include "i2c.h" -#include "sysbus.h" +#include "hw/sysbus.h" +#include "hw/hw.h" +#include "hw/i2c.h" +#include "hw/sysbus.h" #include "audio/audio.h" #define MP_AUDIO_SIZE 0x00001000 @@ -222,7 +222,7 @@ static void mv88w8618_audio_write(void *opaque, hwaddr offset, static void mv88w8618_audio_reset(DeviceState *d) { mv88w8618_audio_state *s = FROM_SYSBUS(mv88w8618_audio_state, - sysbus_from_qdev(d)); + SYS_BUS_DEVICE(d)); s->playback_mode = 0; s->status = 0; @@ -288,7 +288,7 @@ static void mv88w8618_audio_class_init(ObjectClass *klass, void *data) dc->props = mv88w8618_audio_properties; } -static TypeInfo mv88w8618_audio_info = { +static const TypeInfo mv88w8618_audio_info = { .name = "mv88w8618_audio", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mv88w8618_audio_state), diff --git a/hw/max111x.c b/hw/max111x.c index 67640f109a..d477ecdb29 100644 --- a/hw/max111x.c +++ b/hw/max111x.c @@ -10,7 +10,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "ssi.h" +#include "hw/ssi.h" typedef struct { SSISlave ssidev; @@ -162,7 +162,7 @@ static void max1110_class_init(ObjectClass *klass, void *data) k->transfer = max111x_transfer; } -static TypeInfo max1110_info = { +static const TypeInfo max1110_info = { .name = "max1110", .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(MAX111xState), @@ -177,7 +177,7 @@ static void max1111_class_init(ObjectClass *klass, void *data) k->transfer = max111x_transfer; } -static TypeInfo max1111_info = { +static const TypeInfo max1111_info = { .name = "max1111", .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(MAX111xState), diff --git a/hw/max7310.c b/hw/max7310.c index 1ed18ba876..e5cb810a27 100644 --- a/hw/max7310.c +++ b/hw/max7310.c @@ -7,7 +7,7 @@ * This file is licensed under GNU GPL. */ -#include "i2c.h" +#include "hw/i2c.h" typedef struct { I2CSlave i2c; @@ -25,7 +25,7 @@ typedef struct { static void max7310_reset(DeviceState *dev) { - MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, I2C_SLAVE_FROM_QDEV(dev)); + MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, I2C_SLAVE(dev)); s->level &= s->direction; s->direction = 0xff; s->polarity = 0xf0; @@ -198,7 +198,7 @@ static void max7310_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_max7310; } -static TypeInfo max7310_info = { +static const TypeInfo max7310_info = { .name = "max7310", .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(MAX7310State), diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index 2ddd7de09e..a2119ad2f1 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -21,14 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" -#include "mc146818rtc.h" +#include "hw/mc146818rtc.h" #include "qapi/visitor.h" #ifdef TARGET_I386 -#include "apic.h" +#include "hw/apic.h" #endif //#define DEBUG_CMOS @@ -898,7 +898,7 @@ static void rtc_class_initfn(ObjectClass *klass, void *data) dc->props = mc146818rtc_properties; } -static TypeInfo mc146818rtc_info = { +static const TypeInfo mc146818rtc_info = { .name = "mc146818rtc", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(RTCState), diff --git a/hw/mc146818rtc.h b/hw/mc146818rtc.h index f286b6a12a..967403edb5 100644 --- a/hw/mc146818rtc.h +++ b/hw/mc146818rtc.h @@ -1,8 +1,8 @@ #ifndef MC146818RTC_H #define MC146818RTC_H -#include "isa.h" -#include "mc146818rtc_regs.h" +#include "hw/isa.h" +#include "hw/mc146818rtc_regs.h" ISADevice *rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq); void rtc_set_memory(ISADevice *dev, int addr, int val); diff --git a/hw/mcf.h b/hw/mcf.h index f929910f02..fbc8dc26df 100644 --- a/hw/mcf.h +++ b/hw/mcf.h @@ -17,7 +17,7 @@ void mcf_uart_mm_init(struct MemoryRegion *sysmem, /* mcf_intc.c */ qemu_irq *mcf_intc_init(struct MemoryRegion *sysmem, hwaddr base, - CPUM68KState *env); + M68kCPU *cpu); /* mcf_fec.c */ void mcf_fec_init(struct MemoryRegion *sysmem, NICInfo *nd, @@ -25,6 +25,6 @@ void mcf_fec_init(struct MemoryRegion *sysmem, NICInfo *nd, /* mcf5206.c */ qemu_irq *mcf5206_init(struct MemoryRegion *sysmem, - uint32_t base, CPUM68KState *env); + uint32_t base, M68kCPU *cpu); #endif diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c index 2423f64bf6..0227bd852c 100644 --- a/hw/mcf_fec.c +++ b/hw/mcf_fec.c @@ -5,9 +5,9 @@ * * This code is licensed under the GPL */ -#include "hw.h" +#include "hw/hw.h" #include "net/net.h" -#include "mcf.h" +#include "hw/mcf.h" /* For crc32 */ #include #include "exec/address-spaces.h" @@ -174,7 +174,7 @@ static void mcf_fec_do_tx(mcf_fec_state *s) if (bd.flags & FEC_BD_L) { /* Last buffer in frame. */ DPRINTF("Sending packet\n"); - qemu_send_packet(&s->nic->nc, frame, len); + qemu_send_packet(qemu_get_queue(s->nic), frame, len); ptr = frame; frame_size = 0; s->eir |= FEC_INT_TXF; @@ -353,13 +353,13 @@ static void mcf_fec_write(void *opaque, hwaddr addr, static int mcf_fec_can_receive(NetClientState *nc) { - mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + mcf_fec_state *s = qemu_get_nic_opaque(nc); return s->rx_enabled; } static ssize_t mcf_fec_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + mcf_fec_state *s = qemu_get_nic_opaque(nc); mcf_fec_bd bd; uint32_t flags = 0; uint32_t addr; @@ -441,7 +441,7 @@ static const MemoryRegionOps mcf_fec_ops = { static void mcf_fec_cleanup(NetClientState *nc) { - mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + mcf_fec_state *s = qemu_get_nic_opaque(nc); memory_region_del_subregion(s->sysmem, &s->iomem); memory_region_destroy(&s->iomem); @@ -472,9 +472,9 @@ void mcf_fec_init(MemoryRegion *sysmem, NICInfo *nd, memory_region_add_subregion(sysmem, base, &s->iomem); s->conf.macaddr = nd->macaddr; - s->conf.peer = nd->netdev; + s->conf.peers.ncs[0] = nd->netdev; s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf, nd->model, nd->name, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/mcf_uart.c b/hw/mcf_uart.c index c44344317a..aacf0f05ed 100644 --- a/hw/mcf_uart.c +++ b/hw/mcf_uart.c @@ -5,8 +5,8 @@ * * This code is licensed under the GPL */ -#include "hw.h" -#include "mcf.h" +#include "hw/hw.h" +#include "hw/mcf.h" #include "char/char.h" #include "exec/address-spaces.h" diff --git a/hw/megasas.c b/hw/megasas.c index eb191f5e12..9b815d4b8f 100644 --- a/hw/megasas.c +++ b/hw/megasas.c @@ -18,16 +18,16 @@ * License along with this library; if not, see . */ -#include "hw.h" -#include "pci/pci.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" #include "sysemu/dma.h" -#include "pci/msix.h" +#include "hw/pci/msix.h" #include "qemu/iov.h" -#include "scsi.h" -#include "scsi-defs.h" +#include "hw/scsi.h" +#include "hw/scsi-defs.h" #include "trace.h" -#include "mfi.h" +#include "hw/mfi.h" #define MEGASAS_VERSION "1.70" #define MEGASAS_MAX_FRAMES 2048 /* Firmware limit at 65535 */ diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs index 3028e651c8..9e7f249941 100644 --- a/hw/microblaze/Makefile.objs +++ b/hw/microblaze/Makefile.objs @@ -1,10 +1,9 @@ -obj-y = petalogix_s3adsp1800_mmu.o -obj-y += petalogix_ml605_mmu.o -obj-y += microblaze_boot.o obj-y += xilinx_spi.o - -obj-y += microblaze_pic_cpu.o obj-y += xilinx_ethlite.o -obj-$(CONFIG_FDT) += ../device_tree.o obj-y := $(addprefix ../,$(obj-y)) + +obj-y += petalogix_s3adsp1800_mmu.o +obj-y += petalogix_ml605_mmu.o +obj-y += boot.o +obj-y += pic_cpu.o diff --git a/hw/microblaze_boot.c b/hw/microblaze/boot.c similarity index 99% rename from hw/microblaze_boot.c rename to hw/microblaze/boot.c index 3ec5c0f7dd..e13b3e13bb 100644 --- a/hw/microblaze_boot.c +++ b/hw/microblaze/boot.c @@ -28,10 +28,10 @@ #include "qemu/config-file.h" #include "qemu-common.h" #include "sysemu/device_tree.h" -#include "loader.h" +#include "hw/loader.h" #include "elf.h" -#include "microblaze_boot.h" +#include "hw/microblaze_boot.h" static struct { diff --git a/hw/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c similarity index 87% rename from hw/petalogix_ml605_mmu.c rename to hw/microblaze/petalogix_ml605_mmu.c index 1cfdb2f302..cfc02207ab 100644 --- a/hw/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -25,23 +25,23 @@ * THE SOFTWARE. */ -#include "sysbus.h" -#include "hw.h" +#include "hw/sysbus.h" +#include "hw/hw.h" #include "net/net.h" -#include "flash.h" +#include "hw/flash.h" #include "sysemu/sysemu.h" -#include "devices.h" -#include "boards.h" -#include "xilinx.h" +#include "hw/devices.h" +#include "hw/boards.h" +#include "hw/xilinx.h" #include "sysemu/blockdev.h" -#include "serial.h" +#include "hw/serial.h" #include "exec/address-spaces.h" -#include "ssi.h" +#include "hw/ssi.h" -#include "microblaze_boot.h" -#include "microblaze_pic_cpu.h" +#include "hw/microblaze_boot.h" +#include "hw/microblaze_pic_cpu.h" -#include "stream.h" +#include "hw/stream.h" #define LMB_BRAM_SIZE (128 * 1024) #define FLASH_SIZE (32 * 1024 * 1024) @@ -129,17 +129,19 @@ petalogix_ml605_init(QEMUMachineInitArgs *args) xilinx_timer_create(TIMER_BASEADDR, irq[2], 0, 100 * 1000000); /* axi ethernet and dma initialization. */ + qemu_check_nic_model(&nd_table[0], "xlnx.axi-ethernet"); + eth0 = qdev_create(NULL, "xlnx.axi-ethernet"); dma = qdev_create(NULL, "xlnx.axi-dma"); /* FIXME: attach to the sysbus instead */ object_property_add_child(container_get(qdev_get_machine(), "/unattached"), "xilinx-dma", OBJECT(dma), NULL); - eth0 = xilinx_axiethernet_create(&nd_table[0], STREAM_SLAVE(dma), - 0x82780000, irq[3], 0x1000, 0x1000); + xilinx_axiethernet_init(eth0, &nd_table[0], STREAM_SLAVE(dma), + 0x82780000, irq[3], 0x1000, 0x1000); - xilinx_axiethernetdma_init(dma, STREAM_SLAVE(eth0), - 0x84600000, irq[1], irq[0], 100 * 1000000); + xilinx_axidma_init(dma, STREAM_SLAVE(eth0), 0x84600000, irq[1], irq[0], + 100 * 1000000); { SSIBus *spi; @@ -147,7 +149,7 @@ petalogix_ml605_init(QEMUMachineInitArgs *args) dev = qdev_create(NULL, "xlnx.xps-spi"); qdev_prop_set_uint8(dev, "num-ss-bits", NUM_SPI_FLASHES); qdev_init_nofail(dev); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, 0x40a00000); sysbus_connect_irq(busdev, 0, irq[4]); @@ -156,8 +158,7 @@ petalogix_ml605_init(QEMUMachineInitArgs *args) for (i = 0; i < NUM_SPI_FLASHES; i++) { qemu_irq cs_line; - dev = ssi_create_slave_no_init(spi, "m25p80"); - qdev_prop_set_string(dev, "partname", "n25q128"); + dev = ssi_create_slave_no_init(spi, "n25q128"); qdev_init_nofail(dev); cs_line = qdev_get_gpio_in(dev, 0); sysbus_connect_irq(busdev, i+1, cs_line); @@ -173,7 +174,8 @@ static QEMUMachine petalogix_ml605_machine = { .name = "petalogix-ml605", .desc = "PetaLogix linux refdesign for xilinx ml605 little endian", .init = petalogix_ml605_init, - .is_default = 0 + .is_default = 0, + DEFAULT_MACHINE_OPTIONS, }; static void petalogix_ml605_machine_init(void) diff --git a/hw/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c similarity index 94% rename from hw/petalogix_s3adsp1800_mmu.c rename to hw/microblaze/petalogix_s3adsp1800_mmu.c index 27ecfe7752..24983621e5 100644 --- a/hw/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -23,19 +23,19 @@ * THE SOFTWARE. */ -#include "sysbus.h" -#include "hw.h" +#include "hw/sysbus.h" +#include "hw/hw.h" #include "net/net.h" -#include "flash.h" +#include "hw/flash.h" #include "sysemu/sysemu.h" -#include "devices.h" -#include "boards.h" -#include "xilinx.h" +#include "hw/devices.h" +#include "hw/boards.h" +#include "hw/xilinx.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" -#include "microblaze_boot.h" -#include "microblaze_pic_cpu.h" +#include "hw/microblaze_boot.h" +#include "hw/microblaze_pic_cpu.h" #define LMB_BRAM_SIZE (128 * 1024) #define FLASH_SIZE (16 * 1024 * 1024) @@ -115,7 +115,8 @@ static QEMUMachine petalogix_s3adsp1800_machine = { .name = "petalogix-s3adsp1800", .desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800", .init = petalogix_s3adsp1800_init, - .is_default = 1 + .is_default = 1, + DEFAULT_MACHINE_OPTIONS, }; static void petalogix_s3adsp1800_machine_init(void) diff --git a/hw/microblaze_pic_cpu.c b/hw/microblaze/pic_cpu.c similarity index 80% rename from hw/microblaze_pic_cpu.c rename to hw/microblaze/pic_cpu.c index ff36a526fc..6248de92bb 100644 --- a/hw/microblaze_pic_cpu.c +++ b/hw/microblaze/pic_cpu.c @@ -22,23 +22,26 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "microblaze_pic_cpu.h" +#include "hw/hw.h" +#include "hw/microblaze_pic_cpu.h" #define D(x) static void microblaze_pic_cpu_handler(void *opaque, int irq, int level) { - CPUMBState *env = (CPUMBState *)opaque; + MicroBlazeCPU *cpu = opaque; + CPUState *cs = CPU(cpu); int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD; - if (level) - cpu_interrupt(env, type); - else - cpu_reset_interrupt(env, type); + if (level) { + cpu_interrupt(cs, type); + } else { + cpu_reset_interrupt(cs, type); + } } qemu_irq *microblaze_pic_init_cpu(CPUMBState *env) { - return qemu_allocate_irqs(microblaze_pic_cpu_handler, env, 2); + return qemu_allocate_irqs(microblaze_pic_cpu_handler, mb_env_get_cpu(env), + 2); } diff --git a/hw/microblaze_boot.h b/hw/microblaze_boot.h index c1cf836b99..b14ef2b992 100644 --- a/hw/microblaze_boot.h +++ b/hw/microblaze_boot.h @@ -1,7 +1,7 @@ #ifndef __MICROBLAZE_BOOT__ #define __MICROBLAZE_BOOT__ -#include "hw.h" +#include "hw/hw.h" void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, uint32_t ramsize, const char *dtb_filename, diff --git a/hw/milkymist-ac97.c b/hw/milkymist-ac97.c index f46af1c509..e08e9dca16 100644 --- a/hw/milkymist-ac97.c +++ b/hw/milkymist-ac97.c @@ -21,8 +21,8 @@ * http://www.milkymist.org/socdoc/ac97.pdf */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "trace.h" #include "audio/audio.h" #include "qemu/error-report.h" @@ -329,7 +329,7 @@ static void milkymist_ac97_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_milkymist_ac97; } -static TypeInfo milkymist_ac97_info = { +static const TypeInfo milkymist_ac97_info = { .name = "milkymist-ac97", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistAC97State), diff --git a/hw/milkymist-hpdmc.c b/hw/milkymist-hpdmc.c index fd54d3129a..d922f6ffad 100644 --- a/hw/milkymist-hpdmc.c +++ b/hw/milkymist-hpdmc.c @@ -21,8 +21,8 @@ * http://www.milkymist.org/socdoc/hpdmc.pdf */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "trace.h" #include "qemu/error-report.h" @@ -155,7 +155,7 @@ static void milkymist_hpdmc_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_milkymist_hpdmc; } -static TypeInfo milkymist_hpdmc_info = { +static const TypeInfo milkymist_hpdmc_info = { .name = "milkymist-hpdmc", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistHpdmcState), diff --git a/hw/milkymist-hw.h b/hw/milkymist-hw.h index 812ddd2bd1..ced1c5f54e 100644 --- a/hw/milkymist-hw.h +++ b/hw/milkymist-hw.h @@ -1,8 +1,8 @@ #ifndef QEMU_HW_MILKYMIST_H #define QEMU_HW_MILKYMIST_H -#include "qdev.h" -#include "qdev-addr.h" +#include "hw/qdev.h" +#include "hw/qdev-addr.h" #include "net/net.h" static inline DeviceState *milkymist_uart_create(hwaddr base, @@ -12,8 +12,8 @@ static inline DeviceState *milkymist_uart_create(hwaddr base, dev = qdev_create(NULL, "milkymist-uart"); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); return dev; } @@ -24,7 +24,7 @@ static inline DeviceState *milkymist_hpdmc_create(hwaddr base) dev = qdev_create(NULL, "milkymist-hpdmc"); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); return dev; } @@ -35,7 +35,7 @@ static inline DeviceState *milkymist_memcard_create(hwaddr base) dev = qdev_create(NULL, "milkymist-memcard"); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); return dev; } @@ -49,7 +49,7 @@ static inline DeviceState *milkymist_vgafb_create(hwaddr base, qdev_prop_set_uint32(dev, "fb_offset", fb_offset); qdev_prop_set_uint32(dev, "fb_mask", fb_mask); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); return dev; } @@ -67,10 +67,10 @@ static inline DeviceState *milkymist_sysctl_create(hwaddr base, qdev_prop_set_uint32(dev, "capabilities", capabilities); qdev_prop_set_uint32(dev, "gpio_strappings", gpio_strappings); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, gpio_irq); - sysbus_connect_irq(sysbus_from_qdev(dev), 1, timer0_irq); - sysbus_connect_irq(sysbus_from_qdev(dev), 2, timer1_irq); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, gpio_irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, timer0_irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, timer1_irq); return dev; } @@ -82,8 +82,8 @@ static inline DeviceState *milkymist_pfpu_create(hwaddr base, dev = qdev_create(NULL, "milkymist-pfpu"); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); return dev; } @@ -144,8 +144,8 @@ static inline DeviceState *milkymist_tmu2_create(hwaddr base, dev = qdev_create(NULL, "milkymist-tmu2"); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); return dev; #else @@ -161,27 +161,11 @@ static inline DeviceState *milkymist_ac97_create(hwaddr base, dev = qdev_create(NULL, "milkymist-ac97"); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, crrequest_irq); - sysbus_connect_irq(sysbus_from_qdev(dev), 1, crreply_irq); - sysbus_connect_irq(sysbus_from_qdev(dev), 2, dmar_irq); - sysbus_connect_irq(sysbus_from_qdev(dev), 3, dmaw_irq); - - return dev; -} - -static inline DeviceState *milkymist_minimac_create(hwaddr base, - qemu_irq rx_irq, qemu_irq tx_irq) -{ - DeviceState *dev; - - qemu_check_nic_model(&nd_table[0], "minimac"); - dev = qdev_create(NULL, "milkymist-minimac"); - qdev_set_nic_properties(dev, &nd_table[0]); - qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, rx_irq); - sysbus_connect_irq(sysbus_from_qdev(dev), 1, tx_irq); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, crrequest_irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, crreply_irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, dmar_irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 3, dmaw_irq); return dev; } @@ -196,9 +180,9 @@ static inline DeviceState *milkymist_minimac2_create(hwaddr base, qdev_prop_set_taddr(dev, "buffers_base", buffers_base); qdev_set_nic_properties(dev, &nd_table[0]); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, rx_irq); - sysbus_connect_irq(sysbus_from_qdev(dev), 1, tx_irq); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, rx_irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, tx_irq); return dev; } @@ -215,8 +199,8 @@ static inline DeviceState *milkymist_softusb_create(hwaddr base, qdev_prop_set_uint32(dev, "dmem_base", dmem_base); qdev_prop_set_uint32(dev, "dmem_size", dmem_size); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); return dev; } diff --git a/hw/milkymist-memcard.c b/hw/milkymist-memcard.c index f80befc53a..d5944bca69 100644 --- a/hw/milkymist-memcard.c +++ b/hw/milkymist-memcard.c @@ -21,13 +21,13 @@ * http://www.milkymist.org/socdoc/memcard.pdf */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "trace.h" #include "qemu/error-report.h" #include "sysemu/blockdev.h" -#include "sd.h" +#include "hw/sd.h" enum { ENABLE_CMD_TX = (1<<0), @@ -288,7 +288,7 @@ static void milkymist_memcard_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_milkymist_memcard; } -static TypeInfo milkymist_memcard_info = { +static const TypeInfo milkymist_memcard_info = { .name = "milkymist-memcard", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistMemcardState), diff --git a/hw/milkymist-minimac2.c b/hw/milkymist-minimac2.c index 4e92ac3dcb..c20ff904ec 100644 --- a/hw/milkymist-minimac2.c +++ b/hw/milkymist-minimac2.c @@ -22,12 +22,12 @@ * */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "trace.h" #include "net/net.h" #include "qemu/error-report.h" -#include "qdev-addr.h" +#include "hw/qdev-addr.h" #include @@ -257,7 +257,7 @@ static void minimac2_tx(MilkymistMinimac2State *s) trace_milkymist_minimac2_tx_frame(txcount - 12); /* send packet, skipping preamble and sfd */ - qemu_send_packet_raw(&s->nic->nc, buf + 8, txcount - 12); + qemu_send_packet_raw(qemu_get_queue(s->nic), buf + 8, txcount - 12); s->regs[R_TXCOUNT] = 0; @@ -280,7 +280,7 @@ static void update_rx_interrupt(MilkymistMinimac2State *s) static ssize_t minimac2_rx(NetClientState *nc, const uint8_t *buf, size_t size) { - MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque; + MilkymistMinimac2State *s = qemu_get_nic_opaque(nc); uint32_t r_count; uint32_t r_state; @@ -410,7 +410,7 @@ static const MemoryRegionOps minimac2_ops = { static int minimac2_can_rx(NetClientState *nc) { - MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque; + MilkymistMinimac2State *s = qemu_get_nic_opaque(nc); if (s->regs[R_STATE0] == STATE_LOADED) { return 1; @@ -424,7 +424,7 @@ static int minimac2_can_rx(NetClientState *nc) static void minimac2_cleanup(NetClientState *nc) { - MilkymistMinimac2State *s = DO_UPCAST(NICState, nc, nc)->opaque; + MilkymistMinimac2State *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -480,7 +480,7 @@ static int milkymist_minimac2_init(SysBusDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_milkymist_minimac2_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); return 0; } @@ -535,7 +535,7 @@ static void milkymist_minimac2_class_init(ObjectClass *klass, void *data) dc->props = milkymist_minimac2_properties; } -static TypeInfo milkymist_minimac2_info = { +static const TypeInfo milkymist_minimac2_info = { .name = "milkymist-minimac2", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistMinimac2State), diff --git a/hw/milkymist-pfpu.c b/hw/milkymist-pfpu.c index 0521829202..ad44b4db22 100644 --- a/hw/milkymist-pfpu.c +++ b/hw/milkymist-pfpu.c @@ -22,8 +22,8 @@ * */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "trace.h" #include "qemu/log.h" #include "qemu/error-report.h" @@ -529,7 +529,7 @@ static void milkymist_pfpu_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_milkymist_pfpu; } -static TypeInfo milkymist_pfpu_info = { +static const TypeInfo milkymist_pfpu_info = { .name = "milkymist-pfpu", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistPFPUState), diff --git a/hw/milkymist-softusb.c b/hw/milkymist-softusb.c index b7beb4bedb..d911686de7 100644 --- a/hw/milkymist-softusb.c +++ b/hw/milkymist-softusb.c @@ -21,11 +21,11 @@ * not available yet */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "trace.h" #include "ui/console.h" -#include "hid.h" +#include "hw/hid.h" #include "qemu/error-report.h" enum { @@ -316,7 +316,7 @@ static void milkymist_softusb_class_init(ObjectClass *klass, void *data) dc->props = milkymist_softusb_properties; } -static TypeInfo milkymist_softusb_info = { +static const TypeInfo milkymist_softusb_info = { .name = "milkymist-softusb", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistSoftUsbState), diff --git a/hw/milkymist-sysctl.c b/hw/milkymist-sysctl.c index 796e795f04..e083a280c4 100644 --- a/hw/milkymist-sysctl.c +++ b/hw/milkymist-sysctl.c @@ -21,12 +21,12 @@ * http://www.milkymist.org/socdoc/sysctl.pdf */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "trace.h" #include "qemu/timer.h" -#include "ptimer.h" +#include "hw/ptimer.h" #include "qemu/error-report.h" enum { @@ -323,7 +323,7 @@ static void milkymist_sysctl_class_init(ObjectClass *klass, void *data) dc->props = milkymist_sysctl_properties; } -static TypeInfo milkymist_sysctl_info = { +static const TypeInfo milkymist_sysctl_info = { .name = "milkymist-sysctl", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistSysctlState), diff --git a/hw/milkymist-tmu2.c b/hw/milkymist-tmu2.c index a11772aebe..b723a04cc9 100644 --- a/hw/milkymist-tmu2.c +++ b/hw/milkymist-tmu2.c @@ -24,8 +24,8 @@ * */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "trace.h" #include "qemu/error-report.h" @@ -475,7 +475,7 @@ static void milkymist_tmu2_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_milkymist_tmu2; } -static TypeInfo milkymist_tmu2_info = { +static const TypeInfo milkymist_tmu2_info = { .name = "milkymist-tmu2", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistTMU2State), diff --git a/hw/milkymist-uart.c b/hw/milkymist-uart.c index 19e9dbdc75..ac6f5373ad 100644 --- a/hw/milkymist-uart.c +++ b/hw/milkymist-uart.c @@ -21,8 +21,8 @@ * http://www.milkymist.org/socdoc/uart.pdf */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "trace.h" #include "char/char.h" #include "qemu/error-report.h" @@ -228,7 +228,7 @@ static void milkymist_uart_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_milkymist_uart; } -static TypeInfo milkymist_uart_info = { +static const TypeInfo milkymist_uart_info = { .name = "milkymist-uart", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistUartState), diff --git a/hw/milkymist-vgafb.c b/hw/milkymist-vgafb.c index 561285154f..85ebb851bd 100644 --- a/hw/milkymist-vgafb.c +++ b/hw/milkymist-vgafb.c @@ -22,24 +22,24 @@ * http://www.milkymist.org/socdoc/vgafb.pdf */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "trace.h" #include "ui/console.h" -#include "framebuffer.h" +#include "hw/framebuffer.h" #include "ui/pixel_ops.h" #include "qemu/error-report.h" #define BITS 8 -#include "milkymist-vgafb_template.h" +#include "hw/milkymist-vgafb_template.h" #define BITS 15 -#include "milkymist-vgafb_template.h" +#include "hw/milkymist-vgafb_template.h" #define BITS 16 -#include "milkymist-vgafb_template.h" +#include "hw/milkymist-vgafb_template.h" #define BITS 24 -#include "milkymist-vgafb_template.h" +#include "hw/milkymist-vgafb_template.h" #define BITS 32 -#include "milkymist-vgafb_template.h" +#include "hw/milkymist-vgafb_template.h" enum { R_CTRL = 0, @@ -319,7 +319,7 @@ static void milkymist_vgafb_class_init(ObjectClass *klass, void *data) dc->props = milkymist_vgafb_properties; } -static TypeInfo milkymist_vgafb_info = { +static const TypeInfo milkymist_vgafb_info = { .name = "milkymist-vgafb", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistVgafbState), diff --git a/hw/mips/Makefile.objs b/hw/mips/Makefile.objs index 29a5d0db04..1e3bca1c37 100644 --- a/hw/mips/Makefile.objs +++ b/hw/mips/Makefile.objs @@ -1,6 +1,8 @@ -obj-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o -obj-y += mips_addr.o mips_timer.o mips_int.o obj-y += gt64xxx.o mc146818rtc.o -obj-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o +obj-$(CONFIG_FULONG) += bonito.o vt82c686.o obj-y := $(addprefix ../,$(obj-y)) + +obj-y += mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o +obj-y += addr.o cputimer.o mips_int.o +obj-$(CONFIG_FULONG) += mips_fulong2e.o diff --git a/hw/mips_addr.c b/hw/mips/addr.c similarity index 96% rename from hw/mips_addr.c rename to hw/mips/addr.c index aa1c7d84d6..cddc25cf3f 100644 --- a/hw/mips_addr.c +++ b/hw/mips/addr.c @@ -20,8 +20,8 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "mips_cpudevs.h" +#include "hw/hw.h" +#include "hw/mips_cpudevs.h" uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr) { diff --git a/hw/mips_timer.c b/hw/mips/cputimer.c similarity index 98% rename from hw/mips_timer.c rename to hw/mips/cputimer.c index 83c400c158..9ad13f3924 100644 --- a/hw/mips_timer.c +++ b/hw/mips/cputimer.c @@ -20,8 +20,8 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "mips_cpudevs.h" +#include "hw/hw.h" +#include "hw/mips_cpudevs.h" #include "qemu/timer.h" #define TIMER_FREQ 100 * 1000 * 1000 diff --git a/hw/mips_fulong2e.c b/hw/mips/mips_fulong2e.c similarity index 97% rename from hw/mips_fulong2e.c rename to hw/mips/mips_fulong2e.c index 4d8ee8c09c..766aa9dfb5 100644 --- a/hw/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -18,29 +18,29 @@ * http://www.loongsondeveloper.com/doc/Loongson2EUserGuide.pdf */ -#include "hw.h" -#include "pc.h" -#include "serial.h" -#include "fdc.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/serial.h" +#include "hw/fdc.h" #include "net/net.h" -#include "boards.h" -#include "smbus.h" +#include "hw/boards.h" +#include "hw/smbus.h" #include "block/block.h" -#include "flash.h" -#include "mips.h" -#include "mips_cpudevs.h" -#include "pci/pci.h" +#include "hw/flash.h" +#include "hw/mips.h" +#include "hw/mips_cpudevs.h" +#include "hw/pci/pci.h" #include "char/char.h" #include "sysemu/sysemu.h" #include "audio/audio.h" #include "qemu/log.h" -#include "loader.h" -#include "mips-bios.h" -#include "ide.h" +#include "hw/loader.h" +#include "hw/mips-bios.h" +#include "hw/ide.h" #include "elf.h" -#include "vt82c686.h" -#include "mc146818rtc.h" -#include "i8254.h" +#include "hw/vt82c686.h" +#include "hw/mc146818rtc.h" +#include "hw/i8254.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" @@ -400,6 +400,7 @@ static QEMUMachine mips_fulong2e_machine = { .name = "fulong2e", .desc = "Fulong 2e mini pc", .init = mips_fulong2e_init, + DEFAULT_MACHINE_OPTIONS, }; static void mips_fulong2e_machine_init(void) diff --git a/hw/mips_int.c b/hw/mips/mips_int.c similarity index 84% rename from hw/mips_int.c rename to hw/mips/mips_int.c index 6423fd0bd9..0e5e86699c 100644 --- a/hw/mips_int.c +++ b/hw/mips/mips_int.c @@ -20,13 +20,15 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "mips_cpudevs.h" +#include "hw/hw.h" +#include "hw/mips_cpudevs.h" #include "cpu.h" static void cpu_mips_irq_request(void *opaque, int irq, int level) { - CPUMIPSState *env = (CPUMIPSState *)opaque; + MIPSCPU *cpu = opaque; + CPUMIPSState *env = &cpu->env; + CPUState *cs = CPU(cpu); if (irq < 0 || irq > 7) return; @@ -38,9 +40,9 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level) } if (env->CP0_Cause & CP0Ca_IP_mask) { - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } @@ -49,7 +51,7 @@ void cpu_mips_irq_init_cpu(CPUMIPSState *env) qemu_irq *qi; int i; - qi = qemu_allocate_irqs(cpu_mips_irq_request, env, 8); + qi = qemu_allocate_irqs(cpu_mips_irq_request, mips_env_get_cpu(env), 8); for (i = 0; i < 8; i++) { env->irq[i] = qi[i]; } diff --git a/hw/mips_jazz.c b/hw/mips/mips_jazz.c similarity index 95% rename from hw/mips_jazz.c rename to hw/mips/mips_jazz.c index 63df2a734b..daeb985b1d 100644 --- a/hw/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -22,25 +22,25 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "mips.h" -#include "mips_cpudevs.h" -#include "pc.h" -#include "serial.h" -#include "isa.h" -#include "fdc.h" +#include "hw/hw.h" +#include "hw/mips.h" +#include "hw/mips_cpudevs.h" +#include "hw/pc.h" +#include "hw/serial.h" +#include "hw/isa.h" +#include "hw/fdc.h" #include "sysemu/sysemu.h" #include "sysemu/arch_init.h" -#include "boards.h" +#include "hw/boards.h" #include "net/net.h" -#include "esp.h" -#include "mips-bios.h" -#include "loader.h" -#include "mc146818rtc.h" -#include "i8254.h" -#include "pcspk.h" +#include "hw/esp.h" +#include "hw/mips-bios.h" +#include "hw/loader.h" +#include "hw/mc146818rtc.h" +#include "hw/i8254.h" +#include "hw/pcspk.h" #include "sysemu/blockdev.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "exec/address-spaces.h" enum jazz_model_e @@ -209,7 +209,7 @@ static void mips_jazz_init(MemoryRegion *address_space, case JAZZ_MAGNUM: dev = qdev_create(NULL, "sysbus-g364"); qdev_init_nofail(dev); - sysbus = sysbus_from_qdev(dev); + sysbus = SYS_BUS_DEVICE(dev); sysbus_mmio_map(sysbus, 0, 0x60080000); sysbus_mmio_map(sysbus, 1, 0x40000000); sysbus_connect_irq(sysbus, 0, rc4030[3]); @@ -295,7 +295,7 @@ static void mips_jazz_init(MemoryRegion *address_space, /* NVRAM */ dev = qdev_create(NULL, "ds1225y"); qdev_init_nofail(dev); - sysbus = sysbus_from_qdev(dev); + sysbus = SYS_BUS_DEVICE(dev); sysbus_mmio_map(sysbus, 0, 0x80009000); /* LED indicator */ @@ -325,6 +325,7 @@ static QEMUMachine mips_magnum_machine = { .desc = "MIPS Magnum", .init = mips_magnum_init, .block_default_type = IF_SCSI, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine mips_pica61_machine = { @@ -332,6 +333,7 @@ static QEMUMachine mips_pica61_machine = { .desc = "Acer Pica 61", .init = mips_pica61_init, .block_default_type = IF_SCSI, + DEFAULT_MACHINE_OPTIONS, }; static void mips_jazz_machine_init(void) diff --git a/hw/mips_malta.c b/hw/mips/mips_malta.c similarity index 97% rename from hw/mips_malta.c rename to hw/mips/mips_malta.c index 635143d20c..9a67dce207 100644 --- a/hw/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -22,32 +22,32 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "pc.h" -#include "serial.h" -#include "fdc.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/serial.h" +#include "hw/fdc.h" #include "net/net.h" -#include "boards.h" -#include "smbus.h" +#include "hw/boards.h" +#include "hw/smbus.h" #include "block/block.h" -#include "flash.h" -#include "mips.h" -#include "mips_cpudevs.h" -#include "pci/pci.h" +#include "hw/flash.h" +#include "hw/mips.h" +#include "hw/mips_cpudevs.h" +#include "hw/pci/pci.h" #include "char/char.h" #include "sysemu/sysemu.h" #include "sysemu/arch_init.h" -#include "boards.h" +#include "hw/boards.h" #include "qemu/log.h" -#include "mips-bios.h" -#include "ide.h" -#include "loader.h" +#include "hw/mips-bios.h" +#include "hw/ide.h" +#include "hw/loader.h" #include "elf.h" -#include "mc146818rtc.h" -#include "i8254.h" +#include "hw/mc146818rtc.h" +#include "hw/i8254.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" -#include "sysbus.h" /* SysBusDevice */ +#include "hw/sysbus.h" /* SysBusDevice */ //#define DEBUG_BOARD_INIT @@ -743,10 +743,13 @@ static int64_t load_kernel (void) return kernel_entry; } -static void malta_mips_config(CPUMIPSState *env) +static void malta_mips_config(MIPSCPU *cpu) { + CPUMIPSState *env = &cpu->env; + CPUState *cs = CPU(cpu); + env->mvp->CP0_MVPConf0 |= ((smp_cpus - 1) << CP0MVPC0_PVPE) | - ((smp_cpus * env->nr_threads - 1) << CP0MVPC0_PTC); + ((smp_cpus * cs->nr_threads - 1) << CP0MVPC0_PTC); } static void main_cpu_reset(void *opaque) @@ -763,7 +766,7 @@ static void main_cpu_reset(void *opaque) env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL)); } - malta_mips_config(env); + malta_mips_config(cpu); } static void cpu_request_exit(void *opaque, int irq, int level) @@ -1004,7 +1007,7 @@ static void mips_malta_class_init(ObjectClass *klass, void *data) k->init = mips_malta_sysbus_device_init; } -static TypeInfo mips_malta_device = { +static const TypeInfo mips_malta_device = { .name = "mips-malta", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MaltaState), @@ -1017,6 +1020,7 @@ static QEMUMachine mips_malta_machine = { .init = mips_malta_init, .max_cpus = 16, .is_default = 1, + DEFAULT_MACHINE_OPTIONS, }; static void mips_malta_register_types(void) diff --git a/hw/mips_mipssim.c b/hw/mips/mips_mipssim.c similarity index 96% rename from hw/mips_mipssim.c rename to hw/mips/mips_mipssim.c index 67066c0ca1..4935c78c01 100644 --- a/hw/mips_mipssim.c +++ b/hw/mips/mips_mipssim.c @@ -24,18 +24,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "mips.h" -#include "mips_cpudevs.h" -#include "serial.h" -#include "isa.h" +#include "hw/hw.h" +#include "hw/mips.h" +#include "hw/mips_cpudevs.h" +#include "hw/serial.h" +#include "hw/isa.h" #include "net/net.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "mips-bios.h" -#include "loader.h" +#include "hw/boards.h" +#include "hw/mips-bios.h" +#include "hw/loader.h" #include "elf.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "exec/address-spaces.h" static struct _loaderparams { @@ -123,7 +123,7 @@ static void mipsnet_init(int base, qemu_irq irq, NICInfo *nd) qdev_set_nic_properties(dev, nd); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_connect_irq(s, 0, irq); memory_region_add_subregion(get_system_io(), base, @@ -229,6 +229,7 @@ static QEMUMachine mips_mipssim_machine = { .name = "mipssim", .desc = "MIPS MIPSsim platform", .init = mips_mipssim_init, + DEFAULT_MACHINE_OPTIONS, }; static void mips_mipssim_machine_init(void) diff --git a/hw/mips_r4k.c b/hw/mips/mips_r4k.c similarity index 96% rename from hw/mips_r4k.c rename to hw/mips/mips_r4k.c index 59c43e591c..539a562620 100644 --- a/hw/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -7,23 +7,23 @@ * All peripherial devices are attached to this "bus" with * the standard PC ISA addresses. */ -#include "hw.h" -#include "mips.h" -#include "mips_cpudevs.h" -#include "pc.h" -#include "serial.h" -#include "isa.h" +#include "hw/hw.h" +#include "hw/mips.h" +#include "hw/mips_cpudevs.h" +#include "hw/pc.h" +#include "hw/serial.h" +#include "hw/isa.h" #include "net/net.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "flash.h" +#include "hw/boards.h" +#include "hw/flash.h" #include "qemu/log.h" -#include "mips-bios.h" -#include "ide.h" -#include "loader.h" +#include "hw/mips-bios.h" +#include "hw/ide.h" +#include "hw/loader.h" #include "elf.h" -#include "mc146818rtc.h" -#include "i8254.h" +#include "hw/mc146818rtc.h" +#include "hw/i8254.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" @@ -302,6 +302,7 @@ static QEMUMachine mips_machine = { .name = "mips", .desc = "mips r4k platform", .init = mips_r4k_init, + DEFAULT_MACHINE_OPTIONS, }; static void mips_machine_init(void) diff --git a/hw/mipsnet.c b/hw/mipsnet.c index bb752d3950..ac6193a89e 100644 --- a/hw/mipsnet.c +++ b/hw/mipsnet.c @@ -1,7 +1,7 @@ -#include "hw.h" +#include "hw/hw.h" #include "net/net.h" #include "trace.h" -#include "sysbus.h" +#include "hw/sysbus.h" /* MIPSnet register offsets */ @@ -64,7 +64,7 @@ static int mipsnet_buffer_full(MIPSnetState *s) static int mipsnet_can_receive(NetClientState *nc) { - MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + MIPSnetState *s = qemu_get_nic_opaque(nc); if (s->busy) return 0; @@ -73,7 +73,7 @@ static int mipsnet_can_receive(NetClientState *nc) static ssize_t mipsnet_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + MIPSnetState *s = qemu_get_nic_opaque(nc); trace_mipsnet_receive(size); if (!mipsnet_can_receive(nc)) @@ -173,7 +173,7 @@ static void mipsnet_ioport_write(void *opaque, hwaddr addr, if (s->tx_written == s->tx_count) { /* Send buffer. */ trace_mipsnet_send(s->tx_count); - qemu_send_packet(&s->nic->nc, s->tx_buffer, s->tx_count); + qemu_send_packet(qemu_get_queue(s->nic), s->tx_buffer, s->tx_count); s->tx_count = s->tx_written = 0; s->intctl |= MIPSNET_INTCTL_TXDONE; s->busy = 1; @@ -211,7 +211,7 @@ static const VMStateDescription vmstate_mipsnet = { static void mipsnet_cleanup(NetClientState *nc) { - MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + MIPSnetState *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -241,7 +241,7 @@ static int mipsnet_sysbus_init(SysBusDevice *dev) s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); return 0; } @@ -269,7 +269,7 @@ static void mipsnet_class_init(ObjectClass *klass, void *data) dc->props = mipsnet_properties; } -static TypeInfo mipsnet_info = { +static const TypeInfo mipsnet_info = { .name = "mipsnet", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MIPSnetState), diff --git a/hw/msmouse.h b/hw/msmouse.h deleted file mode 100644 index 8cff3a71c3..0000000000 --- a/hw/msmouse.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef HW_MSMOUSE_H -#define HW_MSMOUSE_H 1 - -/* msmouse.c */ -CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts); - -#endif diff --git a/hw/mst_fpga.c b/hw/mst_fpga.c index fb4b739c7c..1dd15054d0 100644 --- a/hw/mst_fpga.c +++ b/hw/mst_fpga.c @@ -10,8 +10,8 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" /* Mainstone FPGA for extern irqs */ #define FPGA_GPIO_PIN 0 @@ -248,7 +248,7 @@ static void mst_fpga_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_mst_fpga_regs; } -static TypeInfo mst_fpga_info = { +static const TypeInfo mst_fpga_info = { .name = "mainstone-fpga", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(mst_irq_state), diff --git a/hw/nand.c b/hw/nand.c index 16950c5ec4..de3e502596 100644 --- a/hw/nand.c +++ b/hw/nand.c @@ -18,10 +18,10 @@ #ifndef NAND_IO -# include "hw.h" -# include "flash.h" +# include "hw/hw.h" +# include "hw/flash.h" # include "sysemu/blockdev.h" -# include "sysbus.h" +# include "hw/sysbus.h" #include "qemu/error-report.h" # define NAND_CMD_READ0 0x00 @@ -46,7 +46,7 @@ # define NAND_IOSTATUS_PLANE1 (1 << 2) # define NAND_IOSTATUS_PLANE2 (1 << 3) # define NAND_IOSTATUS_PLANE3 (1 << 4) -# define NAND_IOSTATUS_BUSY (1 << 6) +# define NAND_IOSTATUS_READY (1 << 6) # define NAND_IOSTATUS_UNPROTCT (1 << 7) # define MAX_PAGE 0x800 @@ -224,13 +224,14 @@ static const struct { static void nand_reset(DeviceState *dev) { - NANDFlashState *s = FROM_SYSBUS(NANDFlashState, sysbus_from_qdev(dev)); + NANDFlashState *s = FROM_SYSBUS(NANDFlashState, SYS_BUS_DEVICE(dev)); s->cmd = NAND_CMD_READ0; s->addr = 0; s->addrlen = 0; s->iolen = 0; s->offset = 0; s->status &= NAND_IOSTATUS_UNPROTCT; + s->status |= NAND_IOSTATUS_READY; } static inline void nand_pushio_byte(NANDFlashState *s, uint8_t value) @@ -435,7 +436,7 @@ static void nand_class_init(ObjectClass *klass, void *data) dc->props = nand_properties; } -static TypeInfo nand_info = { +static const TypeInfo nand_info = { .name = "nand", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(NANDFlashState), diff --git a/hw/ne2000-isa.c b/hw/ne2000-isa.c index c2c00c215f..47c00c3a76 100644 --- a/hw/ne2000-isa.c +++ b/hw/ne2000-isa.c @@ -21,12 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "pc.h" -#include "isa.h" -#include "qdev.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/isa.h" +#include "hw/qdev.h" #include "net/net.h" -#include "ne2000.h" +#include "hw/ne2000.h" #include "exec/address-spaces.h" typedef struct ISANE2000State { @@ -38,7 +38,7 @@ typedef struct ISANE2000State { static void isa_ne2000_cleanup(NetClientState *nc) { - NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + NE2000State *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -77,7 +77,7 @@ static int isa_ne2000_initfn(ISADevice *dev) s->nic = qemu_new_nic(&net_ne2000_isa_info, &s->c, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); return 0; } @@ -97,7 +97,7 @@ static void isa_ne2000_class_initfn(ObjectClass *klass, void *data) dc->props = ne2000_isa_properties; } -static TypeInfo ne2000_isa_info = { +static const TypeInfo ne2000_isa_info = { .name = "ne2k_isa", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISANE2000State), diff --git a/hw/ne2000.c b/hw/ne2000.c index 00efa74a0f..7dadc1cea7 100644 --- a/hw/ne2000.c +++ b/hw/ne2000.c @@ -21,11 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "pci/pci.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" #include "net/net.h" -#include "ne2000.h" -#include "loader.h" +#include "hw/ne2000.h" +#include "hw/loader.h" #include "sysemu/sysemu.h" /* debug NE2000 card */ @@ -167,7 +167,7 @@ static int ne2000_buffer_full(NE2000State *s) int ne2000_can_receive(NetClientState *nc) { - NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + NE2000State *s = qemu_get_nic_opaque(nc); if (s->cmd & E8390_STOP) return 1; @@ -178,7 +178,7 @@ int ne2000_can_receive(NetClientState *nc) ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_) { - NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + NE2000State *s = qemu_get_nic_opaque(nc); int size = size_; uint8_t *p; unsigned int total_len, next, avail, len, index, mcast_idx; @@ -300,7 +300,8 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) index -= NE2000_PMEM_SIZE; /* fail safe: check range on the transmitted length */ if (index + s->tcnt <= NE2000_PMEM_END) { - qemu_send_packet(&s->nic->nc, s->mem + index, s->tcnt); + qemu_send_packet(qemu_get_queue(s->nic), s->mem + index, + s->tcnt); } /* signal end of transfer */ s->tsr = ENTSR_PTX; @@ -705,7 +706,7 @@ void ne2000_setup_io(NE2000State *s, unsigned size) static void ne2000_cleanup(NetClientState *nc) { - NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + NE2000State *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -737,7 +738,7 @@ static int pci_ne2000_init(PCIDevice *pci_dev) s->nic = qemu_new_nic(&net_ne2000_info, &s->c, object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); add_boot_device_path(s->c.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); @@ -750,7 +751,7 @@ static void pci_ne2000_exit(PCIDevice *pci_dev) NE2000State *s = &d->ne2000; memory_region_destroy(&s->io); - qemu_del_net_client(&s->nic->nc); + qemu_del_nic(s->nic); } static Property ne2000_properties[] = { @@ -773,7 +774,7 @@ static void ne2000_class_init(ObjectClass *klass, void *data) dc->props = ne2000_properties; } -static TypeInfo ne2000_info = { +static const TypeInfo ne2000_info = { .name = "ne2k_pci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCINE2000State), diff --git a/hw/null-machine.c b/hw/null-machine.c index d813c089e7..bdf109fef1 100644 --- a/hw/null-machine.c +++ b/hw/null-machine.c @@ -24,6 +24,7 @@ static QEMUMachine machine_none = { .desc = "empty machine", .init = machine_none_init, .max_cpus = 0, + DEFAULT_MACHINE_OPTIONS, }; static void register_machines(void) diff --git a/hw/omap_clk.c b/hw/omap_clk.c index 8448006067..c7b5c11626 100644 --- a/hw/omap_clk.c +++ b/hw/omap_clk.c @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" -#include "omap.h" +#include "hw/hw.h" +#include "hw/omap.h" struct clk { const char *name; diff --git a/hw/omap_dma.c b/hw/omap_dma.c index aec5874311..0c5902f6f9 100644 --- a/hw/omap_dma.c +++ b/hw/omap_dma.c @@ -19,9 +19,9 @@ */ #include "qemu-common.h" #include "qemu/timer.h" -#include "omap.h" -#include "irq.h" -#include "soc_dma.h" +#include "hw/omap.h" +#include "hw/irq.h" +#include "hw/soc_dma.h" struct omap_dma_channel_s { /* transfer data */ @@ -1709,19 +1709,25 @@ static uint64_t omap_dma4_read(void *opaque, hwaddr addr, case 0x14: /* DMA4_IRQSTATUS_L3 */ irqn ++; + /* fall through */ case 0x10: /* DMA4_IRQSTATUS_L2 */ irqn ++; + /* fall through */ case 0x0c: /* DMA4_IRQSTATUS_L1 */ irqn ++; + /* fall through */ case 0x08: /* DMA4_IRQSTATUS_L0 */ return s->irqstat[irqn]; case 0x24: /* DMA4_IRQENABLE_L3 */ irqn ++; + /* fall through */ case 0x20: /* DMA4_IRQENABLE_L2 */ irqn ++; + /* fall through */ case 0x1c: /* DMA4_IRQENABLE_L1 */ irqn ++; + /* fall through */ case 0x18: /* DMA4_IRQENABLE_L0 */ return s->irqen[irqn]; @@ -1856,10 +1862,13 @@ static void omap_dma4_write(void *opaque, hwaddr addr, switch (addr) { case 0x14: /* DMA4_IRQSTATUS_L3 */ irqn ++; + /* fall through */ case 0x10: /* DMA4_IRQSTATUS_L2 */ irqn ++; + /* fall through */ case 0x0c: /* DMA4_IRQSTATUS_L1 */ irqn ++; + /* fall through */ case 0x08: /* DMA4_IRQSTATUS_L0 */ s->irqstat[irqn] &= ~value; if (!s->irqstat[irqn]) @@ -1868,10 +1877,13 @@ static void omap_dma4_write(void *opaque, hwaddr addr, case 0x24: /* DMA4_IRQENABLE_L3 */ irqn ++; + /* fall through */ case 0x20: /* DMA4_IRQENABLE_L2 */ irqn ++; + /* fall through */ case 0x1c: /* DMA4_IRQENABLE_L1 */ irqn ++; + /* fall through */ case 0x18: /* DMA4_IRQENABLE_L0 */ s->irqen[irqn] = value; return; diff --git a/hw/omap_dss.c b/hw/omap_dss.c index ae51bdfe41..948ad8fcc5 100644 --- a/hw/omap_dss.c +++ b/hw/omap_dss.c @@ -17,9 +17,9 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "ui/console.h" -#include "omap.h" +#include "hw/omap.h" struct omap_dss_s { qemu_irq irq; diff --git a/hw/omap_gpio.c b/hw/omap_gpio.c index 25655325d0..c79f61c2ba 100644 --- a/hw/omap_gpio.c +++ b/hw/omap_gpio.c @@ -18,9 +18,9 @@ * with this program; if not, see . */ -#include "hw.h" -#include "omap.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/omap.h" +#include "hw/sysbus.h" struct omap_gpio_s { qemu_irq irq; @@ -588,7 +588,7 @@ static const MemoryRegionOps omap2_gpio_module_ops = { static void omap_gpif_reset(DeviceState *dev) { struct omap_gpif_s *s = FROM_SYSBUS(struct omap_gpif_s, - sysbus_from_qdev(dev)); + SYS_BUS_DEVICE(dev)); omap_gpio_reset(&s->omap1); } @@ -596,7 +596,7 @@ static void omap2_gpif_reset(DeviceState *dev) { int i; struct omap2_gpif_s *s = FROM_SYSBUS(struct omap2_gpif_s, - sysbus_from_qdev(dev)); + SYS_BUS_DEVICE(dev)); for (i = 0; i < s->modulecount; i++) { omap2_gpio_module_reset(&s->modules[i]); } @@ -747,7 +747,7 @@ static void omap_gpio_class_init(ObjectClass *klass, void *data) dc->props = omap_gpio_properties; } -static TypeInfo omap_gpio_info = { +static const TypeInfo omap_gpio_info = { .name = "omap-gpio", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct omap_gpif_s), @@ -776,7 +776,7 @@ static void omap2_gpio_class_init(ObjectClass *klass, void *data) dc->props = omap2_gpio_properties; } -static TypeInfo omap2_gpio_info = { +static const TypeInfo omap2_gpio_info = { .name = "omap2-gpio", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct omap2_gpif_s), diff --git a/hw/omap_gpmc.c b/hw/omap_gpmc.c index 02ab0ab568..ebb259c283 100644 --- a/hw/omap_gpmc.c +++ b/hw/omap_gpmc.c @@ -18,9 +18,9 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" -#include "flash.h" -#include "omap.h" +#include "hw/hw.h" +#include "hw/flash.h" +#include "hw/omap.h" #include "exec/memory.h" #include "exec/address-spaces.h" diff --git a/hw/omap_gptimer.c b/hw/omap_gptimer.c index a5db710dcb..8485ee84f5 100644 --- a/hw/omap_gptimer.c +++ b/hw/omap_gptimer.c @@ -17,9 +17,9 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" -#include "omap.h" +#include "hw/omap.h" /* GP timers */ struct omap_gp_timer_s { diff --git a/hw/omap_i2c.c b/hw/omap_i2c.c index ba08e6400c..92f7b371ea 100644 --- a/hw/omap_i2c.c +++ b/hw/omap_i2c.c @@ -16,10 +16,10 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" -#include "i2c.h" -#include "omap.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/i2c.h" +#include "hw/omap.h" +#include "hw/sysbus.h" typedef struct OMAPI2CState { @@ -131,7 +131,7 @@ static void omap_i2c_fifo_run(OMAPI2CState *s) static void omap_i2c_reset(DeviceState *dev) { OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, - sysbus_from_qdev(dev)); + SYS_BUS_DEVICE(dev)); s->mask = 0; s->stat = 0; s->dma = 0; @@ -471,7 +471,7 @@ static void omap_i2c_class_init(ObjectClass *klass, void *data) dc->reset = omap_i2c_reset; } -static TypeInfo omap_i2c_info = { +static const TypeInfo omap_i2c_info = { .name = "omap_i2c", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OMAPI2CState), @@ -485,7 +485,7 @@ static void omap_i2c_register_types(void) i2c_bus *omap_i2c_bus(DeviceState *omap_i2c) { - OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, sysbus_from_qdev(omap_i2c)); + OMAPI2CState *s = FROM_SYSBUS(OMAPI2CState, SYS_BUS_DEVICE(omap_i2c)); return s->bus; } diff --git a/hw/omap_intc.c b/hw/omap_intc.c index 61e0dafbdd..7da9c3548c 100644 --- a/hw/omap_intc.c +++ b/hw/omap_intc.c @@ -17,9 +17,9 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" -#include "omap.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/omap.h" +#include "hw/sysbus.h" /* Interrupt Handlers */ struct omap_intr_handler_bank_s { @@ -329,7 +329,7 @@ static const MemoryRegionOps omap_inth_mem_ops = { static void omap_inth_reset(DeviceState *dev) { struct omap_intr_handler_s *s = FROM_SYSBUS(struct omap_intr_handler_s, - sysbus_from_qdev(dev)); + SYS_BUS_DEVICE(dev)); int i; for (i = 0; i < s->nbanks; ++i){ @@ -389,7 +389,7 @@ static void omap_intc_class_init(ObjectClass *klass, void *data) dc->props = omap_intc_properties; } -static TypeInfo omap_intc_info = { +static const TypeInfo omap_intc_info = { .name = "omap-intc", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct omap_intr_handler_s), @@ -633,7 +633,7 @@ static void omap2_intc_class_init(ObjectClass *klass, void *data) dc->props = omap2_intc_properties; } -static TypeInfo omap2_intc_info = { +static const TypeInfo omap2_intc_info = { .name = "omap2-intc", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct omap_intr_handler_s), diff --git a/hw/omap_l4.c b/hw/omap_l4.c index 09e983f319..cbe8a06033 100644 --- a/hw/omap_l4.c +++ b/hw/omap_l4.c @@ -17,8 +17,8 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" -#include "omap.h" +#include "hw/hw.h" +#include "hw/omap.h" struct omap_l4_s { MemoryRegion *address_space; diff --git a/hw/omap_lcdc.c b/hw/omap_lcdc.c index 936850a621..c426f3a13a 100644 --- a/hw/omap_lcdc.c +++ b/hw/omap_lcdc.c @@ -16,10 +16,10 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "ui/console.h" -#include "omap.h" -#include "framebuffer.h" +#include "hw/omap.h" +#include "hw/framebuffer.h" #include "ui/pixel_ops.h" struct omap_lcd_panel_s { @@ -70,13 +70,13 @@ static void omap_lcd_interrupts(struct omap_lcd_panel_s *s) #define draw_line_func drawfn #define DEPTH 8 -#include "omap_lcd_template.h" +#include "hw/omap_lcd_template.h" #define DEPTH 15 -#include "omap_lcd_template.h" +#include "hw/omap_lcd_template.h" #define DEPTH 16 -#include "omap_lcd_template.h" +#include "hw/omap_lcd_template.h" #define DEPTH 32 -#include "omap_lcd_template.h" +#include "hw/omap_lcd_template.h" static draw_line_func draw_line_table2[33] = { [0 ... 32] = NULL, diff --git a/hw/omap_mmc.c b/hw/omap_mmc.c index 7ecd9bd4ca..6e48110c9e 100644 --- a/hw/omap_mmc.c +++ b/hw/omap_mmc.c @@ -16,9 +16,9 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" -#include "omap.h" -#include "sd.h" +#include "hw/hw.h" +#include "hw/omap.h" +#include "hw/sd.h" struct omap_mmc_s { qemu_irq irq; diff --git a/hw/omap_sdrc.c b/hw/omap_sdrc.c index b0f3b8e675..510e6cc580 100644 --- a/hw/omap_sdrc.c +++ b/hw/omap_sdrc.c @@ -17,8 +17,8 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" -#include "omap.h" +#include "hw/hw.h" +#include "hw/omap.h" /* SDRAM Controller Subsystem */ struct omap_sdrc_s { diff --git a/hw/omap_spi.c b/hw/omap_spi.c index 42d5149a2b..1cbd98d338 100644 --- a/hw/omap_spi.c +++ b/hw/omap_spi.c @@ -19,8 +19,8 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "hw.h" -#include "omap.h" +#include "hw/hw.h" +#include "hw/omap.h" /* Multichannel SPI */ struct omap_mcspi_s { @@ -167,32 +167,47 @@ static uint64_t omap_mcspi_read(void *opaque, hwaddr addr, return s->control; case 0x68: ch ++; + /* fall through */ case 0x54: ch ++; + /* fall through */ case 0x40: ch ++; + /* fall through */ case 0x2c: /* MCSPI_CHCONF */ return s->ch[ch].config; case 0x6c: ch ++; + /* fall through */ case 0x58: ch ++; + /* fall through */ case 0x44: ch ++; + /* fall through */ case 0x30: /* MCSPI_CHSTAT */ return s->ch[ch].status; case 0x70: ch ++; + /* fall through */ case 0x5c: ch ++; + /* fall through */ case 0x48: ch ++; + /* fall through */ case 0x34: /* MCSPI_CHCTRL */ return s->ch[ch].control; case 0x74: ch ++; + /* fall through */ case 0x60: ch ++; + /* fall through */ case 0x4c: ch ++; + /* fall through */ case 0x38: /* MCSPI_TX */ return s->ch[ch].tx; case 0x78: ch ++; + /* fall through */ case 0x64: ch ++; + /* fall through */ case 0x50: ch ++; + /* fall through */ case 0x3c: /* MCSPI_RX */ s->ch[ch].status &= ~(1 << 0); /* RXS */ ret = s->ch[ch].rx; @@ -269,8 +284,11 @@ static void omap_mcspi_write(void *opaque, hwaddr addr, break; case 0x68: ch ++; + /* fall through */ case 0x54: ch ++; + /* fall through */ case 0x40: ch ++; + /* fall through */ case 0x2c: /* MCSPI_CHCONF */ if ((value ^ s->ch[ch].config) & (3 << 14)) /* DMAR | DMAW */ omap_mcspi_dmarequest_update(s->ch + ch); @@ -283,8 +301,11 @@ static void omap_mcspi_write(void *opaque, hwaddr addr, break; case 0x70: ch ++; + /* fall through */ case 0x5c: ch ++; + /* fall through */ case 0x48: ch ++; + /* fall through */ case 0x34: /* MCSPI_CHCTRL */ if (value & ~s->ch[ch].control & 1) { /* EN */ s->ch[ch].control |= 1; @@ -294,8 +315,11 @@ static void omap_mcspi_write(void *opaque, hwaddr addr, break; case 0x74: ch ++; + /* fall through */ case 0x60: ch ++; + /* fall through */ case 0x4c: ch ++; + /* fall through */ case 0x38: /* MCSPI_TX */ s->ch[ch].tx = value; s->ch[ch].status &= ~(1 << 1); /* TXS */ diff --git a/hw/omap_synctimer.c b/hw/omap_synctimer.c index 945711eff5..13e7280e69 100644 --- a/hw/omap_synctimer.c +++ b/hw/omap_synctimer.c @@ -17,9 +17,9 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" -#include "omap.h" +#include "hw/omap.h" struct omap_synctimer_s { MemoryRegion iomem; uint32_t val; diff --git a/hw/omap_tap.c b/hw/omap_tap.c index e273e971ed..181ecee1a5 100644 --- a/hw/omap_tap.c +++ b/hw/omap_tap.c @@ -18,8 +18,8 @@ * with this program; if not, see . */ -#include "hw.h" -#include "omap.h" +#include "hw/hw.h" +#include "hw/omap.h" /* TEST-Chip-level TAP */ static uint64_t omap_tap_read(void *opaque, hwaddr addr, diff --git a/hw/omap_uart.c b/hw/omap_uart.c index 0ebfbf8cae..af51ce7534 100644 --- a/hw/omap_uart.c +++ b/hw/omap_uart.c @@ -18,9 +18,9 @@ * with this program; if not, see . */ #include "char/char.h" -#include "hw.h" -#include "omap.h" -#include "serial.h" +#include "hw/hw.h" +#include "hw/omap.h" +#include "hw/serial.h" #include "exec/address-spaces.h" /* UARTs */ diff --git a/hw/onenand.c b/hw/onenand.c index 26bf991d6d..ddba366ef5 100644 --- a/hw/onenand.c +++ b/hw/onenand.c @@ -19,13 +19,13 @@ */ #include "qemu-common.h" -#include "hw.h" -#include "flash.h" -#include "irq.h" +#include "hw/hw.h" +#include "hw/flash.h" +#include "hw/irq.h" #include "sysemu/blockdev.h" #include "exec/memory.h" #include "exec/address-spaces.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/error-report.h" /* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */ @@ -224,7 +224,7 @@ static void onenand_reset(OneNANDState *s, int cold) static void onenand_system_reset(DeviceState *dev) { - onenand_reset(FROM_SYSBUS(OneNANDState, sysbus_from_qdev(dev)), 1); + onenand_reset(FROM_SYSBUS(OneNANDState, SYS_BUS_DEVICE(dev)), 1); } static inline int onenand_load_main(OneNANDState *s, int sec, int secn, @@ -821,7 +821,7 @@ static void onenand_class_init(ObjectClass *klass, void *data) dc->props = onenand_properties; } -static TypeInfo onenand_info = { +static const TypeInfo onenand_info = { .name = "onenand", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OneNANDState), @@ -835,7 +835,7 @@ static void onenand_register_types(void) void *onenand_raw_otp(DeviceState *onenand_device) { - return FROM_SYSBUS(OneNANDState, sysbus_from_qdev(onenand_device))->otp; + return FROM_SYSBUS(OneNANDState, SYS_BUS_DEVICE(onenand_device))->otp; } type_init(onenand_register_types) diff --git a/hw/opencores_eth.c b/hw/opencores_eth.c index a0dfdce1f9..be64bf2a68 100644 --- a/hw/opencores_eth.c +++ b/hw/opencores_eth.c @@ -31,8 +31,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #include "net/net.h" #include "sysemu/sysemu.h" #include "trace.h" @@ -313,7 +313,7 @@ static void open_eth_int_source_write(OpenEthState *s, static void open_eth_set_link_status(NetClientState *nc) { - OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; + OpenEthState *s = qemu_get_nic_opaque(nc); if (GET_REGBIT(s, MIICOMMAND, SCANSTAT)) { SET_REGFIELD(s, MIISTATUS, LINKFAIL, nc->link_down); @@ -339,12 +339,12 @@ static void open_eth_reset(void *opaque) s->rx_desc = 0x40; mii_reset(&s->mii); - open_eth_set_link_status(&s->nic->nc); + open_eth_set_link_status(qemu_get_queue(s->nic)); } static int open_eth_can_receive(NetClientState *nc) { - OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; + OpenEthState *s = qemu_get_nic_opaque(nc); return GET_REGBIT(s, MODER, RXEN) && (s->regs[TX_BD_NUM] < 0x80) && @@ -354,7 +354,7 @@ static int open_eth_can_receive(NetClientState *nc) static ssize_t open_eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; + OpenEthState *s = qemu_get_nic_opaque(nc); size_t maxfl = GET_REGFIELD(s, PACKETLEN, MAXFL); size_t minfl = GET_REGFIELD(s, PACKETLEN, MINFL); size_t fcsl = 4; @@ -499,7 +499,7 @@ static void open_eth_start_xmit(OpenEthState *s, desc *tx) if (tx_len > len) { memset(buf + len, 0, tx_len - len); } - qemu_send_packet(&s->nic->nc, buf, tx_len); + qemu_send_packet(qemu_get_queue(s->nic), buf, tx_len); if (tx->len_flags & TXD_WR) { s->tx_desc = 0; @@ -606,7 +606,7 @@ static void open_eth_mii_command_host_write(OpenEthState *s, uint32_t val) } else { s->regs[MIIRX_DATA] = 0xffff; } - SET_REGFIELD(s, MIISTATUS, LINKFAIL, s->nic->nc.link_down); + SET_REGFIELD(s, MIISTATUS, LINKFAIL, qemu_get_queue(s->nic)->link_down); } } @@ -718,7 +718,7 @@ static void open_eth_class_init(ObjectClass *klass, void *data) dc->props = open_eth_properties; } -static TypeInfo open_eth_info = { +static const TypeInfo open_eth_info = { .name = "open_eth", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OpenEthState), diff --git a/hw/openpic.c b/hw/openpic.c index 9c956b9dcc..03a7075c39 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -33,32 +33,40 @@ * Serial interrupts, as implemented in Raven chipset are not supported yet. * */ -#include "hw.h" -#include "ppc_mac.h" -#include "pci/pci.h" -#include "openpic.h" -#include "sysbus.h" -#include "pci/msi.h" +#include "hw/hw.h" +#include "hw/ppc/mac.h" +#include "hw/pci/pci.h" +#include "hw/openpic.h" +#include "hw/sysbus.h" +#include "hw/pci/msi.h" +#include "qemu/bitops.h" +#include "hw/ppc.h" //#define DEBUG_OPENPIC #ifdef DEBUG_OPENPIC -#define DPRINTF(fmt, ...) do { printf(fmt , ## __VA_ARGS__); } while (0) +static const int debug_openpic = 1; #else -#define DPRINTF(fmt, ...) do { } while (0) +static const int debug_openpic = 0; #endif -#define MAX_CPU 15 +#define DPRINTF(fmt, ...) do { \ + if (debug_openpic) { \ + printf(fmt , ## __VA_ARGS__); \ + } \ + } while (0) + +#define MAX_CPU 32 #define MAX_SRC 256 #define MAX_TMR 4 -#define VECTOR_BITS 8 #define MAX_IPI 4 #define MAX_MSI 8 #define MAX_IRQ (MAX_SRC + MAX_IPI + MAX_TMR) #define VID 0x03 /* MPIC version ID */ /* OpenPIC capability flags */ -#define OPENPIC_FLAG_IDE_CRIT (1 << 0) +#define OPENPIC_FLAG_IDR_CRIT (1 << 0) +#define OPENPIC_FLAG_ILR (2 << 0) /* OpenPIC address map */ #define OPENPIC_GLB_REG_START 0x0 @@ -67,6 +75,8 @@ #define OPENPIC_TMR_REG_SIZE 0x220 #define OPENPIC_MSI_REG_START 0x1600 #define OPENPIC_MSI_REG_SIZE 0x200 +#define OPENPIC_SUMMARY_REG_START 0x3800 +#define OPENPIC_SUMMARY_REG_SIZE 0x800 #define OPENPIC_SRC_REG_START 0x10000 #define OPENPIC_SRC_REG_SIZE (MAX_SRC * 0x20) #define OPENPIC_CPU_REG_START 0x20000 @@ -87,42 +97,34 @@ /* First doorbell IRQ */ #define RAVEN_DBL_IRQ (RAVEN_IPI_IRQ + (RAVEN_MAX_CPU * RAVEN_MAX_IPI)) -/* FSL_MPIC_20 */ -#define FSL_MPIC_20_MAX_CPU 1 -#define FSL_MPIC_20_MAX_EXT 12 -#define FSL_MPIC_20_MAX_INT 64 -#define FSL_MPIC_20_MAX_IRQ MAX_IRQ +typedef struct FslMpicInfo { + int max_ext; +} FslMpicInfo; -/* Interrupt definitions */ -/* IRQs, accessible through the IRQ region */ -#define FSL_MPIC_20_EXT_IRQ 0x00 -#define FSL_MPIC_20_INT_IRQ 0x10 -#define FSL_MPIC_20_MSG_IRQ 0xb0 -#define FSL_MPIC_20_MSI_IRQ 0xe0 -/* These are available through separate regions, but - for simplicity's sake mapped into the same number space */ -#define FSL_MPIC_20_TMR_IRQ 0x100 -#define FSL_MPIC_20_IPI_IRQ 0x104 +static FslMpicInfo fsl_mpic_20 = { + .max_ext = 12, +}; -/* - * Block Revision Register1 (BRR1): QEMU does not fully emulate - * any version on MPIC. So to start with, set the IP version to 0. - * - * NOTE: This is Freescale MPIC specific register. Keep it here till - * this code is refactored for different variants of OPENPIC and MPIC. - */ -#define FSL_BRR1_IPID (0x0040 << 16) /* 16 bit IP-block ID */ -#define FSL_BRR1_IPMJ (0x00 << 8) /* 8 bit IP major number */ -#define FSL_BRR1_IPMN 0x00 /* 8 bit IP minor number */ +static FslMpicInfo fsl_mpic_42 = { + .max_ext = 12, +}; -#define FREP_NIRQ_SHIFT 16 -#define FREP_NCPU_SHIFT 8 -#define FREP_VID_SHIFT 0 +#define FRR_NIRQ_SHIFT 16 +#define FRR_NCPU_SHIFT 8 +#define FRR_VID_SHIFT 0 #define VID_REVISION_1_2 2 #define VID_REVISION_1_3 3 -#define VENI_GENERIC 0x00000000 /* Generic Vendor ID */ +#define VIR_GENERIC 0x00000000 /* Generic Vendor ID */ + +#define GCR_RESET 0x80000000 +#define GCR_MODE_PASS 0x00000000 +#define GCR_MODE_MIXED 0x20000000 +#define GCR_MODE_PROXY 0x60000000 + +#define TBCR_CI 0x80000000 /* count inhibit */ +#define TCCR_TOG 0x80000000 /* toggles when decrement to zero */ #define IDR_EP_SHIFT 31 #define IDR_EP_MASK (1 << IDR_EP_SHIFT) @@ -131,33 +133,65 @@ #define IDR_P1_SHIFT 1 #define IDR_P0_SHIFT 0 +#define ILR_INTTGT_MASK 0x000000ff +#define ILR_INTTGT_INT 0x00 +#define ILR_INTTGT_CINT 0x01 /* critical */ +#define ILR_INTTGT_MCP 0x02 /* machine check */ + +/* The currently supported INTTGT values happen to be the same as QEMU's + * openpic output codes, but don't depend on this. The output codes + * could change (unlikely, but...) or support could be added for + * more INTTGT values. + */ +static const int inttgt_output[][2] = { + { ILR_INTTGT_INT, OPENPIC_OUTPUT_INT }, + { ILR_INTTGT_CINT, OPENPIC_OUTPUT_CINT }, + { ILR_INTTGT_MCP, OPENPIC_OUTPUT_MCK }, +}; + +static int inttgt_to_output(int inttgt) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(inttgt_output); i++) { + if (inttgt_output[i][0] == inttgt) { + return inttgt_output[i][1]; + } + } + + fprintf(stderr, "%s: unsupported inttgt %d\n", __func__, inttgt); + return OPENPIC_OUTPUT_INT; +} + +static int output_to_inttgt(int output) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(inttgt_output); i++) { + if (inttgt_output[i][1] == output) { + return inttgt_output[i][0]; + } + } + + abort(); +} + #define MSIIR_OFFSET 0x140 #define MSIIR_SRS_SHIFT 29 #define MSIIR_SRS_MASK (0x7 << MSIIR_SRS_SHIFT) #define MSIIR_IBS_SHIFT 24 #define MSIIR_IBS_MASK (0x1f << MSIIR_IBS_SHIFT) -#define BF_WIDTH(_bits_) \ -(((_bits_) + (sizeof(uint32_t) * 8) - 1) / (sizeof(uint32_t) * 8)) - -static inline void set_bit(uint32_t *field, int bit) -{ - field[bit >> 5] |= 1 << (bit & 0x1F); -} - -static inline void reset_bit(uint32_t *field, int bit) -{ - field[bit >> 5] &= ~(1 << (bit & 0x1F)); -} - -static inline int test_bit(uint32_t *field, int bit) -{ - return (field[bit >> 5] & 1 << (bit & 0x1F)) != 0; -} - static int get_current_cpu(void) { - return cpu_single_env->cpu_index; + CPUState *cpu_single_cpu; + + if (!cpu_single_env) { + return -1; + } + + cpu_single_cpu = ENV_GET_CPU(cpu_single_env); + return cpu_single_cpu->cpu_index; } static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, @@ -165,78 +199,98 @@ static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, static void openpic_cpu_write_internal(void *opaque, hwaddr addr, uint32_t val, int idx); -typedef struct IRQ_queue_t { - uint32_t queue[BF_WIDTH(MAX_IRQ)]; +typedef enum IRQType { + IRQ_TYPE_NORMAL = 0, + IRQ_TYPE_FSLINT, /* FSL internal interrupt -- level only */ + IRQ_TYPE_FSLSPECIAL, /* FSL timer/IPI interrupt, edge, no polarity */ +} IRQType; + +typedef struct IRQQueue { + /* Round up to the nearest 64 IRQs so that the queue length + * won't change when moving between 32 and 64 bit hosts. + */ + unsigned long queue[BITS_TO_LONGS((MAX_IRQ + 63) & ~63)]; int next; int priority; - int pending; /* nr of pending bits in queue */ -} IRQ_queue_t; +} IRQQueue; -typedef struct IRQ_src_t { - uint32_t ipvp; /* IRQ vector/priority register */ - uint32_t ide; /* IRQ destination register */ +typedef struct IRQSource { + uint32_t ivpr; /* IRQ vector/priority register */ + uint32_t idr; /* IRQ destination register */ + uint32_t destmask; /* bitmap of CPU destinations */ int last_cpu; + int output; /* IRQ level, e.g. OPENPIC_OUTPUT_INT */ int pending; /* TRUE if IRQ is pending */ -} IRQ_src_t; + IRQType type; + bool level:1; /* level-triggered */ + bool nomask:1; /* critical interrupts ignore mask on some FSL MPICs */ +} IRQSource; -#define IPVP_MASK_SHIFT 31 -#define IPVP_MASK_MASK (1 << IPVP_MASK_SHIFT) -#define IPVP_ACTIVITY_SHIFT 30 -#define IPVP_ACTIVITY_MASK (1 << IPVP_ACTIVITY_SHIFT) -#define IPVP_MODE_SHIFT 29 -#define IPVP_MODE_MASK (1 << IPVP_MODE_SHIFT) -#define IPVP_POLARITY_SHIFT 23 -#define IPVP_POLARITY_MASK (1 << IPVP_POLARITY_SHIFT) -#define IPVP_SENSE_SHIFT 22 -#define IPVP_SENSE_MASK (1 << IPVP_SENSE_SHIFT) +#define IVPR_MASK_SHIFT 31 +#define IVPR_MASK_MASK (1 << IVPR_MASK_SHIFT) +#define IVPR_ACTIVITY_SHIFT 30 +#define IVPR_ACTIVITY_MASK (1 << IVPR_ACTIVITY_SHIFT) +#define IVPR_MODE_SHIFT 29 +#define IVPR_MODE_MASK (1 << IVPR_MODE_SHIFT) +#define IVPR_POLARITY_SHIFT 23 +#define IVPR_POLARITY_MASK (1 << IVPR_POLARITY_SHIFT) +#define IVPR_SENSE_SHIFT 22 +#define IVPR_SENSE_MASK (1 << IVPR_SENSE_SHIFT) -#define IPVP_PRIORITY_MASK (0x1F << 16) -#define IPVP_PRIORITY(_ipvpr_) ((int)(((_ipvpr_) & IPVP_PRIORITY_MASK) >> 16)) -#define IPVP_VECTOR_MASK ((1 << VECTOR_BITS) - 1) -#define IPVP_VECTOR(_ipvpr_) ((_ipvpr_) & IPVP_VECTOR_MASK) +#define IVPR_PRIORITY_MASK (0xF << 16) +#define IVPR_PRIORITY(_ivprr_) ((int)(((_ivprr_) & IVPR_PRIORITY_MASK) >> 16)) +#define IVPR_VECTOR(opp, _ivprr_) ((_ivprr_) & (opp)->vector_mask) -typedef struct IRQ_dst_t { - uint32_t pctp; /* CPU current task priority */ - uint32_t pcsr; /* CPU sensitivity register */ - IRQ_queue_t raised; - IRQ_queue_t servicing; +/* IDR[EP/CI] are only for FSL MPIC prior to v4.0 */ +#define IDR_EP 0x80000000 /* external pin */ +#define IDR_CI 0x40000000 /* critical interrupt */ + +typedef struct IRQDest { + int32_t ctpr; /* CPU current task priority */ + IRQQueue raised; + IRQQueue servicing; qemu_irq *irqs; -} IRQ_dst_t; + + /* Count of IRQ sources asserting on non-INT outputs */ + uint32_t outputs_active[OPENPIC_OUTPUT_NB]; +} IRQDest; typedef struct OpenPICState { SysBusDevice busdev; MemoryRegion mem; /* Behavior control */ + FslMpicInfo *fsl; uint32_t model; uint32_t flags; uint32_t nb_irqs; uint32_t vid; - uint32_t veni; /* Vendor identification register */ - uint32_t spve_mask; - uint32_t tifr_reset; - uint32_t ipvp_reset; - uint32_t ide_reset; + uint32_t vir; /* Vendor identification register */ + uint32_t vector_mask; + uint32_t tfrr_reset; + uint32_t ivpr_reset; + uint32_t idr_reset; uint32_t brr1; + uint32_t mpic_mode_mask; /* Sub-regions */ - MemoryRegion sub_io_mem[5]; + MemoryRegion sub_io_mem[6]; /* Global registers */ - uint32_t frep; /* Feature reporting register */ - uint32_t glbc; /* Global configuration register */ - uint32_t pint; /* Processor initialization register */ + uint32_t frr; /* Feature reporting register */ + uint32_t gcr; /* Global configuration register */ + uint32_t pir; /* Processor initialization register */ uint32_t spve; /* Spurious vector register */ - uint32_t tifr; /* Timer frequency reporting register */ + uint32_t tfrr; /* Timer frequency reporting register */ /* Source registers */ - IRQ_src_t src[MAX_IRQ]; + IRQSource src[MAX_IRQ]; /* Local registers per output pin */ - IRQ_dst_t dst[MAX_CPU]; + IRQDest dst[MAX_CPU]; uint32_t nb_cpus; /* Timer registers */ struct { - uint32_t ticc; /* Global timer current count register */ - uint32_t tibc; /* Global timer base count register */ + uint32_t tccr; /* Global timer current count register */ + uint32_t tbcr; /* Global timer base count register */ } timers[MAX_TMR]; /* Shared MSI registers */ struct { @@ -248,156 +302,195 @@ typedef struct OpenPICState { uint32_t irq_msi; } OpenPICState; -static void openpic_irq_raise(OpenPICState *opp, int n_CPU, IRQ_src_t *src); - -static inline void IRQ_setbit(IRQ_queue_t *q, int n_IRQ) +static inline void IRQ_setbit(IRQQueue *q, int n_IRQ) { - q->pending++; - set_bit(q->queue, n_IRQ); + set_bit(n_IRQ, q->queue); } -static inline void IRQ_resetbit(IRQ_queue_t *q, int n_IRQ) +static inline void IRQ_resetbit(IRQQueue *q, int n_IRQ) { - q->pending--; - reset_bit(q->queue, n_IRQ); + clear_bit(n_IRQ, q->queue); } -static inline int IRQ_testbit(IRQ_queue_t *q, int n_IRQ) +static inline int IRQ_testbit(IRQQueue *q, int n_IRQ) { - return test_bit(q->queue, n_IRQ); + return test_bit(n_IRQ, q->queue); } -static void IRQ_check(OpenPICState *opp, IRQ_queue_t *q) +static void IRQ_check(OpenPICState *opp, IRQQueue *q) { - int next, i; - int priority; + int irq = -1; + int next = -1; + int priority = -1; - next = -1; - priority = -1; + for (;;) { + irq = find_next_bit(q->queue, opp->max_irq, irq + 1); + if (irq == opp->max_irq) { + break; + } - if (!q->pending) { - /* IRQ bitmap is empty */ - goto out; - } + DPRINTF("IRQ_check: irq %d set ivpr_pr=%d pr=%d\n", + irq, IVPR_PRIORITY(opp->src[irq].ivpr), priority); - for (i = 0; i < opp->max_irq; i++) { - if (IRQ_testbit(q, i)) { - DPRINTF("IRQ_check: irq %d set ipvp_pr=%d pr=%d\n", - i, IPVP_PRIORITY(opp->src[i].ipvp), priority); - if (IPVP_PRIORITY(opp->src[i].ipvp) > priority) { - next = i; - priority = IPVP_PRIORITY(opp->src[i].ipvp); - } + if (IVPR_PRIORITY(opp->src[irq].ivpr) > priority) { + next = irq; + priority = IVPR_PRIORITY(opp->src[irq].ivpr); } } -out: q->next = next; q->priority = priority; } -static int IRQ_get_next(OpenPICState *opp, IRQ_queue_t *q) +static int IRQ_get_next(OpenPICState *opp, IRQQueue *q) { - if (q->next == -1) { - /* XXX: optimize */ - IRQ_check(opp, q); - } + /* XXX: optimize */ + IRQ_check(opp, q); return q->next; } -static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ) +static void IRQ_local_pipe(OpenPICState *opp, int n_CPU, int n_IRQ, + bool active, bool was_active) { - IRQ_dst_t *dst; - IRQ_src_t *src; + IRQDest *dst; + IRQSource *src; int priority; dst = &opp->dst[n_CPU]; src = &opp->src[n_IRQ]; - priority = IPVP_PRIORITY(src->ipvp); - if (priority <= dst->pctp) { - /* Too low priority */ - DPRINTF("%s: IRQ %d has too low priority on CPU %d\n", - __func__, n_IRQ, n_CPU); + + DPRINTF("%s: IRQ %d active %d was %d\n", + __func__, n_IRQ, active, was_active); + + if (src->output != OPENPIC_OUTPUT_INT) { + DPRINTF("%s: output %d irq %d active %d was %d count %d\n", + __func__, src->output, n_IRQ, active, was_active, + dst->outputs_active[src->output]); + + /* On Freescale MPIC, critical interrupts ignore priority, + * IACK, EOI, etc. Before MPIC v4.1 they also ignore + * masking. + */ + if (active) { + if (!was_active && dst->outputs_active[src->output]++ == 0) { + DPRINTF("%s: Raise OpenPIC output %d cpu %d irq %d\n", + __func__, src->output, n_CPU, n_IRQ); + qemu_irq_raise(dst->irqs[src->output]); + } + } else { + if (was_active && --dst->outputs_active[src->output] == 0) { + DPRINTF("%s: Lower OpenPIC output %d cpu %d irq %d\n", + __func__, src->output, n_CPU, n_IRQ); + qemu_irq_lower(dst->irqs[src->output]); + } + } + return; } - if (IRQ_testbit(&dst->raised, n_IRQ)) { - /* Interrupt miss */ - DPRINTF("%s: IRQ %d was missed on CPU %d\n", - __func__, n_IRQ, n_CPU); - return; + + priority = IVPR_PRIORITY(src->ivpr); + + /* Even if the interrupt doesn't have enough priority, + * it is still raised, in case ctpr is lowered later. + */ + if (active) { + IRQ_setbit(&dst->raised, n_IRQ); + } else { + IRQ_resetbit(&dst->raised, n_IRQ); } - src->ipvp |= IPVP_ACTIVITY_MASK; - IRQ_setbit(&dst->raised, n_IRQ); - if (priority < dst->raised.priority) { - /* An higher priority IRQ is already raised */ - DPRINTF("%s: IRQ %d is hidden by raised IRQ %d on CPU %d\n", - __func__, n_IRQ, dst->raised.next, n_CPU); - return; + + IRQ_check(opp, &dst->raised); + + if (active && priority <= dst->ctpr) { + DPRINTF("%s: IRQ %d priority %d too low for ctpr %d on CPU %d\n", + __func__, n_IRQ, priority, dst->ctpr, n_CPU); + active = 0; } - IRQ_get_next(opp, &dst->raised); - if (IRQ_get_next(opp, &dst->servicing) != -1 && - priority <= dst->servicing.priority) { - DPRINTF("%s: IRQ %d is hidden by servicing IRQ %d on CPU %d\n", - __func__, n_IRQ, dst->servicing.next, n_CPU); - /* Already servicing a higher priority IRQ */ - return; + + if (active) { + if (IRQ_get_next(opp, &dst->servicing) >= 0 && + priority <= dst->servicing.priority) { + DPRINTF("%s: IRQ %d is hidden by servicing IRQ %d on CPU %d\n", + __func__, n_IRQ, dst->servicing.next, n_CPU); + } else { + DPRINTF("%s: Raise OpenPIC INT output cpu %d irq %d/%d\n", + __func__, n_CPU, n_IRQ, dst->raised.next); + qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]); + } + } else { + IRQ_get_next(opp, &dst->servicing); + if (dst->raised.priority > dst->ctpr && + dst->raised.priority > dst->servicing.priority) { + DPRINTF("%s: IRQ %d inactive, IRQ %d prio %d above %d/%d, CPU %d\n", + __func__, n_IRQ, dst->raised.next, dst->raised.priority, + dst->ctpr, dst->servicing.priority, n_CPU); + /* IRQ line stays asserted */ + } else { + DPRINTF("%s: IRQ %d inactive, current prio %d/%d, CPU %d\n", + __func__, n_IRQ, dst->ctpr, dst->servicing.priority, n_CPU); + qemu_irq_lower(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]); + } } - DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", n_CPU, n_IRQ); - openpic_irq_raise(opp, n_CPU, src); } /* update pic state because registers for n_IRQ have changed value */ static void openpic_update_irq(OpenPICState *opp, int n_IRQ) { - IRQ_src_t *src; + IRQSource *src; + bool active, was_active; int i; src = &opp->src[n_IRQ]; + active = src->pending; - if (!src->pending) { - /* no irq pending */ - DPRINTF("%s: IRQ %d is not pending\n", __func__, n_IRQ); - return; - } - if (src->ipvp & IPVP_MASK_MASK) { + if ((src->ivpr & IVPR_MASK_MASK) && !src->nomask) { /* Interrupt source is disabled */ DPRINTF("%s: IRQ %d is disabled\n", __func__, n_IRQ); + active = false; + } + + was_active = !!(src->ivpr & IVPR_ACTIVITY_MASK); + + /* + * We don't have a similar check for already-active because + * ctpr may have changed and we need to withdraw the interrupt. + */ + if (!active && !was_active) { + DPRINTF("%s: IRQ %d is already inactive\n", __func__, n_IRQ); return; } - if (IPVP_PRIORITY(src->ipvp) == 0) { - /* Priority set to zero */ - DPRINTF("%s: IRQ %d has 0 priority\n", __func__, n_IRQ); - return; + + if (active) { + src->ivpr |= IVPR_ACTIVITY_MASK; + } else { + src->ivpr &= ~IVPR_ACTIVITY_MASK; } - if (src->ipvp & IPVP_ACTIVITY_MASK) { - /* IRQ already active */ - DPRINTF("%s: IRQ %d is already active\n", __func__, n_IRQ); - return; - } - if (src->ide == 0x00000000) { + + if (src->destmask == 0) { /* No target */ DPRINTF("%s: IRQ %d has no target\n", __func__, n_IRQ); return; } - if (src->ide == (1 << src->last_cpu)) { + if (src->destmask == (1 << src->last_cpu)) { /* Only one CPU is allowed to receive this IRQ */ - IRQ_local_pipe(opp, src->last_cpu, n_IRQ); - } else if (!(src->ipvp & IPVP_MODE_MASK)) { + IRQ_local_pipe(opp, src->last_cpu, n_IRQ, active, was_active); + } else if (!(src->ivpr & IVPR_MODE_MASK)) { /* Directed delivery mode */ for (i = 0; i < opp->nb_cpus; i++) { - if (src->ide & (1 << i)) { - IRQ_local_pipe(opp, i, n_IRQ); + if (src->destmask & (1 << i)) { + IRQ_local_pipe(opp, i, n_IRQ, active, was_active); } } } else { /* Distributed delivery mode */ for (i = src->last_cpu + 1; i != src->last_cpu; i++) { - if (i == opp->nb_cpus) + if (i == opp->nb_cpus) { i = 0; - if (src->ide & (1 << i)) { - IRQ_local_pipe(opp, i, n_IRQ); + } + if (src->destmask & (1 << i)) { + IRQ_local_pipe(opp, i, n_IRQ, active, was_active); src->last_cpu = i; break; } @@ -408,103 +501,235 @@ static void openpic_update_irq(OpenPICState *opp, int n_IRQ) static void openpic_set_irq(void *opaque, int n_IRQ, int level) { OpenPICState *opp = opaque; - IRQ_src_t *src; + IRQSource *src; + + if (n_IRQ >= MAX_IRQ) { + fprintf(stderr, "%s: IRQ %d out of range\n", __func__, n_IRQ); + abort(); + } src = &opp->src[n_IRQ]; - DPRINTF("openpic: set irq %d = %d ipvp=%08x\n", - n_IRQ, level, src->ipvp); - if (src->ipvp & IPVP_SENSE_MASK) { + DPRINTF("openpic: set irq %d = %d ivpr=0x%08x\n", + n_IRQ, level, src->ivpr); + if (src->level) { /* level-sensitive irq */ src->pending = level; - if (!level) { - src->ipvp &= ~IPVP_ACTIVITY_MASK; - } + openpic_update_irq(opp, n_IRQ); } else { /* edge-sensitive irq */ - if (level) + if (level) { src->pending = 1; + openpic_update_irq(opp, n_IRQ); + } + + if (src->output != OPENPIC_OUTPUT_INT) { + /* Edge-triggered interrupts shouldn't be used + * with non-INT delivery, but just in case, + * try to make it do something sane rather than + * cause an interrupt storm. This is close to + * what you'd probably see happen in real hardware. + */ + src->pending = 0; + openpic_update_irq(opp, n_IRQ); + } } - openpic_update_irq(opp, n_IRQ); } static void openpic_reset(DeviceState *d) { - OpenPICState *opp = FROM_SYSBUS(typeof (*opp), sysbus_from_qdev(d)); + OpenPICState *opp = FROM_SYSBUS(typeof(*opp), SYS_BUS_DEVICE(d)); int i; - opp->glbc = 0x80000000; + opp->gcr = GCR_RESET; /* Initialise controller registers */ - opp->frep = ((opp->nb_irqs -1) << FREP_NIRQ_SHIFT) | - ((opp->nb_cpus -1) << FREP_NCPU_SHIFT) | - (opp->vid << FREP_VID_SHIFT); + opp->frr = ((opp->nb_irqs - 1) << FRR_NIRQ_SHIFT) | + ((opp->nb_cpus - 1) << FRR_NCPU_SHIFT) | + (opp->vid << FRR_VID_SHIFT); - opp->pint = 0x00000000; - opp->spve = -1 & opp->spve_mask; - opp->tifr = opp->tifr_reset; + opp->pir = 0; + opp->spve = -1 & opp->vector_mask; + opp->tfrr = opp->tfrr_reset; /* Initialise IRQ sources */ for (i = 0; i < opp->max_irq; i++) { - opp->src[i].ipvp = opp->ipvp_reset; - opp->src[i].ide = opp->ide_reset; + opp->src[i].ivpr = opp->ivpr_reset; + opp->src[i].idr = opp->idr_reset; + + switch (opp->src[i].type) { + case IRQ_TYPE_NORMAL: + opp->src[i].level = !!(opp->ivpr_reset & IVPR_SENSE_MASK); + break; + + case IRQ_TYPE_FSLINT: + opp->src[i].ivpr |= IVPR_POLARITY_MASK; + break; + + case IRQ_TYPE_FSLSPECIAL: + break; + } } /* Initialise IRQ destinations */ for (i = 0; i < MAX_CPU; i++) { - opp->dst[i].pctp = 0x0000000F; - opp->dst[i].pcsr = 0x00000000; - memset(&opp->dst[i].raised, 0, sizeof(IRQ_queue_t)); + opp->dst[i].ctpr = 15; + memset(&opp->dst[i].raised, 0, sizeof(IRQQueue)); opp->dst[i].raised.next = -1; - memset(&opp->dst[i].servicing, 0, sizeof(IRQ_queue_t)); + memset(&opp->dst[i].servicing, 0, sizeof(IRQQueue)); opp->dst[i].servicing.next = -1; } /* Initialise timers */ for (i = 0; i < MAX_TMR; i++) { - opp->timers[i].ticc = 0x00000000; - opp->timers[i].tibc = 0x80000000; + opp->timers[i].tccr = 0; + opp->timers[i].tbcr = TBCR_CI; } /* Go out of RESET state */ - opp->glbc = 0x00000000; + opp->gcr = 0; } -static inline uint32_t read_IRQreg_ide(OpenPICState *opp, int n_IRQ) +static inline uint32_t read_IRQreg_idr(OpenPICState *opp, int n_IRQ) { - return opp->src[n_IRQ].ide; + return opp->src[n_IRQ].idr; } -static inline uint32_t read_IRQreg_ipvp(OpenPICState *opp, int n_IRQ) +static inline uint32_t read_IRQreg_ilr(OpenPICState *opp, int n_IRQ) { - return opp->src[n_IRQ].ipvp; + if (opp->flags & OPENPIC_FLAG_ILR) { + return output_to_inttgt(opp->src[n_IRQ].output); + } + + return 0xffffffff; } -static inline void write_IRQreg_ide(OpenPICState *opp, int n_IRQ, uint32_t val) +static inline uint32_t read_IRQreg_ivpr(OpenPICState *opp, int n_IRQ) { - uint32_t tmp; - - tmp = val & 0xC0000000; - tmp |= val & ((1ULL << MAX_CPU) - 1); - opp->src[n_IRQ].ide = tmp; - DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide); + return opp->src[n_IRQ].ivpr; } -static inline void write_IRQreg_ipvp(OpenPICState *opp, int n_IRQ, uint32_t val) +static inline void write_IRQreg_idr(OpenPICState *opp, int n_IRQ, uint32_t val) { - /* NOTE: not fully accurate for special IRQs, but simple and sufficient */ + IRQSource *src = &opp->src[n_IRQ]; + uint32_t normal_mask = (1UL << opp->nb_cpus) - 1; + uint32_t crit_mask = 0; + uint32_t mask = normal_mask; + int crit_shift = IDR_EP_SHIFT - opp->nb_cpus; + int i; + + if (opp->flags & OPENPIC_FLAG_IDR_CRIT) { + crit_mask = mask << crit_shift; + mask |= crit_mask | IDR_EP; + } + + src->idr = val & mask; + DPRINTF("Set IDR %d to 0x%08x\n", n_IRQ, src->idr); + + if (opp->flags & OPENPIC_FLAG_IDR_CRIT) { + if (src->idr & crit_mask) { + if (src->idr & normal_mask) { + DPRINTF("%s: IRQ configured for multiple output types, using " + "critical\n", __func__); + } + + src->output = OPENPIC_OUTPUT_CINT; + src->nomask = true; + src->destmask = 0; + + for (i = 0; i < opp->nb_cpus; i++) { + int n_ci = IDR_CI0_SHIFT - i; + + if (src->idr & (1UL << n_ci)) { + src->destmask |= 1UL << i; + } + } + } else { + src->output = OPENPIC_OUTPUT_INT; + src->nomask = false; + src->destmask = src->idr & normal_mask; + } + } else { + src->destmask = src->idr; + } +} + +static inline void write_IRQreg_ilr(OpenPICState *opp, int n_IRQ, uint32_t val) +{ + if (opp->flags & OPENPIC_FLAG_ILR) { + IRQSource *src = &opp->src[n_IRQ]; + + src->output = inttgt_to_output(val & ILR_INTTGT_MASK); + DPRINTF("Set ILR %d to 0x%08x, output %d\n", n_IRQ, src->idr, + src->output); + + /* TODO: on MPIC v4.0 only, set nomask for non-INT */ + } +} + +static inline void write_IRQreg_ivpr(OpenPICState *opp, int n_IRQ, uint32_t val) +{ + uint32_t mask; + + /* NOTE when implementing newer FSL MPIC models: starting with v4.0, + * the polarity bit is read-only on internal interrupts. + */ + mask = IVPR_MASK_MASK | IVPR_PRIORITY_MASK | IVPR_SENSE_MASK | + IVPR_POLARITY_MASK | opp->vector_mask; + /* ACTIVITY bit is read-only */ - opp->src[n_IRQ].ipvp = (opp->src[n_IRQ].ipvp & 0x40000000) - | (val & 0x800F00FF); + opp->src[n_IRQ].ivpr = + (opp->src[n_IRQ].ivpr & IVPR_ACTIVITY_MASK) | (val & mask); + + /* For FSL internal interrupts, The sense bit is reserved and zero, + * and the interrupt is always level-triggered. Timers and IPIs + * have no sense or polarity bits, and are edge-triggered. + */ + switch (opp->src[n_IRQ].type) { + case IRQ_TYPE_NORMAL: + opp->src[n_IRQ].level = !!(opp->src[n_IRQ].ivpr & IVPR_SENSE_MASK); + break; + + case IRQ_TYPE_FSLINT: + opp->src[n_IRQ].ivpr &= ~IVPR_SENSE_MASK; + break; + + case IRQ_TYPE_FSLSPECIAL: + opp->src[n_IRQ].ivpr &= ~(IVPR_POLARITY_MASK | IVPR_SENSE_MASK); + break; + } + openpic_update_irq(opp, n_IRQ); - DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n", n_IRQ, val, - opp->src[n_IRQ].ipvp); + DPRINTF("Set IVPR %d to 0x%08x -> 0x%08x\n", n_IRQ, val, + opp->src[n_IRQ].ivpr); +} + +static void openpic_gcr_write(OpenPICState *opp, uint64_t val) +{ + bool mpic_proxy = false; + + if (val & GCR_RESET) { + openpic_reset(&opp->busdev.qdev); + return; + } + + opp->gcr &= ~opp->mpic_mode_mask; + opp->gcr |= val & opp->mpic_mode_mask; + + /* Set external proxy mode */ + if ((val & opp->mpic_mode_mask) == GCR_MODE_PROXY) { + mpic_proxy = true; + } + + ppce500_set_mpic_proxy(mpic_proxy); } static void openpic_gbl_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) { OpenPICState *opp = opaque; - IRQ_dst_t *dst; + IRQDest *dst; int idx; - DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val); - if (addr & 0xF) + DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", + __func__, addr, val); + if (addr & 0xF) { return; + } switch (addr) { case 0x00: /* Block Revision Register1 (BRR1) is Readonly */ break; @@ -518,41 +743,39 @@ static void openpic_gbl_write(void *opaque, hwaddr addr, uint64_t val, case 0xB0: openpic_cpu_write_internal(opp, addr, val, get_current_cpu()); break; - case 0x1000: /* FREP */ + case 0x1000: /* FRR */ break; - case 0x1020: /* GLBC */ - if (val & 0x80000000) { - openpic_reset(&opp->busdev.qdev); - } + case 0x1020: /* GCR */ + openpic_gcr_write(opp, val); break; - case 0x1080: /* VENI */ + case 0x1080: /* VIR */ break; - case 0x1090: /* PINT */ + case 0x1090: /* PIR */ for (idx = 0; idx < opp->nb_cpus; idx++) { - if ((val & (1 << idx)) && !(opp->pint & (1 << idx))) { + if ((val & (1 << idx)) && !(opp->pir & (1 << idx))) { DPRINTF("Raise OpenPIC RESET output for CPU %d\n", idx); dst = &opp->dst[idx]; qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_RESET]); - } else if (!(val & (1 << idx)) && (opp->pint & (1 << idx))) { + } else if (!(val & (1 << idx)) && (opp->pir & (1 << idx))) { DPRINTF("Lower OpenPIC RESET output for CPU %d\n", idx); dst = &opp->dst[idx]; qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_RESET]); } } - opp->pint = val; + opp->pir = val; break; - case 0x10A0: /* IPI_IPVP */ + case 0x10A0: /* IPI_IVPR */ case 0x10B0: case 0x10C0: case 0x10D0: { int idx; idx = (addr - 0x10A0) >> 4; - write_IRQreg_ipvp(opp, opp->irq_ipi0 + idx, val); + write_IRQreg_ivpr(opp, opp->irq_ipi0 + idx, val); } break; case 0x10E0: /* SPVE */ - opp->spve = val & opp->spve_mask; + opp->spve = val & opp->vector_mask; break; default: break; @@ -564,24 +787,27 @@ static uint64_t openpic_gbl_read(void *opaque, hwaddr addr, unsigned len) OpenPICState *opp = opaque; uint32_t retval; - DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr); + DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); retval = 0xFFFFFFFF; - if (addr & 0xF) + if (addr & 0xF) { return retval; + } switch (addr) { - case 0x1000: /* FREP */ - retval = opp->frep; + case 0x1000: /* FRR */ + retval = opp->frr; break; - case 0x1020: /* GLBC */ - retval = opp->glbc; + case 0x1020: /* GCR */ + retval = opp->gcr; break; - case 0x1080: /* VENI */ - retval = opp->veni; + case 0x1080: /* VIR */ + retval = opp->vir; break; - case 0x1090: /* PINT */ + case 0x1090: /* PIR */ retval = 0x00000000; break; case 0x00: /* Block Revision Register1 (BRR1) */ + retval = opp->brr1; + break; case 0x40: case 0x50: case 0x60: @@ -592,14 +818,14 @@ static uint64_t openpic_gbl_read(void *opaque, hwaddr addr, unsigned len) case 0xB0: retval = openpic_cpu_read_internal(opp, addr, get_current_cpu()); break; - case 0x10A0: /* IPI_IPVP */ + case 0x10A0: /* IPI_IVPR */ case 0x10B0: case 0x10C0: case 0x10D0: { int idx; idx = (addr - 0x10A0) >> 4; - retval = read_IRQreg_ipvp(opp, opp->irq_ipi0 + idx); + retval = read_IRQreg_ivpr(opp, opp->irq_ipi0 + idx); } break; case 0x10E0: /* SPVE */ @@ -608,7 +834,7 @@ static uint64_t openpic_gbl_read(void *opaque, hwaddr addr, unsigned len) default: break; } - DPRINTF("%s: => %08x\n", __func__, retval); + DPRINTF("%s: => 0x%08x\n", __func__, retval); return retval; } @@ -619,32 +845,39 @@ static void openpic_tmr_write(void *opaque, hwaddr addr, uint64_t val, OpenPICState *opp = opaque; int idx; - DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); - if (addr & 0xF) + addr += 0x10f0; + + DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", + __func__, addr, val); + if (addr & 0xF) { return; + } + + if (addr == 0x10f0) { + /* TFRR */ + opp->tfrr = val; + return; + } + idx = (addr >> 6) & 0x3; addr = addr & 0x30; - if (addr == 0x0) { - /* TIFR (TFRR) */ - opp->tifr = val; - return; - } switch (addr & 0x30) { - case 0x00: /* TICC (GTCCR) */ + case 0x00: /* TCCR */ break; - case 0x10: /* TIBC (GTBCR) */ - if ((opp->timers[idx].ticc & 0x80000000) != 0 && - (val & 0x80000000) == 0 && - (opp->timers[idx].tibc & 0x80000000) != 0) - opp->timers[idx].ticc &= ~0x80000000; - opp->timers[idx].tibc = val; + case 0x10: /* TBCR */ + if ((opp->timers[idx].tccr & TCCR_TOG) != 0 && + (val & TBCR_CI) == 0 && + (opp->timers[idx].tbcr & TBCR_CI) != 0) { + opp->timers[idx].tccr &= ~TCCR_TOG; + } + opp->timers[idx].tbcr = val; break; - case 0x20: /* TIVP (GTIVPR) */ - write_IRQreg_ipvp(opp, opp->irq_tim0 + idx, val); + case 0x20: /* TVPR */ + write_IRQreg_ivpr(opp, opp->irq_tim0 + idx, val); break; - case 0x30: /* TIDE (GTIDR) */ - write_IRQreg_ide(opp, opp->irq_tim0 + idx, val); + case 0x30: /* TDR */ + write_IRQreg_idr(opp, opp->irq_tim0 + idx, val); break; } } @@ -655,33 +888,33 @@ static uint64_t openpic_tmr_read(void *opaque, hwaddr addr, unsigned len) uint32_t retval = -1; int idx; - DPRINTF("%s: addr %08x\n", __func__, addr); + DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); if (addr & 0xF) { goto out; } idx = (addr >> 6) & 0x3; if (addr == 0x0) { - /* TIFR (TFRR) */ - retval = opp->tifr; + /* TFRR */ + retval = opp->tfrr; goto out; } switch (addr & 0x30) { - case 0x00: /* TICC (GTCCR) */ - retval = opp->timers[idx].ticc; + case 0x00: /* TCCR */ + retval = opp->timers[idx].tccr; break; - case 0x10: /* TIBC (GTBCR) */ - retval = opp->timers[idx].tibc; + case 0x10: /* TBCR */ + retval = opp->timers[idx].tbcr; break; - case 0x20: /* TIPV (TIPV) */ - retval = read_IRQreg_ipvp(opp, opp->irq_tim0 + idx); + case 0x20: /* TIPV */ + retval = read_IRQreg_ivpr(opp, opp->irq_tim0 + idx); break; case 0x30: /* TIDE (TIDR) */ - retval = read_IRQreg_ide(opp, opp->irq_tim0 + idx); + retval = read_IRQreg_idr(opp, opp->irq_tim0 + idx); break; } out: - DPRINTF("%s: => %08x\n", __func__, retval); + DPRINTF("%s: => 0x%08x\n", __func__, retval); return retval; } @@ -692,17 +925,22 @@ static void openpic_src_write(void *opaque, hwaddr addr, uint64_t val, OpenPICState *opp = opaque; int idx; - DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); - if (addr & 0xF) - return; - addr = addr & 0xFFF0; + DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n", + __func__, addr, val); + + addr = addr & 0xffff; idx = addr >> 5; - if (addr & 0x10) { - /* EXDE / IFEDE / IEEDE */ - write_IRQreg_ide(opp, idx, val); - } else { - /* EXVP / IFEVP / IEEVP */ - write_IRQreg_ipvp(opp, idx, val); + + switch (addr & 0x1f) { + case 0x00: + write_IRQreg_ivpr(opp, idx, val); + break; + case 0x10: + write_IRQreg_idr(opp, idx, val); + break; + case 0x18: + write_IRQreg_ilr(opp, idx, val); + break; } } @@ -712,21 +950,25 @@ static uint64_t openpic_src_read(void *opaque, uint64_t addr, unsigned len) uint32_t retval; int idx; - DPRINTF("%s: addr %08x\n", __func__, addr); + DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); retval = 0xFFFFFFFF; - if (addr & 0xF) - return retval; - addr = addr & 0xFFF0; - idx = addr >> 5; - if (addr & 0x10) { - /* EXDE / IFEDE / IEEDE */ - retval = read_IRQreg_ide(opp, idx); - } else { - /* EXVP / IFEVP / IEEVP */ - retval = read_IRQreg_ipvp(opp, idx); - } - DPRINTF("%s: => %08x\n", __func__, retval); + addr = addr & 0xffff; + idx = addr >> 5; + + switch (addr & 0x1f) { + case 0x00: + retval = read_IRQreg_ivpr(opp, idx); + break; + case 0x10: + retval = read_IRQreg_idr(opp, idx); + break; + case 0x18: + retval = read_IRQreg_ilr(opp, idx); + break; + } + + DPRINTF("%s: => 0x%08x\n", __func__, retval); return retval; } @@ -737,7 +979,8 @@ static void openpic_msi_write(void *opaque, hwaddr addr, uint64_t val, int idx = opp->irq_msi; int srs, ibs; - DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val); + DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64 "\n", + __func__, addr, val); if (addr & 0xF) { return; } @@ -762,7 +1005,7 @@ static uint64_t openpic_msi_read(void *opaque, hwaddr addr, unsigned size) uint64_t r = 0; int i, srs; - DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr); + DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); if (addr & 0xF) { return -1; } @@ -781,6 +1024,7 @@ static uint64_t openpic_msi_read(void *opaque, hwaddr addr, unsigned size) r = opp->msi[srs].msir; /* Clear on read */ opp->msi[srs].msir = 0; + openpic_set_irq(opp, opp->irq_msi + srs, 0); break; case 0x120: /* MSISR */ for (i = 0; i < MAX_MSI; i++) { @@ -792,18 +1036,44 @@ static uint64_t openpic_msi_read(void *opaque, hwaddr addr, unsigned size) return r; } +static uint64_t openpic_summary_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t r = 0; + + DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr); + + /* TODO: EISR/EIMR */ + + return r; +} + +static void openpic_summary_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64 "\n", + __func__, addr, val); + + /* TODO: EISR/EIMR */ +} + static void openpic_cpu_write_internal(void *opaque, hwaddr addr, uint32_t val, int idx) { OpenPICState *opp = opaque; - IRQ_src_t *src; - IRQ_dst_t *dst; + IRQSource *src; + IRQDest *dst; int s_IRQ, n_IRQ; - DPRINTF("%s: cpu %d addr " TARGET_FMT_plx " <= %08x\n", __func__, idx, + DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx " <= 0x%08x\n", __func__, idx, addr, val); - if (addr & 0xF) + + if (idx < 0) { return; + } + + if (addr & 0xF) { + return; + } dst = &opp->dst[idx]; addr &= 0xFF0; switch (addr) { @@ -813,25 +1083,44 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr, case 0x70: idx = (addr - 0x40) >> 4; /* we use IDE as mask which CPUs to deliver the IPI to still. */ - write_IRQreg_ide(opp, opp->irq_ipi0 + idx, - opp->src[opp->irq_ipi0 + idx].ide | val); + opp->src[opp->irq_ipi0 + idx].destmask |= val; openpic_set_irq(opp, opp->irq_ipi0 + idx, 1); openpic_set_irq(opp, opp->irq_ipi0 + idx, 0); break; - case 0x80: /* PCTP */ - dst->pctp = val & 0x0000000F; + case 0x80: /* CTPR */ + dst->ctpr = val & 0x0000000F; + + DPRINTF("%s: set CPU %d ctpr to %d, raised %d servicing %d\n", + __func__, idx, dst->ctpr, dst->raised.priority, + dst->servicing.priority); + + if (dst->raised.priority <= dst->ctpr) { + DPRINTF("%s: Lower OpenPIC INT output cpu %d due to ctpr\n", + __func__, idx); + qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]); + } else if (dst->raised.priority > dst->servicing.priority) { + DPRINTF("%s: Raise OpenPIC INT output cpu %d irq %d\n", + __func__, idx, dst->raised.next); + qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_INT]); + } + break; case 0x90: /* WHOAMI */ /* Read-only register */ break; - case 0xA0: /* PIAC */ + case 0xA0: /* IACK */ /* Read-only register */ break; - case 0xB0: /* PEOI */ - DPRINTF("PEOI\n"); + case 0xB0: /* EOI */ + DPRINTF("EOI\n"); s_IRQ = IRQ_get_next(opp, &dst->servicing); + + if (s_IRQ < 0) { + DPRINTF("%s: EOI with no interrupt in service\n", __func__); + break; + } + IRQ_resetbit(&dst->servicing, s_IRQ); - dst->servicing.next = -1; /* Set up next servicing IRQ */ s_IRQ = IRQ_get_next(opp, &dst->servicing); /* Check queued interrupts. */ @@ -839,10 +1128,10 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr, src = &opp->src[n_IRQ]; if (n_IRQ != -1 && (s_IRQ == -1 || - IPVP_PRIORITY(src->ipvp) > dst->servicing.priority)) { + IVPR_PRIORITY(src->ivpr) > dst->servicing.priority)) { DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", idx, n_IRQ); - openpic_irq_raise(opp, idx, src); + qemu_irq_raise(opp->dst[idx].irqs[OPENPIC_OUTPUT_INT]); } break; default: @@ -856,81 +1145,93 @@ static void openpic_cpu_write(void *opaque, hwaddr addr, uint64_t val, openpic_cpu_write_internal(opaque, addr, val, (addr & 0x1f000) >> 12); } + +static uint32_t openpic_iack(OpenPICState *opp, IRQDest *dst, int cpu) +{ + IRQSource *src; + int retval, irq; + + DPRINTF("Lower OpenPIC INT output\n"); + qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]); + + irq = IRQ_get_next(opp, &dst->raised); + DPRINTF("IACK: irq=%d\n", irq); + + if (irq == -1) { + /* No more interrupt pending */ + return opp->spve; + } + + src = &opp->src[irq]; + if (!(src->ivpr & IVPR_ACTIVITY_MASK) || + !(IVPR_PRIORITY(src->ivpr) > dst->ctpr)) { + fprintf(stderr, "%s: bad raised IRQ %d ctpr %d ivpr 0x%08x\n", + __func__, irq, dst->ctpr, src->ivpr); + openpic_update_irq(opp, irq); + retval = opp->spve; + } else { + /* IRQ enter servicing state */ + IRQ_setbit(&dst->servicing, irq); + retval = IVPR_VECTOR(opp, src->ivpr); + } + + if (!src->level) { + /* edge-sensitive IRQ */ + src->ivpr &= ~IVPR_ACTIVITY_MASK; + src->pending = 0; + IRQ_resetbit(&dst->raised, irq); + } + + if ((irq >= opp->irq_ipi0) && (irq < (opp->irq_ipi0 + MAX_IPI))) { + src->destmask &= ~(1 << cpu); + if (src->destmask && !src->level) { + /* trigger on CPUs that didn't know about it yet */ + openpic_set_irq(opp, irq, 1); + openpic_set_irq(opp, irq, 0); + /* if all CPUs knew about it, set active bit again */ + src->ivpr |= IVPR_ACTIVITY_MASK; + } + } + + return retval; +} + static uint32_t openpic_cpu_read_internal(void *opaque, hwaddr addr, int idx) { OpenPICState *opp = opaque; - IRQ_src_t *src; - IRQ_dst_t *dst; + IRQDest *dst; uint32_t retval; - int n_IRQ; - DPRINTF("%s: cpu %d addr " TARGET_FMT_plx "\n", __func__, idx, addr); + DPRINTF("%s: cpu %d addr %#" HWADDR_PRIx "\n", __func__, idx, addr); retval = 0xFFFFFFFF; - if (addr & 0xF) + + if (idx < 0) { return retval; + } + + if (addr & 0xF) { + return retval; + } dst = &opp->dst[idx]; addr &= 0xFF0; switch (addr) { - case 0x00: /* Block Revision Register1 (BRR1) */ - retval = opp->brr1; - break; - case 0x80: /* PCTP */ - retval = dst->pctp; + case 0x80: /* CTPR */ + retval = dst->ctpr; break; case 0x90: /* WHOAMI */ retval = idx; break; - case 0xA0: /* PIAC */ - DPRINTF("Lower OpenPIC INT output\n"); - qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]); - n_IRQ = IRQ_get_next(opp, &dst->raised); - DPRINTF("PIAC: irq=%d\n", n_IRQ); - if (n_IRQ == -1) { - /* No more interrupt pending */ - retval = IPVP_VECTOR(opp->spve); - } else { - src = &opp->src[n_IRQ]; - if (!(src->ipvp & IPVP_ACTIVITY_MASK) || - !(IPVP_PRIORITY(src->ipvp) > dst->pctp)) { - /* - Spurious level-sensitive IRQ - * - Priorities has been changed - * and the pending IRQ isn't allowed anymore - */ - src->ipvp &= ~IPVP_ACTIVITY_MASK; - retval = IPVP_VECTOR(opp->spve); - } else { - /* IRQ enter servicing state */ - IRQ_setbit(&dst->servicing, n_IRQ); - retval = IPVP_VECTOR(src->ipvp); - } - IRQ_resetbit(&dst->raised, n_IRQ); - dst->raised.next = -1; - if (!(src->ipvp & IPVP_SENSE_MASK)) { - /* edge-sensitive IRQ */ - src->ipvp &= ~IPVP_ACTIVITY_MASK; - src->pending = 0; - } - - if ((n_IRQ >= opp->irq_ipi0) && (n_IRQ < (opp->irq_ipi0 + MAX_IPI))) { - src->ide &= ~(1 << idx); - if (src->ide && !(src->ipvp & IPVP_SENSE_MASK)) { - /* trigger on CPUs that didn't know about it yet */ - openpic_set_irq(opp, n_IRQ, 1); - openpic_set_irq(opp, n_IRQ, 0); - /* if all CPUs knew about it, set active bit again */ - src->ipvp |= IPVP_ACTIVITY_MASK; - } - } - } + case 0xA0: /* IACK */ + retval = openpic_iack(opp, dst, idx); break; - case 0xB0: /* PEOI */ + case 0xB0: /* EOI */ retval = 0; break; default: break; } - DPRINTF("%s: => %08x\n", __func__, retval); + DPRINTF("%s: => 0x%08x\n", __func__, retval); return retval; } @@ -1020,16 +1321,6 @@ static const MemoryRegionOps openpic_src_ops_be = { }, }; -static const MemoryRegionOps openpic_msi_ops_le = { - .read = openpic_msi_read, - .write = openpic_msi_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - static const MemoryRegionOps openpic_msi_ops_be = { .read = openpic_msi_read, .write = openpic_msi_write, @@ -1040,12 +1331,30 @@ static const MemoryRegionOps openpic_msi_ops_be = { }, }; -static void openpic_save_IRQ_queue(QEMUFile* f, IRQ_queue_t *q) +static const MemoryRegionOps openpic_summary_ops_be = { + .read = openpic_summary_read, + .write = openpic_summary_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void openpic_save_IRQ_queue(QEMUFile* f, IRQQueue *q) { unsigned int i; - for (i = 0; i < BF_WIDTH(MAX_IRQ); i++) - qemu_put_be32s(f, &q->queue[i]); + for (i = 0; i < ARRAY_SIZE(q->queue); i++) { + /* Always put the lower half of a 64-bit long first, in case we + * restore on a 32-bit host. The least significant bits correspond + * to lower IRQ numbers in the bitmap. + */ + qemu_put_be32(f, (uint32_t)q->queue[i]); +#if LONG_MAX > 0x7FFFFFFF + qemu_put_be32(f, (uint32_t)(q->queue[i] >> 32)); +#endif + } qemu_put_sbe32s(f, &q->next); qemu_put_sbe32s(f, &q->priority); @@ -1056,40 +1365,51 @@ static void openpic_save(QEMUFile* f, void *opaque) OpenPICState *opp = (OpenPICState *)opaque; unsigned int i; - qemu_put_be32s(f, &opp->glbc); - qemu_put_be32s(f, &opp->veni); - qemu_put_be32s(f, &opp->pint); + qemu_put_be32s(f, &opp->gcr); + qemu_put_be32s(f, &opp->vir); + qemu_put_be32s(f, &opp->pir); qemu_put_be32s(f, &opp->spve); - qemu_put_be32s(f, &opp->tifr); - - for (i = 0; i < opp->max_irq; i++) { - qemu_put_be32s(f, &opp->src[i].ipvp); - qemu_put_be32s(f, &opp->src[i].ide); - qemu_put_sbe32s(f, &opp->src[i].last_cpu); - qemu_put_sbe32s(f, &opp->src[i].pending); - } + qemu_put_be32s(f, &opp->tfrr); qemu_put_be32s(f, &opp->nb_cpus); for (i = 0; i < opp->nb_cpus; i++) { - qemu_put_be32s(f, &opp->dst[i].pctp); - qemu_put_be32s(f, &opp->dst[i].pcsr); + qemu_put_sbe32s(f, &opp->dst[i].ctpr); openpic_save_IRQ_queue(f, &opp->dst[i].raised); openpic_save_IRQ_queue(f, &opp->dst[i].servicing); + qemu_put_buffer(f, (uint8_t *)&opp->dst[i].outputs_active, + sizeof(opp->dst[i].outputs_active)); } for (i = 0; i < MAX_TMR; i++) { - qemu_put_be32s(f, &opp->timers[i].ticc); - qemu_put_be32s(f, &opp->timers[i].tibc); + qemu_put_be32s(f, &opp->timers[i].tccr); + qemu_put_be32s(f, &opp->timers[i].tbcr); + } + + for (i = 0; i < opp->max_irq; i++) { + qemu_put_be32s(f, &opp->src[i].ivpr); + qemu_put_be32s(f, &opp->src[i].idr); + qemu_get_be32s(f, &opp->src[i].destmask); + qemu_put_sbe32s(f, &opp->src[i].last_cpu); + qemu_put_sbe32s(f, &opp->src[i].pending); } } -static void openpic_load_IRQ_queue(QEMUFile* f, IRQ_queue_t *q) +static void openpic_load_IRQ_queue(QEMUFile* f, IRQQueue *q) { unsigned int i; - for (i = 0; i < BF_WIDTH(MAX_IRQ); i++) - qemu_get_be32s(f, &q->queue[i]); + for (i = 0; i < ARRAY_SIZE(q->queue); i++) { + unsigned long val; + + val = qemu_get_be32(f); +#if LONG_MAX > 0x7FFFFFFF + val <<= 32; + val |= qemu_get_be32(f); +#endif + + q->queue[i] = val; + } qemu_get_sbe32s(f, &q->next); qemu_get_sbe32s(f, &q->priority); @@ -1100,144 +1420,200 @@ static int openpic_load(QEMUFile* f, void *opaque, int version_id) OpenPICState *opp = (OpenPICState *)opaque; unsigned int i; - if (version_id != 1) + if (version_id != 1) { return -EINVAL; - - qemu_get_be32s(f, &opp->glbc); - qemu_get_be32s(f, &opp->veni); - qemu_get_be32s(f, &opp->pint); - qemu_get_be32s(f, &opp->spve); - qemu_get_be32s(f, &opp->tifr); - - for (i = 0; i < opp->max_irq; i++) { - qemu_get_be32s(f, &opp->src[i].ipvp); - qemu_get_be32s(f, &opp->src[i].ide); - qemu_get_sbe32s(f, &opp->src[i].last_cpu); - qemu_get_sbe32s(f, &opp->src[i].pending); } + qemu_get_be32s(f, &opp->gcr); + qemu_get_be32s(f, &opp->vir); + qemu_get_be32s(f, &opp->pir); + qemu_get_be32s(f, &opp->spve); + qemu_get_be32s(f, &opp->tfrr); + qemu_get_be32s(f, &opp->nb_cpus); for (i = 0; i < opp->nb_cpus; i++) { - qemu_get_be32s(f, &opp->dst[i].pctp); - qemu_get_be32s(f, &opp->dst[i].pcsr); + qemu_get_sbe32s(f, &opp->dst[i].ctpr); openpic_load_IRQ_queue(f, &opp->dst[i].raised); openpic_load_IRQ_queue(f, &opp->dst[i].servicing); + qemu_get_buffer(f, (uint8_t *)&opp->dst[i].outputs_active, + sizeof(opp->dst[i].outputs_active)); } for (i = 0; i < MAX_TMR; i++) { - qemu_get_be32s(f, &opp->timers[i].ticc); - qemu_get_be32s(f, &opp->timers[i].tibc); + qemu_get_be32s(f, &opp->timers[i].tccr); + qemu_get_be32s(f, &opp->timers[i].tbcr); + } + + for (i = 0; i < opp->max_irq; i++) { + uint32_t val; + + val = qemu_get_be32(f); + write_IRQreg_idr(opp, i, val); + val = qemu_get_be32(f); + write_IRQreg_ivpr(opp, i, val); + + qemu_get_be32s(f, &opp->src[i].ivpr); + qemu_get_be32s(f, &opp->src[i].idr); + qemu_get_be32s(f, &opp->src[i].destmask); + qemu_get_sbe32s(f, &opp->src[i].last_cpu); + qemu_get_sbe32s(f, &opp->src[i].pending); } return 0; } -static void openpic_irq_raise(OpenPICState *opp, int n_CPU, IRQ_src_t *src) -{ - int n_ci = IDR_CI0_SHIFT - n_CPU; +typedef struct MemReg { + const char *name; + MemoryRegionOps const *ops; + hwaddr start_addr; + ram_addr_t size; +} MemReg; - if ((opp->flags & OPENPIC_FLAG_IDE_CRIT) && (src->ide & (1 << n_ci))) { - qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_CINT]); - } else { - qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]); +static void fsl_common_init(OpenPICState *opp) +{ + int i; + int virq = MAX_SRC; + + opp->vid = VID_REVISION_1_2; + opp->vir = VIR_GENERIC; + opp->vector_mask = 0xFFFF; + opp->tfrr_reset = 0; + opp->ivpr_reset = IVPR_MASK_MASK; + opp->idr_reset = 1 << 0; + opp->max_irq = MAX_IRQ; + + opp->irq_ipi0 = virq; + virq += MAX_IPI; + opp->irq_tim0 = virq; + virq += MAX_TMR; + + assert(virq <= MAX_IRQ); + + opp->irq_msi = 224; + + msi_supported = true; + for (i = 0; i < opp->fsl->max_ext; i++) { + opp->src[i].level = false; + } + + /* Internal interrupts, including message and MSI */ + for (i = 16; i < MAX_SRC; i++) { + opp->src[i].type = IRQ_TYPE_FSLINT; + opp->src[i].level = true; + } + + /* timers and IPIs */ + for (i = MAX_SRC; i < virq; i++) { + opp->src[i].type = IRQ_TYPE_FSLSPECIAL; + opp->src[i].level = false; } } -struct memreg { - const char *name; - MemoryRegionOps const *ops; - bool map; - hwaddr start_addr; - ram_addr_t size; -}; +static void map_list(OpenPICState *opp, const MemReg *list, int *count) +{ + while (list->name) { + assert(*count < ARRAY_SIZE(opp->sub_io_mem)); + + memory_region_init_io(&opp->sub_io_mem[*count], list->ops, opp, + list->name, list->size); + + memory_region_add_subregion(&opp->mem, list->start_addr, + &opp->sub_io_mem[*count]); + + (*count)++; + list++; + } +} static int openpic_init(SysBusDevice *dev) { OpenPICState *opp = FROM_SYSBUS(typeof (*opp), dev); int i, j; - struct memreg list_le[] = { - {"glb", &openpic_glb_ops_le, true, + int list_count = 0; + static const MemReg list_le[] = { + {"glb", &openpic_glb_ops_le, OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE}, - {"tmr", &openpic_tmr_ops_le, true, + {"tmr", &openpic_tmr_ops_le, OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE}, - {"msi", &openpic_msi_ops_le, true, - OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE}, - {"src", &openpic_src_ops_le, true, + {"src", &openpic_src_ops_le, OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE}, - {"cpu", &openpic_cpu_ops_le, true, + {"cpu", &openpic_cpu_ops_le, OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE}, + {NULL} }; - struct memreg list_be[] = { - {"glb", &openpic_glb_ops_be, true, + static const MemReg list_be[] = { + {"glb", &openpic_glb_ops_be, OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE}, - {"tmr", &openpic_tmr_ops_be, true, + {"tmr", &openpic_tmr_ops_be, OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE}, - {"msi", &openpic_msi_ops_be, true, - OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE}, - {"src", &openpic_src_ops_be, true, + {"src", &openpic_src_ops_be, OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE}, - {"cpu", &openpic_cpu_ops_be, true, + {"cpu", &openpic_cpu_ops_be, OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE}, + {NULL} }; - struct memreg *list; + static const MemReg list_fsl[] = { + {"msi", &openpic_msi_ops_be, + OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE}, + {"summary", &openpic_summary_ops_be, + OPENPIC_SUMMARY_REG_START, OPENPIC_SUMMARY_REG_SIZE}, + {NULL} + }; + + memory_region_init(&opp->mem, "openpic", 0x40000); switch (opp->model) { case OPENPIC_MODEL_FSL_MPIC_20: default: - opp->flags |= OPENPIC_FLAG_IDE_CRIT; + opp->fsl = &fsl_mpic_20; + opp->brr1 = 0x00400200; + opp->flags |= OPENPIC_FLAG_IDR_CRIT; opp->nb_irqs = 80; - opp->vid = VID_REVISION_1_2; - opp->veni = VENI_GENERIC; - opp->spve_mask = 0xFFFF; - opp->tifr_reset = 0x00000000; - opp->ipvp_reset = 0x80000000; - opp->ide_reset = 0x00000001; - opp->max_irq = FSL_MPIC_20_MAX_IRQ; - opp->irq_ipi0 = FSL_MPIC_20_IPI_IRQ; - opp->irq_tim0 = FSL_MPIC_20_TMR_IRQ; - opp->irq_msi = FSL_MPIC_20_MSI_IRQ; - opp->brr1 = FSL_BRR1_IPID | FSL_BRR1_IPMJ | FSL_BRR1_IPMN; - msi_supported = true; - list = list_be; + opp->mpic_mode_mask = GCR_MODE_MIXED; + + fsl_common_init(opp); + map_list(opp, list_be, &list_count); + map_list(opp, list_fsl, &list_count); + break; + + case OPENPIC_MODEL_FSL_MPIC_42: + opp->fsl = &fsl_mpic_42; + opp->brr1 = 0x00400402; + opp->flags |= OPENPIC_FLAG_ILR; + opp->nb_irqs = 196; + opp->mpic_mode_mask = GCR_MODE_PROXY; + + fsl_common_init(opp); + map_list(opp, list_be, &list_count); + map_list(opp, list_fsl, &list_count); + + break; + case OPENPIC_MODEL_RAVEN: opp->nb_irqs = RAVEN_MAX_EXT; opp->vid = VID_REVISION_1_3; - opp->veni = VENI_GENERIC; - opp->spve_mask = 0xFF; - opp->tifr_reset = 0x003F7A00; - opp->ipvp_reset = 0xA0000000; - opp->ide_reset = 0x00000000; + opp->vir = VIR_GENERIC; + opp->vector_mask = 0xFF; + opp->tfrr_reset = 4160000; + opp->ivpr_reset = IVPR_MASK_MASK | IVPR_MODE_MASK; + opp->idr_reset = 0; opp->max_irq = RAVEN_MAX_IRQ; opp->irq_ipi0 = RAVEN_IPI_IRQ; opp->irq_tim0 = RAVEN_TMR_IRQ; opp->brr1 = -1; - list = list_le; - /* Don't map MSI region */ - list[2].map = false; + opp->mpic_mode_mask = GCR_MODE_MIXED; /* Only UP supported today */ if (opp->nb_cpus != 1) { return -EINVAL; } + + map_list(opp, list_le, &list_count); break; } - memory_region_init(&opp->mem, "openpic", 0x40000); - - for (i = 0; i < ARRAY_SIZE(list_le); i++) { - if (!list[i].map) { - continue; - } - - memory_region_init_io(&opp->sub_io_mem[i], list[i].ops, opp, - list[i].name, list[i].size); - - memory_region_add_subregion(&opp->mem, list[i].start_addr, - &opp->sub_io_mem[i]); - } - for (i = 0; i < opp->nb_cpus; i++) { opp->dst[i].irqs = g_new(qemu_irq, OPENPIC_OUTPUT_NB); for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { @@ -1270,7 +1646,7 @@ static void openpic_class_init(ObjectClass *klass, void *data) dc->reset = openpic_reset; } -static TypeInfo openpic_info = { +static const TypeInfo openpic_info = { .name = "openpic", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OpenPICState), diff --git a/hw/openpic.h b/hw/openpic.h index e226d7b563..9dcaf0e7cd 100644 --- a/hw/openpic.h +++ b/hw/openpic.h @@ -13,5 +13,6 @@ enum { #define OPENPIC_MODEL_RAVEN 0 #define OPENPIC_MODEL_FSL_MPIC_20 1 +#define OPENPIC_MODEL_FSL_MPIC_42 2 #endif /* __OPENPIC_H__ */ diff --git a/hw/openrisc/Makefile.objs b/hw/openrisc/Makefile.objs index 38ff8f5d6d..61246b149b 100644 --- a/hw/openrisc/Makefile.objs +++ b/hw/openrisc/Makefile.objs @@ -1,3 +1,2 @@ -obj-y = openrisc_pic.o openrisc_sim.o openrisc_timer.o - -obj-y := $(addprefix ../,$(obj-y)) +obj-y = pic_cpu.o cputimer.o +obj-y += openrisc_sim.o diff --git a/hw/openrisc_timer.c b/hw/openrisc/cputimer.c similarity index 96% rename from hw/openrisc_timer.c rename to hw/openrisc/cputimer.c index d965be77de..4144b34be7 100644 --- a/hw/openrisc_timer.c +++ b/hw/openrisc/cputimer.c @@ -19,7 +19,7 @@ */ #include "cpu.h" -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" #define TIMER_FREQ (20 * 1000 * 1000) /* 20MHz */ @@ -73,8 +73,10 @@ static void openrisc_timer_cb(void *opaque) if ((cpu->env.ttmr & TTMR_IE) && qemu_timer_expired(cpu->env.timer, qemu_get_clock_ns(vm_clock))) { + CPUState *cs = CPU(cpu); + cpu->env.ttmr |= TTMR_IP; - cpu->env.interrupt_request |= CPU_INTERRUPT_TIMER; + cs->interrupt_request |= CPU_INTERRUPT_TIMER; } switch (cpu->env.ttmr & TTMR_M) { diff --git a/hw/openrisc_sim.c b/hw/openrisc/openrisc_sim.c similarity index 95% rename from hw/openrisc_sim.c rename to hw/openrisc/openrisc_sim.c index d2b2379ae2..db2aac8cf8 100644 --- a/hw/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -18,15 +18,15 @@ * License along with this library; if not, see . */ -#include "hw.h" -#include "boards.h" +#include "hw/hw.h" +#include "hw/boards.h" #include "elf.h" -#include "serial.h" +#include "hw/serial.h" #include "net/net.h" -#include "loader.h" +#include "hw/loader.h" #include "exec/address-spaces.h" #include "sysemu/sysemu.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "sysemu/qtest.h" #define KERNEL_LOAD_ADDR 0x100 @@ -50,7 +50,7 @@ static void openrisc_sim_net_init(MemoryRegion *address_space, qdev_set_nic_properties(dev, nd); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_connect_irq(s, 0, irq); memory_region_add_subregion(address_space, base, sysbus_mmio_get_region(s, 0)); @@ -107,7 +107,7 @@ static void openrisc_sim_init(QEMUMachineInitArgs *args) for (n = 0; n < smp_cpus; n++) { cpu = cpu_openrisc_init(cpu_model); if (cpu == NULL) { - qemu_log("Unable to find CPU defineition!\n"); + qemu_log("Unable to find CPU definition!\n"); exit(1); } qemu_register_reset(main_cpu_reset, cpu); @@ -139,6 +139,7 @@ static QEMUMachine openrisc_sim_machine = { .init = openrisc_sim_init, .max_cpus = 1, .is_default = 1, + DEFAULT_MACHINE_OPTIONS, }; static void openrisc_sim_machine_init(void) diff --git a/hw/openrisc_pic.c b/hw/openrisc/pic_cpu.c similarity index 91% rename from hw/openrisc_pic.c rename to hw/openrisc/pic_cpu.c index aaeb9a9171..ca0b7c11bd 100644 --- a/hw/openrisc_pic.c +++ b/hw/openrisc/pic_cpu.c @@ -18,13 +18,14 @@ * License along with this library; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "cpu.h" /* OpenRISC pic handler */ static void openrisc_pic_cpu_handler(void *opaque, int irq, int level) { OpenRISCCPU *cpu = (OpenRISCCPU *)opaque; + CPUState *cs = CPU(cpu); int i; uint32_t irq_bit = 1 << irq; @@ -40,9 +41,9 @@ static void openrisc_pic_cpu_handler(void *opaque, int irq, int level) for (i = 0; i < 32; i++) { if ((cpu->env.picsr && (1 << i)) && (cpu->env.picmr && (1 << i))) { - cpu_interrupt(&cpu->env, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { - cpu_reset_interrupt(&cpu->env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); cpu->env.picsr &= ~(1 << i); } } diff --git a/hw/pam.c b/hw/pam.c index 1d72e88e62..6c0061e06e 100644 --- a/hw/pam.c +++ b/hw/pam.c @@ -27,7 +27,7 @@ * THE SOFTWARE. */ #include "sysemu/sysemu.h" -#include "pam.h" +#include "hw/pam.h" void smram_update(MemoryRegion *smram_region, uint8_t smram, uint8_t smm_enabled) diff --git a/hw/parallel.c b/hw/parallel.c index 64a46c6055..0b9af43d8b 100644 --- a/hw/parallel.c +++ b/hw/parallel.c @@ -22,10 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "hw/hw.h" #include "char/char.h" -#include "isa.h" -#include "pc.h" +#include "hw/isa.h" +#include "hw/pc.h" #include "sysemu/sysemu.h" //#define DEBUG_PARALLEL @@ -599,7 +599,7 @@ static void parallel_isa_class_initfn(ObjectClass *klass, void *data) dc->props = parallel_isa_properties; } -static TypeInfo parallel_isa_info = { +static const TypeInfo parallel_isa_info = { .name = "isa-parallel", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISAParallelState), diff --git a/hw/pc-testdev.c b/hw/pc-testdev.c new file mode 100644 index 0000000000..8236bce0c7 --- /dev/null +++ b/hw/pc-testdev.c @@ -0,0 +1,187 @@ +/* + * QEMU x86 ISA testdev + * + * Copyright (c) 2012 Avi Kivity, Gerd Hoffmann, Marcelo Tosatti + * + * 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. + */ + +/* + * This device is used to test KVM features specific to the x86 port, such + * as emulation, power management, interrupt routing, among others. It's meant + * to be used like: + * + * qemu-system-x86_64 -device pc-testdev -serial stdio \ + * -device isa-debug-exit,iobase=0xf4,iosize=0x4 \ + * -kernel /home/lmr/Code/virt-test.git/kvm/unittests/msr.flat + * + * Where msr.flat is one of the KVM unittests, present on a separate repo, + * git://git.kernel.org/pub/scm/virt/kvm/kvm-unit-tests.git +*/ + +#include "config-host.h" +#if defined(CONFIG_POSIX) +#include +#endif +#include "hw/hw.h" +#include "hw/qdev.h" +#include "hw/isa.h" + +#define IOMEM_LEN 0x10000 + +typedef struct PCTestdev { + ISADevice parent_obj; + + MemoryRegion ioport; + MemoryRegion flush; + MemoryRegion irq; + MemoryRegion iomem; + uint32_t ioport_data; + char iomem_buf[IOMEM_LEN]; +} PCTestdev; + +#define TYPE_TESTDEV "pc-testdev" +#define TESTDEV(obj) \ + OBJECT_CHECK(PCTestdev, (obj), TYPE_TESTDEV) + +static void test_irq_line(void *opaque, hwaddr addr, uint64_t data, + unsigned len) +{ + PCTestdev *dev = opaque; + ISADevice *isa = ISA_DEVICE(dev); + + qemu_set_irq(isa_get_irq(isa, addr), !!data); +} + +static const MemoryRegionOps test_irq_ops = { + .write = test_irq_line, + .valid.min_access_size = 1, + .valid.max_access_size = 1, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void test_ioport_write(void *opaque, hwaddr addr, uint64_t data, + unsigned len) +{ + PCTestdev *dev = opaque; + dev->ioport_data = data; +} + +static uint64_t test_ioport_read(void *opaque, hwaddr addr, unsigned len) +{ + PCTestdev *dev = opaque; + return dev->ioport_data; +} + +static const MemoryRegionOps test_ioport_ops = { + .read = test_ioport_read, + .write = test_ioport_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void test_flush_page(void *opaque, hwaddr addr, uint64_t data, + unsigned len) +{ + hwaddr page = 4096; + void *a = cpu_physical_memory_map(data & ~0xffful, &page, 0); + + /* We might not be able to get the full page, only mprotect what we actually + have mapped */ +#if defined(CONFIG_POSIX) + mprotect(a, page, PROT_NONE); + mprotect(a, page, PROT_READ|PROT_WRITE); +#endif + cpu_physical_memory_unmap(a, page, 0, 0); +} + +static const MemoryRegionOps test_flush_ops = { + .write = test_flush_page, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t test_iomem_read(void *opaque, hwaddr addr, unsigned len) +{ + PCTestdev *dev = opaque; + uint64_t ret = 0; + memcpy(&ret, &dev->iomem_buf[addr], len); + ret = le64_to_cpu(ret); + + return ret; +} + +static void test_iomem_write(void *opaque, hwaddr addr, uint64_t val, + unsigned len) +{ + PCTestdev *dev = opaque; + val = cpu_to_le64(val); + memcpy(&dev->iomem_buf[addr], &val, len); + dev->iomem_buf[addr] = val; +} + +static const MemoryRegionOps test_iomem_ops = { + .read = test_iomem_read, + .write = test_iomem_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static int init_test_device(ISADevice *isa) +{ + PCTestdev *dev = TESTDEV(isa); + MemoryRegion *mem = isa_address_space(isa); + MemoryRegion *io = isa_address_space_io(isa); + + memory_region_init_io(&dev->ioport, &test_ioport_ops, dev, + "pc-testdev-ioport", 4); + memory_region_init_io(&dev->flush, &test_flush_ops, dev, + "pc-testdev-flush-page", 4); + memory_region_init_io(&dev->irq, &test_irq_ops, dev, + "pc-testdev-irq-line", 24); + memory_region_init_io(&dev->iomem, &test_iomem_ops, dev, + "pc-testdev-iomem", IOMEM_LEN); + + memory_region_add_subregion(io, 0xe0, &dev->ioport); + memory_region_add_subregion(io, 0xe4, &dev->flush); + memory_region_add_subregion(io, 0x2000, &dev->irq); + memory_region_add_subregion(mem, 0xff000000, &dev->iomem); + + return 0; +} + +static void testdev_class_init(ObjectClass *klass, void *data) +{ + ISADeviceClass *k = ISA_DEVICE_CLASS(klass); + + k->init = init_test_device; +} + +static const TypeInfo testdev_info = { + .name = TYPE_TESTDEV, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(PCTestdev), + .class_init = testdev_class_init, +}; + +static void testdev_register_types(void) +{ + type_register_static(&testdev_info); +} + +type_init(testdev_register_types) diff --git a/hw/pc.h b/hw/pc.h index a73e3e7b5b..dbbd8cde9e 100644 --- a/hw/pc.h +++ b/hw/pc.h @@ -4,11 +4,11 @@ #include "qemu-common.h" #include "exec/memory.h" #include "exec/ioport.h" -#include "isa.h" -#include "fdc.h" +#include "hw/isa.h" +#include "hw/fdc.h" #include "net/net.h" #include "exec/memory.h" -#include "ioapic.h" +#include "hw/ioapic.h" /* PC-style peripherals (also used by other machines). */ @@ -40,8 +40,8 @@ qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq); qemu_irq *kvm_i8259_init(ISABus *bus); int pic_read_irq(DeviceState *d); int pic_get_output(DeviceState *d); -void pic_info(Monitor *mon); -void irq_info(Monitor *mon); +void pic_info(Monitor *mon, const QDict *qdict); +void irq_info(Monitor *mon, const QDict *qdict); /* Global System Interrupts */ @@ -79,6 +79,7 @@ void pc_register_ferr_irq(qemu_irq irq); void pc_acpi_smi_interrupt(void *opaque, int irq, int level); void pc_cpus_init(const char *cpu_model); +void pc_acpi_init(const char *default_dsdt); void *pc_memory_init(MemoryRegion *system_memory, const char *kernel_filename, const char *kernel_cmdline, @@ -186,4 +187,40 @@ void pc_system_firmware_init(MemoryRegion *rom_memory); int e820_add_entry(uint64_t, uint64_t, uint32_t); +#define PC_COMPAT_1_4 \ + {\ + .driver = "scsi-hd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "scsi-cd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "scsi-disk",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "ide-hd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "ide-cd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "ide-drive",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "virtio-blk-pci",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "virtio-serial-pci",\ + .property = "vectors",\ + /* DEV_NVECTORS_UNSPECIFIED as a uint32_t string */\ + .value = stringify(0xFFFFFFFF),\ + } + #endif diff --git a/hw/pc87312.c b/hw/pc87312.c new file mode 100644 index 0000000000..c4e4c6273b --- /dev/null +++ b/hw/pc87312.c @@ -0,0 +1,402 @@ +/* + * QEMU National Semiconductor PC87312 (Super I/O) + * + * Copyright (c) 2010-2012 Herve Poussineau + * Copyright (c) 2011-2012 Andreas Färber + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/pc87312.h" +#include "qemu/error-report.h" +#include "sysemu/blockdev.h" +#include "sysemu/sysemu.h" +#include "char/char.h" +#include "trace.h" + + +#define REG_FER 0 +#define REG_FAR 1 +#define REG_PTR 2 + +#define FER_PARALLEL_EN 0x01 +#define FER_UART1_EN 0x02 +#define FER_UART2_EN 0x04 +#define FER_FDC_EN 0x08 +#define FER_FDC_4 0x10 +#define FER_FDC_ADDR 0x20 +#define FER_IDE_EN 0x40 +#define FER_IDE_ADDR 0x80 + +#define FAR_PARALLEL_ADDR 0x03 +#define FAR_UART1_ADDR 0x0C +#define FAR_UART2_ADDR 0x30 +#define FAR_UART_3_4 0xC0 + +#define PTR_POWER_DOWN 0x01 +#define PTR_CLOCK_DOWN 0x02 +#define PTR_PWDN 0x04 +#define PTR_IRQ_5_7 0x08 +#define PTR_UART1_TEST 0x10 +#define PTR_UART2_TEST 0x20 +#define PTR_LOCK_CONF 0x40 +#define PTR_EPP_MODE 0x80 + + +/* Parallel port */ + +static inline bool is_parallel_enabled(PC87312State *s) +{ + return s->regs[REG_FER] & FER_PARALLEL_EN; +} + +static const uint32_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 }; + +static inline uint32_t get_parallel_iobase(PC87312State *s) +{ + return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR]; +} + +static const uint32_t parallel_irq[] = { 5, 7, 5, 0 }; + +static inline uint32_t get_parallel_irq(PC87312State *s) +{ + int idx; + idx = (s->regs[REG_FAR] & FAR_PARALLEL_ADDR); + if (idx == 0) { + return (s->regs[REG_PTR] & PTR_IRQ_5_7) ? 7 : 5; + } else { + return parallel_irq[idx]; + } +} + +static inline bool is_parallel_epp(PC87312State *s) +{ + return s->regs[REG_PTR] & PTR_EPP_MODE; +} + + +/* UARTs */ + +static const uint32_t uart_base[2][4] = { + { 0x3e8, 0x338, 0x2e8, 0x220 }, + { 0x2e8, 0x238, 0x2e0, 0x228 } +}; + +static inline uint32_t get_uart_iobase(PC87312State *s, int i) +{ + int idx; + idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3; + if (idx == 0) { + return 0x3f8; + } else if (idx == 1) { + return 0x2f8; + } else { + return uart_base[idx & 1][(s->regs[REG_FAR] & FAR_UART_3_4) >> 6]; + } +} + +static inline uint32_t get_uart_irq(PC87312State *s, int i) +{ + int idx; + idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3; + return (idx & 1) ? 3 : 4; +} + +static inline bool is_uart_enabled(PC87312State *s, int i) +{ + return s->regs[REG_FER] & (FER_UART1_EN << i); +} + + +/* Floppy controller */ + +static inline bool is_fdc_enabled(PC87312State *s) +{ + return s->regs[REG_FER] & FER_FDC_EN; +} + +static inline uint32_t get_fdc_iobase(PC87312State *s) +{ + return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0; +} + + +/* IDE controller */ + +static inline bool is_ide_enabled(PC87312State *s) +{ + return s->regs[REG_FER] & FER_IDE_EN; +} + +static inline uint32_t get_ide_iobase(PC87312State *s) +{ + return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0; +} + + +static void reconfigure_devices(PC87312State *s) +{ + error_report("pc87312: unsupported device reconfiguration (%02x %02x %02x)", + s->regs[REG_FER], s->regs[REG_FAR], s->regs[REG_PTR]); +} + +static void pc87312_soft_reset(PC87312State *s) +{ + static const uint8_t fer_init[] = { + 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4b, 0x4b, + 0x4b, 0x4b, 0x4b, 0x4b, 0x0f, 0x0f, 0x0f, 0x0f, + 0x49, 0x49, 0x49, 0x49, 0x07, 0x07, 0x07, 0x07, + 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x08, 0x00, + }; + static const uint8_t far_init[] = { + 0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x00, 0x01, + 0x01, 0x09, 0x08, 0x08, 0x10, 0x11, 0x39, 0x24, + 0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x39, 0x24, + 0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x10, 0x10, + }; + static const uint8_t ptr_init[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + }; + + s->read_id_step = 0; + s->selected_index = REG_FER; + + s->regs[REG_FER] = fer_init[s->config & 0x1f]; + s->regs[REG_FAR] = far_init[s->config & 0x1f]; + s->regs[REG_PTR] = ptr_init[s->config & 0x1f]; +} + +static void pc87312_hard_reset(PC87312State *s) +{ + pc87312_soft_reset(s); +} + +static void pc87312_io_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + PC87312State *s = opaque; + + trace_pc87312_io_write(addr, val); + + if ((addr & 1) == 0) { + /* Index register */ + s->read_id_step = 2; + s->selected_index = val; + } else { + /* Data register */ + if (s->selected_index < 3) { + s->regs[s->selected_index] = val; + reconfigure_devices(s); + } + } +} + +static uint64_t pc87312_io_read(void *opaque, hwaddr addr, unsigned int size) +{ + PC87312State *s = opaque; + uint32_t val; + + if ((addr & 1) == 0) { + /* Index register */ + if (s->read_id_step++ == 0) { + val = 0x88; + } else if (s->read_id_step++ == 1) { + val = 0; + } else { + val = s->selected_index; + } + } else { + /* Data register */ + if (s->selected_index < 3) { + val = s->regs[s->selected_index]; + } else { + /* Invalid selected index */ + val = 0; + } + } + + trace_pc87312_io_read(addr, val); + return val; +} + +static const MemoryRegionOps pc87312_io_ops = { + .read = pc87312_io_read, + .write = pc87312_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static int pc87312_post_load(void *opaque, int version_id) +{ + PC87312State *s = opaque; + + reconfigure_devices(s); + return 0; +} + +static void pc87312_reset(DeviceState *d) +{ + PC87312State *s = PC87312(d); + + pc87312_soft_reset(s); +} + +static int pc87312_init(ISADevice *dev) +{ + PC87312State *s; + DeviceState *d; + ISADevice *isa; + ISABus *bus; + CharDriverState *chr; + DriveInfo *drive; + char name[5]; + int i; + + s = PC87312(dev); + bus = isa_bus_from_device(dev); + pc87312_hard_reset(s); + isa_register_ioport(dev, &s->io, s->iobase); + + if (is_parallel_enabled(s)) { + chr = parallel_hds[0]; + if (chr == NULL) { + chr = qemu_chr_new("par0", "null", NULL); + } + isa = isa_create(bus, "isa-parallel"); + d = DEVICE(isa); + qdev_prop_set_uint32(d, "index", 0); + qdev_prop_set_uint32(d, "iobase", get_parallel_iobase(s)); + qdev_prop_set_uint32(d, "irq", get_parallel_irq(s)); + qdev_prop_set_chr(d, "chardev", chr); + qdev_init_nofail(d); + s->parallel.dev = isa; + trace_pc87312_info_parallel(get_parallel_iobase(s), + get_parallel_irq(s)); + } + + for (i = 0; i < 2; i++) { + if (is_uart_enabled(s, i)) { + chr = serial_hds[i]; + if (chr == NULL) { + snprintf(name, sizeof(name), "ser%d", i); + chr = qemu_chr_new(name, "null", NULL); + } + isa = isa_create(bus, "isa-serial"); + d = DEVICE(isa); + qdev_prop_set_uint32(d, "index", i); + qdev_prop_set_uint32(d, "iobase", get_uart_iobase(s, i)); + qdev_prop_set_uint32(d, "irq", get_uart_irq(s, i)); + qdev_prop_set_chr(d, "chardev", chr); + qdev_init_nofail(d); + s->uart[i].dev = isa; + trace_pc87312_info_serial(i, get_uart_iobase(s, i), + get_uart_irq(s, i)); + } + } + + if (is_fdc_enabled(s)) { + isa = isa_create(bus, "isa-fdc"); + d = DEVICE(isa); + qdev_prop_set_uint32(d, "iobase", get_fdc_iobase(s)); + qdev_prop_set_uint32(d, "irq", 6); + drive = drive_get(IF_FLOPPY, 0, 0); + if (drive != NULL) { + qdev_prop_set_drive_nofail(d, "driveA", drive->bdrv); + } + drive = drive_get(IF_FLOPPY, 0, 1); + if (drive != NULL) { + qdev_prop_set_drive_nofail(d, "driveB", drive->bdrv); + } + qdev_init_nofail(d); + s->fdc.dev = isa; + trace_pc87312_info_floppy(get_fdc_iobase(s)); + } + + if (is_ide_enabled(s)) { + isa = isa_create(bus, "isa-ide"); + d = DEVICE(isa); + qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s)); + qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206); + qdev_prop_set_uint32(d, "irq", 14); + qdev_init_nofail(d); + s->ide.dev = isa; + trace_pc87312_info_ide(get_ide_iobase(s)); + } + + return 0; +} + +static void pc87312_initfn(Object *obj) +{ + PC87312State *s = PC87312(obj); + + memory_region_init_io(&s->io, &pc87312_io_ops, s, "pc87312", 2); +} + +static const VMStateDescription vmstate_pc87312 = { + .name = "pc87312", + .version_id = 1, + .minimum_version_id = 1, + .post_load = pc87312_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(read_id_step, PC87312State), + VMSTATE_UINT8(selected_index, PC87312State), + VMSTATE_UINT8_ARRAY(regs, PC87312State, 3), + VMSTATE_END_OF_LIST() + } +}; + +static Property pc87312_properties[] = { + DEFINE_PROP_HEX32("iobase", PC87312State, iobase, 0x398), + DEFINE_PROP_UINT8("config", PC87312State, config, 1), + DEFINE_PROP_END_OF_LIST() +}; + +static void pc87312_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ISADeviceClass *ic = ISA_DEVICE_CLASS(klass); + + ic->init = pc87312_init; + dc->reset = pc87312_reset; + dc->vmsd = &vmstate_pc87312; + dc->props = pc87312_properties; +} + +static const TypeInfo pc87312_type_info = { + .name = TYPE_PC87312, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(PC87312State), + .instance_init = pc87312_initfn, + .class_init = pc87312_class_init, +}; + +static void pc87312_register_types(void) +{ + type_register_static(&pc87312_type_info); +} + +type_init(pc87312_register_types) diff --git a/hw/pc87312.h b/hw/pc87312.h new file mode 100644 index 0000000000..ad087c73e5 --- /dev/null +++ b/hw/pc87312.h @@ -0,0 +1,68 @@ +/* + * QEMU National Semiconductor PC87312 (Super I/O) + * + * Copyright (c) 2010-2012 Herve Poussineau + * Copyright (c) 2011-2012 Andreas Färber + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_PC87312_H +#define QEMU_PC87312_H + +#include "hw/isa.h" + + +#define TYPE_PC87312 "pc87312" +#define PC87312(obj) OBJECT_CHECK(PC87312State, (obj), TYPE_PC87312) + +typedef struct PC87312State { + ISADevice dev; + + uint32_t iobase; + uint8_t config; /* initial configuration */ + + struct { + ISADevice *dev; + } parallel; + + struct { + ISADevice *dev; + } uart[2]; + + struct { + ISADevice *dev; + BlockDriverState *drive[2]; + uint32_t base; + } fdc; + + struct { + ISADevice *dev; + uint32_t base; + } ide; + + MemoryRegion io; + + uint8_t read_id_step; + uint8_t selected_index; + + uint8_t regs[3]; +} PC87312State; + + +#endif diff --git a/hw/pc_sysfw.c b/hw/pc_sysfw.c index 87e1fa961b..3e01528e78 100644 --- a/hw/pc_sysfw.c +++ b/hw/pc_sysfw.c @@ -24,13 +24,14 @@ */ #include "sysemu/blockdev.h" -#include "sysbus.h" -#include "hw.h" -#include "pc.h" +#include "qemu/error-report.h" +#include "hw/sysbus.h" +#include "hw/hw.h" +#include "hw/pc.h" #include "hw/boards.h" -#include "loader.h" +#include "hw/loader.h" #include "sysemu/sysemu.h" -#include "flash.h" +#include "hw/flash.h" #include "sysemu/kvm.h" #define BIOS_FILENAME "bios.bin" @@ -84,6 +85,10 @@ static void pc_fw_add_pflash_drv(void) bios_name = BIOS_FILENAME; } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!filename) { + error_report("Can't open BIOS image %s", bios_name); + exit(1); + } opts = drive_add(IF_PFLASH, -1, filename, "readonly=on"); @@ -252,7 +257,7 @@ static void pcsysfw_class_init (ObjectClass *klass, void *data) dc->props = pcsysfw_properties; } -static TypeInfo pcsysfw_info = { +static const TypeInfo pcsysfw_info = { .name = "pc-sysfw", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof (PcSysFwDevice), diff --git a/hw/pci/Makefile.objs b/hw/pci/Makefile.objs index fe965fe2f6..1cd6cde2ee 100644 --- a/hw/pci/Makefile.objs +++ b/hw/pci/Makefile.objs @@ -6,4 +6,4 @@ common-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o common-obj-$(CONFIG_NO_PCI) += pci-stub.o -extra-obj-y += pci-stub.o +common-obj-$(CONFIG_ALL) += pci-stub.o diff --git a/hw/pci/msix.c b/hw/pci/msix.c index 073e22c315..e231a0dc4b 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -27,7 +27,7 @@ #define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8) #define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8) -static MSIMessage msix_get_message(PCIDevice *dev, unsigned vector) +MSIMessage msix_get_message(PCIDevice *dev, unsigned vector) { uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE; MSIMessage msg; @@ -65,7 +65,7 @@ static int msix_is_pending(PCIDevice *dev, int vector) return *msix_pending_byte(dev, vector) & msix_pending_mask(vector); } -static void msix_set_pending(PCIDevice *dev, int vector) +void msix_set_pending(PCIDevice *dev, unsigned int vector) { *msix_pending_byte(dev, vector) |= msix_pending_mask(vector); } @@ -75,13 +75,13 @@ static void msix_clr_pending(PCIDevice *dev, int vector) *msix_pending_byte(dev, vector) &= ~msix_pending_mask(vector); } -static bool msix_vector_masked(PCIDevice *dev, int vector, bool fmask) +static bool msix_vector_masked(PCIDevice *dev, unsigned int vector, bool fmask) { unsigned offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; return fmask || dev->msix_table[offset] & PCI_MSIX_ENTRY_CTRL_MASKBIT; } -static bool msix_is_masked(PCIDevice *dev, int vector) +bool msix_is_masked(PCIDevice *dev, unsigned int vector) { return msix_vector_masked(dev, vector, dev->msix_function_masked); } @@ -191,6 +191,11 @@ static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, unsigned size) { PCIDevice *dev = opaque; + if (dev->msix_vector_poll_notifier) { + unsigned vector_start = addr * 8; + unsigned vector_end = MIN(addr + size * 8, dev->msix_entries_nr); + dev->msix_vector_poll_notifier(dev, vector_start, vector_end); + } return pci_get_long(dev->msix_pba + addr); } @@ -513,7 +518,8 @@ static void msix_unset_notifier_for_vector(PCIDevice *dev, unsigned int vector) int msix_set_vector_notifiers(PCIDevice *dev, MSIVectorUseNotifier use_notifier, - MSIVectorReleaseNotifier release_notifier) + MSIVectorReleaseNotifier release_notifier, + MSIVectorPollNotifier poll_notifier) { int vector, ret; @@ -521,6 +527,7 @@ int msix_set_vector_notifiers(PCIDevice *dev, dev->msix_vector_use_notifier = use_notifier; dev->msix_vector_release_notifier = release_notifier; + dev->msix_vector_poll_notifier = poll_notifier; if ((dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & (MSIX_ENABLE_MASK | MSIX_MASKALL_MASK)) == MSIX_ENABLE_MASK) { @@ -531,6 +538,9 @@ int msix_set_vector_notifiers(PCIDevice *dev, } } } + if (dev->msix_vector_poll_notifier) { + dev->msix_vector_poll_notifier(dev, 0, dev->msix_entries_nr); + } return 0; undo: @@ -557,4 +567,5 @@ void msix_unset_vector_notifiers(PCIDevice *dev) } dev->msix_vector_use_notifier = NULL; dev->msix_vector_release_notifier = NULL; + dev->msix_vector_poll_notifier = NULL; } diff --git a/hw/pci/msix.h b/hw/pci/msix.h index ff07ae2e8f..e648410535 100644 --- a/hw/pci/msix.h +++ b/hw/pci/msix.h @@ -5,6 +5,7 @@ #include "hw/pci/pci.h" void msix_set_message(PCIDevice *dev, int vector, MSIMessage msg); +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, @@ -26,6 +27,9 @@ void msix_load(PCIDevice *dev, QEMUFile *f); int msix_enabled(PCIDevice *dev); int msix_present(PCIDevice *dev); +bool msix_is_masked(PCIDevice *dev, unsigned vector); +void msix_set_pending(PCIDevice *dev, unsigned vector); + int msix_vector_use(PCIDevice *dev, unsigned vector); void msix_vector_unuse(PCIDevice *dev, unsigned vector); void msix_unuse_all_vectors(PCIDevice *dev); @@ -36,6 +40,7 @@ void msix_reset(PCIDevice *dev); int msix_set_vector_notifiers(PCIDevice *dev, MSIVectorUseNotifier use_notifier, - MSIVectorReleaseNotifier release_notifier); + MSIVectorReleaseNotifier release_notifier, + MSIVectorPollNotifier poll_notifier); void msix_unset_vector_notifiers(PCIDevice *dev); #endif diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 94840c4af7..2f45c8f02f 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -274,13 +274,12 @@ int pci_find_domain(const PCIBus *bus) return -1; } -void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, +static void pci_bus_init(PCIBus *bus, DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, uint8_t devfn_min) { - qbus_create_inplace(&bus->qbus, TYPE_PCI_BUS, parent, name); assert(PCI_FUNC(devfn_min) == 0); bus->devfn_min = devfn_min; bus->address_space_mem = address_space_mem; @@ -293,6 +292,17 @@ void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, vmstate_register(NULL, -1, &vmstate_pcibus, bus); } +void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, + const char *name, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io, + uint8_t devfn_min) +{ + qbus_create_inplace(bus, TYPE_PCI_BUS, parent, name); + pci_bus_init(bus, parent, name, address_space_mem, + address_space_io, devfn_min); +} + PCIBus *pci_bus_new(DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, @@ -300,10 +310,9 @@ PCIBus *pci_bus_new(DeviceState *parent, const char *name, { PCIBus *bus; - bus = g_malloc0(sizeof(*bus)); - pci_bus_new_inplace(bus, parent, name, address_space_mem, - address_space_io, devfn_min); - OBJECT(bus)->free = g_free; + bus = PCI_BUS(qbus_create(TYPE_PCI_BUS, parent, name)); + pci_bus_init(bus, parent, name, address_space_mem, + address_space_io, devfn_min); return bus; } @@ -1123,7 +1132,7 @@ PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin) } while (dev); if (!bus->route_intx_to_irq) { - error_report("PCI: Bug - unimplemented PCI INTx routing (%s)\n", + error_report("PCI: Bug - unimplemented PCI INTx routing (%s)", object_get_typename(OBJECT(bus->qbus.parent))); return (PCIINTxRoute) { PCI_INTX_DISABLED, -1 }; } @@ -2150,7 +2159,7 @@ void pci_setup_iommu(PCIBus *bus, PCIDMAContextFunc fn, void *opaque) bus->dma_context_opaque = opaque; } -static TypeInfo pci_device_type_info = { +static const TypeInfo pci_device_type_info = { .name = TYPE_PCI_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(PCIDevice), diff --git a/hw/pci/pci.h b/hw/pci/pci.h index 3152050856..f340fe57c9 100644 --- a/hw/pci/pci.h +++ b/hw/pci/pci.h @@ -77,6 +77,14 @@ #define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003 #define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 #define PCI_DEVICE_ID_VIRTIO_RNG 0x1005 +#define PCI_DEVICE_ID_VIRTIO_9P 0x1009 + +#define PCI_VENDOR_ID_REDHAT 0x1b36 +#define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001 +#define PCI_DEVICE_ID_REDHAT_SERIAL 0x0002 +#define PCI_DEVICE_ID_REDHAT_SERIAL2 0x0003 +#define PCI_DEVICE_ID_REDHAT_SERIAL4 0x0004 +#define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 @@ -187,6 +195,9 @@ typedef void (*PCIINTxRoutingNotifier)(PCIDevice *dev); typedef int (*MSIVectorUseNotifier)(PCIDevice *dev, unsigned int vector, MSIMessage msg); typedef void (*MSIVectorReleaseNotifier)(PCIDevice *dev, unsigned int vector); +typedef void (*MSIVectorPollNotifier)(PCIDevice *dev, + unsigned int vector_start, + unsigned int vector_end); struct PCIDevice { DeviceState qdev; @@ -271,6 +282,7 @@ struct PCIDevice { /* MSI-X notifiers */ MSIVectorUseNotifier msix_vector_use_notifier; MSIVectorReleaseNotifier msix_vector_release_notifier; + MSIVectorPollNotifier msix_vector_poll_notifier; }; void pci_register_bar(PCIDevice *pci_dev, int region_num, diff --git a/hw/pci/pci_host.h b/hw/pci/pci_host.h index 1845d4dfd5..236cd0f75c 100644 --- a/hw/pci/pci_host.h +++ b/hw/pci/pci_host.h @@ -40,7 +40,6 @@ struct PCIHostState { MemoryRegion conf_mem; MemoryRegion data_mem; MemoryRegion mmcfg; - MemoryRegion *address_space; uint32_t config_reg; PCIBus *bus; }; diff --git a/hw/pci/pci_ids.h b/hw/pci/pci_ids.h index f17a512bb2..f955d21f6a 100644 --- a/hw/pci/pci_ids.h +++ b/hw/pci/pci_ids.h @@ -149,6 +149,9 @@ #define PCI_VENDOR_ID_NEC 0x1033 #define PCI_DEVICE_ID_NEC_UPD720200 0x0194 +#define PCI_VENDOR_ID_TEWS 0x1498 +#define PCI_DEVICE_ID_TEWS_TPCI200 0x30C8 + #define PCI_VENDOR_ID_NVIDIA 0x10de #define PCI_DEVICE_ID_NVIDIA_MCPX_APU 0x01b0 #define PCI_DEVICE_ID_NVIDIA_MCPX_ACI 0x01b1 diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 6c916d15ec..485c94c1b2 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -494,7 +494,7 @@ uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id) static void pcie_ext_cap_set_next(PCIDevice *dev, uint16_t pos, uint16_t next) { - uint16_t header = pci_get_long(dev->config + pos); + uint32_t header = pci_get_long(dev->config + pos); assert(!(next & (PCI_EXT_CAP_ALIGN - 1))); header = (header & ~PCI_EXT_CAP_NEXT_MASK) | ((next << PCI_EXT_CAP_NEXT_SHIFT) & PCI_EXT_CAP_NEXT_MASK); diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index f07266da66..d35c2ee965 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -1,7 +1,8 @@ +#include "qemu-common.h" #include #include #include "qemu/range.h" -#include "qemu/range.h" +#include "qemu/error-report.h" #include "hw/pci/shpc.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" diff --git a/hw/pci/slotid_cap.c b/hw/pci/slotid_cap.c index 99a30f429d..62f7bae2f1 100644 --- a/hw/pci/slotid_cap.c +++ b/hw/pci/slotid_cap.c @@ -1,5 +1,6 @@ #include "hw/pci/slotid_cap.h" #include "hw/pci/pci.h" +#include "qemu/error-report.h" #define SLOTID_CAP_LENGTH 4 #define SLOTID_NSLOTS_SHIFT (ffs(PCI_SID_ESR_NSLOTS) - 1) diff --git a/hw/pci_bridge_dev.c b/hw/pci_bridge_dev.c index 7818dcc350..9cc6a4082d 100644 --- a/hw/pci_bridge_dev.c +++ b/hw/pci_bridge_dev.c @@ -19,17 +19,13 @@ * with this program; if not, see . */ -#include "pci/pci_bridge.h" -#include "pci/pci_ids.h" -#include "pci/msi.h" -#include "pci/shpc.h" -#include "pci/slotid_cap.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_ids.h" +#include "hw/pci/msi.h" +#include "hw/pci/shpc.h" +#include "hw/pci/slotid_cap.h" #include "exec/memory.h" -#include "pci/pci_bus.h" - -#define REDHAT_PCI_VENDOR_ID 0x1b36 -#define PCI_BRIDGE_DEV_VENDOR_ID REDHAT_PCI_VENDOR_ID -#define PCI_BRIDGE_DEV_DEVICE_ID 0x1 +#include "hw/pci/pci_bus.h" struct PCIBridgeDev { PCIBridge bridge; @@ -146,8 +142,8 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) k->init = pci_bridge_dev_initfn; k->exit = pci_bridge_dev_exitfn; k->config_write = pci_bridge_dev_write_config; - k->vendor_id = PCI_BRIDGE_DEV_VENDOR_ID; - k->device_id = PCI_BRIDGE_DEV_DEVICE_ID; + k->vendor_id = PCI_VENDOR_ID_REDHAT; + k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE; k->class_id = PCI_CLASS_BRIDGE_PCI; k->is_bridge = 1, dc->desc = "Standard PCI Bridge"; @@ -156,7 +152,7 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) dc->vmsd = &pci_bridge_dev_vmstate; } -static TypeInfo pci_bridge_dev_info = { +static const TypeInfo pci_bridge_dev_info = { .name = "pci-bridge", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIBridgeDev), diff --git a/hw/pckbd.c b/hw/pckbd.c index 6db7bbcc06..cc63df0570 100644 --- a/hw/pckbd.c +++ b/hw/pckbd.c @@ -21,10 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "isa.h" -#include "pc.h" -#include "ps2.h" +#include "hw/hw.h" +#include "hw/isa.h" +#include "hw/pc.h" +#include "hw/ps2.h" #include "sysemu/sysemu.h" /* debug PC keyboard */ @@ -512,7 +512,7 @@ static void i8042_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_kbd_isa; } -static TypeInfo i8042_info = { +static const TypeInfo i8042_info = { .name = "i8042", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISAKBDState), diff --git a/hw/pcmcia.h b/hw/pcmcia.h index aac1d77cc7..f91669305e 100644 --- a/hw/pcmcia.h +++ b/hw/pcmcia.h @@ -14,7 +14,7 @@ typedef struct { void pcmcia_socket_register(PCMCIASocket *socket); void pcmcia_socket_unregister(PCMCIASocket *socket); -void pcmcia_info(Monitor *mon); +void pcmcia_info(Monitor *mon, const QDict *qdict); struct PCMCIACardState { void *state; diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c index 40a0e6eda4..55f80ca671 100644 --- a/hw/pcnet-pci.c +++ b/hw/pcnet-pci.c @@ -27,13 +27,13 @@ * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000 */ -#include "pci/pci.h" +#include "hw/pci/pci.h" #include "net/net.h" -#include "loader.h" +#include "hw/loader.h" #include "qemu/timer.h" #include "sysemu/dma.h" -#include "pcnet.h" +#include "hw/pcnet.h" //#define PCNET_DEBUG //#define PCNET_DEBUG_IO @@ -266,7 +266,7 @@ static void pci_physical_memory_read(void *dma_opaque, hwaddr addr, static void pci_pcnet_cleanup(NetClientState *nc) { - PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque; + PCNetState *d = qemu_get_nic_opaque(nc); pcnet_common_cleanup(d); } @@ -279,7 +279,7 @@ static void pci_pcnet_uninit(PCIDevice *dev) memory_region_destroy(&d->io_bar); qemu_del_timer(d->state.poll_timer); qemu_free_timer(d->state.poll_timer); - qemu_del_net_client(&d->state.nic->nc); + qemu_del_nic(d->state.nic); } static NetClientInfo net_pci_pcnet_info = { @@ -361,7 +361,7 @@ static void pcnet_class_init(ObjectClass *klass, void *data) dc->props = pcnet_properties; } -static TypeInfo pcnet_info = { +static const TypeInfo pcnet_info = { .name = "pcnet", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIPCNetState), diff --git a/hw/pcnet.c b/hw/pcnet.c index 30f100007a..b0b462b02e 100644 --- a/hw/pcnet.c +++ b/hw/pcnet.c @@ -35,13 +35,13 @@ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt */ -#include "qdev.h" +#include "hw/qdev.h" #include "net/net.h" #include "qemu/timer.h" #include "qemu/sockets.h" #include "sysemu/sysemu.h" -#include "pcnet.h" +#include "hw/pcnet.h" //#define PCNET_DEBUG //#define PCNET_DEBUG_IO @@ -1006,7 +1006,7 @@ static int pcnet_tdte_poll(PCNetState *s) int pcnet_can_receive(NetClientState *nc) { - PCNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + PCNetState *s = qemu_get_nic_opaque(nc); if (CSR_STOP(s) || CSR_SPND(s)) return 0; @@ -1017,7 +1017,7 @@ int pcnet_can_receive(NetClientState *nc) ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) { - PCNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + PCNetState *s = qemu_get_nic_opaque(nc); int is_padr = 0, is_bcast = 0, is_ladr = 0; uint8_t buf1[60]; int remaining; @@ -1199,7 +1199,7 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) void pcnet_set_link_status(NetClientState *nc) { - PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque; + PCNetState *d = qemu_get_nic_opaque(nc); d->lnkst = nc->link_down ? 0 : 0x40; } @@ -1261,11 +1261,12 @@ static void pcnet_transmit(PCNetState *s) if (BCR_SWSTYLE(s) == 1) add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS); s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC; - pcnet_receive(&s->nic->nc, s->buffer, s->xmit_pos); + pcnet_receive(qemu_get_queue(s->nic), s->buffer, s->xmit_pos); s->looptest = 0; } else if (s->nic) - qemu_send_packet(&s->nic->nc, s->buffer, s->xmit_pos); + qemu_send_packet(qemu_get_queue(s->nic), s->buffer, + s->xmit_pos); s->csr[0] &= ~0x0008; /* clear TDMD */ s->csr[4] |= 0x0004; /* set TXSTRT */ @@ -1730,7 +1731,7 @@ int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0"); diff --git a/hw/pcspk.c b/hw/pcspk.c index 6d55ebe82f..d533415950 100644 --- a/hw/pcspk.c +++ b/hw/pcspk.c @@ -22,13 +22,13 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "pc.h" -#include "isa.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/isa.h" #include "audio/audio.h" #include "qemu/timer.h" -#include "i8254.h" -#include "pcspk.h" +#include "hw/i8254.h" +#include "hw/pcspk.h" #define PCSPK_BUF_LEN 1792 #define PCSPK_SAMPLE_RATE 32000 @@ -187,7 +187,7 @@ static void pcspk_class_initfn(ObjectClass *klass, void *data) dc->props = pcspk_properties; } -static TypeInfo pcspk_info = { +static const TypeInfo pcspk_info = { .name = "isa-pcspk", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(PCSpkState), diff --git a/hw/pcspk.h b/hw/pcspk.h index 7f42bac1c8..f448d221da 100644 --- a/hw/pcspk.h +++ b/hw/pcspk.h @@ -25,8 +25,8 @@ #ifndef HW_PCSPK_H #define HW_PCSPK_H -#include "hw.h" -#include "isa.h" +#include "hw/hw.h" +#include "hw/isa.h" static inline ISADevice *pcspk_init(ISABus *bus, ISADevice *pit) { diff --git a/hw/pflash_cfi01.c b/hw/pflash_cfi01.c index 95e07e7cdb..5d57babe07 100644 --- a/hw/pflash_cfi01.c +++ b/hw/pflash_cfi01.c @@ -36,13 +36,13 @@ * It does not implement much more ... */ -#include "hw.h" -#include "flash.h" +#include "hw/hw.h" +#include "hw/flash.h" #include "block/block.h" #include "qemu/timer.h" #include "exec/address-spaces.h" #include "qemu/host-utils.h" -#include "sysbus.h" +#include "hw/sysbus.h" #define PFLASH_BUG(fmt, ...) \ do { \ @@ -122,6 +122,12 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, __func__, offset, pfl->cmd, width); #endif switch (pfl->cmd) { + default: + /* This should never happen : reset state & treat it as a read */ + DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd); + pfl->wcycle = 0; + pfl->cmd = 0; + /* fall through to read code */ case 0x00: /* Flash area read */ p = pfl->storage; @@ -162,7 +168,10 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, } break; + case 0x10: /* Single byte program */ case 0x20: /* Block erase */ + case 0x28: /* Block erase */ + case 0x40: /* single byte program */ case 0x50: /* Clear status register */ case 0x60: /* Block /un)lock */ case 0x70: /* Status Register */ @@ -194,11 +203,6 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, else ret = pfl->cfi_table[boff]; break; - default: - /* This should never happen : reset state & treat it as a read */ - DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd); - pfl->wcycle = 0; - pfl->cmd = 0; } return ret; } @@ -319,6 +323,9 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, DPRINTF("%s: Write to buffer\n", __func__); pfl->status |= 0x80; /* Ready! */ break; + case 0xf0: /* Probe for AMD flash */ + DPRINTF("%s: Probe for AMD flash\n", __func__); + goto reset_flash; case 0xff: /* Read array mode */ DPRINTF("%s: Read array mode\n", __func__); goto reset_flash; @@ -726,7 +733,7 @@ pflash_t *pflash_cfi01_register(hwaddr base, uint16_t id2, uint16_t id3, int be) { DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); - SysBusDevice *busdev = sysbus_from_qdev(dev); + SysBusDevice *busdev = SYS_BUS_DEVICE(dev); pflash_t *pfl = (pflash_t *)object_dynamic_cast(OBJECT(dev), "cfi.pflash01"); diff --git a/hw/pflash_cfi02.c b/hw/pflash_cfi02.c index cfb91cb143..37b4fcc234 100644 --- a/hw/pflash_cfi02.c +++ b/hw/pflash_cfi02.c @@ -35,13 +35,13 @@ * It does not implement multiple sectors erase */ -#include "hw.h" -#include "flash.h" +#include "hw/hw.h" +#include "hw/flash.h" #include "qemu/timer.h" #include "block/block.h" #include "exec/address-spaces.h" #include "qemu/host-utils.h" -#include "sysbus.h" +#include "hw/sysbus.h" //#define PFLASH_DEBUG #ifdef PFLASH_DEBUG @@ -157,6 +157,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd); pfl->wcycle = 0; pfl->cmd = 0; + /* fall through to the read code */ case 0x80: /* We accept reads during second unlock sequence... */ case 0x00: @@ -760,7 +761,7 @@ pflash_t *pflash_cfi02_register(hwaddr base, int be) { DeviceState *dev = qdev_create(NULL, "cfi.pflash02"); - SysBusDevice *busdev = sysbus_from_qdev(dev); + SysBusDevice *busdev = SYS_BUS_DEVICE(dev); pflash_t *pfl = (pflash_t *)object_dynamic_cast(OBJECT(dev), "cfi.pflash02"); diff --git a/hw/piix4.c b/hw/piix4.c index 799ed1729c..0f5cd014e5 100644 --- a/hw/piix4.c +++ b/hw/piix4.c @@ -22,11 +22,11 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "pc.h" -#include "pci/pci.h" -#include "isa.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/pci/pci.h" +#include "hw/isa.h" +#include "hw/sysbus.h" PCIDevice *piix4_dev; @@ -117,7 +117,7 @@ static void piix4_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_piix4; } -static TypeInfo piix4_info = { +static const TypeInfo piix4_info = { .name = "PIIX4", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PIIX4State), diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 3d79c73fda..e10bc1c6a0 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -22,15 +22,16 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "pc.h" -#include "pci/pci.h" -#include "pci/pci_host.h" -#include "isa.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/isa.h" +#include "hw/sysbus.h" #include "qemu/range.h" -#include "xen.h" -#include "pam.h" +#include "hw/xen.h" +#include "hw/pam.h" +#include "sysemu/sysemu.h" /* * I440FX chipset data sheet. @@ -46,6 +47,12 @@ typedef struct I440FXState { #define XEN_PIIX_NUM_PIRQS 128ULL #define PIIX_PIRQC 0x60 +/* + * Reset Control Register: PCI-accessible ISA-Compatible Register at address + * 0xcf9, provided by the PCI/ISA bridge (PIIX3 PCI function 0, 8086:7000). + */ +#define RCR_IOPORT 0xcf9 + typedef struct PIIX3State { PCIDevice dev; @@ -67,6 +74,12 @@ typedef struct PIIX3State { /* This member isn't used. Just for save/load compatibility */ int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS]; + + /* Reset Control Register contents */ + uint8_t rcr; + + /* IO memory region for Reset Control Register (RCR_IOPORT) */ + MemoryRegion rcr_mem; } PIIX3State; struct PCII440FXState { @@ -231,7 +244,6 @@ static PCIBus *i440fx_common_init(const char *device_name, dev = qdev_create(NULL, "i440FX-pcihost"); s = PCI_HOST_BRIDGE(dev); - s->address_space = address_space_mem; b = pci_bus_new(dev, NULL, pci_address_space, address_space_io, 0); s->bus = b; @@ -442,6 +454,7 @@ static void piix3_reset(void *opaque) pci_conf[0xae] = 0x00; d->pic_levels = 0; + d->rcr = 0; } static int piix3_post_load(void *opaque, int version_id) @@ -462,6 +475,23 @@ static void piix3_pre_save(void *opaque) } } +static bool piix3_rcr_needed(void *opaque) +{ + PIIX3State *piix3 = opaque; + + return (piix3->rcr != 0); +} + +static const VMStateDescription vmstate_piix3_rcr = { + .name = "PIIX3/rcr", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT8(rcr, PIIX3State), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_piix3 = { .name = "PIIX3", .version_id = 3, @@ -469,19 +499,56 @@ static const VMStateDescription vmstate_piix3 = { .minimum_version_id_old = 2, .post_load = piix3_post_load, .pre_save = piix3_pre_save, - .fields = (VMStateField []) { + .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(dev, PIIX3State), VMSTATE_INT32_ARRAY_V(pci_irq_levels_vmstate, PIIX3State, PIIX_NUM_PIRQS, 3), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmstate_piix3_rcr, + .needed = piix3_rcr_needed, + }, + { 0 } } }; + +static void rcr_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) +{ + PIIX3State *d = opaque; + + if (val & 4) { + qemu_system_reset_request(); + return; + } + d->rcr = val & 2; /* keep System Reset type only */ +} + +static uint64_t rcr_read(void *opaque, hwaddr addr, unsigned len) +{ + PIIX3State *d = opaque; + + return d->rcr; +} + +static const MemoryRegionOps rcr_ops = { + .read = rcr_read, + .write = rcr_write, + .endianness = DEVICE_LITTLE_ENDIAN +}; + static int piix3_initfn(PCIDevice *dev) { PIIX3State *d = DO_UPCAST(PIIX3State, dev, dev); isa_bus_new(&d->dev.qdev, pci_address_space_io(dev)); + + memory_region_init_io(&d->rcr_mem, &rcr_ops, d, "piix3-reset-control", 1); + memory_region_add_subregion_overlap(pci_address_space_io(dev), RCR_IOPORT, + &d->rcr_mem, 1); + qemu_register_reset(piix3_reset, d); return 0; } diff --git a/hw/pl011.c b/hw/pl011.c index 35835f36c0..332d5b970c 100644 --- a/hw/pl011.c +++ b/hw/pl011.c @@ -7,7 +7,7 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "char/char.h" typedef struct { @@ -300,7 +300,7 @@ static void pl011_arm_class_init(ObjectClass *klass, void *data) sdc->init = pl011_arm_init; } -static TypeInfo pl011_arm_info = { +static const TypeInfo pl011_arm_info = { .name = "pl011", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl011_state), @@ -314,7 +314,7 @@ static void pl011_luminary_class_init(ObjectClass *klass, void *data) sdc->init = pl011_luminary_init; } -static TypeInfo pl011_luminary_info = { +static const TypeInfo pl011_luminary_info = { .name = "pl011_luminary", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl011_state), diff --git a/hw/pl022.c b/hw/pl022.c index fbd7ded0cf..536c2166fe 100644 --- a/hw/pl022.c +++ b/hw/pl022.c @@ -7,8 +7,8 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" -#include "ssi.h" +#include "hw/sysbus.h" +#include "hw/ssi.h" //#define DEBUG_PL022 1 @@ -293,7 +293,7 @@ static void pl022_class_init(ObjectClass *klass, void *data) sdc->init = pl022_init; } -static TypeInfo pl022_info = { +static const TypeInfo pl022_info = { .name = "pl022", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl022_state), diff --git a/hw/pl031.c b/hw/pl031.c index 3a23ecde48..764940be7e 100644 --- a/hw/pl031.c +++ b/hw/pl031.c @@ -11,7 +11,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" @@ -250,7 +250,7 @@ static void pl031_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pl031; } -static TypeInfo pl031_info = { +static const TypeInfo pl031_info = { .name = "pl031", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl031_state), diff --git a/hw/pl041.c b/hw/pl041.c index 4436d97c50..92dddc2923 100644 --- a/hw/pl041.c +++ b/hw/pl041.c @@ -20,10 +20,10 @@ * */ -#include "sysbus.h" +#include "hw/sysbus.h" -#include "pl041.h" -#include "lm4549.h" +#include "hw/pl041.h" +#include "hw/lm4549.h" #if 0 #define PL041_DEBUG_LEVEL 1 @@ -632,7 +632,7 @@ static void pl041_device_class_init(ObjectClass *klass, void *data) dc->props = pl041_device_properties; } -static TypeInfo pl041_device_info = { +static const TypeInfo pl041_device_info = { .name = "pl041", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl041_state), diff --git a/hw/pl050.c b/hw/pl050.c index 47032f1260..bc31ab6885 100644 --- a/hw/pl050.c +++ b/hw/pl050.c @@ -7,8 +7,8 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" -#include "ps2.h" +#include "hw/sysbus.h" +#include "hw/ps2.h" typedef struct { SysBusDevice busdev; @@ -168,7 +168,7 @@ static void pl050_kbd_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pl050; } -static TypeInfo pl050_kbd_info = { +static const TypeInfo pl050_kbd_info = { .name = "pl050_keyboard", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl050_state), @@ -184,7 +184,7 @@ static void pl050_mouse_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pl050; } -static TypeInfo pl050_mouse_info = { +static const TypeInfo pl050_mouse_info = { .name = "pl050_mouse", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl050_state), diff --git a/hw/pl061.c b/hw/pl061.c index f1ed5ced1d..74bc109488 100644 --- a/hw/pl061.c +++ b/hw/pl061.c @@ -8,7 +8,7 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" +#include "hw/sysbus.h" //#define DEBUG_PL061 1 @@ -304,7 +304,7 @@ static void pl061_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pl061; } -static TypeInfo pl061_info = { +static const TypeInfo pl061_info = { .name = "pl061", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl061_state), @@ -320,7 +320,7 @@ static void pl061_luminary_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pl061; } -static TypeInfo pl061_luminary_info = { +static const TypeInfo pl061_luminary_info = { .name = "pl061_luminary", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl061_state), diff --git a/hw/pl080.c b/hw/pl080.c index 26150af757..00b66b45b0 100644 --- a/hw/pl080.c +++ b/hw/pl080.c @@ -7,7 +7,7 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" +#include "hw/sysbus.h" #define PL080_MAX_CHANNELS 8 #define PL080_CONF_E 0x1 @@ -386,7 +386,7 @@ static void pl080_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pl080; } -static TypeInfo pl080_info = { +static const TypeInfo pl080_info = { .name = "pl080", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl080_state), @@ -403,7 +403,7 @@ static void pl081_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pl080; } -static TypeInfo pl081_info = { +static const TypeInfo pl081_info = { .name = "pl081", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl080_state), diff --git a/hw/pl110.c b/hw/pl110.c index 098e335aea..924642d697 100644 --- a/hw/pl110.c +++ b/hw/pl110.c @@ -7,9 +7,9 @@ * This code is licensed under the GNU LGPL */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "ui/console.h" -#include "framebuffer.h" +#include "hw/framebuffer.h" #include "ui/pixel_ops.h" #define PL110_CR_EN 0x001 @@ -111,15 +111,15 @@ static const unsigned char *idregs[] = { }; #define BITS 8 -#include "pl110_template.h" +#include "hw/pl110_template.h" #define BITS 15 -#include "pl110_template.h" +#include "hw/pl110_template.h" #define BITS 16 -#include "pl110_template.h" +#include "hw/pl110_template.h" #define BITS 24 -#include "pl110_template.h" +#include "hw/pl110_template.h" #define BITS 32 -#include "pl110_template.h" +#include "hw/pl110_template.h" static int pl110_enabled(pl110_state *s) { @@ -480,7 +480,7 @@ static void pl110_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pl110; } -static TypeInfo pl110_info = { +static const TypeInfo pl110_info = { .name = "pl110", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl110_state), @@ -497,7 +497,7 @@ static void pl110_versatile_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pl110; } -static TypeInfo pl110_versatile_info = { +static const TypeInfo pl110_versatile_info = { .name = "pl110_versatile", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl110_state), @@ -514,7 +514,7 @@ static void pl111_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pl110; } -static TypeInfo pl111_info = { +static const TypeInfo pl111_info = { .name = "pl111", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl110_state), diff --git a/hw/pl110_template.h b/hw/pl110_template.h index e738e4a241..ec4bfd6f8c 100644 --- a/hw/pl110_template.h +++ b/hw/pl110_template.h @@ -27,20 +27,20 @@ #undef RGB #define BORDER bgr #define ORDER 0 -#include "pl110_template.h" +#include "hw/pl110_template.h" #define ORDER 1 -#include "pl110_template.h" +#include "hw/pl110_template.h" #define ORDER 2 -#include "pl110_template.h" +#include "hw/pl110_template.h" #undef BORDER #define RGB #define BORDER rgb #define ORDER 0 -#include "pl110_template.h" +#include "hw/pl110_template.h" #define ORDER 1 -#include "pl110_template.h" +#include "hw/pl110_template.h" #define ORDER 2 -#include "pl110_template.h" +#include "hw/pl110_template.h" #undef BORDER static drawfn glue(pl110_draw_fn_,BITS)[48] = diff --git a/hw/pl181.c b/hw/pl181.c index cbddb741ce..2527296776 100644 --- a/hw/pl181.c +++ b/hw/pl181.c @@ -8,8 +8,8 @@ */ #include "sysemu/blockdev.h" -#include "sysbus.h" -#include "sd.h" +#include "hw/sysbus.h" +#include "hw/sd.h" //#define DEBUG_PL181 1 @@ -500,7 +500,7 @@ static void pl181_class_init(ObjectClass *klass, void *data) k->no_user = 1; } -static TypeInfo pl181_info = { +static const TypeInfo pl181_info = { .name = "pl181", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl181_state), diff --git a/hw/pl190.c b/hw/pl190.c index 40199302a9..9610673d94 100644 --- a/hw/pl190.c +++ b/hw/pl190.c @@ -7,7 +7,7 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" +#include "hw/sysbus.h" /* The number of virtual priority levels. 16 user vectors plus the unvectored IRQ. Chained interrupts would require an additional level @@ -274,7 +274,7 @@ static void pl190_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_pl190; } -static TypeInfo pl190_info = { +static const TypeInfo pl190_info = { .name = "pl190", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(pl190_state), diff --git a/hw/pm_smbus.c b/hw/pm_smbus.c index ea1380ca68..790061065c 100644 --- a/hw/pm_smbus.c +++ b/hw/pm_smbus.c @@ -17,10 +17,10 @@ * License along with this library; if not, see * . */ -#include "hw.h" -#include "pc.h" -#include "pm_smbus.h" -#include "smbus.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/pm_smbus.h" +#include "hw/smbus.h" /* no save/load? */ diff --git a/hw/ppc.h b/hw/ppc.h index 17005c795a..acaf0d6580 100644 --- a/hw/ppc.h +++ b/hw/ppc.h @@ -1,7 +1,7 @@ #ifndef HW_PPC_H #define HW_PPC_H 1 -void ppc_set_irq (CPUPPCState *env, int n_IRQ, int level); +void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level); /* PowerPC hardware exceptions management helpers */ typedef void (*clk_setup_cb)(void *opaque, uint32_t freq); @@ -58,9 +58,9 @@ clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq, unsigned int decr_excp); /* Embedded PowerPC reset */ -void ppc40x_core_reset (CPUPPCState *env); -void ppc40x_chip_reset (CPUPPCState *env); -void ppc40x_system_reset (CPUPPCState *env); +void ppc40x_core_reset(PowerPCCPU *cpu); +void ppc40x_chip_reset(PowerPCCPU *cpu); +void ppc40x_system_reset(PowerPCCPU *cpu); void PREP_debug_write (void *opaque, uint32_t addr, uint32_t val); extern CPUWriteMemoryFunc * const PPC_io_write[]; @@ -73,6 +73,8 @@ void ppc6xx_irq_init (CPUPPCState *env); void ppc970_irq_init (CPUPPCState *env); void ppcPOWER7_irq_init (CPUPPCState *env); +void ppce500_set_mpic_proxy(bool enabled); + /* PPC machines for OpenBIOS */ enum { ARCH_PREP = 0, @@ -92,6 +94,6 @@ enum { #define PPC_SERIAL_MM_BAUDBASE 399193 /* ppc_booke.c */ -void ppc_booke_timers_init(CPUPPCState *env, uint32_t freq, uint32_t flags); +void ppc_booke_timers_init(PowerPCCPU *cpu, uint32_t freq, uint32_t flags); #endif diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index afdcc0e531..4de02098e2 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -1,31 +1,34 @@ -# shared objects -obj-y = ppc.o ppc_booke.o # PREP target obj-y += mc146818rtc.o -obj-y += ppc_prep.o -# OldWorld PowerMac -obj-y += ppc_oldworld.o -# NewWorld PowerMac -obj-y += ppc_newworld.o # IBM pSeries (sPAPR) -obj-$(CONFIG_PSERIES) += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o -obj-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o -obj-$(CONFIG_PSERIES) += spapr_pci.o pci/pci-hotplug.o spapr_iommu.o -obj-$(CONFIG_PSERIES) += spapr_events.o spapr_nvram.o +obj-$(CONFIG_PSERIES) += spapr_vty.o spapr_llan.o spapr_vscsi.o +obj-$(CONFIG_PSERIES) += spapr_pci.o pci/pci-hotplug.o +obj-$(CONFIG_PSERIES) += spapr_nvram.o # PowerPC 4xx boards -obj-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o -obj-y += ppc440_bamboo.o -# PowerPC E500 boards -obj-$(CONFIG_FDT) += mpc8544_guts.o ppce500_spin.o -# PowerPC 440 Xilinx ML507 reference board. -obj-y += virtex_ml507.o +obj-y += ppc4xx_pci.o # PowerPC OpenPIC obj-y += openpic.o -obj-$(CONFIG_FDT) += ../device_tree.o # Xilinx PPC peripherals obj-y += xilinx_ethlite.o obj-y := $(addprefix ../,$(obj-y)) -obj-$(CONFIG_FDT) += e500.o mpc8544ds.o e500plat.o +# shared objects +obj-y += ppc.o ppc_booke.o +# IBM pSeries (sPAPR) +obj-$(CONFIG_PSERIES) += spapr.o xics.o spapr_vio.o spapr_events.o +obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o +# PowerPC 4xx boards +obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o +# PReP +obj-y += prep.o +# OldWorld PowerMac +obj-y += mac_oldworld.o +# NewWorld PowerMac +obj-y += mac_newworld.o +# e500 +obj-$(CONFIG_E500) += e500.o mpc8544ds.o e500plat.o +obj-$(CONFIG_E500) += mpc8544_guts.o ppce500_spin.o +# PowerPC 440 Xilinx ML507 reference board. +obj-y += virtex_ml507.o diff --git a/hw/ppc/e500-ccsr.h b/hw/ppc/e500-ccsr.h index f20f51bcd2..12a2ba4b97 100644 --- a/hw/ppc/e500-ccsr.h +++ b/hw/ppc/e500-ccsr.h @@ -1,7 +1,7 @@ #ifndef E500_CCSR_H #define E500_CCSR_H -#include "../sysbus.h" +#include "hw/sysbus.h" typedef struct PPCE500CCSRState { /*< private >*/ diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index aa54fd84d7..fef9c5d842 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -41,6 +41,7 @@ #define UIMAGE_LOAD_BASE 0 #define DTC_LOAD_PAD 0x1800000 #define DTC_PAD_MASK 0xFFFFF +#define DTB_MAX_SIZE (8 * 1024 * 1024) #define INITRD_LOAD_PAD 0x2000000 #define INITRD_PAD_MASK 0xFFFFFF @@ -225,6 +226,10 @@ static int ppce500_load_device_tree(CPUPPCState *env, kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); qemu_devtree_setprop(fdt, "/hypervisor", "hcall-instructions", hypercall, sizeof(hypercall)); + /* if KVM supports the idle hcall, set property indicating this */ + if (kvmppc_get_hasidle(env)) { + qemu_devtree_setprop(fdt, "/hypervisor", "has-idle", NULL, 0); + } } /* Create CPU nodes */ @@ -235,25 +240,23 @@ static int ppce500_load_device_tree(CPUPPCState *env, /* We need to generate the cpu nodes in reverse order, so Linux can pick the first node as boot node and be happy */ for (i = smp_cpus - 1; i >= 0; i--) { + CPUState *cpu; char cpu_name[128]; uint64_t cpu_release_addr = MPC8544_SPIN_BASE + (i * 0x20); - for (env = first_cpu; env != NULL; env = env->next_cpu) { - if (env->cpu_index == i) { - break; - } - } - - if (!env) { + cpu = qemu_get_cpu(i); + if (cpu == NULL) { continue; } + env = cpu->env_ptr; - snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", env->cpu_index); + snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", + cpu->cpu_index); qemu_devtree_add_subnode(fdt, cpu_name); qemu_devtree_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); qemu_devtree_setprop_string(fdt, cpu_name, "device_type", "cpu"); - qemu_devtree_setprop_cell(fdt, cpu_name, "reg", env->cpu_index); + qemu_devtree_setprop_cell(fdt, cpu_name, "reg", cpu->cpu_index); qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-line-size", env->dcache_line_size); qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-line-size", @@ -261,7 +264,7 @@ static int ppce500_load_device_tree(CPUPPCState *env, qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000); qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000); qemu_devtree_setprop_cell(fdt, cpu_name, "bus-frequency", 0); - if (env->cpu_index) { + if (cpu->cpu_index) { qemu_devtree_setprop_string(fdt, cpu_name, "status", "disabled"); qemu_devtree_setprop_string(fdt, cpu_name, "enable-method", "spin-table"); qemu_devtree_setprop_u64(fdt, cpu_name, "cpu-release-addr", @@ -289,7 +292,7 @@ static int ppce500_load_device_tree(CPUPPCState *env, snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET); qemu_devtree_add_subnode(fdt, mpic); qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic"); - qemu_devtree_setprop_string(fdt, mpic, "compatible", "chrp,open-pic"); + qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_OFFSET, 0x40000); qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0); @@ -417,26 +420,28 @@ static void mmubooke_create_initial_mapping(CPUPPCState *env) static void ppce500_cpu_reset_sec(void *opaque) { PowerPCCPU *cpu = opaque; + CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - cpu_reset(CPU(cpu)); + cpu_reset(cs); /* Secondary CPU starts in halted state for now. Needs to change when implementing non-kernel boot. */ - env->halted = 1; + cs->halted = 1; env->exception_index = EXCP_HLT; } static void ppce500_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; + CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; struct boot_info *bi = env->load_info; - cpu_reset(CPU(cpu)); + cpu_reset(cs); /* Set initial guest state. */ - env->halted = 0; + cs->halted = 0; env->gpr[1] = (16<<20) - 8; env->gpr[3] = bi->dt_base; env->nip = bi->entry; @@ -456,7 +461,8 @@ void ppce500_init(PPCE500Params *params) target_long kernel_size=0; target_ulong dt_base = 0; target_ulong initrd_base = 0; - target_long initrd_size=0; + target_long initrd_size = 0; + target_ulong cur_base = 0; int i = 0, j, k; unsigned int pci_irq_nrs[4] = {1, 2, 3, 4}; qemu_irq **irqs, *mpic; @@ -475,6 +481,7 @@ void ppce500_init(PPCE500Params *params) irqs[0] = g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); for (i = 0; i < smp_cpus; i++) { PowerPCCPU *cpu; + CPUState *cs; qemu_irq *input; cpu = cpu_ppc_init(params->cpu_model); @@ -483,6 +490,7 @@ void ppce500_init(PPCE500Params *params) exit(1); } env = &cpu->env; + cs = CPU(cpu); if (!firstenv) { firstenv = env; @@ -492,11 +500,11 @@ void ppce500_init(PPCE500Params *params) input = (qemu_irq *)env->irq_inputs; irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; - env->spr[SPR_BOOKE_PIR] = env->cpu_index = i; - env->mpic_cpu_base = MPC8544_CCSRBAR_BASE + - MPC8544_MPIC_REGS_OFFSET + 0x20000; + env->spr[SPR_BOOKE_PIR] = cs->cpu_index = i; + env->mpic_iack = MPC8544_CCSRBAR_BASE + + MPC8544_MPIC_REGS_OFFSET + 0xa0; - ppc_booke_timers_init(env, 400000000, PPC_TIMER_E500); + ppc_booke_timers_init(cpu, 400000000, PPC_TIMER_E500); /* Register reset handler */ if (!i) { @@ -534,9 +542,9 @@ void ppce500_init(PPCE500Params *params) mpic = g_new(qemu_irq, 256); dev = qdev_create(NULL, "openpic"); qdev_prop_set_uint32(dev, "nb_cpus", smp_cpus); - qdev_prop_set_uint32(dev, "model", OPENPIC_MODEL_FSL_MPIC_20); + qdev_prop_set_uint32(dev, "model", params->mpic_version); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); k = 0; for (i = 0; i < smp_cpus; i++) { @@ -588,7 +596,7 @@ void ppce500_init(PPCE500Params *params) if (!pci_bus) printf("couldn't create PCI controller!\n"); - sysbus_mmio_map(sysbus_from_qdev(dev), 1, MPC8544_PCI_IO); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, MPC8544_PCI_IO); if (pci_bus) { /* Register network interfaces. */ @@ -617,12 +625,17 @@ void ppce500_init(PPCE500Params *params) params->kernel_filename); exit(1); } + + cur_base = loadaddr + kernel_size; + + /* Reserve space for dtb */ + dt_base = (cur_base + DTC_LOAD_PAD) & ~DTC_PAD_MASK; + cur_base += DTB_MAX_SIZE; } /* Load initrd. */ if (params->initrd_filename) { - initrd_base = (loadaddr + kernel_size + INITRD_LOAD_PAD) & - ~INITRD_PAD_MASK; + initrd_base = (cur_base + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK; initrd_size = load_image_targphys(params->initrd_filename, initrd_base, ram_size - initrd_base); @@ -631,6 +644,8 @@ void ppce500_init(PPCE500Params *params) params->initrd_filename); exit(1); } + + cur_base = initrd_base + initrd_size; } /* If we're loading a kernel directly, we must load the device tree too. */ @@ -638,13 +653,13 @@ void ppce500_init(PPCE500Params *params) struct boot_info *boot_info; int dt_size; - dt_base = (loadaddr + kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; dt_size = ppce500_load_device_tree(env, params, dt_base, initrd_base, initrd_size); if (dt_size < 0) { fprintf(stderr, "couldn't load device tree\n"); exit(1); } + assert(dt_size < DTB_MAX_SIZE); boot_info = env->load_info; boot_info->entry = entry; diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index f5ff27385b..226c93d248 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -16,6 +16,8 @@ typedef struct PPCE500Params { /* required -- must at least add toplevel board compatible */ void (*fixup_devtree)(struct PPCE500Params *params, void *fdt); + + int mpic_version; } PPCE500Params; void ppce500_init(PPCE500Params *params); diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 4deb02ac38..4b3057528c 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -12,9 +12,10 @@ #include "config.h" #include "qemu-common.h" #include "e500.h" -#include "../boards.h" +#include "hw/boards.h" #include "sysemu/device_tree.h" #include "hw/pci/pci.h" +#include "hw/openpic.h" static void e500plat_fixup_devtree(PPCE500Params *params, void *fdt) { @@ -44,6 +45,7 @@ static void e500plat_init(QEMUMachineInitArgs *args) .pci_first_slot = 0x1, .pci_nr_slots = PCI_SLOT_MAX - 1, .fixup_devtree = e500plat_fixup_devtree, + .mpic_version = OPENPIC_MODEL_FSL_MPIC_42, }; ppce500_init(¶ms); @@ -53,7 +55,8 @@ static QEMUMachine e500plat_machine = { .name = "ppce500", .desc = "generic paravirt e500 platform", .init = e500plat_init, - .max_cpus = 15, + .max_cpus = 32, + DEFAULT_MACHINE_OPTIONS, }; static void e500plat_machine_init(void) diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h new file mode 100644 index 0000000000..b17107b797 --- /dev/null +++ b/hw/ppc/mac.h @@ -0,0 +1,181 @@ +/* + * QEMU PowerMac emulation shared definitions and prototypes + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * 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. + */ +#if !defined(__PPC_MAC_H__) +#define __PPC_MAC_H__ + +#include "exec/memory.h" +#include "hw/sysbus.h" +#include "hw/ide/internal.h" +#include "hw/adb.h" + +/* SMP is not enabled, for now */ +#define MAX_CPUS 1 + +#define BIOS_SIZE (1024 * 1024) +#define BIOS_FILENAME "ppc_rom.bin" +#define NVRAM_SIZE 0x2000 +#define PROM_FILENAME "openbios-ppc" +#define PROM_ADDR 0xfff00000 + +#define KERNEL_LOAD_ADDR 0x01000000 +#define KERNEL_GAP 0x00100000 + +#define ESCC_CLOCK 3686400 + +/* Cuda */ +#define TYPE_CUDA "cuda" +#define CUDA(obj) OBJECT_CHECK(CUDAState, (obj), TYPE_CUDA) + +/** + * CUDATimer: + * @counter_value: counter value at load time + */ +typedef struct CUDATimer { + int index; + uint16_t latch; + uint16_t counter_value; + int64_t load_time; + int64_t next_irq_time; + QEMUTimer *timer; +} CUDATimer; + +/** + * CUDAState: + * @b: B-side data + * @a: A-side data + * @dirb: B-side direction (1=output) + * @dira: A-side direction (1=output) + * @sr: Shift register + * @acr: Auxiliary control register + * @pcr: Peripheral control register + * @ifr: Interrupt flag register + * @ier: Interrupt enable register + * @anh: A-side data, no handshake + * @last_b: last value of B register + * @last_acr: last value of ACR register + */ +typedef struct CUDAState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion mem; + /* cuda registers */ + uint8_t b; + uint8_t a; + uint8_t dirb; + uint8_t dira; + uint8_t sr; + uint8_t acr; + uint8_t pcr; + uint8_t ifr; + uint8_t ier; + uint8_t anh; + + ADBBusState adb_bus; + CUDATimer timers[2]; + + uint32_t tick_offset; + + uint8_t last_b; + uint8_t last_acr; + + int data_in_size; + int data_in_index; + int data_out_index; + + qemu_irq irq; + uint8_t autopoll; + uint8_t data_in[128]; + uint8_t data_out[16]; + QEMUTimer *adb_poll_timer; +} CUDAState; + +/* MacIO */ +#define TYPE_OLDWORLD_MACIO "macio-oldworld" +#define TYPE_NEWWORLD_MACIO "macio-newworld" + +#define TYPE_MACIO_IDE "macio-ide" +#define MACIO_IDE(obj) OBJECT_CHECK(MACIOIDEState, (obj), TYPE_MACIO_IDE) + +typedef struct MACIOIDEState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + qemu_irq irq; + qemu_irq dma_irq; + + MemoryRegion mem; + IDEBus bus; + BlockDriverAIOCB *aiocb; +} MACIOIDEState; + +void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); +void macio_ide_register_dma(MACIOIDEState *ide, void *dbdma, int channel); + +void macio_init(PCIDevice *dev, + MemoryRegion *pic_mem, + MemoryRegion *escc_mem); + +/* Heathrow PIC */ +qemu_irq *heathrow_pic_init(MemoryRegion **pmem, + int nb_cpus, qemu_irq **irqs); + +/* Grackle PCI */ +#define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" +PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io); + +/* UniNorth PCI */ +PCIBus *pci_pmac_init(qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io); +PCIBus *pci_pmac_u3_init(qemu_irq *pic, + MemoryRegion *address_space_mem, + MemoryRegion *address_space_io); + +/* Mac NVRAM */ +#define TYPE_MACIO_NVRAM "macio-nvram" +#define MACIO_NVRAM(obj) \ + OBJECT_CHECK(MacIONVRAMState, (obj), TYPE_MACIO_NVRAM) + +typedef struct MacIONVRAMState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + uint32_t size; + uint32_t it_shift; + + MemoryRegion mem; + uint8_t *data; +} MacIONVRAMState; + +void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len); +uint8_t macio_nvram_read(MacIONVRAMState *s, uint32_t addr); +void macio_nvram_write(MacIONVRAMState *s, uint32_t addr, uint8_t val); +#endif /* !defined(__PPC_MAC_H__) */ diff --git a/hw/ppc_newworld.c b/hw/ppc/mac_newworld.c similarity index 89% rename from hw/ppc_newworld.c rename to hw/ppc/mac_newworld.c index fabcc08b40..a08a6b2086 100644 --- a/hw/ppc_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -46,28 +46,28 @@ * 0001:05:0c.0 IDE interface [0101]: Broadcom K2 SATA [1166:0240] * */ -#include "hw.h" -#include "ppc.h" -#include "ppc_mac.h" -#include "adb.h" -#include "mac_dbdma.h" -#include "nvram.h" -#include "pci/pci.h" +#include "hw/hw.h" +#include "hw/ppc.h" +#include "hw/ppc/mac.h" +#include "hw/adb.h" +#include "hw/mac_dbdma.h" +#include "hw/nvram.h" +#include "hw/pci/pci.h" #include "net/net.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "fw_cfg.h" -#include "escc.h" -#include "openpic.h" -#include "ide.h" -#include "loader.h" +#include "hw/boards.h" +#include "hw/fw_cfg.h" +#include "hw/escc.h" +#include "hw/openpic.h" +#include "hw/ide.h" +#include "hw/loader.h" #include "elf.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "hw/usb.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" -#include "sysbus.h" +#include "hw/sysbus.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 @@ -147,15 +147,16 @@ static void ppc_core99_init(QEMUMachineInitArgs *args) hwaddr kernel_base, initrd_base, cmdline_base = 0; long kernel_size, initrd_size; PCIBus *pci_bus; + PCIDevice *macio; + MACIOIDEState *macio_ide; + BusState *adb_bus; MacIONVRAMState *nvr; int bios_size; - MemoryRegion *pic_mem, *dbdma_mem, *cuda_mem, *escc_mem; + MemoryRegion *pic_mem, *escc_mem; MemoryRegion *escc_bar = g_new(MemoryRegion, 1); - MemoryRegion *ide_mem[3]; int ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; - void *dbdma; int machine_arch; SysBusDevice *s; DeviceState *dev; @@ -329,7 +330,7 @@ static void ppc_core99_init(QEMUMachineInitArgs *args) dev = qdev_create(NULL, "openpic"); qdev_prop_set_uint32(dev, "model", OPENPIC_MODEL_RAVEN); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); pic_mem = s->mmio[0].memory; k = 0; for (i = 0; i < smp_cpus; i++) { @@ -362,20 +363,31 @@ static void ppc_core99_init(QEMUMachineInitArgs *args) pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL); ide_drive_get(hd, MAX_IDE_BUS); - dbdma = DBDMA_init(&dbdma_mem); + + macio = pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO); + dev = DEVICE(macio); + qdev_connect_gpio_out(dev, 0, pic[0x19]); /* CUDA */ + qdev_connect_gpio_out(dev, 1, pic[0x0d]); /* IDE */ + qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE DMA */ + qdev_connect_gpio_out(dev, 3, pic[0x0e]); /* IDE */ + qdev_connect_gpio_out(dev, 4, pic[0x03]); /* IDE DMA */ + macio_init(macio, pic_mem, escc_bar); /* We only emulate 2 out of 3 IDE controllers for now */ - ide_mem[0] = NULL; - ide_mem[1] = pmac_ide_init(hd, pic[0x0d], dbdma, 0x16, pic[0x02]); - ide_mem[2] = pmac_ide_init(&hd[MAX_IDE_DEVS], pic[0x0e], dbdma, 0x1a, pic[0x02]); + macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), + "ide[0]")); + macio_ide_init_drives(macio_ide, hd); - cuda_init(&cuda_mem, pic[0x19]); + macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), + "ide[1]")); + macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); - adb_kbd_init(&adb_bus); - adb_mouse_init(&adb_bus); - - macio_init(pci_bus, PCI_DEVICE_ID_APPLE_UNI_N_KEYL, 0, pic_mem, - dbdma_mem, cuda_mem, NULL, 3, ide_mem, escc_bar); + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + adb_bus = qdev_get_child_bus(dev, "adb.0"); + dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); + qdev_init_nofail(dev); + dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); + qdev_init_nofail(dev); if (usb_enabled(machine_arch == ARCH_MAC99_U3)) { pci_create_simple(pci_bus, -1, "pci-ohci"); @@ -391,12 +403,17 @@ static void ppc_core99_init(QEMUMachineInitArgs *args) graphic_depth = 15; /* The NewWorld NVRAM is not located in the MacIO device */ - nvr = macio_nvram_init(0x2000, 1); + dev = qdev_create(NULL, TYPE_MACIO_NVRAM); + qdev_prop_set_uint32(dev, "size", 0x2000); + qdev_prop_set_uint32(dev, "it_shift", 1); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xFFF04000); + nvr = MACIO_NVRAM(dev); pmac_format_nvram_partition(nvr, 0x2000); - macio_nvram_setup_bar(nvr, get_system_memory(), 0xFFF04000); /* No PCI init: the BIOS will do it */ fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, machine_arch); @@ -442,6 +459,7 @@ static QEMUMachine core99_machine = { #ifdef TARGET_PPC64 .is_default = 1, #endif + DEFAULT_MACHINE_OPTIONS, }; static void core99_machine_init(void) diff --git a/hw/ppc_oldworld.c b/hw/ppc/mac_oldworld.c similarity index 89% rename from hw/ppc_oldworld.c rename to hw/ppc/mac_oldworld.c index fff5129ca9..2778e45879 100644 --- a/hw/ppc_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -23,21 +23,20 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "ppc.h" -#include "ppc_mac.h" -#include "adb.h" -#include "mac_dbdma.h" -#include "nvram.h" +#include "hw/hw.h" +#include "hw/ppc.h" +#include "mac.h" +#include "hw/adb.h" +#include "hw/nvram.h" #include "sysemu/sysemu.h" #include "net/net.h" -#include "isa.h" -#include "pci/pci.h" -#include "boards.h" -#include "fw_cfg.h" -#include "escc.h" -#include "ide.h" -#include "loader.h" +#include "hw/isa.h" +#include "hw/pci/pci.h" +#include "hw/boards.h" +#include "hw/fw_cfg.h" +#include "hw/escc.h" +#include "hw/ide.h" +#include "hw/loader.h" #include "elf.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" @@ -90,14 +89,16 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args) uint32_t kernel_base, initrd_base, cmdline_base = 0; int32_t kernel_size, initrd_size; PCIBus *pci_bus; - MacIONVRAMState *nvr; + PCIDevice *macio; + MACIOIDEState *macio_ide; + DeviceState *dev; + BusState *adb_bus; int bios_size; - MemoryRegion *pic_mem, *dbdma_mem, *cuda_mem; - MemoryRegion *escc_mem, *escc_bar = g_new(MemoryRegion, 1), *ide_mem[2]; + MemoryRegion *pic_mem; + MemoryRegion *escc_mem, *escc_bar = g_new(MemoryRegion, 1); uint16_t ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; - void *dbdma; linux_boot = (kernel_filename != NULL); @@ -263,10 +264,17 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args) ide_drive_get(hd, MAX_IDE_BUS); + macio = pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO); + dev = DEVICE(macio); + qdev_connect_gpio_out(dev, 0, pic[0x12]); /* CUDA */ + qdev_connect_gpio_out(dev, 1, pic[0x0D]); /* IDE */ + qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE DMA */ + macio_init(macio, pic_mem, escc_bar); + /* First IDE channel is a MAC IDE on the MacIO bus */ - dbdma = DBDMA_init(&dbdma_mem); - ide_mem[0] = NULL; - ide_mem[1] = pmac_ide_init(hd, pic[0x0D], dbdma, 0x16, pic[0x02]); + macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), + "ide")); + macio_ide_init_drives(macio_ide, hd); /* Second IDE channel is a CMD646 on the PCI bus */ hd[0] = hd[MAX_IDE_DEVS]; @@ -274,17 +282,12 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args) hd[3] = hd[2] = NULL; pci_cmd646_ide_init(pci_bus, hd, 0); - /* cuda also initialize ADB */ - cuda_init(&cuda_mem, pic[0x12]); - - adb_kbd_init(&adb_bus); - adb_mouse_init(&adb_bus); - - nvr = macio_nvram_init(0x2000, 4); - pmac_format_nvram_partition(nvr, 0x2000); - - macio_init(pci_bus, PCI_DEVICE_ID_APPLE_343S1201, 1, pic_mem, - dbdma_mem, cuda_mem, nvr, 2, ide_mem, escc_bar); + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + adb_bus = qdev_get_child_bus(dev, "adb.0"); + dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); + qdev_init_nofail(dev); + dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); + qdev_init_nofail(dev); if (usb_enabled(false)) { pci_create_simple(pci_bus, -1, "pci-ohci"); @@ -296,6 +299,7 @@ static void ppc_heathrow_init(QEMUMachineInitArgs *args) /* No PCI init: the BIOS will do it */ fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_HEATHROW); @@ -341,6 +345,7 @@ static QEMUMachine heathrow_machine = { #ifndef TARGET_PPC64 .is_default = 1, #endif + DEFAULT_MACHINE_OPTIONS, }; static void heathrow_machine_init(void) diff --git a/hw/mpc8544_guts.c b/hw/ppc/mpc8544_guts.c similarity index 96% rename from hw/mpc8544_guts.c rename to hw/ppc/mpc8544_guts.c index 84522e9722..193beab2c2 100644 --- a/hw/mpc8544_guts.c +++ b/hw/ppc/mpc8544_guts.c @@ -17,9 +17,9 @@ * */ -#include "hw.h" +#include "hw/hw.h" #include "sysemu/sysemu.h" -#include "sysbus.h" +#include "hw/sysbus.h" #define MPC8544_GUTS_MMIO_SIZE 0x1000 #define MPC8544_GUTS_RSTCR_RESET 0x02 @@ -112,7 +112,7 @@ static int mpc8544_guts_initfn(SysBusDevice *dev) { GutsState *s; - s = FROM_SYSBUS(GutsState, sysbus_from_qdev(dev)); + s = FROM_SYSBUS(GutsState, SYS_BUS_DEVICE(dev)); memory_region_init_io(&s->iomem, &mpc8544_guts_ops, s, "mpc6544.guts", MPC8544_GUTS_MMIO_SIZE); @@ -128,7 +128,7 @@ static void mpc8544_guts_class_init(ObjectClass *klass, void *data) k->init = mpc8544_guts_initfn; } -static TypeInfo mpc8544_guts_info = { +static const TypeInfo mpc8544_guts_info = { .name = "mpc8544-guts", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(GutsState), diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index f9ae20f5a3..cf29788c4d 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -12,8 +12,9 @@ #include "config.h" #include "qemu-common.h" #include "e500.h" -#include "../boards.h" +#include "hw/boards.h" #include "sysemu/device_tree.h" +#include "hw/openpic.h" static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt) { @@ -43,6 +44,7 @@ static void mpc8544ds_init(QEMUMachineInitArgs *args) .pci_first_slot = 0x11, .pci_nr_slots = 2, .fixup_devtree = mpc8544ds_fixup_devtree, + .mpic_version = OPENPIC_MODEL_FSL_MPIC_20, }; ppce500_init(¶ms); @@ -54,6 +56,7 @@ static QEMUMachine ppce500_machine = { .desc = "mpc8544ds", .init = mpc8544ds_init, .max_cpus = 15, + DEFAULT_MACHINE_OPTIONS, }; static void ppce500_machine_init(void) diff --git a/hw/ppc.c b/hw/ppc/ppc.c similarity index 88% rename from hw/ppc.c rename to hw/ppc/ppc.c index 1559982625..85bc821d94 100644 --- a/hw/ppc.c +++ b/hw/ppc/ppc.c @@ -21,13 +21,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "ppc.h" +#include "hw/hw.h" +#include "hw/ppc.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" -#include "nvram.h" +#include "hw/nvram.h" #include "qemu/log.h" -#include "loader.h" +#include "hw/loader.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" @@ -50,28 +50,31 @@ static void cpu_ppc_tb_stop (CPUPPCState *env); static void cpu_ppc_tb_start (CPUPPCState *env); -void ppc_set_irq(CPUPPCState *env, int n_IRQ, int level) +void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level) { + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; unsigned int old_pending = env->pending_interrupts; if (level) { env->pending_interrupts |= 1 << n_IRQ; - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { env->pending_interrupts &= ~(1 << n_IRQ); - if (env->pending_interrupts == 0) - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + if (env->pending_interrupts == 0) { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } } if (old_pending != env->pending_interrupts) { #ifdef CONFIG_KVM - kvmppc_set_interrupt(env, n_IRQ, level); + kvmppc_set_interrupt(cpu, n_IRQ, level); #endif } LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32 "req %08x\n", __func__, env, n_IRQ, level, - env->pending_interrupts, env->interrupt_request); + env->pending_interrupts, CPU(cpu)->interrupt_request); } /* PowerPC 6xx / 7xx internal IRQ controller */ @@ -86,6 +89,8 @@ static void ppc6xx_set_irq(void *opaque, int pin, int level) cur_level = (env->irq_input_state >> pin) & 1; /* Don't generate spurious events */ if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { + CPUState *cs = CPU(cpu); + switch (pin) { case PPC6xx_INPUT_TBEN: /* Level sensitive - active high */ @@ -100,13 +105,13 @@ static void ppc6xx_set_irq(void *opaque, int pin, int level) /* Level sensitive - active high */ LOG_IRQ("%s: set the external IRQ state to %d\n", __func__, level); - ppc_set_irq(env, PPC_INTERRUPT_EXT, level); + ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); break; case PPC6xx_INPUT_SMI: /* Level sensitive - active high */ LOG_IRQ("%s: set the SMI IRQ state to %d\n", __func__, level); - ppc_set_irq(env, PPC_INTERRUPT_SMI, level); + ppc_set_irq(cpu, PPC_INTERRUPT_SMI, level); break; case PPC6xx_INPUT_MCP: /* Negative edge sensitive */ @@ -116,7 +121,7 @@ static void ppc6xx_set_irq(void *opaque, int pin, int level) if (cur_level == 1 && level == 0) { LOG_IRQ("%s: raise machine check state\n", __func__); - ppc_set_irq(env, PPC_INTERRUPT_MCK, 1); + ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1); } break; case PPC6xx_INPUT_CKSTP_IN: @@ -125,20 +130,20 @@ static void ppc6xx_set_irq(void *opaque, int pin, int level) /* XXX: Note that the only way to restart the CPU is to reset it */ if (level) { LOG_IRQ("%s: stop the CPU\n", __func__); - env->halted = 1; + cs->halted = 1; } break; case PPC6xx_INPUT_HRESET: /* Level sensitive - active low */ if (level) { LOG_IRQ("%s: reset the CPU\n", __func__); - cpu_interrupt(env, CPU_INTERRUPT_RESET); + cpu_interrupt(cs, CPU_INTERRUPT_RESET); } break; case PPC6xx_INPUT_SRESET: LOG_IRQ("%s: set the RESET IRQ state to %d\n", __func__, level); - ppc_set_irq(env, PPC_INTERRUPT_RESET, level); + ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level); break; default: /* Unknown pin - do nothing */ @@ -173,18 +178,20 @@ static void ppc970_set_irq(void *opaque, int pin, int level) cur_level = (env->irq_input_state >> pin) & 1; /* Don't generate spurious events */ if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { + CPUState *cs = CPU(cpu); + switch (pin) { case PPC970_INPUT_INT: /* Level sensitive - active high */ LOG_IRQ("%s: set the external IRQ state to %d\n", __func__, level); - ppc_set_irq(env, PPC_INTERRUPT_EXT, level); + ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); break; case PPC970_INPUT_THINT: /* Level sensitive - active high */ LOG_IRQ("%s: set the SMI IRQ state to %d\n", __func__, level); - ppc_set_irq(env, PPC_INTERRUPT_THERM, level); + ppc_set_irq(cpu, PPC_INTERRUPT_THERM, level); break; case PPC970_INPUT_MCP: /* Negative edge sensitive */ @@ -194,7 +201,7 @@ static void ppc970_set_irq(void *opaque, int pin, int level) if (cur_level == 1 && level == 0) { LOG_IRQ("%s: raise machine check state\n", __func__); - ppc_set_irq(env, PPC_INTERRUPT_MCK, 1); + ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1); } break; case PPC970_INPUT_CKSTP: @@ -202,23 +209,23 @@ static void ppc970_set_irq(void *opaque, int pin, int level) /* XXX: TODO: relay the signal to CKSTP_OUT pin */ if (level) { LOG_IRQ("%s: stop the CPU\n", __func__); - env->halted = 1; + cs->halted = 1; } else { LOG_IRQ("%s: restart the CPU\n", __func__); - env->halted = 0; - qemu_cpu_kick(CPU(cpu)); + cs->halted = 0; + qemu_cpu_kick(cs); } break; case PPC970_INPUT_HRESET: /* Level sensitive - active low */ if (level) { - cpu_interrupt(env, CPU_INTERRUPT_RESET); + cpu_interrupt(cs, CPU_INTERRUPT_RESET); } break; case PPC970_INPUT_SRESET: LOG_IRQ("%s: set the RESET IRQ state to %d\n", __func__, level); - ppc_set_irq(env, PPC_INTERRUPT_RESET, level); + ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level); break; case PPC970_INPUT_TBEN: LOG_IRQ("%s: set the TBEN state to %d\n", __func__, @@ -259,7 +266,7 @@ static void power7_set_irq(void *opaque, int pin, int level) /* Level sensitive - active high */ LOG_IRQ("%s: set the external IRQ state to %d\n", __func__, level); - ppc_set_irq(env, PPC_INTERRUPT_EXT, level); + ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); break; default: /* Unknown pin - do nothing */ @@ -294,55 +301,57 @@ static void ppc40x_set_irq(void *opaque, int pin, int level) cur_level = (env->irq_input_state >> pin) & 1; /* Don't generate spurious events */ if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { + CPUState *cs = CPU(cpu); + switch (pin) { case PPC40x_INPUT_RESET_SYS: if (level) { LOG_IRQ("%s: reset the PowerPC system\n", __func__); - ppc40x_system_reset(env); + ppc40x_system_reset(cpu); } break; case PPC40x_INPUT_RESET_CHIP: if (level) { LOG_IRQ("%s: reset the PowerPC chip\n", __func__); - ppc40x_chip_reset(env); + ppc40x_chip_reset(cpu); } break; case PPC40x_INPUT_RESET_CORE: /* XXX: TODO: update DBSR[MRR] */ if (level) { LOG_IRQ("%s: reset the PowerPC core\n", __func__); - ppc40x_core_reset(env); + ppc40x_core_reset(cpu); } break; case PPC40x_INPUT_CINT: /* Level sensitive - active high */ LOG_IRQ("%s: set the critical IRQ state to %d\n", __func__, level); - ppc_set_irq(env, PPC_INTERRUPT_CEXT, level); + ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level); break; case PPC40x_INPUT_INT: /* Level sensitive - active high */ LOG_IRQ("%s: set the external IRQ state to %d\n", __func__, level); - ppc_set_irq(env, PPC_INTERRUPT_EXT, level); + ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); break; case PPC40x_INPUT_HALT: /* Level sensitive - active low */ if (level) { LOG_IRQ("%s: stop the CPU\n", __func__); - env->halted = 1; + cs->halted = 1; } else { LOG_IRQ("%s: restart the CPU\n", __func__); - env->halted = 0; - qemu_cpu_kick(CPU(cpu)); + cs->halted = 0; + qemu_cpu_kick(cs); } break; case PPC40x_INPUT_DEBUG: /* Level sensitive - active high */ LOG_IRQ("%s: set the debug pin state to %d\n", __func__, level); - ppc_set_irq(env, PPC_INTERRUPT_DEBUG, level); + ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level); break; default: /* Unknown pin - do nothing */ @@ -387,26 +396,26 @@ static void ppce500_set_irq(void *opaque, int pin, int level) case PPCE500_INPUT_RESET_CORE: if (level) { LOG_IRQ("%s: reset the PowerPC core\n", __func__); - ppc_set_irq(env, PPC_INTERRUPT_MCK, level); + ppc_set_irq(cpu, PPC_INTERRUPT_MCK, level); } break; case PPCE500_INPUT_CINT: /* Level sensitive - active high */ LOG_IRQ("%s: set the critical IRQ state to %d\n", __func__, level); - ppc_set_irq(env, PPC_INTERRUPT_CEXT, level); + ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level); break; case PPCE500_INPUT_INT: /* Level sensitive - active high */ LOG_IRQ("%s: set the core IRQ state to %d\n", __func__, level); - ppc_set_irq(env, PPC_INTERRUPT_EXT, level); + ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); break; case PPCE500_INPUT_DEBUG: /* Level sensitive - active high */ LOG_IRQ("%s: set the debug pin state to %d\n", __func__, level); - ppc_set_irq(env, PPC_INTERRUPT_DEBUG, level); + ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level); break; default: /* Unknown pin - do nothing */ @@ -427,6 +436,23 @@ void ppce500_irq_init(CPUPPCState *env) env->irq_inputs = (void **)qemu_allocate_irqs(&ppce500_set_irq, cpu, PPCE500_INPUT_NB); } + +/* Enable or Disable the E500 EPR capability */ +void ppce500_set_mpic_proxy(bool enabled) +{ + CPUPPCState *env; + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); + CPUState *cs = CPU(cpu); + + env->mpic_proxy = enabled; + if (kvm_enabled()) { + kvmppc_set_mpic_proxy(POWERPC_CPU(cs), enabled); + } + } +} + /*****************************************************************************/ /* PowerPC time base and decrementer emulation */ @@ -643,26 +669,27 @@ uint64_t cpu_ppc_load_purr (CPUPPCState *env) /* When decrementer expires, * all we need to do is generate or queue a CPU exception */ -static inline void cpu_ppc_decr_excp(CPUPPCState *env) +static inline void cpu_ppc_decr_excp(PowerPCCPU *cpu) { /* Raise it */ LOG_TB("raise decrementer exception\n"); - ppc_set_irq(env, PPC_INTERRUPT_DECR, 1); + ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 1); } -static inline void cpu_ppc_hdecr_excp(CPUPPCState *env) +static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu) { /* Raise it */ LOG_TB("raise decrementer exception\n"); - ppc_set_irq(env, PPC_INTERRUPT_HDECR, 1); + ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1); } -static void __cpu_ppc_store_decr (CPUPPCState *env, uint64_t *nextp, - struct QEMUTimer *timer, - void (*raise_excp)(CPUPPCState *), - uint32_t decr, uint32_t value, - int is_excp) +static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, + struct QEMUTimer *timer, + void (*raise_excp)(PowerPCCPU *), + uint32_t decr, uint32_t value, + int is_excp) { + CPUPPCState *env = &cpu->env; ppc_tb_t *tb_env = env->tb_env; uint64_t now, next; @@ -692,53 +719,61 @@ static void __cpu_ppc_store_decr (CPUPPCState *env, uint64_t *nextp, if ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && (value & 0x80000000) && !(decr & 0x80000000)) { - (*raise_excp)(env); + (*raise_excp)(cpu); } } -static inline void _cpu_ppc_store_decr(CPUPPCState *env, uint32_t decr, +static inline void _cpu_ppc_store_decr(PowerPCCPU *cpu, uint32_t decr, uint32_t value, int is_excp) { - ppc_tb_t *tb_env = env->tb_env; + ppc_tb_t *tb_env = cpu->env.tb_env; - __cpu_ppc_store_decr(env, &tb_env->decr_next, tb_env->decr_timer, + __cpu_ppc_store_decr(cpu, &tb_env->decr_next, tb_env->decr_timer, &cpu_ppc_decr_excp, decr, value, is_excp); } void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value) { - _cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0); + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + _cpu_ppc_store_decr(cpu, cpu_ppc_load_decr(env), value, 0); } -static void cpu_ppc_decr_cb (void *opaque) +static void cpu_ppc_decr_cb(void *opaque) { - _cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1); + PowerPCCPU *cpu = opaque; + + _cpu_ppc_store_decr(cpu, 0x00000000, 0xFFFFFFFF, 1); } -static inline void _cpu_ppc_store_hdecr(CPUPPCState *env, uint32_t hdecr, +static inline void _cpu_ppc_store_hdecr(PowerPCCPU *cpu, uint32_t hdecr, uint32_t value, int is_excp) { - ppc_tb_t *tb_env = env->tb_env; + ppc_tb_t *tb_env = cpu->env.tb_env; if (tb_env->hdecr_timer != NULL) { - __cpu_ppc_store_decr(env, &tb_env->hdecr_next, tb_env->hdecr_timer, + __cpu_ppc_store_decr(cpu, &tb_env->hdecr_next, tb_env->hdecr_timer, &cpu_ppc_hdecr_excp, hdecr, value, is_excp); } } void cpu_ppc_store_hdecr (CPUPPCState *env, uint32_t value) { - _cpu_ppc_store_hdecr(env, cpu_ppc_load_hdecr(env), value, 0); + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + _cpu_ppc_store_hdecr(cpu, cpu_ppc_load_hdecr(env), value, 0); } -static void cpu_ppc_hdecr_cb (void *opaque) +static void cpu_ppc_hdecr_cb(void *opaque) { - _cpu_ppc_store_hdecr(opaque, 0x00000000, 0xFFFFFFFF, 1); + PowerPCCPU *cpu = opaque; + + _cpu_ppc_store_hdecr(cpu, 0x00000000, 0xFFFFFFFF, 1); } -static void cpu_ppc_store_purr(CPUPPCState *env, uint64_t value) +static void cpu_ppc_store_purr(PowerPCCPU *cpu, uint64_t value) { - ppc_tb_t *tb_env = env->tb_env; + ppc_tb_t *tb_env = cpu->env.tb_env; tb_env->purr_load = value; tb_env->purr_start = qemu_get_clock_ns(vm_clock); @@ -747,6 +782,7 @@ static void cpu_ppc_store_purr(CPUPPCState *env, uint64_t value) static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) { CPUPPCState *env = opaque; + PowerPCCPU *cpu = ppc_env_get_cpu(env); ppc_tb_t *tb_env = env->tb_env; tb_env->tb_freq = freq; @@ -755,25 +791,27 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) * if a decrementer exception is pending when it enables msr_ee at startup, * it's not ready to handle it... */ - _cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0); - _cpu_ppc_store_hdecr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0); - cpu_ppc_store_purr(env, 0x0000000000000000ULL); + _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 0); + _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 0); + cpu_ppc_store_purr(cpu, 0x0000000000000000ULL); } /* Set up (once) timebase frequency (in Hz) */ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); ppc_tb_t *tb_env; tb_env = g_malloc0(sizeof(ppc_tb_t)); env->tb_env = tb_env; tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; /* Create new timer */ - tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_decr_cb, env); + tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_decr_cb, cpu); if (0) { /* XXX: find a suitable condition to enable the hypervisor decrementer */ - tb_env->hdecr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_hdecr_cb, env); + tb_env->hdecr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_hdecr_cb, + cpu); } else { tb_env->hdecr_timer = NULL; } @@ -829,12 +867,14 @@ struct ppc40x_timer_t { /* Fixed interval timer */ static void cpu_4xx_fit_cb (void *opaque) { + PowerPCCPU *cpu; CPUPPCState *env; ppc_tb_t *tb_env; ppc40x_timer_t *ppc40x_timer; uint64_t now, next; env = opaque; + cpu = ppc_env_get_cpu(env); tb_env = env->tb_env; ppc40x_timer = tb_env->opaque; now = qemu_get_clock_ns(vm_clock); @@ -860,8 +900,9 @@ static void cpu_4xx_fit_cb (void *opaque) next++; qemu_mod_timer(ppc40x_timer->fit_timer, next); env->spr[SPR_40x_TSR] |= 1 << 26; - if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) - ppc_set_irq(env, PPC_INTERRUPT_FIT, 1); + if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) { + ppc_set_irq(cpu, PPC_INTERRUPT_FIT, 1); + } LOG_TB("%s: ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__, (int)((env->spr[SPR_40x_TCR] >> 23) & 0x1), env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); @@ -897,16 +938,19 @@ static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp) static void cpu_4xx_pit_cb (void *opaque) { + PowerPCCPU *cpu; CPUPPCState *env; ppc_tb_t *tb_env; ppc40x_timer_t *ppc40x_timer; env = opaque; + cpu = ppc_env_get_cpu(env); tb_env = env->tb_env; ppc40x_timer = tb_env->opaque; env->spr[SPR_40x_TSR] |= 1 << 27; - if ((env->spr[SPR_40x_TCR] >> 26) & 0x1) - ppc_set_irq(env, ppc40x_timer->decr_excp, 1); + if ((env->spr[SPR_40x_TCR] >> 26) & 0x1) { + ppc_set_irq(cpu, ppc40x_timer->decr_excp, 1); + } start_stop_pit(env, tb_env, 1); LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " " "%016" PRIx64 "\n", __func__, @@ -919,12 +963,14 @@ static void cpu_4xx_pit_cb (void *opaque) /* Watchdog timer */ static void cpu_4xx_wdt_cb (void *opaque) { + PowerPCCPU *cpu; CPUPPCState *env; ppc_tb_t *tb_env; ppc40x_timer_t *ppc40x_timer; uint64_t now, next; env = opaque; + cpu = ppc_env_get_cpu(env); tb_env = env->tb_env; ppc40x_timer = tb_env->opaque; now = qemu_get_clock_ns(vm_clock); @@ -961,8 +1007,9 @@ static void cpu_4xx_wdt_cb (void *opaque) qemu_mod_timer(ppc40x_timer->wdt_timer, next); ppc40x_timer->wdt_next = next; env->spr[SPR_40x_TSR] |= 1 << 30; - if ((env->spr[SPR_40x_TCR] >> 27) & 0x1) - ppc_set_irq(env, PPC_INTERRUPT_WDT, 1); + if ((env->spr[SPR_40x_TCR] >> 27) & 0x1) { + ppc_set_irq(cpu, PPC_INTERRUPT_WDT, 1); + } break; case 0x3: env->spr[SPR_40x_TSR] &= ~0x30000000; @@ -972,13 +1019,13 @@ static void cpu_4xx_wdt_cb (void *opaque) /* No reset */ break; case 0x1: /* Core reset */ - ppc40x_core_reset(env); + ppc40x_core_reset(cpu); break; case 0x2: /* Chip reset */ - ppc40x_chip_reset(env); + ppc40x_chip_reset(cpu); break; case 0x3: /* System reset */ - ppc40x_system_reset(env); + ppc40x_system_reset(cpu); break; } } @@ -1150,7 +1197,7 @@ void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val) break; case 2: printf("Set loglevel to %04" PRIx32 "\n", val); - cpu_set_log(val | 0x100); + qemu_set_log(val | 0x100); break; } } diff --git a/hw/ppc405_boards.c b/hw/ppc/ppc405_boards.c similarity index 98% rename from hw/ppc405_boards.c rename to hw/ppc/ppc405_boards.c index 8f7f0d07d1..ba443cf8ef 100644 --- a/hw/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -21,16 +21,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "ppc.h" -#include "ppc405.h" -#include "nvram.h" -#include "flash.h" +#include "hw/hw.h" +#include "hw/ppc.h" +#include "hw/ppc405.h" +#include "hw/nvram.h" +#include "hw/flash.h" #include "sysemu/sysemu.h" #include "block/block.h" -#include "boards.h" +#include "hw/boards.h" #include "qemu/log.h" -#include "loader.h" +#include "hw/loader.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" @@ -362,6 +362,7 @@ static QEMUMachine ref405ep_machine = { .name = "ref405ep", .desc = "ref405ep", .init = ref405ep_init, + DEFAULT_MACHINE_OPTIONS, }; /*****************************************************************************/ @@ -649,6 +650,7 @@ static QEMUMachine taihu_machine = { .name = "taihu", .desc = "taihu", .init = taihu_405ep_init, + DEFAULT_MACHINE_OPTIONS, }; static void ppc405_machine_init(void) diff --git a/hw/ppc405_uc.c b/hw/ppc/ppc405_uc.c similarity index 98% rename from hw/ppc405_uc.c rename to hw/ppc/ppc405_uc.c index 7e56ecb4af..56bae8f6e0 100644 --- a/hw/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -21,10 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "ppc.h" -#include "ppc405.h" -#include "serial.h" +#include "hw/hw.h" +#include "hw/ppc.h" +#include "hw/ppc405.h" +#include "hw/serial.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "qemu/log.h" @@ -1770,24 +1770,26 @@ static void ppc405_mal_init(CPUPPCState *env, qemu_irq irqs[4]) /*****************************************************************************/ /* SPR */ -void ppc40x_core_reset (CPUPPCState *env) +void ppc40x_core_reset(PowerPCCPU *cpu) { + CPUPPCState *env = &cpu->env; target_ulong dbsr; printf("Reset PowerPC core\n"); - cpu_interrupt(env, CPU_INTERRUPT_RESET); + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_RESET); dbsr = env->spr[SPR_40x_DBSR]; dbsr &= ~0x00000300; dbsr |= 0x00000100; env->spr[SPR_40x_DBSR] = dbsr; } -void ppc40x_chip_reset (CPUPPCState *env) +void ppc40x_chip_reset(PowerPCCPU *cpu) { + CPUPPCState *env = &cpu->env; target_ulong dbsr; printf("Reset PowerPC chip\n"); - cpu_interrupt(env, CPU_INTERRUPT_RESET); + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_RESET); /* XXX: TODO reset all internal peripherals */ dbsr = env->spr[SPR_40x_DBSR]; dbsr &= ~0x00000300; @@ -1795,7 +1797,7 @@ void ppc40x_chip_reset (CPUPPCState *env) env->spr[SPR_40x_DBSR] = dbsr; } -void ppc40x_system_reset (CPUPPCState *env) +void ppc40x_system_reset(PowerPCCPU *cpu) { printf("Reset PowerPC system\n"); qemu_system_reset_request(); @@ -1803,21 +1805,23 @@ void ppc40x_system_reset (CPUPPCState *env) void store_40x_dbcr0 (CPUPPCState *env, uint32_t val) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); + switch ((val >> 28) & 0x3) { case 0x0: /* No action */ break; case 0x1: /* Core reset */ - ppc40x_core_reset(env); + ppc40x_core_reset(cpu); break; case 0x2: /* Chip reset */ - ppc40x_chip_reset(env); + ppc40x_chip_reset(cpu); break; case 0x3: /* System reset */ - ppc40x_system_reset(env); + ppc40x_system_reset(cpu); break; } } @@ -2111,12 +2115,14 @@ CPUPPCState *ppc405cr_init(MemoryRegion *address_space_mem, { clk_setup_t clk_setup[PPC405CR_CLK_NB]; qemu_irq dma_irqs[4]; + PowerPCCPU *cpu; CPUPPCState *env; qemu_irq *pic, *irqs; memset(clk_setup, 0, sizeof(clk_setup)); - env = ppc4xx_init("405cr", &clk_setup[PPC405CR_CPU_CLK], + cpu = ppc4xx_init("405cr", &clk_setup[PPC405CR_CPU_CLK], &clk_setup[PPC405CR_TMR_CLK], sysclk); + env = &cpu->env; /* Memory mapped devices registers */ /* PLB arbitrer */ ppc4xx_plb_init(env); @@ -2460,13 +2466,15 @@ CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem, { clk_setup_t clk_setup[PPC405EP_CLK_NB], tlb_clk_setup; qemu_irq dma_irqs[4], gpt_irqs[5], mal_irqs[4]; + PowerPCCPU *cpu; CPUPPCState *env; qemu_irq *pic, *irqs; memset(clk_setup, 0, sizeof(clk_setup)); /* init CPUs */ - env = ppc4xx_init("405ep", &clk_setup[PPC405EP_CPU_CLK], + cpu = ppc4xx_init("405ep", &clk_setup[PPC405EP_CPU_CLK], &tlb_clk_setup, sysclk); + env = &cpu->env; clk_setup[PPC405EP_CPU_CLK].cb = tlb_clk_setup.cb; clk_setup[PPC405EP_CPU_CLK].opaque = tlb_clk_setup.opaque; /* Internal devices init */ @@ -2478,7 +2486,7 @@ CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem, /* OBP arbitrer */ ppc4xx_opba_init(0xef600600); /* Initialize timers */ - ppc_booke_timers_init(env, sysclk, 0); + ppc_booke_timers_init(cpu, sysclk, 0); /* Universal interrupt controller */ irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB); irqs[PPCUIC_OUTPUT_INT] = diff --git a/hw/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c similarity index 97% rename from hw/ppc440_bamboo.c rename to hw/ppc/ppc440_bamboo.c index 4f1b734c22..66911b58c6 100644 --- a/hw/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -14,20 +14,20 @@ #include "config.h" #include "qemu-common.h" #include "net/net.h" -#include "hw.h" -#include "pci/pci.h" -#include "boards.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/boards.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "sysemu/device_tree.h" -#include "loader.h" +#include "hw/loader.h" #include "elf.h" #include "exec/address-spaces.h" -#include "serial.h" -#include "ppc.h" -#include "ppc405.h" +#include "hw/serial.h" +#include "hw/ppc.h" +#include "hw/ppc405.h" #include "sysemu/sysemu.h" -#include "sysbus.h" +#include "hw/sysbus.h" #define BINARY_DEVICE_TREE_FILE "bamboo.dtb" @@ -195,7 +195,7 @@ static void bamboo_init(QEMUMachineInitArgs *args) env = &cpu->env; qemu_register_reset(main_cpu_reset, cpu); - ppc_booke_timers_init(env, 400000000, 0); + ppc_booke_timers_init(cpu, 400000000, 0); ppc_dcr_init(env, NULL, NULL); /* interrupt controller */ @@ -295,6 +295,7 @@ static QEMUMachine bamboo_machine = { .name = "bamboo", .desc = "bamboo", .init = bamboo_init, + DEFAULT_MACHINE_OPTIONS, }; static void bamboo_machine_init(void) diff --git a/hw/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c similarity index 98% rename from hw/ppc4xx_devs.c rename to hw/ppc/ppc4xx_devs.c index d1fb1576b0..49ec728a7b 100644 --- a/hw/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -21,9 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "ppc.h" -#include "ppc4xx.h" +#include "hw/hw.h" +#include "hw/ppc.h" +#include "hw/ppc4xx.h" #include "qemu/log.h" #include "exec/address-spaces.h" @@ -47,9 +47,9 @@ static void ppc4xx_reset(void *opaque) /*****************************************************************************/ /* Generic PowerPC 4xx processor instantiation */ -CPUPPCState *ppc4xx_init (const char *cpu_model, - clk_setup_t *cpu_clk, clk_setup_t *tb_clk, - uint32_t sysclk) +PowerPCCPU *ppc4xx_init(const char *cpu_model, + clk_setup_t *cpu_clk, clk_setup_t *tb_clk, + uint32_t sysclk) { PowerPCCPU *cpu; CPUPPCState *env; @@ -72,7 +72,7 @@ CPUPPCState *ppc4xx_init (const char *cpu_model, /* Register qemu callbacks */ qemu_register_reset(ppc4xx_reset, cpu); - return env; + return cpu; } /*****************************************************************************/ @@ -700,7 +700,7 @@ ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, vmstate_register_ram_global(&ram_memories[i]); ram_bases[i] = base; ram_sizes[i] = bank_size; - base += ram_size; + base += bank_size; size_left -= bank_size; break; } diff --git a/hw/ppc_booke.c b/hw/ppc/ppc_booke.c similarity index 85% rename from hw/ppc_booke.c rename to hw/ppc/ppc_booke.c index 5c89fc313d..30375c0c41 100644 --- a/hw/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -21,13 +21,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "ppc.h" +#include "hw/hw.h" +#include "hw/ppc.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" -#include "nvram.h" +#include "hw/nvram.h" #include "qemu/log.h" -#include "loader.h" +#include "hw/loader.h" /* Timer Control Register */ @@ -71,17 +71,19 @@ struct booke_timer_t { uint32_t flags; }; -static void booke_update_irq(CPUPPCState *env) +static void booke_update_irq(PowerPCCPU *cpu) { - ppc_set_irq(env, PPC_INTERRUPT_DECR, + CPUPPCState *env = &cpu->env; + + ppc_set_irq(cpu, PPC_INTERRUPT_DECR, (env->spr[SPR_BOOKE_TSR] & TSR_DIS && env->spr[SPR_BOOKE_TCR] & TCR_DIE)); - ppc_set_irq(env, PPC_INTERRUPT_WDT, + ppc_set_irq(cpu, PPC_INTERRUPT_WDT, (env->spr[SPR_BOOKE_TSR] & TSR_WIS && env->spr[SPR_BOOKE_TCR] & TCR_WIE)); - ppc_set_irq(env, PPC_INTERRUPT_FIT, + ppc_set_irq(cpu, PPC_INTERRUPT_FIT, (env->spr[SPR_BOOKE_TSR] & TSR_FIS && env->spr[SPR_BOOKE_TCR] & TCR_FIE)); } @@ -153,10 +155,11 @@ static void booke_update_fixed_timer(CPUPPCState *env, static void booke_decr_cb(void *opaque) { - CPUPPCState *env = opaque; + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; env->spr[SPR_BOOKE_TSR] |= TSR_DIS; - booke_update_irq(env); + booke_update_irq(cpu); if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) { /* Auto Reload */ @@ -166,16 +169,16 @@ static void booke_decr_cb(void *opaque) static void booke_fit_cb(void *opaque) { - CPUPPCState *env; + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; ppc_tb_t *tb_env; booke_timer_t *booke_timer; - env = opaque; tb_env = env->tb_env; booke_timer = tb_env->opaque; env->spr[SPR_BOOKE_TSR] |= TSR_FIS; - booke_update_irq(env); + booke_update_irq(cpu); booke_update_fixed_timer(env, booke_get_fit_target(env, tb_env), @@ -185,17 +188,17 @@ static void booke_fit_cb(void *opaque) static void booke_wdt_cb(void *opaque) { - CPUPPCState *env; + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; ppc_tb_t *tb_env; booke_timer_t *booke_timer; - env = opaque; tb_env = env->tb_env; booke_timer = tb_env->opaque; /* TODO: There's lots of complicated stuff to do here */ - booke_update_irq(env); + booke_update_irq(cpu); booke_update_fixed_timer(env, booke_get_wdt_target(env, tb_env), @@ -205,19 +208,22 @@ static void booke_wdt_cb(void *opaque) void store_booke_tsr(CPUPPCState *env, target_ulong val) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); + env->spr[SPR_BOOKE_TSR] &= ~val; - booke_update_irq(env); + booke_update_irq(cpu); } void store_booke_tcr(CPUPPCState *env, target_ulong val) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); ppc_tb_t *tb_env = env->tb_env; booke_timer_t *booke_timer = tb_env->opaque; tb_env = env->tb_env; env->spr[SPR_BOOKE_TCR] = val; - booke_update_irq(env); + booke_update_irq(cpu); booke_update_fixed_timer(env, booke_get_fit_target(env, tb_env), @@ -231,7 +237,18 @@ void store_booke_tcr(CPUPPCState *env, target_ulong val) } -void ppc_booke_timers_init(CPUPPCState *env, uint32_t freq, uint32_t flags) +static void ppc_booke_timer_reset_handle(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + + env->spr[SPR_BOOKE_TSR] = 0; + env->spr[SPR_BOOKE_TCR] = 0; + + booke_update_irq(cpu); +} + +void ppc_booke_timers_init(PowerPCCPU *cpu, uint32_t freq, uint32_t flags) { ppc_tb_t *tb_env; booke_timer_t *booke_timer; @@ -239,16 +256,18 @@ void ppc_booke_timers_init(CPUPPCState *env, uint32_t freq, uint32_t flags) tb_env = g_malloc0(sizeof(ppc_tb_t)); booke_timer = g_malloc0(sizeof(booke_timer_t)); - env->tb_env = tb_env; + cpu->env.tb_env = tb_env; tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED; tb_env->tb_freq = freq; tb_env->decr_freq = freq; tb_env->opaque = booke_timer; - tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &booke_decr_cb, env); + tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &booke_decr_cb, cpu); booke_timer->fit_timer = - qemu_new_timer_ns(vm_clock, &booke_fit_cb, env); + qemu_new_timer_ns(vm_clock, &booke_fit_cb, cpu); booke_timer->wdt_timer = - qemu_new_timer_ns(vm_clock, &booke_wdt_cb, env); + qemu_new_timer_ns(vm_clock, &booke_wdt_cb, cpu); + + qemu_register_reset(ppc_booke_timer_reset_handle, cpu); } diff --git a/hw/ppce500_spin.c b/hw/ppc/ppce500_spin.c similarity index 92% rename from hw/ppce500_spin.c rename to hw/ppc/ppce500_spin.c index 177aa2d122..1290d37bb9 100644 --- a/hw/ppce500_spin.c +++ b/hw/ppc/ppce500_spin.c @@ -27,9 +27,9 @@ * */ -#include "hw.h" +#include "hw/hw.h" #include "sysemu/sysemu.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "sysemu/kvm.h" #define MAX_CPUS 32 @@ -112,7 +112,7 @@ static void spin_kick(void *data) map_start = ldq_p(&curspin->addr) & ~(map_size - 1); mmubooke_create_initial_mapping(env, 0, map_start, map_size); - env->halted = 0; + cpu->halted = 0; env->exception_index = -1; cpu->stopped = false; qemu_cpu_kick(cpu); @@ -123,22 +123,17 @@ static void spin_write(void *opaque, hwaddr addr, uint64_t value, { SpinState *s = opaque; int env_idx = addr / sizeof(SpinInfo); - CPUPPCState *env; + CPUState *cpu; SpinInfo *curspin = &s->spin[env_idx]; uint8_t *curspin_p = (uint8_t*)curspin; - for (env = first_cpu; env != NULL; env = env->next_cpu) { - if (env->cpu_index == env_idx) { - break; - } - } - - if (!env) { + cpu = qemu_get_cpu(env_idx); + if (cpu == NULL) { /* Unknown CPU */ return; } - if (!env->cpu_index) { + if (cpu->cpu_index == 0) { /* primary CPU doesn't spin */ return; } @@ -159,11 +154,11 @@ static void spin_write(void *opaque, hwaddr addr, uint64_t value, if (!(ldq_p(&curspin->addr) & 1)) { /* run CPU */ SpinKick kick = { - .cpu = ppc_env_get_cpu(env), + .cpu = POWERPC_CPU(cpu), .spin = curspin, }; - run_on_cpu(CPU(kick.cpu), spin_kick, &kick); + run_on_cpu(cpu, spin_kick, &kick); } } @@ -194,7 +189,7 @@ static int ppce500_spin_initfn(SysBusDevice *dev) { SpinState *s; - s = FROM_SYSBUS(SpinState, sysbus_from_qdev(dev)); + s = FROM_SYSBUS(SpinState, SYS_BUS_DEVICE(dev)); memory_region_init_io(&s->iomem, &spin_rw_ops, s, "e500 spin pv device", sizeof(SpinInfo) * MAX_CPUS); @@ -212,7 +207,7 @@ static void ppce500_spin_class_init(ObjectClass *klass, void *data) k->init = ppce500_spin_initfn; } -static TypeInfo ppce500_spin_info = { +static const TypeInfo ppce500_spin_info = { .name = "e500-spin", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SpinState), diff --git a/hw/ppc_prep.c b/hw/ppc/prep.c similarity index 93% rename from hw/ppc_prep.c rename to hw/ppc/prep.c index 9c78c863e8..292091180d 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc/prep.c @@ -21,22 +21,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "nvram.h" -#include "pc.h" -#include "serial.h" -#include "fdc.h" +#include "hw/hw.h" +#include "hw/nvram.h" +#include "hw/pc.h" +#include "hw/serial.h" +#include "hw/fdc.h" #include "net/net.h" #include "sysemu/sysemu.h" -#include "isa.h" -#include "pci/pci.h" -#include "pci/pci_host.h" -#include "ppc.h" -#include "boards.h" +#include "hw/isa.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "hw/ppc.h" +#include "hw/boards.h" #include "qemu/log.h" -#include "ide.h" -#include "loader.h" -#include "mc146818rtc.h" +#include "hw/ide.h" +#include "hw/loader.h" +#include "hw/mc146818rtc.h" +#include "hw/pc87312.h" #include "sysemu/blockdev.h" #include "sysemu/arch_init.h" #include "exec/address-spaces.h" @@ -181,7 +182,6 @@ typedef struct sysctrl_t { M48t59State *nvram; uint8_t state; uint8_t syscontrol; - uint8_t fake_io[2]; int contiguous_map; int endian; } sysctrl_t; @@ -192,24 +192,6 @@ enum { static sysctrl_t *sysctrl; -static void PREP_io_write (void *opaque, uint32_t addr, uint32_t val) -{ - sysctrl_t *sysctrl = opaque; - - PPC_IO_DPRINTF("0x%08" PRIx32 " => 0x%02" PRIx32 "\n", addr - PPC_IO_BASE, - val); - sysctrl->fake_io[addr - 0x0398] = val; -} - -static uint32_t PREP_io_read (void *opaque, uint32_t addr) -{ - sysctrl_t *sysctrl = opaque; - - PPC_IO_DPRINTF("0x%08" PRIx32 " <= 0x%02" PRIx32 "\n", addr - PPC_IO_BASE, - sysctrl->fake_io[addr - 0x0398]); - return sysctrl->fake_io[addr - 0x0398]; -} - static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) { sysctrl_t *sysctrl = opaque; @@ -476,10 +458,10 @@ static void ppc_prep_init(QEMUMachineInitArgs *args) PCIBus *pci_bus; PCIDevice *pci; ISABus *isa_bus; + ISADevice *isa; qemu_irq *cpu_exit_irq; int ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - DriveInfo *fd[MAX_FD]; sysctrl = g_malloc0(sizeof(sysctrl_t)); @@ -585,7 +567,6 @@ static void ppc_prep_init(QEMUMachineInitArgs *args) dev = qdev_create(NULL, "raven-pcihost"); pcihost = PCI_HOST_BRIDGE(dev); - pcihost->address_space = get_system_memory(); object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL); qdev_init_nofail(dev); pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); @@ -606,6 +587,11 @@ static void ppc_prep_init(QEMUMachineInitArgs *args) sysbus_connect_irq(&pcihost->busdev, 3, qdev_get_gpio_in(&pci->qdev, 11)); isa_bus = DO_UPCAST(ISABus, qbus, qdev_get_child_bus(&pci->qdev, "isa.0")); + /* Super I/O (parallel + serial ports) */ + isa = isa_create(isa_bus, TYPE_PC87312); + qdev_prop_set_uint8(&isa->qdev, "config", 13); /* fdc, ser0, ser1, par0 */ + qdev_init_nofail(&isa->qdev); + /* Register 8 MB of ISA IO space (needed for non-contiguous map) */ memory_region_init_io(PPC_io_memory, &PPC_prep_io_ops, sysctrl, "ppc-io", 0x00800000); @@ -614,8 +600,6 @@ static void ppc_prep_init(QEMUMachineInitArgs *args) /* init basic PC hardware */ pci_vga_init(pci_bus); - if (serial_hds[0]) - serial_isa_init(isa_bus, 0, serial_hds[0]); nb_nics1 = nb_nics; if (nb_nics1 > NE2000_NB_MAX) nb_nics1 = NE2000_NB_MAX; @@ -639,17 +623,7 @@ static void ppc_prep_init(QEMUMachineInitArgs *args) } isa_create_simple(isa_bus, "i8042"); - // SB16_init(); - - for(i = 0; i < MAX_FD; i++) { - fd[i] = drive_get(IF_FLOPPY, 0, i); - } - fdctrl_init_isa(isa_bus, fd); - - /* Register fake IO ports for PREP */ sysctrl->reset_irq = first_cpu->irq_inputs[PPC6xx_INPUT_HRESET]; - register_ioport_read(0x398, 2, 1, &PREP_io_read, sysctrl); - register_ioport_write(0x398, 2, 1, &PREP_io_write, sysctrl); /* System control ports */ register_ioport_read(0x0092, 0x01, 1, &PREP_io_800_readb, sysctrl); register_ioport_write(0x0092, 0x01, 1, &PREP_io_800_writeb, sysctrl); @@ -694,6 +668,7 @@ static QEMUMachine prep_machine = { .desc = "PowerPC PREP platform", .init = ppc_prep_init, .max_cpus = MAX_CPUS, + DEFAULT_MACHINE_OPTIONS, }; static void prep_machine_init(void) diff --git a/hw/spapr.c b/hw/ppc/spapr.c similarity index 96% rename from hw/spapr.c rename to hw/ppc/spapr.c index fdd1eb6925..f355a9bb84 100644 --- a/hw/spapr.c +++ b/hw/ppc/spapr.c @@ -25,7 +25,7 @@ * */ #include "sysemu/sysemu.h" -#include "hw.h" +#include "hw/hw.h" #include "elf.h" #include "net/net.h" #include "sysemu/blockdev.h" @@ -45,7 +45,7 @@ #include "sysemu/kvm.h" #include "kvm_ppc.h" -#include "pci/pci.h" +#include "hw/pci/pci.h" #include "exec/address-spaces.h" #include "hw/usb.h" @@ -77,12 +77,6 @@ #define MAX_CPUS 256 #define XICS_IRQS 1024 -#define SPAPR_PCI_BUID 0x800000020000001ULL -#define SPAPR_PCI_MEM_WIN_ADDR (0x10000000000ULL + 0xA0000000) -#define SPAPR_PCI_MEM_WIN_SIZE 0x20000000 -#define SPAPR_PCI_IO_WIN_ADDR (0x10000000000ULL + 0x80000000) -#define SPAPR_PCI_MSI_WIN_ADDR (0x10000000000ULL + 0x90000000) - #define PHANDLE_XICP 0x00001111 #define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift)) @@ -140,6 +134,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr) { int ret = 0, offset; CPUPPCState *env; + CPUState *cpu; char cpu_model[32]; int smt = kvmppc_smt_threads(); uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; @@ -147,19 +142,20 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr) assert(spapr->cpu_model); for (env = first_cpu; env != NULL; env = env->next_cpu) { + cpu = CPU(ppc_env_get_cpu(env)); uint32_t associativity[] = {cpu_to_be32(0x5), cpu_to_be32(0x0), cpu_to_be32(0x0), cpu_to_be32(0x0), - cpu_to_be32(env->numa_node), - cpu_to_be32(env->cpu_index)}; + cpu_to_be32(cpu->numa_node), + cpu_to_be32(cpu->cpu_index)}; - if ((env->cpu_index % smt) != 0) { + if ((cpu->cpu_index % smt) != 0) { continue; } snprintf(cpu_model, 32, "/cpus/%s@%x", spapr->cpu_model, - env->cpu_index); + cpu->cpu_index); offset = fdt_path_offset(fdt, cpu_model); if (offset < 0) { @@ -264,6 +260,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_begin_node(fdt, ""))); _FDT((fdt_property_string(fdt, "device_type", "chrp"))); _FDT((fdt_property_string(fdt, "model", "IBM pSeries (emulated by qemu)"))); + _FDT((fdt_property_string(fdt, "compatible", "qemu,pseries"))); _FDT((fdt_property_cell(fdt, "#address-cells", 0x2))); _FDT((fdt_property_cell(fdt, "#size-cells", 0x2))); @@ -285,7 +282,9 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property(fdt, "qemu,boot-kernel", &kprop, sizeof(kprop)))); } - _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device))); + if (boot_device) { + _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device))); + } _FDT((fdt_property_cell(fdt, "qemu,graphic-width", graphic_width))); _FDT((fdt_property_cell(fdt, "qemu,graphic-height", graphic_height))); _FDT((fdt_property_cell(fdt, "qemu,graphic-depth", graphic_depth))); @@ -308,7 +307,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model, spapr->cpu_model = g_strdup(modelname); for (env = first_cpu; env != NULL; env = env->next_cpu) { - int index = env->cpu_index; + CPUState *cpu = CPU(ppc_env_get_cpu(env)); + int index = cpu->cpu_index; uint32_t servers_prop[smp_threads]; uint32_t gservers_prop[smp_threads * 2]; char *nodename; @@ -323,14 +323,11 @@ static void *spapr_create_fdt_skel(const char *cpu_model, continue; } - if (asprintf(&nodename, "%s@%x", modelname, index) < 0) { - fprintf(stderr, "Allocation failure\n"); - exit(1); - } + nodename = g_strdup_printf("%s@%x", modelname, index); _FDT((fdt_begin_node(fdt, nodename))); - free(nodename); + g_free(nodename); _FDT((fdt_property_cell(fdt, "reg", index))); _FDT((fdt_property_string(fdt, "device_type", "cpu"))); @@ -620,6 +617,8 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) static void ppc_spapr_reset(void) { + CPUState *first_cpu_cpu; + /* Reset the hash table & recalc the RMA */ spapr_reset_htab(spapr); @@ -630,9 +629,10 @@ static void ppc_spapr_reset(void) spapr->rtas_size); /* Set up the entry state */ + first_cpu_cpu = CPU(first_cpu); first_cpu->gpr[3] = spapr->fdt_addr; first_cpu->gpr[5] = 0; - first_cpu->halted = 0; + first_cpu_cpu->halted = 0; first_cpu->nip = spapr->entry_point; } @@ -640,14 +640,15 @@ static void ppc_spapr_reset(void) static void spapr_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; + CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - cpu_reset(CPU(cpu)); + cpu_reset(cs); /* All CPUs start halted. CPU0 is unhalted from the machine level * reset code and the rest are explicitly started up by the guest * using an RTAS call */ - env->halted = 1; + cs->halted = 1; env->spr[SPR_HIOR] = 0; @@ -798,7 +799,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) /* Tell KVM that we're in PAPR mode */ if (kvm_enabled()) { - kvmppc_set_papr(env); + kvmppc_set_papr(cpu); } qemu_register_reset(spapr_cpu_reset, cpu); @@ -855,12 +856,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) /* Set up PCI */ spapr_pci_rtas_init(); - spapr_create_phb(spapr, "pci", SPAPR_PCI_BUID, - SPAPR_PCI_MEM_WIN_ADDR, - SPAPR_PCI_MEM_WIN_SIZE, - SPAPR_PCI_IO_WIN_ADDR, - SPAPR_PCI_MSI_WIN_ADDR); - phb = PCI_HOST_BRIDGE(QLIST_FIRST(&spapr->phbs)); + phb = spapr_create_phb(spapr, 0, "pci"); for (i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; @@ -961,6 +957,7 @@ static QEMUMachine spapr_machine = { .block_default_type = IF_SCSI, .max_cpus = MAX_CPUS, .no_parallel = 1, + .boot_order = NULL, }; static void spapr_machine_init(void) diff --git a/hw/spapr_events.c b/hw/ppc/spapr_events.c similarity index 100% rename from hw/spapr_events.c rename to hw/ppc/spapr_events.c diff --git a/hw/spapr_hcall.c b/hw/ppc/spapr_hcall.c similarity index 95% rename from hw/spapr_hcall.c rename to hw/ppc/spapr_hcall.c index afb12973f2..dd72743b52 100644 --- a/hw/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -323,6 +323,36 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, return H_SUCCESS; } +static target_ulong h_read(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUPPCState *env = &cpu->env; + target_ulong flags = args[0]; + target_ulong pte_index = args[1]; + uint8_t *hpte; + int i, ridx, n_entries = 1; + + if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + return H_PARAMETER; + } + + if (flags & H_READ_4) { + /* Clear the two low order bits */ + pte_index &= ~(3ULL); + n_entries = 4; + } + + hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); + + for (i = 0, ridx = 0; i < n_entries; i++) { + args[ridx++] = ldq_p(hpte); + args[ridx++] = ldq_p(hpte + (HASH_PTE_SIZE_64/2)); + hpte += HASH_PTE_SIZE_64; + } + + return H_SUCCESS; +} + static target_ulong h_set_dabr(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { @@ -467,16 +497,13 @@ static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong vpa = args[2]; target_ulong ret = H_PARAMETER; CPUPPCState *tenv; + CPUState *tcpu; - for (tenv = first_cpu; tenv; tenv = tenv->next_cpu) { - if (tenv->cpu_index == procno) { - break; - } - } - - if (!tenv) { + tcpu = qemu_get_cpu(procno); + if (!tcpu) { return H_PARAMETER; } + tenv = tcpu->env_ptr; switch (flags) { case FLAGS_REGISTER_VPA: @@ -511,13 +538,14 @@ static target_ulong h_cede(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); env->msr |= (1ULL << MSR_EE); hreg_compute_hflags(env); - if (!cpu_has_work(CPU(cpu))) { - env->halted = 1; + if (!cpu_has_work(cs)) { + cs->halted = 1; env->exception_index = EXCP_HLT; - env->exit_request = 1; + cs->exit_request = 1; } return H_SUCCESS; } @@ -712,6 +740,7 @@ static void hypercall_register_types(void) spapr_register_hypercall(H_ENTER, h_enter); spapr_register_hypercall(H_REMOVE, h_remove); spapr_register_hypercall(H_PROTECT, h_protect); + spapr_register_hypercall(H_READ, h_read); /* hcall-bulk */ spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove); diff --git a/hw/spapr_iommu.c b/hw/ppc/spapr_iommu.c similarity index 99% rename from hw/spapr_iommu.c rename to hw/ppc/spapr_iommu.c index d8a098cb1b..8d500bf6be 100644 --- a/hw/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -16,9 +16,9 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "sysemu/kvm.h" -#include "qdev.h" +#include "hw/qdev.h" #include "kvm_ppc.h" #include "sysemu/dma.h" #include "exec/address-spaces.h" diff --git a/hw/spapr_rtas.c b/hw/ppc/spapr_rtas.c similarity index 97% rename from hw/spapr_rtas.c rename to hw/ppc/spapr_rtas.c index 81eecd0940..a24e853d4d 100644 --- a/hw/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -131,6 +131,7 @@ static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr, { target_ulong id; CPUPPCState *env; + CPUState *cpu; if (nargs != 1 || nret != 2) { rtas_st(rets, 0, -3); @@ -139,11 +140,12 @@ static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr, id = rtas_ld(args, 0); for (env = first_cpu; env; env = env->next_cpu) { - if (env->cpu_index != id) { + cpu = CPU(ppc_env_get_cpu(env)); + if (cpu->cpu_index != id) { continue; } - if (env->halted) { + if (cpu->halted) { rtas_st(rets, 1, 0); } else { rtas_st(rets, 1, 2); @@ -176,13 +178,13 @@ static void rtas_start_cpu(sPAPREnvironment *spapr, r3 = rtas_ld(args, 2); for (env = first_cpu; env; env = env->next_cpu) { - cpu = ENV_GET_CPU(env); + cpu = CPU(ppc_env_get_cpu(env)); - if (env->cpu_index != id) { + if (cpu->cpu_index != id) { continue; } - if (!env->halted) { + if (!cpu->halted) { rtas_st(rets, 0, -1); return; } @@ -195,7 +197,7 @@ static void rtas_start_cpu(sPAPREnvironment *spapr, env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME); env->nip = start; env->gpr[3] = r3; - env->halted = 0; + cpu->halted = 0; qemu_cpu_kick(cpu); diff --git a/hw/spapr_vio.c b/hw/ppc/spapr_vio.c similarity index 96% rename from hw/spapr_vio.c rename to hw/ppc/spapr_vio.c index a58621d17e..6eb3ab5482 100644 --- a/hw/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -19,11 +19,11 @@ * License along with this library; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "sysemu/sysemu.h" -#include "boards.h" +#include "hw/boards.h" #include "monitor/monitor.h" -#include "loader.h" +#include "hw/loader.h" #include "elf.h" #include "hw/sysbus.h" #include "sysemu/kvm.h" @@ -80,9 +80,7 @@ static char *vio_format_dev_name(VIOsPAPRDevice *dev) char *name; /* Device tree style name device@reg */ - if (asprintf(&name, "%s@%x", pc->dt_name, dev->reg) < 0) { - return NULL; - } + name = g_strdup_printf("%s@%x", pc->dt_name, dev->reg); return name; } @@ -101,12 +99,8 @@ static int vio_make_devnode(VIOsPAPRDevice *dev, } dt_name = vio_format_dev_name(dev); - if (!dt_name) { - return -ENOMEM; - } - node_off = fdt_add_subnode(fdt, vdevice_off, dt_name); - free(dt_name); + g_free(dt_name); if (node_off < 0) { return node_off; } @@ -444,9 +438,6 @@ static int spapr_vio_busdev_init(DeviceState *qdev) /* Don't overwrite ids assigned on the command line */ if (!dev->qdev.id) { id = vio_format_dev_name(dev); - if (!id) { - return -1; - } dev->qdev.id = id; } @@ -501,7 +492,7 @@ VIOsPAPRBus *spapr_vio_bus_init(void) qbus = qbus_create(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio"); bus = DO_UPCAST(VIOsPAPRBus, bus, qbus); - bus->next_reg = 0x1000; + bus->next_reg = 0x71000000; /* hcall-vio */ spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal); @@ -536,7 +527,7 @@ static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo spapr_vio_bridge_info = { +static const TypeInfo spapr_vio_bridge_info = { .name = "spapr-vio-bridge", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysBusDevice), @@ -552,7 +543,7 @@ static void vio_spapr_device_class_init(ObjectClass *klass, void *data) k->props = spapr_vio_props; } -static TypeInfo spapr_vio_type_info = { +static const TypeInfo spapr_vio_type_info = { .name = TYPE_VIO_SPAPR_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(VIOsPAPRDevice), @@ -646,20 +637,12 @@ int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus) } name = vio_format_dev_name(dev); - if (!name) { - return -ENOMEM; - } - - if (asprintf(&path, "/vdevice/%s", name) < 0) { - path = NULL; - ret = -ENOMEM; - goto out; - } + path = g_strdup_printf("/vdevice/%s", name); ret = fdt_setprop_string(fdt, offset, "linux,stdout-path", path); -out: - free(name); - free(path); + + g_free(name); + g_free(path); return ret; } diff --git a/hw/virtex_ml507.c b/hw/ppc/virtex_ml507.c similarity index 96% rename from hw/virtex_ml507.c rename to hw/ppc/virtex_ml507.c index 5238c7cbd6..41eab1697c 100644 --- a/hw/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -22,25 +22,25 @@ * THE SOFTWARE. */ -#include "sysbus.h" -#include "hw.h" -#include "serial.h" -#include "flash.h" +#include "hw/sysbus.h" +#include "hw/hw.h" +#include "hw/serial.h" +#include "hw/flash.h" #include "sysemu/sysemu.h" -#include "devices.h" -#include "boards.h" +#include "hw/devices.h" +#include "hw/boards.h" #include "sysemu/device_tree.h" -#include "loader.h" +#include "hw/loader.h" #include "elf.h" #include "qemu/log.h" #include "exec/address-spaces.h" -#include "ppc.h" -#include "ppc4xx.h" -#include "ppc405.h" +#include "hw/ppc.h" +#include "hw/ppc4xx.h" +#include "hw/ppc405.h" #include "sysemu/blockdev.h" -#include "xilinx.h" +#include "hw/xilinx.h" #define EPAPR_MAGIC (0x45504150) #define FLASH_SIZE (16 * 1024 * 1024) @@ -93,7 +93,7 @@ static PowerPCCPU *ppc440_init_xilinx(ram_addr_t *ram_size, } env = &cpu->env; - ppc_booke_timers_init(env, sysclk, 0/* no flags */); + ppc_booke_timers_init(cpu, sysclk, 0/* no flags */); ppc_dcr_init(env, NULL, NULL); @@ -263,6 +263,7 @@ static QEMUMachine virtex_machine = { .name = "virtex-ml507", .desc = "Xilinx Virtex ML507 reference design", .init = virtex_init, + DEFAULT_MACHINE_OPTIONS, }; static void virtex_machine_init(void) diff --git a/hw/xics.c b/hw/ppc/xics.c similarity index 96% rename from hw/xics.c rename to hw/ppc/xics.c index 55899ce77d..c3ef12fff4 100644 --- a/hw/xics.c +++ b/hw/ppc/xics.c @@ -25,7 +25,7 @@ * */ -#include "hw.h" +#include "hw/hw.h" #include "trace.h" #include "hw/spapr.h" #include "hw/xics.h" @@ -357,10 +357,10 @@ void xics_set_irq_type(struct icp_state *icp, int irq, bool lsi) static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { - CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); target_ulong cppr = args[0]; - icp_set_cppr(spapr->icp, env->cpu_index, cppr); + icp_set_cppr(spapr->icp, cs->cpu_index, cppr); return H_SUCCESS; } @@ -376,14 +376,13 @@ static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr, icp_set_mfrr(spapr->icp, server, mfrr); return H_SUCCESS; - } static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { - CPUPPCState *env = &cpu->env; - uint32_t xirr = icp_accept(spapr->icp->ss + env->cpu_index); + CPUState *cs = CPU(cpu); + uint32_t xirr = icp_accept(spapr->icp->ss + cs->cpu_index); args[0] = xirr; return H_SUCCESS; @@ -392,10 +391,10 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr, static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { - CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); target_ulong xirr = args[0]; - icp_eoi(spapr->icp, env->cpu_index, xirr); + icp_eoi(spapr->icp, cs->cpu_index, xirr); return H_SUCCESS; } @@ -525,14 +524,16 @@ static void xics_reset(void *opaque) struct icp_state *xics_system_init(int nr_irqs) { CPUPPCState *env; + CPUState *cpu; int max_server_num; struct icp_state *icp; struct ics_state *ics; max_server_num = -1; for (env = first_cpu; env != NULL; env = env->next_cpu) { - if (env->cpu_index > max_server_num) { - max_server_num = env->cpu_index; + cpu = CPU(ppc_env_get_cpu(env)); + if (cpu->cpu_index > max_server_num) { + max_server_num = cpu->cpu_index; } } @@ -541,7 +542,8 @@ struct icp_state *xics_system_init(int nr_irqs) icp->ss = g_malloc0(icp->nr_servers*sizeof(struct icp_server_state)); for (env = first_cpu; env != NULL; env = env->next_cpu) { - struct icp_server_state *ss = &icp->ss[env->cpu_index]; + cpu = CPU(ppc_env_get_cpu(env)); + struct icp_server_state *ss = &icp->ss[cpu->cpu_index]; switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_POWER7: diff --git a/hw/ppc405.h b/hw/ppc405.h index 535cbfb339..45c2159aa6 100644 --- a/hw/ppc405.h +++ b/hw/ppc405.h @@ -25,7 +25,7 @@ #if !defined(PPC_405_H) #define PPC_405_H -#include "ppc4xx.h" +#include "hw/ppc4xx.h" /* Bootinfo as set-up by u-boot */ typedef struct ppc4xx_bd_info_t ppc4xx_bd_info_t; diff --git a/hw/ppc4xx.h b/hw/ppc4xx.h index 2b96d47f36..91d84bad63 100644 --- a/hw/ppc4xx.h +++ b/hw/ppc4xx.h @@ -25,12 +25,12 @@ #if !defined(PPC_4XX_H) #define PPC_4XX_H -#include "pci/pci.h" +#include "hw/pci/pci.h" /* PowerPC 4xx core initialization */ -CPUPPCState *ppc4xx_init (const char *cpu_model, - clk_setup_t *cpu_clk, clk_setup_t *tb_clk, - uint32_t sysclk); +PowerPCCPU *ppc4xx_init(const char *cpu_model, + clk_setup_t *cpu_clk, clk_setup_t *tb_clk, + uint32_t sysclk); /* PowerPC 4xx universal interrupt controller */ enum { diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c index ba2d669b83..f3bbe88529 100644 --- a/hw/ppc4xx_pci.c +++ b/hw/ppc4xx_pci.c @@ -19,11 +19,11 @@ /* This file implements emulation of the 32-bit PCI controller found in some * 4xx SoCs, such as the 440EP. */ -#include "hw.h" -#include "ppc.h" -#include "ppc4xx.h" -#include "pci/pci.h" -#include "pci/pci_host.h" +#include "hw/hw.h" +#include "hw/ppc.h" +#include "hw/ppc4xx.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" #include "exec/address-spaces.h" #undef DEBUG diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h deleted file mode 100644 index 89c7d66386..0000000000 --- a/hw/ppc_mac.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * QEMU PowerMac emulation shared definitions and prototypes - * - * Copyright (c) 2004-2007 Fabrice Bellard - * Copyright (c) 2007 Jocelyn Mayer - * - * 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. - */ -#if !defined(__PPC_MAC_H__) -#define __PPC_MAC_H__ - -#include "exec/memory.h" - -/* SMP is not enabled, for now */ -#define MAX_CPUS 1 - -#define BIOS_SIZE (1024 * 1024) -#define BIOS_FILENAME "ppc_rom.bin" -#define NVRAM_SIZE 0x2000 -#define PROM_FILENAME "openbios-ppc" -#define PROM_ADDR 0xfff00000 - -#define KERNEL_LOAD_ADDR 0x01000000 -#define KERNEL_GAP 0x00100000 - -#define ESCC_CLOCK 3686400 - -/* Cuda */ -void cuda_init (MemoryRegion **cuda_mem, qemu_irq irq); - -/* MacIO */ -void macio_init (PCIBus *bus, int device_id, int is_oldworld, - MemoryRegion *pic_mem, MemoryRegion *dbdma_mem, - MemoryRegion *cuda_mem, void *nvram, - int nb_ide, MemoryRegion **ide_mem, MemoryRegion *escc_mem); - -/* Heathrow PIC */ -qemu_irq *heathrow_pic_init(MemoryRegion **pmem, - int nb_cpus, qemu_irq **irqs); - -/* Grackle PCI */ -#define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" -PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); - -/* UniNorth PCI */ -PCIBus *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); -PCIBus *pci_pmac_u3_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); - -/* Mac NVRAM */ -typedef struct MacIONVRAMState MacIONVRAMState; - -MacIONVRAMState *macio_nvram_init (hwaddr size, - unsigned int it_shift); -void macio_nvram_setup_bar(MacIONVRAMState *s, MemoryRegion *bar, - hwaddr mem_base); -void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len); -uint32_t macio_nvram_read (void *opaque, uint32_t addr); -void macio_nvram_write (void *opaque, uint32_t addr, uint32_t val); -#endif /* !defined(__PPC_MAC_H__) */ diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c index 1e1ade3d2e..310ae1c03d 100644 --- a/hw/ppce500_pci.c +++ b/hw/ppce500_pci.c @@ -14,12 +14,12 @@ * (at your option) any later version. */ -#include "hw.h" +#include "hw/hw.h" #include "hw/ppc/e500-ccsr.h" -#include "pci/pci.h" -#include "pci/pci_host.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" #include "qemu/bswap.h" -#include "ppce500_pci.h" +#include "hw/ppce500_pci.h" #ifdef DEBUG_PCI #define pci_debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) diff --git a/hw/prep_pci.c b/hw/prep_pci.c index 212a2ac4f1..d21e87671e 100644 --- a/hw/prep_pci.c +++ b/hw/prep_pci.c @@ -2,6 +2,7 @@ * QEMU PREP PCI host * * Copyright (c) 2006 Fabrice Bellard + * Copyright (c) 2011-2013 Andreas Färber * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,14 +23,23 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "pci/pci.h" -#include "pci/pci_host.h" -#include "pc.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci_host.h" +#include "hw/pc.h" #include "exec/address-spaces.h" +#define TYPE_RAVEN_PCI_DEVICE "raven" #define TYPE_RAVEN_PCI_HOST_BRIDGE "raven-pcihost" +#define RAVEN_PCI_DEVICE(obj) \ + OBJECT_CHECK(RavenPCIState, (obj), TYPE_RAVEN_PCI_DEVICE) + +typedef struct RavenPCIState { + PCIDevice dev; +} RavenPCIState; + #define RAVEN_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(PREPPCIState, (obj), TYPE_RAVEN_PCI_HOST_BRIDGE) @@ -38,12 +48,10 @@ typedef struct PRePPCIState { MemoryRegion intack; qemu_irq irq[4]; + PCIBus pci_bus; + RavenPCIState pci_dev; } PREPPCIState; -typedef struct RavenPCIState { - PCIDevice dev; -} RavenPCIState; - static inline uint32_t PPC_PCIIO_config(hwaddr addr) { int i; @@ -103,23 +111,19 @@ static void prep_set_irq(void *opaque, int irq_num, int level) qemu_set_irq(pic[irq_num] , level); } -static int raven_pcihost_init(SysBusDevice *dev) +static void raven_pcihost_realizefn(DeviceState *d, Error **errp) { + SysBusDevice *dev = SYS_BUS_DEVICE(d); PCIHostState *h = PCI_HOST_BRIDGE(dev); PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(dev); MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *address_space_io = get_system_io(); - PCIBus *bus; int i; for (i = 0; i < 4; i++) { sysbus_init_irq(dev, &s->irq[i]); } - bus = pci_register_bus(DEVICE(dev), NULL, - prep_set_irq, prep_map_irq, s->irq, - address_space_mem, address_space_io, 0, 4); - h->bus = bus; + pci_bus_irqs(&s->pci_bus, prep_set_irq, prep_map_irq, s->irq, 4); memory_region_init_io(&h->conf_mem, &pci_host_conf_be_ops, s, "pci-conf-idx", 1); @@ -136,9 +140,29 @@ static int raven_pcihost_init(SysBusDevice *dev) memory_region_init_io(&s->intack, &PPC_intack_ops, s, "pci-intack", 1); memory_region_add_subregion(address_space_mem, 0xbffffff0, &s->intack); - pci_create_simple(bus, 0, "raven"); - return 0; + /* TODO Remove once realize propagates to child devices. */ + object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp); +} + +static void raven_pcihost_initfn(Object *obj) +{ + PCIHostState *h = PCI_HOST_BRIDGE(obj); + PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(obj); + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *address_space_io = get_system_io(); + DeviceState *pci_dev; + + pci_bus_new_inplace(&s->pci_bus, DEVICE(obj), NULL, + address_space_mem, address_space_io, 0); + h->bus = &s->pci_bus; + + object_initialize(&s->pci_dev, TYPE_RAVEN_PCI_DEVICE); + pci_dev = DEVICE(&s->pci_dev); + qdev_set_parent_bus(pci_dev, BUS(&s->pci_bus)); + object_property_set_int(OBJECT(&s->pci_dev), PCI_DEVFN(0, 0), "addr", + NULL); + qdev_prop_set_bit(pci_dev, "multifunction", false); } static int raven_init(PCIDevice *d) @@ -176,7 +200,7 @@ static void raven_class_init(ObjectClass *klass, void *data) } static const TypeInfo raven_info = { - .name = "raven", + .name = TYPE_RAVEN_PCI_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(RavenPCIState), .class_init = raven_class_init, @@ -184,10 +208,9 @@ static const TypeInfo raven_info = { static void raven_pcihost_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = raven_pcihost_init; + dc->realize = raven_pcihost_realizefn; dc->fw_name = "pci"; dc->no_user = 1; } @@ -196,6 +219,7 @@ static const TypeInfo raven_pcihost_info = { .name = TYPE_RAVEN_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(PREPPCIState), + .instance_init = raven_pcihost_initfn, .class_init = raven_pcihost_class_init, }; diff --git a/hw/ps2.c b/hw/ps2.c index 15cfd5bb76..233a087a5e 100644 --- a/hw/ps2.c +++ b/hw/ps2.c @@ -21,8 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "ps2.h" +#include "hw/hw.h" +#include "hw/ps2.h" #include "ui/console.h" #include "sysemu/sysemu.h" diff --git a/hw/ptimer.c b/hw/ptimer.c index 24af6a2afe..4bc96c9fa2 100644 --- a/hw/ptimer.c +++ b/hw/ptimer.c @@ -5,9 +5,9 @@ * * This code is licensed under the GNU LGPL. */ -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" -#include "ptimer.h" +#include "hw/ptimer.h" #include "qemu/host-utils.h" struct ptimer_state diff --git a/hw/puv3_dma.c b/hw/puv3_dma.c index 9de63b4c34..c05a14ea16 100644 --- a/hw/puv3_dma.c +++ b/hw/puv3_dma.c @@ -8,11 +8,11 @@ * published by the Free Software Foundation, or any later version. * See the COPYING file in the top-level directory. */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #undef DEBUG_PUV3 -#include "puv3.h" +#include "hw/puv3.h" #define PUV3_DMA_CH_NR (6) #define PUV3_DMA_CH_MASK (0xff) diff --git a/hw/puv3_gpio.c b/hw/puv3_gpio.c index 152248d291..b2a790b683 100644 --- a/hw/puv3_gpio.c +++ b/hw/puv3_gpio.c @@ -8,11 +8,11 @@ * published by the Free Software Foundation, or any later version. * See the COPYING file in the top-level directory. */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #undef DEBUG_PUV3 -#include "puv3.h" +#include "hw/puv3.h" typedef struct { SysBusDevice busdev; diff --git a/hw/puv3_intc.c b/hw/puv3_intc.c index 07f5649065..6bc9e1a752 100644 --- a/hw/puv3_intc.c +++ b/hw/puv3_intc.c @@ -8,10 +8,10 @@ * published by the Free Software Foundation, or any later version. * See the COPYING file in the top-level directory. */ -#include "sysbus.h" +#include "hw/sysbus.h" #undef DEBUG_PUV3 -#include "puv3.h" +#include "hw/puv3.h" typedef struct { SysBusDevice busdev; diff --git a/hw/puv3_ost.c b/hw/puv3_ost.c index 14c6f21a75..10a522adbb 100644 --- a/hw/puv3_ost.c +++ b/hw/puv3_ost.c @@ -8,11 +8,11 @@ * published by the Free Software Foundation, or any later version. * See the COPYING file in the top-level directory. */ -#include "sysbus.h" -#include "ptimer.h" +#include "hw/sysbus.h" +#include "hw/ptimer.h" #undef DEBUG_PUV3 -#include "puv3.h" +#include "hw/puv3.h" /* puv3 ostimer implementation. */ typedef struct { diff --git a/hw/puv3_pm.c b/hw/puv3_pm.c index 87a687afae..6b8d94dd07 100644 --- a/hw/puv3_pm.c +++ b/hw/puv3_pm.c @@ -8,11 +8,11 @@ * published by the Free Software Foundation, or any later version. * See the COPYING file in the top-level directory. */ -#include "hw.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sysbus.h" #undef DEBUG_PUV3 -#include "puv3.h" +#include "hw/puv3.h" typedef struct { SysBusDevice busdev; diff --git a/hw/pxa.h b/hw/pxa.h index c2577d1d94..668232cead 100644 --- a/hw/pxa.h +++ b/hw/pxa.h @@ -69,7 +69,7 @@ DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu); /* pxa2xx_gpio.c */ DeviceState *pxa2xx_gpio_init(hwaddr base, - CPUARMState *env, DeviceState *pic, int lines); + ARMCPU *cpu, DeviceState *pic, int lines); void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler); /* pxa2xx_dma.c */ diff --git a/hw/pxa2xx_dma.c b/hw/pxa2xx_dma.c index dbea1d2098..1db21c99ab 100644 --- a/hw/pxa2xx_dma.c +++ b/hw/pxa2xx_dma.c @@ -8,9 +8,9 @@ * This code is licensed under the GPL. */ -#include "hw.h" -#include "pxa.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/pxa.h" +#include "hw/sysbus.h" #define PXA255_DMA_NUM_CHANNELS 16 #define PXA27X_DMA_NUM_CHANNELS 32 @@ -481,8 +481,8 @@ DeviceState *pxa27x_dma_init(hwaddr base, qemu_irq irq) qdev_prop_set_int32(dev, "channels", PXA27X_DMA_NUM_CHANNELS); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); return dev; } @@ -495,8 +495,8 @@ DeviceState *pxa255_dma_init(hwaddr base, qemu_irq irq) qdev_prop_set_int32(dev, "channels", PXA27X_DMA_NUM_CHANNELS); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); return dev; } @@ -559,7 +559,7 @@ static void pxa2xx_dma_class_init(ObjectClass *klass, void *data) dc->props = pxa2xx_dma_properties; } -static TypeInfo pxa2xx_dma_info = { +static const TypeInfo pxa2xx_dma_info = { .name = "pxa2xx-dma", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxDMAState), diff --git a/hw/pxa2xx_keypad.c b/hw/pxa2xx_keypad.c index 4ff04ad63b..32ea7a5d34 100644 --- a/hw/pxa2xx_keypad.c +++ b/hw/pxa2xx_keypad.c @@ -11,8 +11,8 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "pxa.h" +#include "hw/hw.h" +#include "hw/pxa.h" #include "ui/console.h" /* diff --git a/hw/pxa2xx_lcd.c b/hw/pxa2xx_lcd.c index 512a27e702..6484d27de1 100644 --- a/hw/pxa2xx_lcd.c +++ b/hw/pxa2xx_lcd.c @@ -10,13 +10,13 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" +#include "hw/hw.h" #include "ui/console.h" -#include "pxa.h" +#include "hw/pxa.h" #include "ui/pixel_ops.h" /* FIXME: For graphic_rotate. Should probably be done in common code. */ #include "sysemu/sysemu.h" -#include "framebuffer.h" +#include "hw/framebuffer.h" struct DMAChannel { uint32_t branch; @@ -976,15 +976,15 @@ static const VMStateDescription vmstate_pxa2xx_lcdc = { }; #define BITS 8 -#include "pxa2xx_template.h" +#include "hw/pxa2xx_template.h" #define BITS 15 -#include "pxa2xx_template.h" +#include "hw/pxa2xx_template.h" #define BITS 16 -#include "pxa2xx_template.h" +#include "hw/pxa2xx_template.h" #define BITS 24 -#include "pxa2xx_template.h" +#include "hw/pxa2xx_template.h" #define BITS 32 -#include "pxa2xx_template.h" +#include "hw/pxa2xx_template.h" PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem, hwaddr base, qemu_irq irq) diff --git a/hw/pxa2xx_mmci.c b/hw/pxa2xx_mmci.c index 3589968712..0df83cc1df 100644 --- a/hw/pxa2xx_mmci.c +++ b/hw/pxa2xx_mmci.c @@ -10,10 +10,10 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "pxa.h" -#include "sd.h" -#include "qdev.h" +#include "hw/hw.h" +#include "hw/pxa.h" +#include "hw/sd.h" +#include "hw/qdev.h" struct PXA2xxMMCIState { MemoryRegion iomem; diff --git a/hw/pxa2xx_pcmcia.c b/hw/pxa2xx_pcmcia.c index 3a79c728ab..66fefba58c 100644 --- a/hw/pxa2xx_pcmcia.c +++ b/hw/pxa2xx_pcmcia.c @@ -10,9 +10,9 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "pcmcia.h" -#include "pxa.h" +#include "hw/hw.h" +#include "hw/pcmcia.h" +#include "hw/pxa.h" struct PXA2xxPCMCIAState { diff --git a/hw/pxa2xx_timer.c b/hw/pxa2xx_timer.c index e4ffb15bb2..c173fe4c11 100644 --- a/hw/pxa2xx_timer.c +++ b/hw/pxa2xx_timer.c @@ -7,11 +7,11 @@ * This code is licensed under the GPL. */ -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" -#include "pxa.h" -#include "sysbus.h" +#include "hw/pxa.h" +#include "hw/sysbus.h" #define OSMR0 0x00 #define OSMR1 0x04 @@ -157,17 +157,27 @@ static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset, switch (offset) { case OSMR3: tm ++; + /* fall through */ case OSMR2: tm ++; + /* fall through */ case OSMR1: tm ++; + /* fall through */ case OSMR0: return s->timer[tm].value; case OSMR11: tm ++; + /* fall through */ case OSMR10: tm ++; + /* fall through */ case OSMR9: tm ++; + /* fall through */ case OSMR8: tm ++; + /* fall through */ case OSMR7: tm ++; + /* fall through */ case OSMR6: tm ++; + /* fall through */ case OSMR5: tm ++; + /* fall through */ case OSMR4: if (!pxa2xx_timer_has_tm4(s)) goto badreg; @@ -176,12 +186,19 @@ static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset, return s->clock + muldiv64(qemu_get_clock_ns(vm_clock) - s->lastload, s->freq, get_ticks_per_sec()); case OSCR11: tm ++; + /* fall through */ case OSCR10: tm ++; + /* fall through */ case OSCR9: tm ++; + /* fall through */ case OSCR8: tm ++; + /* fall through */ case OSCR7: tm ++; + /* fall through */ case OSCR6: tm ++; + /* fall through */ case OSCR5: tm ++; + /* fall through */ case OSCR4: if (!pxa2xx_timer_has_tm4(s)) goto badreg; @@ -207,12 +224,19 @@ static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset, case OWER: return s->reset3; case OMCR11: tm ++; + /* fall through */ case OMCR10: tm ++; + /* fall through */ case OMCR9: tm ++; + /* fall through */ case OMCR8: tm ++; + /* fall through */ case OMCR7: tm ++; + /* fall through */ case OMCR6: tm ++; + /* fall through */ case OMCR5: tm ++; + /* fall through */ case OMCR4: if (!pxa2xx_timer_has_tm4(s)) goto badreg; @@ -235,19 +259,29 @@ static void pxa2xx_timer_write(void *opaque, hwaddr offset, switch (offset) { case OSMR3: tm ++; + /* fall through */ case OSMR2: tm ++; + /* fall through */ case OSMR1: tm ++; + /* fall through */ case OSMR0: s->timer[tm].value = value; pxa2xx_timer_update(s, qemu_get_clock_ns(vm_clock)); break; case OSMR11: tm ++; + /* fall through */ case OSMR10: tm ++; + /* fall through */ case OSMR9: tm ++; + /* fall through */ case OSMR8: tm ++; + /* fall through */ case OSMR7: tm ++; + /* fall through */ case OSMR6: tm ++; + /* fall through */ case OSMR5: tm ++; + /* fall through */ case OSMR4: if (!pxa2xx_timer_has_tm4(s)) goto badreg; @@ -261,12 +295,19 @@ static void pxa2xx_timer_write(void *opaque, hwaddr offset, pxa2xx_timer_update(s, s->lastload); break; case OSCR11: tm ++; + /* fall through */ case OSCR10: tm ++; + /* fall through */ case OSCR9: tm ++; + /* fall through */ case OSCR8: tm ++; + /* fall through */ case OSCR7: tm ++; + /* fall through */ case OSCR6: tm ++; + /* fall through */ case OSCR5: tm ++; + /* fall through */ case OSCR4: if (!pxa2xx_timer_has_tm4(s)) goto badreg; @@ -291,8 +332,11 @@ static void pxa2xx_timer_write(void *opaque, hwaddr offset, s->reset3 = value; break; case OMCR7: tm ++; + /* fall through */ case OMCR6: tm ++; + /* fall through */ case OMCR5: tm ++; + /* fall through */ case OMCR4: if (!pxa2xx_timer_has_tm4(s)) goto badreg; @@ -306,8 +350,11 @@ static void pxa2xx_timer_write(void *opaque, hwaddr offset, } break; case OMCR11: tm ++; + /* fall through */ case OMCR10: tm ++; + /* fall through */ case OMCR9: tm ++; + /* fall through */ case OMCR8: tm += 4; if (!pxa2xx_timer_has_tm4(s)) goto badreg; @@ -495,7 +542,7 @@ static void pxa25x_timer_dev_class_init(ObjectClass *klass, void *data) dc->props = pxa25x_timer_dev_properties; } -static TypeInfo pxa25x_timer_dev_info = { +static const TypeInfo pxa25x_timer_dev_info = { .name = "pxa25x-timer", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxTimerInfo), @@ -520,7 +567,7 @@ static void pxa27x_timer_dev_class_init(ObjectClass *klass, void *data) dc->props = pxa27x_timer_dev_properties; } -static TypeInfo pxa27x_timer_dev_info = { +static const TypeInfo pxa27x_timer_dev_info = { .name = "pxa27x-timer", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxTimerInfo), diff --git a/hw/q35.c b/hw/q35.c index efebc2786a..0a25b8bf1f 100644 --- a/hw/q35.c +++ b/hw/q35.c @@ -27,8 +27,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "q35.h" +#include "hw/hw.h" +#include "hw/q35.h" /**************************************************************************** * Q35 host diff --git a/hw/q35.h b/hw/q35.h index 246c12cb04..d766bb7b02 100644 --- a/hw/q35.h +++ b/hw/q35.h @@ -22,18 +22,18 @@ #ifndef HW_Q35_H #define HW_Q35_H -#include "hw.h" +#include "hw/hw.h" #include "qemu/range.h" -#include "isa.h" -#include "sysbus.h" -#include "pc.h" -#include "apm.h" -#include "apic.h" -#include "pci/pci.h" -#include "pci/pcie_host.h" -#include "acpi.h" -#include "acpi_ich9.h" -#include "pam.h" +#include "hw/isa.h" +#include "hw/sysbus.h" +#include "hw/pc.h" +#include "hw/apm.h" +#include "hw/apic.h" +#include "hw/pci/pci.h" +#include "hw/pci/pcie_host.h" +#include "hw/acpi.h" +#include "hw/acpi_ich9.h" +#include "hw/pam.h" #define TYPE_Q35_HOST_DEVICE "q35-pcihost" #define Q35_HOST_DEVICE(obj) \ diff --git a/hw/qdev-addr.c b/hw/qdev-addr.c index 3bfe101d79..2398b4a37f 100644 --- a/hw/qdev-addr.c +++ b/hw/qdev-addr.c @@ -1,6 +1,7 @@ -#include "qdev.h" -#include "qdev-addr.h" +#include "hw/qdev.h" +#include "hw/qdev-addr.h" #include "exec/hwaddr.h" +#include "qapi/qmp/qerror.h" #include "qapi/visitor.h" /* --- target physical address --- */ @@ -40,7 +41,7 @@ static void set_taddr(Object *obj, Visitor *v, void *opaque, Error *local_err = NULL; int64_t value; - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } diff --git a/hw/qdev-core.h b/hw/qdev-core.h index fdf14ec4a6..2486f36853 100644 --- a/hw/qdev-core.h +++ b/hw/qdev-core.h @@ -8,11 +8,6 @@ #include "hw/irq.h" #include "qapi/error.h" -enum DevState { - DEV_STATE_CREATED = 1, - DEV_STATE_INITIALIZED, -}; - enum { DEV_NVECTORS_UNSPECIFIED = -1, }; @@ -25,11 +20,65 @@ enum { typedef int (*qdev_initfn)(DeviceState *dev); typedef int (*qdev_event)(DeviceState *dev); typedef void (*qdev_resetfn)(DeviceState *dev); +typedef void (*DeviceRealize)(DeviceState *dev, Error **errp); +typedef void (*DeviceUnrealize)(DeviceState *dev, Error **errp); struct VMStateDescription; +/** + * DeviceClass: + * @props: Properties accessing state fields. + * @realize: Callback function invoked when the #DeviceState:realized + * property is changed to %true. The default invokes @init if not %NULL. + * @unrealize: Callback function invoked when the #DeviceState:realized + * property is changed to %false. + * @init: Callback function invoked when the #DeviceState::realized property + * is changed to %true. Deprecated, new types inheriting directly from + * TYPE_DEVICE should use @realize instead, new leaf types should consult + * their respective parent type. + * + * # Realization # + * Devices are constructed in two stages, + * 1) object instantiation via object_initialize() and + * 2) device realization via #DeviceState:realized property. + * The former may not fail (it might assert or exit), the latter may return + * error information to the caller and must be re-entrant. + * Trivial field initializations should go into #TypeInfo.instance_init. + * Operations depending on @props static properties should go into @realize. + * After successful realization, setting static properties will fail. + * + * As an interim step, the #DeviceState:realized property is set by deprecated + * functions qdev_init() and qdev_init_nofail(). + * In the future, devices will propagate this state change to their children + * and along busses they expose. + * The point in time will be deferred to machine creation, so that values + * set in @realize will not be introspectable beforehand. Therefore devices + * must not create children during @realize; they should initialize them via + * object_initialize() in their own #TypeInfo.instance_init and forward the + * realization events appropriately. + * + * The @init callback is considered private to a particular bus implementation + * (immediate abstract child types of TYPE_DEVICE). Derived leaf types set an + * "init" callback on their parent class instead. + * + * Any type may override the @realize and/or @unrealize callbacks but needs + * to call the parent type's implementation if keeping their functionality + * is desired. Refer to QOM documentation for further discussion and examples. + * + * + * + * If a type derived directly from TYPE_DEVICE implements @realize, it does + * not need to implement @init and therefore does not need to store and call + * #DeviceClass' default @realize callback. + * For other types consult the documentation and implementation of the + * respective parent types. + * + * + */ typedef struct DeviceClass { + /*< private >*/ ObjectClass parent_class; + /*< public >*/ const char *fw_name; const char *desc; @@ -38,24 +87,33 @@ typedef struct DeviceClass { /* callbacks */ void (*reset)(DeviceState *dev); + DeviceRealize realize; + DeviceUnrealize unrealize; /* device state */ const struct VMStateDescription *vmsd; /* Private to qdev / bus. */ - qdev_initfn init; + qdev_initfn init; /* TODO remove, once users are converted to realize */ qdev_event unplug; qdev_event exit; const char *bus_type; } DeviceClass; -/* This structure should not be accessed directly. We declare it here - so that it can be embedded in individual device state structures. */ +/** + * DeviceState: + * @realized: Indicates whether the device has been fully constructed. + * + * This structure should not be accessed directly. We declare it here + * so that it can be embedded in individual device state structures. + */ struct DeviceState { + /*< private >*/ Object parent_obj; + /*< public >*/ const char *id; - enum DevState state; + bool realized; QemuOpts *opts; int hotplugged; BusState *parent_bus; @@ -87,6 +145,8 @@ struct BusClass { */ char *(*get_fw_dev_path)(DeviceState *dev); int (*reset)(BusState *bus); + /* maximum devices allowed on the bus, 0: no limit. */ + int max_dev; }; typedef struct BusChild { @@ -171,7 +231,7 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id); typedef int (qbus_walkerfn)(BusState *bus, void *opaque); typedef int (qdev_walkerfn)(DeviceState *dev, void *opaque); -void qbus_create_inplace(BusState *bus, const char *typename, +void qbus_create_inplace(void *bus, const char *typename, DeviceState *parent, const char *name); BusState *qbus_create(const char *typename, DeviceState *parent, const char *name); /* Returns > 0 if either devfn or busfn skip walk somewhere in cursion, @@ -182,6 +242,18 @@ int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn, int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn, qbus_walkerfn *busfn, void *opaque); void qdev_reset_all(DeviceState *dev); + +/** + * @qbus_reset_all: + * @bus: Bus to be reset. + * + * Reset @bus and perform a bus-level ("hard") reset of all devices connected + * to it, including recursive processing of all buses below @bus itself. A + * hard reset means that qbus_reset_all will reset all state of the device. + * For PCI devices, for example, this will include the base address registers + * or configuration space. + */ +void qbus_reset_all(BusState *bus); void qbus_reset_all_fn(void *opaque); void qbus_free(BusState *bus); diff --git a/hw/qdev-properties-system.c b/hw/qdev-properties-system.c new file mode 100644 index 0000000000..87951444a1 --- /dev/null +++ b/hw/qdev-properties-system.c @@ -0,0 +1,390 @@ +/* + * qdev property parsing and global properties + * (parts specific for qemu-system-*) + * + * This file is based on code from hw/qdev-properties.c from + * commit 074a86fccd185616469dfcdc0e157f438aebba18, + * Copyright (c) Gerd Hoffmann and other contributors. + * + * 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 "net/net.h" +#include "hw/qdev.h" +#include "qapi/qmp/qerror.h" +#include "sysemu/blockdev.h" +#include "hw/block-common.h" +#include "net/hub.h" +#include "qapi/visitor.h" +#include "char/char.h" + +static void get_pointer(Object *obj, Visitor *v, Property *prop, + const char *(*print)(void *ptr), + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + void **ptr = qdev_get_prop_ptr(dev, prop); + char *p; + + p = (char *) (*ptr ? print(*ptr) : ""); + visit_type_str(v, &p, name, errp); +} + +static void set_pointer(Object *obj, Visitor *v, Property *prop, + int (*parse)(DeviceState *dev, const char *str, + void **ptr), + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Error *local_err = NULL; + void **ptr = qdev_get_prop_ptr(dev, prop); + char *str; + int ret; + + if (dev->realized) { + error_set(errp, QERR_PERMISSION_DENIED); + return; + } + + visit_type_str(v, &str, name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + if (!*str) { + g_free(str); + *ptr = NULL; + return; + } + ret = parse(dev, str, ptr); + error_set_from_qdev_prop_error(errp, ret, dev, prop, str); + g_free(str); +} + +/* --- drive --- */ + +static int parse_drive(DeviceState *dev, const char *str, void **ptr) +{ + BlockDriverState *bs; + + bs = bdrv_find(str); + if (bs == NULL) { + return -ENOENT; + } + if (bdrv_attach_dev(bs, dev) < 0) { + return -EEXIST; + } + *ptr = bs; + return 0; +} + +static void release_drive(Object *obj, const char *name, void *opaque) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop); + + if (*ptr) { + bdrv_detach_dev(*ptr, dev); + blockdev_auto_del(*ptr); + } +} + +static const char *print_drive(void *ptr) +{ + return bdrv_get_device_name(ptr); +} + +static void get_drive(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + get_pointer(obj, v, opaque, print_drive, name, errp); +} + +static void set_drive(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + set_pointer(obj, v, opaque, parse_drive, name, errp); +} + +PropertyInfo qdev_prop_drive = { + .name = "drive", + .get = get_drive, + .set = set_drive, + .release = release_drive, +}; + +/* --- character device --- */ + +static int parse_chr(DeviceState *dev, const char *str, void **ptr) +{ + CharDriverState *chr = qemu_chr_find(str); + if (chr == NULL) { + return -ENOENT; + } + if (chr->avail_connections < 1) { + return -EEXIST; + } + *ptr = chr; + --chr->avail_connections; + return 0; +} + +static void release_chr(Object *obj, const char *name, void *opaque) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + CharDriverState **ptr = qdev_get_prop_ptr(dev, prop); + + if (*ptr) { + qemu_chr_add_handlers(*ptr, NULL, NULL, NULL, NULL); + } +} + + +static const char *print_chr(void *ptr) +{ + CharDriverState *chr = ptr; + + return chr->label ? chr->label : ""; +} + +static void get_chr(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + get_pointer(obj, v, opaque, print_chr, name, errp); +} + +static void set_chr(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + set_pointer(obj, v, opaque, parse_chr, name, errp); +} + +PropertyInfo qdev_prop_chr = { + .name = "chr", + .get = get_chr, + .set = set_chr, + .release = release_chr, +}; + +/* --- netdev device --- */ + +static int parse_netdev(DeviceState *dev, const char *str, void **ptr) +{ + NICPeers *peers_ptr = (NICPeers *)ptr; + NICConf *conf = container_of(peers_ptr, NICConf, peers); + NetClientState **ncs = peers_ptr->ncs; + NetClientState *peers[MAX_QUEUE_NUM]; + int queues, i = 0; + int ret; + + queues = qemu_find_net_clients_except(str, peers, + NET_CLIENT_OPTIONS_KIND_NIC, + MAX_QUEUE_NUM); + if (queues == 0) { + ret = -ENOENT; + goto err; + } + + if (queues > MAX_QUEUE_NUM) { + ret = -E2BIG; + goto err; + } + + for (i = 0; i < queues; i++) { + if (peers[i] == NULL) { + ret = -ENOENT; + goto err; + } + + if (peers[i]->peer) { + ret = -EEXIST; + goto err; + } + + ncs[i] = peers[i]; + ncs[i]->queue_index = i; + } + + conf->queues = queues; + + return 0; + +err: + return ret; +} + +static const char *print_netdev(void *ptr) +{ + NetClientState *netdev = ptr; + + return netdev->name ? netdev->name : ""; +} + +static void get_netdev(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + get_pointer(obj, v, opaque, print_netdev, name, errp); +} + +static void set_netdev(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + set_pointer(obj, v, opaque, parse_netdev, name, errp); +} + +PropertyInfo qdev_prop_netdev = { + .name = "netdev", + .get = get_netdev, + .set = set_netdev, +}; + +/* --- vlan --- */ + +static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len) +{ + NetClientState **ptr = qdev_get_prop_ptr(dev, prop); + + if (*ptr) { + int id; + if (!net_hub_id_for_client(*ptr, &id)) { + return snprintf(dest, len, "%d", id); + } + } + + return snprintf(dest, len, ""); +} + +static void get_vlan(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + NetClientState **ptr = qdev_get_prop_ptr(dev, prop); + int32_t id = -1; + + if (*ptr) { + int hub_id; + if (!net_hub_id_for_client(*ptr, &hub_id)) { + id = hub_id; + } + } + + visit_type_int32(v, &id, name, errp); +} + +static void set_vlan(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); + NetClientState **ptr = &peers_ptr->ncs[0]; + Error *local_err = NULL; + int32_t id; + NetClientState *hubport; + + if (dev->realized) { + error_set(errp, QERR_PERMISSION_DENIED); + return; + } + + visit_type_int32(v, &id, name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + if (id == -1) { + *ptr = NULL; + return; + } + + hubport = net_hub_port_find(id); + if (!hubport) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, + name, prop->info->name); + return; + } + *ptr = hubport; +} + +PropertyInfo qdev_prop_vlan = { + .name = "vlan", + .print = print_vlan, + .get = get_vlan, + .set = set_vlan, +}; + +int qdev_prop_set_drive(DeviceState *dev, const char *name, + BlockDriverState *value) +{ + Error *errp = NULL; + const char *bdrv_name = value ? bdrv_get_device_name(value) : ""; + object_property_set_str(OBJECT(dev), bdrv_name, + name, &errp); + if (errp) { + qerror_report_err(errp); + error_free(errp); + return -1; + } + return 0; +} + +void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, + BlockDriverState *value) +{ + if (qdev_prop_set_drive(dev, name, value) < 0) { + exit(1); + } +} +void qdev_prop_set_chr(DeviceState *dev, const char *name, + CharDriverState *value) +{ + Error *errp = NULL; + assert(!value || value->label); + object_property_set_str(OBJECT(dev), + value ? value->label : "", name, &errp); + assert_no_error(errp); +} + +void qdev_prop_set_netdev(DeviceState *dev, const char *name, + NetClientState *value) +{ + Error *errp = NULL; + assert(!value || value->name); + object_property_set_str(OBJECT(dev), + value ? value->name : "", name, &errp); + assert_no_error(errp); +} + +void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) +{ + qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a); + if (nd->netdev) { + qdev_prop_set_netdev(dev, "netdev", nd->netdev); + } + if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED && + object_property_find(OBJECT(dev), "vectors", NULL)) { + qdev_prop_set_uint32(dev, "vectors", nd->nvectors); + } + nd->instantiated = 1; +} + +static int qdev_add_one_global(QemuOpts *opts, void *opaque) +{ + GlobalProperty *g; + + g = g_malloc0(sizeof(*g)); + g->driver = qemu_opt_get(opts, "driver"); + g->property = qemu_opt_get(opts, "property"); + g->value = qemu_opt_get(opts, "value"); + qdev_prop_register_global(g); + return 0; +} + +void qemu_add_globals(void) +{ + qemu_opts_foreach(qemu_find_opts("global"), qdev_add_one_global, NULL, 0); +} diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c index 04d605dd37..0307a7830b 100644 --- a/hw/qdev-properties.c +++ b/hw/qdev-properties.c @@ -1,5 +1,5 @@ #include "net/net.h" -#include "qdev.h" +#include "hw/qdev.h" #include "qapi/qmp/qerror.h" #include "sysemu/blockdev.h" #include "hw/block-common.h" @@ -14,49 +14,6 @@ void *qdev_get_prop_ptr(DeviceState *dev, Property *prop) return ptr; } -static void get_pointer(Object *obj, Visitor *v, Property *prop, - const char *(*print)(void *ptr), - const char *name, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - void **ptr = qdev_get_prop_ptr(dev, prop); - char *p; - - p = (char *) (*ptr ? print(*ptr) : ""); - visit_type_str(v, &p, name, errp); -} - -static void set_pointer(Object *obj, Visitor *v, Property *prop, - int (*parse)(DeviceState *dev, const char *str, - void **ptr), - const char *name, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Error *local_err = NULL; - void **ptr = qdev_get_prop_ptr(dev, prop); - char *str; - int ret; - - if (dev->state != DEV_STATE_CREATED) { - error_set(errp, QERR_PERMISSION_DENIED); - return; - } - - visit_type_str(v, &str, name, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - if (!*str) { - g_free(str); - *ptr = NULL; - return; - } - ret = parse(dev, str, ptr); - error_set_from_qdev_prop_error(errp, ret, dev, prop, str); - g_free(str); -} - static void get_enum(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { @@ -75,7 +32,7 @@ static void set_enum(Object *obj, Visitor *v, void *opaque, Property *prop = opaque; int *ptr = qdev_get_prop_ptr(dev, prop); - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } @@ -96,10 +53,11 @@ static void bit_prop_set(DeviceState *dev, Property *props, bool val) { uint32_t *p = qdev_get_prop_ptr(dev, props); uint32_t mask = qdev_get_prop_mask(props); - if (val) + if (val) { *p |= mask; - else + } else { *p &= ~mask; + } } static int print_bit(DeviceState *dev, Property *prop, char *dest, size_t len) @@ -127,7 +85,7 @@ static void set_bit(Object *obj, Visitor *v, void *opaque, Error *local_err = NULL; bool value; - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } @@ -167,7 +125,7 @@ static void set_uint8(Object *obj, Visitor *v, void *opaque, Property *prop = opaque; uint8_t *ptr = qdev_get_prop_ptr(dev, prop); - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } @@ -234,7 +192,7 @@ static void set_uint16(Object *obj, Visitor *v, void *opaque, Property *prop = opaque; uint16_t *ptr = qdev_get_prop_ptr(dev, prop); - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } @@ -267,7 +225,7 @@ static void set_uint32(Object *obj, Visitor *v, void *opaque, Property *prop = opaque; uint32_t *ptr = qdev_get_prop_ptr(dev, prop); - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } @@ -292,7 +250,7 @@ static void set_int32(Object *obj, Visitor *v, void *opaque, Property *prop = opaque; int32_t *ptr = qdev_get_prop_ptr(dev, prop); - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } @@ -365,7 +323,7 @@ static void set_uint64(Object *obj, Visitor *v, void *opaque, Property *prop = opaque; uint64_t *ptr = qdev_get_prop_ptr(dev, prop); - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } @@ -421,11 +379,13 @@ static void release_string(Object *obj, const char *name, void *opaque) g_free(*(char **)qdev_get_prop_ptr(DEVICE(obj), prop)); } -static int print_string(DeviceState *dev, Property *prop, char *dest, size_t len) +static int print_string(DeviceState *dev, Property *prop, char *dest, + size_t len) { char **ptr = qdev_get_prop_ptr(dev, prop); - if (!*ptr) + if (!*ptr) { return snprintf(dest, len, ""); + } return snprintf(dest, len, "\"%s\"", *ptr); } @@ -453,7 +413,7 @@ static void set_string(Object *obj, Visitor *v, void *opaque, Error *local_err = NULL; char *str; - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } @@ -477,227 +437,6 @@ PropertyInfo qdev_prop_string = { .set = set_string, }; -/* --- drive --- */ - -static int parse_drive(DeviceState *dev, const char *str, void **ptr) -{ - BlockDriverState *bs; - - bs = bdrv_find(str); - if (bs == NULL) - return -ENOENT; - if (bdrv_attach_dev(bs, dev) < 0) - return -EEXIST; - *ptr = bs; - return 0; -} - -static void release_drive(Object *obj, const char *name, void *opaque) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop); - - if (*ptr) { - bdrv_detach_dev(*ptr, dev); - blockdev_auto_del(*ptr); - } -} - -static const char *print_drive(void *ptr) -{ - return bdrv_get_device_name(ptr); -} - -static void get_drive(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - get_pointer(obj, v, opaque, print_drive, name, errp); -} - -static void set_drive(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - set_pointer(obj, v, opaque, parse_drive, name, errp); -} - -PropertyInfo qdev_prop_drive = { - .name = "drive", - .get = get_drive, - .set = set_drive, - .release = release_drive, -}; - -/* --- character device --- */ - -static int parse_chr(DeviceState *dev, const char *str, void **ptr) -{ - CharDriverState *chr = qemu_chr_find(str); - if (chr == NULL) { - return -ENOENT; - } - if (chr->avail_connections < 1) { - return -EEXIST; - } - *ptr = chr; - --chr->avail_connections; - return 0; -} - -static void release_chr(Object *obj, const char *name, void *opaque) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - CharDriverState **ptr = qdev_get_prop_ptr(dev, prop); - - if (*ptr) { - qemu_chr_add_handlers(*ptr, NULL, NULL, NULL, NULL); - } -} - - -static const char *print_chr(void *ptr) -{ - CharDriverState *chr = ptr; - - return chr->label ? chr->label : ""; -} - -static void get_chr(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - get_pointer(obj, v, opaque, print_chr, name, errp); -} - -static void set_chr(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - set_pointer(obj, v, opaque, parse_chr, name, errp); -} - -PropertyInfo qdev_prop_chr = { - .name = "chr", - .get = get_chr, - .set = set_chr, - .release = release_chr, -}; - -/* --- netdev device --- */ - -static int parse_netdev(DeviceState *dev, const char *str, void **ptr) -{ - NetClientState *netdev = qemu_find_netdev(str); - - if (netdev == NULL) { - return -ENOENT; - } - if (netdev->peer) { - return -EEXIST; - } - *ptr = netdev; - return 0; -} - -static const char *print_netdev(void *ptr) -{ - NetClientState *netdev = ptr; - - return netdev->name ? netdev->name : ""; -} - -static void get_netdev(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - get_pointer(obj, v, opaque, print_netdev, name, errp); -} - -static void set_netdev(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - set_pointer(obj, v, opaque, parse_netdev, name, errp); -} - -PropertyInfo qdev_prop_netdev = { - .name = "netdev", - .get = get_netdev, - .set = set_netdev, -}; - -/* --- vlan --- */ - -static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len) -{ - NetClientState **ptr = qdev_get_prop_ptr(dev, prop); - - if (*ptr) { - int id; - if (!net_hub_id_for_client(*ptr, &id)) { - return snprintf(dest, len, "%d", id); - } - } - - return snprintf(dest, len, ""); -} - -static void get_vlan(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - NetClientState **ptr = qdev_get_prop_ptr(dev, prop); - int32_t id = -1; - - if (*ptr) { - int hub_id; - if (!net_hub_id_for_client(*ptr, &hub_id)) { - id = hub_id; - } - } - - visit_type_int32(v, &id, name, errp); -} - -static void set_vlan(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - NetClientState **ptr = qdev_get_prop_ptr(dev, prop); - Error *local_err = NULL; - int32_t id; - NetClientState *hubport; - - if (dev->state != DEV_STATE_CREATED) { - error_set(errp, QERR_PERMISSION_DENIED); - return; - } - - visit_type_int32(v, &id, name, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - if (id == -1) { - *ptr = NULL; - return; - } - - hubport = net_hub_port_find(id); - if (!hubport) { - error_set(errp, QERR_INVALID_PARAMETER_VALUE, - name, prop->info->name); - return; - } - *ptr = hubport; -} - -PropertyInfo qdev_prop_vlan = { - .name = "vlan", - .print = print_vlan, - .get = get_vlan, - .set = set_vlan, -}; - /* --- pointer --- */ /* Not a proper property, just for dirty hacks. TODO Remove it! */ @@ -738,7 +477,7 @@ static void set_mac(Object *obj, Visitor *v, void *opaque, int i, pos; char *str, *p; - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } @@ -750,16 +489,20 @@ static void set_mac(Object *obj, Visitor *v, void *opaque, } for (i = 0, pos = 0; i < 6; i++, pos += 3) { - if (!qemu_isxdigit(str[pos])) + if (!qemu_isxdigit(str[pos])) { goto inval; - if (!qemu_isxdigit(str[pos+1])) + } + if (!qemu_isxdigit(str[pos+1])) { goto inval; + } if (i == 5) { - if (str[pos+2] != '\0') + if (str[pos+2] != '\0') { goto inval; + } } else { - if (str[pos+2] != ':' && str[pos+2] != '-') + if (str[pos+2] != ':' && str[pos+2] != '-') { goto inval; + } } mac->a[i] = strtol(str+pos, &p, 16); } @@ -826,7 +569,7 @@ static void set_pci_devfn(Object *obj, Visitor *v, void *opaque, Error *local_err = NULL; char *str; - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } @@ -865,7 +608,8 @@ invalid: g_free(str); } -static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest, size_t len) +static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest, + size_t len) { int32_t *ptr = qdev_get_prop_ptr(dev, prop); @@ -896,7 +640,7 @@ static void set_blocksize(Object *obj, Visitor *v, void *opaque, const int64_t min = 512; const int64_t max = 32768; - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } @@ -964,7 +708,7 @@ static void set_pci_host_devaddr(Object *obj, Visitor *v, void *opaque, unsigned long dom = 0, bus = 0; unsigned int slot = 0, func = 0; - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } @@ -1039,11 +783,13 @@ PropertyInfo qdev_prop_pci_host_devaddr = { static Property *qdev_prop_walk(Property *props, const char *name) { - if (!props) + if (!props) { return NULL; + } while (props->name) { - if (strcmp(props->name, name) == 0) + if (strcmp(props->name, name) == 0) { return props; + } props++; } return NULL; @@ -1159,44 +905,6 @@ void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value) assert_no_error(errp); } -int qdev_prop_set_drive(DeviceState *dev, const char *name, BlockDriverState *value) -{ - Error *errp = NULL; - const char *bdrv_name = value ? bdrv_get_device_name(value) : ""; - object_property_set_str(OBJECT(dev), bdrv_name, - name, &errp); - if (errp) { - qerror_report_err(errp); - error_free(errp); - return -1; - } - return 0; -} - -void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockDriverState *value) -{ - if (qdev_prop_set_drive(dev, name, value) < 0) { - exit(1); - } -} -void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value) -{ - Error *errp = NULL; - assert(!value || value->label); - object_property_set_str(OBJECT(dev), - value ? value->label : "", name, &errp); - assert_no_error(errp); -} - -void qdev_prop_set_netdev(DeviceState *dev, const char *name, NetClientState *value) -{ - Error *errp = NULL; - assert(!value || value->name); - object_property_set_str(OBJECT(dev), - value ? value->name : "", name, &errp); - assert_no_error(errp); -} - void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value) { Error *errp = NULL; @@ -1230,9 +938,10 @@ void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value) *ptr = value; } -static QTAILQ_HEAD(, GlobalProperty) global_props = QTAILQ_HEAD_INITIALIZER(global_props); +static QTAILQ_HEAD(, GlobalProperty) global_props = + QTAILQ_HEAD_INITIALIZER(global_props); -static void qdev_prop_register_global(GlobalProperty *prop) +void qdev_prop_register_global(GlobalProperty *prop) { QTAILQ_INSERT_TAIL(&global_props, prop, next); } @@ -1263,20 +972,3 @@ void qdev_prop_set_globals(DeviceState *dev) class = object_class_get_parent(class); } while (class); } - -static int qdev_add_one_global(QemuOpts *opts, void *opaque) -{ - GlobalProperty *g; - - g = g_malloc0(sizeof(*g)); - g->driver = qemu_opt_get(opts, "driver"); - g->property = qemu_opt_get(opts, "property"); - g->value = qemu_opt_get(opts, "value"); - qdev_prop_register_global(g); - return 0; -} - -void qemu_add_globals(void) -{ - qemu_opts_foreach(qemu_find_opts("global"), qdev_add_one_global, NULL, 0); -} diff --git a/hw/qdev-properties.h b/hw/qdev-properties.h index 5b046abb28..0b0465c9b3 100644 --- a/hw/qdev-properties.h +++ b/hw/qdev-properties.h @@ -1,7 +1,7 @@ #ifndef QEMU_QDEV_PROPERTIES_H #define QEMU_QDEV_PROPERTIES_H -#include "qdev-core.h" +#include "hw/qdev-core.h" /*** qdev-properties.c ***/ @@ -31,7 +31,7 @@ extern PropertyInfo qdev_prop_pci_host_devaddr; .name = (_name), \ .info = &(_prop), \ .offset = offsetof(_state, _field) \ - + type_check(_type,typeof_field(_state, _field)), \ + + type_check(_type, typeof_field(_state, _field)), \ } #define DEFINE_PROP_DEFAULT(_name, _state, _field, _defval, _prop, _type) { \ .name = (_name), \ @@ -77,9 +77,9 @@ extern PropertyInfo qdev_prop_pci_host_devaddr; #define DEFINE_PROP_STRING(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_string, char*) #define DEFINE_PROP_NETDEV(_n, _s, _f) \ - DEFINE_PROP(_n, _s, _f, qdev_prop_netdev, NetClientState*) + DEFINE_PROP(_n, _s, _f, qdev_prop_netdev, NICPeers) #define DEFINE_PROP_VLAN(_n, _s, _f) \ - DEFINE_PROP(_n, _s, _f, qdev_prop_vlan, NetClientState*) + DEFINE_PROP(_n, _s, _f, qdev_prop_vlan, NICPeers) #define DEFINE_PROP_DRIVE(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_drive, BlockDriverState *) #define DEFINE_PROP_MACADDR(_n, _s, _f) \ @@ -116,6 +116,7 @@ void qdev_prop_set_enum(DeviceState *dev, const char *name, int value); /* FIXME: Remove opaque pointer properties. */ void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value); +void qdev_prop_register_global(GlobalProperty *prop); void qdev_prop_register_global_list(GlobalProperty *props); void qdev_prop_set_globals(DeviceState *dev); void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev, diff --git a/hw/qdev.c b/hw/qdev.c index c4a9857a05..0b20280133 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -25,10 +25,10 @@ inherit from a particular bus (e.g. PCI or I2C) rather than this API directly. */ -#include "net/net.h" -#include "qdev.h" +#include "hw/qdev.h" #include "sysemu/sysemu.h" #include "qapi/error.h" +#include "qapi/qmp/qerror.h" #include "qapi/visitor.h" int qdev_hotplug = 0; @@ -65,7 +65,10 @@ static void bus_remove_child(BusState *bus, DeviceState *child) snprintf(name, sizeof(name), "child[%d]", kid->index); QTAILQ_REMOVE(&bus->children, kid, sibling); + + /* This gives back ownership of kid->child back to us. */ object_property_del(OBJECT(bus), name, NULL); + object_unref(OBJECT(kid->child)); g_free(kid); return; } @@ -83,9 +86,11 @@ static void bus_add_child(BusState *bus, DeviceState *child) kid->index = bus->max_index++; kid->child = child; + object_ref(OBJECT(kid->child)); QTAILQ_INSERT_HEAD(&bus->children, kid, sibling); + /* This transfers ownership of kid->child to the property. */ snprintf(name, sizeof(name), "child[%d]", kid->index); object_property_add_link(OBJECT(bus), name, object_get_typename(OBJECT(child)), @@ -96,6 +101,7 @@ static void bus_add_child(BusState *bus, DeviceState *child) void qdev_set_parent_bus(DeviceState *dev, BusState *bus) { dev->parent_bus = bus; + object_ref(OBJECT(bus)); bus_add_child(bus, dev); } @@ -109,11 +115,11 @@ DeviceState *qdev_create(BusState *bus, const char *name) dev = qdev_try_create(bus, name); if (!dev) { if (bus) { - error_report("Unknown device '%s' for bus '%s'\n", name, + error_report("Unknown device '%s' for bus '%s'", name, object_get_typename(OBJECT(bus))); abort(); } else { - error_report("Unknown device '%s' for default sysbus\n", name); + error_report("Unknown device '%s' for default sysbus", name); abort(); } } @@ -138,7 +144,7 @@ DeviceState *qdev_try_create(BusState *bus, const char *type) } qdev_set_parent_bus(dev, bus); - + object_unref(OBJECT(dev)); return dev; } @@ -149,43 +155,36 @@ DeviceState *qdev_try_create(BusState *bus, const char *type) Return 0 on success. */ int qdev_init(DeviceState *dev) { - DeviceClass *dc = DEVICE_GET_CLASS(dev); - int rc; + Error *local_err = NULL; - assert(dev->state == DEV_STATE_CREATED); + assert(!dev->realized); - rc = dc->init(dev); - if (rc < 0) { + object_property_set_bool(OBJECT(dev), true, "realized", &local_err); + if (local_err != NULL) { + error_free(local_err); qdev_free(dev); - return rc; - } - - if (!OBJECT(dev)->parent) { - static int unattached_count = 0; - gchar *name = g_strdup_printf("device[%d]", unattached_count++); - - object_property_add_child(container_get(qdev_get_machine(), - "/unattached"), - name, OBJECT(dev), NULL); - g_free(name); - } - - if (qdev_get_vmsd(dev)) { - vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev, - dev->instance_id_alias, - dev->alias_required_for_version); - } - dev->state = DEV_STATE_INITIALIZED; - if (dev->hotplugged) { - device_reset(dev); + return -1; } return 0; } +static void device_realize(DeviceState *dev, Error **err) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (dc->init) { + int rc = dc->init(dev); + if (rc < 0) { + error_setg(err, "Device initialization failed."); + return; + } + } +} + void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, int required_for_version) { - assert(dev->state == DEV_STATE_CREATED); + assert(!dev->realized); dev->instance_id_alias = alias_id; dev->alias_required_for_version = required_for_version; } @@ -229,10 +228,15 @@ void qdev_reset_all(DeviceState *dev) qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL); } +void qbus_reset_all(BusState *bus) +{ + qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL); +} + void qbus_reset_all_fn(void *opaque) { BusState *bus = opaque; - qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL); + qbus_reset_all(bus); } /* can be used as ->unplug() callback for the simple cases */ @@ -264,7 +268,7 @@ void qdev_init_nofail(DeviceState *dev) /* Unlink device from bus and free the structure. */ void qdev_free(DeviceState *dev) { - object_delete(OBJECT(dev)); + object_unparent(OBJECT(dev)); } void qdev_machine_creation_done(void) @@ -312,18 +316,6 @@ void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin) dev->gpio_out[n] = pin; } -void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) -{ - qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a); - if (nd->netdev) - qdev_prop_set_netdev(dev, "netdev", nd->netdev); - if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED && - object_property_find(OBJECT(dev), "vectors", NULL)) { - qdev_prop_set_uint32(dev, "vectors", nd->nvectors); - } - nd->instantiated = 1; -} - BusState *qdev_get_child_bus(DeviceState *dev, const char *name) { BusState *bus; @@ -405,14 +397,16 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id) return NULL; } -static void qbus_realize(BusState *bus) +static void qbus_realize(BusState *bus, DeviceState *parent, const char *name) { const char *typename = object_get_typename(OBJECT(bus)); char *buf; int i,len; - if (bus->name) { - /* use supplied name */ + bus->parent = parent; + + if (name) { + bus->name = g_strdup(name); } else if (bus->parent && bus->parent->id) { /* parent device has id -> use it for bus name */ len = strlen(bus->parent->id) + 16; @@ -434,6 +428,7 @@ static void qbus_realize(BusState *bus) QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling); bus->parent->num_child_bus++; object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL); + object_unref(OBJECT(bus)); } else if (bus != sysbus_get_default()) { /* TODO: once all bus devices are qdevified, only reset handler for main_system_bus should be registered here. */ @@ -441,14 +436,30 @@ static void qbus_realize(BusState *bus) } } -void qbus_create_inplace(BusState *bus, const char *typename, +static void bus_unparent(Object *obj) +{ + BusState *bus = BUS(obj); + BusChild *kid; + + while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { + DeviceState *dev = kid->child; + qdev_free(dev); + } + if (bus->parent) { + QLIST_REMOVE(bus, sibling); + bus->parent->num_child_bus--; + bus->parent = NULL; + } else { + assert(bus != sysbus_get_default()); /* main_system_bus is never freed */ + qemu_unregister_reset(qbus_reset_all_fn, bus); + } +} + +void qbus_create_inplace(void *bus, const char *typename, DeviceState *parent, const char *name) { object_initialize(bus, typename); - - bus->parent = parent; - bus->name = name ? g_strdup(name) : NULL; - qbus_realize(bus); + qbus_realize(bus, parent, name); } BusState *qbus_create(const char *typename, DeviceState *parent, const char *name) @@ -456,17 +467,14 @@ BusState *qbus_create(const char *typename, DeviceState *parent, const char *nam BusState *bus; bus = BUS(object_new(typename)); - - bus->parent = parent; - bus->name = name ? g_strdup(name) : NULL; - qbus_realize(bus); + qbus_realize(bus, parent, name); return bus; } void qbus_free(BusState *bus) { - object_delete(OBJECT(bus)); + object_unparent(OBJECT(bus)); } static char *bus_get_fw_dev_path(BusState *bus, DeviceState *dev) @@ -554,7 +562,7 @@ static void qdev_set_legacy_property(Object *obj, Visitor *v, void *opaque, char *ptr = NULL; int ret; - if (dev->state != DEV_STATE_CREATED) { + if (dev->realized) { error_set(errp, QERR_PERMISSION_DENIED); return; } @@ -649,6 +657,55 @@ void qdev_property_add_static(DeviceState *dev, Property *prop, assert_no_error(local_err); } +static bool device_get_realized(Object *obj, Error **err) +{ + DeviceState *dev = DEVICE(obj); + return dev->realized; +} + +static void device_set_realized(Object *obj, bool value, Error **err) +{ + DeviceState *dev = DEVICE(obj); + DeviceClass *dc = DEVICE_GET_CLASS(dev); + Error *local_err = NULL; + + if (value && !dev->realized) { + if (dc->realize) { + dc->realize(dev, &local_err); + } + + if (!obj->parent && local_err == NULL) { + static int unattached_count; + gchar *name = g_strdup_printf("device[%d]", unattached_count++); + + object_property_add_child(container_get(qdev_get_machine(), + "/unattached"), + name, obj, &local_err); + g_free(name); + } + + if (qdev_get_vmsd(dev) && local_err == NULL) { + vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev, + dev->instance_id_alias, + dev->alias_required_for_version); + } + if (dev->hotplugged && local_err == NULL) { + device_reset(dev); + } + } else if (!value && dev->realized) { + if (dc->unrealize) { + dc->unrealize(dev, &local_err); + } + } + + if (local_err != NULL) { + error_propagate(err, local_err); + return; + } + + dev->realized = value; +} + static void device_initfn(Object *obj) { DeviceState *dev = DEVICE(obj); @@ -661,7 +718,10 @@ static void device_initfn(Object *obj) } dev->instance_id_alias = -1; - dev->state = DEV_STATE_CREATED; + dev->realized = false; + + object_property_add_bool(obj, "realized", + device_get_realized, device_set_realized, NULL); class = object_get_class(OBJECT(dev)); do { @@ -681,23 +741,8 @@ static void device_initfn(Object *obj) static void device_finalize(Object *obj) { DeviceState *dev = DEVICE(obj); - BusState *bus; - DeviceClass *dc = DEVICE_GET_CLASS(dev); - - if (dev->state == DEV_STATE_INITIALIZED) { - while (dev->num_child_bus) { - bus = QLIST_FIRST(&dev->child_bus); - qbus_free(bus); - } - if (qdev_get_vmsd(dev)) { - vmstate_unregister(dev, qdev_get_vmsd(dev), dev); - } - if (dc->exit) { - dc->exit(dev); - } - if (dev->opts) { - qemu_opts_del(dev->opts); - } + if (dev->opts) { + qemu_opts_del(dev->opts); } } @@ -711,16 +756,37 @@ static void device_class_base_init(ObjectClass *class, void *data) klass->props = NULL; } -static void qdev_remove_from_bus(Object *obj) +static void device_unparent(Object *obj) { DeviceState *dev = DEVICE(obj); + DeviceClass *dc = DEVICE_GET_CLASS(dev); + BusState *bus; - bus_remove_child(dev->parent_bus, dev); + while (dev->num_child_bus) { + bus = QLIST_FIRST(&dev->child_bus); + qbus_free(bus); + } + if (dev->realized) { + if (qdev_get_vmsd(dev)) { + vmstate_unregister(dev, qdev_get_vmsd(dev), dev); + } + if (dc->exit) { + dc->exit(dev); + } + } + if (dev->parent_bus) { + bus_remove_child(dev->parent_bus, dev); + object_unref(OBJECT(dev->parent_bus)); + dev->parent_bus = NULL; + } } static void device_class_init(ObjectClass *class, void *data) { - class->unparent = qdev_remove_from_bus; + DeviceClass *dc = DEVICE_CLASS(class); + + class->unparent = device_unparent; + dc->realize = device_realize; } void device_reset(DeviceState *dev) @@ -743,7 +809,7 @@ Object *qdev_get_machine(void) return dev; } -static TypeInfo device_type_info = { +static const TypeInfo device_type_info = { .name = TYPE_DEVICE, .parent = TYPE_OBJECT, .instance_size = sizeof(DeviceState), @@ -762,22 +828,15 @@ static void qbus_initfn(Object *obj) QTAILQ_INIT(&bus->children); } +static void bus_class_init(ObjectClass *class, void *data) +{ + class->unparent = bus_unparent; +} + static void qbus_finalize(Object *obj) { BusState *bus = BUS(obj); - BusChild *kid; - while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { - DeviceState *dev = kid->child; - qdev_free(dev); - } - if (bus->parent) { - QLIST_REMOVE(bus, sibling); - bus->parent->num_child_bus--; - } else { - assert(bus != sysbus_get_default()); /* main_system_bus is never freed */ - qemu_unregister_reset(qbus_reset_all_fn, bus); - } g_free((char *)bus->name); } @@ -789,6 +848,7 @@ static const TypeInfo bus_info = { .class_size = sizeof(BusClass), .instance_init = qbus_initfn, .instance_finalize = qbus_finalize, + .class_init = bus_class_init, }; static void qdev_register_types(void) diff --git a/hw/qdev.h b/hw/qdev.h index 365b8d6ca2..5cb8b080a6 100644 --- a/hw/qdev.h +++ b/hw/qdev.h @@ -2,8 +2,7 @@ #define QDEV_H #include "hw/hw.h" -#include "qdev-core.h" -#include "qdev-properties.h" -#include "qdev-monitor.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" #endif diff --git a/hw/qxl-logger.c b/hw/qxl-logger.c index 3cd85d9b97..84f9aa1eda 100644 --- a/hw/qxl-logger.c +++ b/hw/qxl-logger.c @@ -20,7 +20,7 @@ */ #include "qemu/timer.h" -#include "qxl.h" +#include "hw/qxl.h" static const char *qxl_type[] = { [ QXL_CMD_NOP ] = "nop", diff --git a/hw/qxl-render.c b/hw/qxl-render.c index 88e63f8085..d77df42b7e 100644 --- a/hw/qxl-render.c +++ b/hw/qxl-render.c @@ -19,7 +19,7 @@ * along with this program; if not, see . */ -#include "qxl.h" +#include "hw/qxl.h" static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) { @@ -118,7 +118,8 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) qxl->guest_primary.surface.height, qxl->guest_primary.bits_pp, qxl->guest_primary.abs_stride, - qxl->guest_primary.data); + qxl->guest_primary.data, + false); } else { qemu_resize_displaysurface(vga->ds, qxl->guest_primary.surface.width, diff --git a/hw/qxl.c b/hw/qxl.c index d08b9bd3c1..ef693486c2 100644 --- a/hw/qxl.c +++ b/hw/qxl.c @@ -27,7 +27,7 @@ #include "sysemu/sysemu.h" #include "trace.h" -#include "qxl.h" +#include "hw/qxl.h" /* * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as @@ -37,33 +37,25 @@ */ #undef SPICE_RING_PROD_ITEM #define SPICE_RING_PROD_ITEM(qxl, r, ret) { \ - typeof(r) start = r; \ - typeof(r) end = r + 1; \ uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \ - typeof(&(r)->items[prod]) m_item = &(r)->items[prod]; \ - if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \ + if (prod >= ARRAY_SIZE((r)->items)) { \ qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " \ - "! %p <= %p < %p", (uint8_t *)start, \ - (uint8_t *)m_item, (uint8_t *)end); \ + "%u >= %zu", prod, ARRAY_SIZE((r)->items)); \ ret = NULL; \ } else { \ - ret = &m_item->el; \ + ret = &(r)->items[prod].el; \ } \ } #undef SPICE_RING_CONS_ITEM #define SPICE_RING_CONS_ITEM(qxl, r, ret) { \ - typeof(r) start = r; \ - typeof(r) end = r + 1; \ uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \ - typeof(&(r)->items[cons]) m_item = &(r)->items[cons]; \ - if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \ + if (cons >= ARRAY_SIZE((r)->items)) { \ qxl_set_guest_bug(qxl, "SPICE_RING_CONS_ITEM indices mismatch " \ - "! %p <= %p < %p", (uint8_t *)start, \ - (uint8_t *)m_item, (uint8_t *)end); \ + "%u >= %zu", cons, ARRAY_SIZE((r)->items)); \ ret = NULL; \ } else { \ - ret = &m_item->el; \ + ret = &(r)->items[cons].el; \ } \ } @@ -88,9 +80,7 @@ #define QXL_MODE_EX(x_res, y_res) \ QXL_MODE_16_32(x_res, y_res, 0), \ - QXL_MODE_16_32(y_res, x_res, 1), \ - QXL_MODE_16_32(x_res, y_res, 2), \ - QXL_MODE_16_32(y_res, x_res, 3) + QXL_MODE_16_32(x_res, y_res, 1) static QXLMode qxl_modes[] = { QXL_MODE_EX(640, 480), @@ -314,10 +304,13 @@ static inline uint32_t msb_mask(uint32_t val) static ram_addr_t qxl_rom_size(void) { - uint32_t rom_size = sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes); + uint32_t required_rom_size = sizeof(QXLRom) + sizeof(QXLModes) + + sizeof(qxl_modes); + uint32_t rom_size = 8192; /* two pages */ - rom_size = MAX(rom_size, TARGET_PAGE_SIZE); - rom_size = msb_mask(rom_size * 2 - 1); + required_rom_size = MAX(required_rom_size, TARGET_PAGE_SIZE); + required_rom_size = msb_mask(required_rom_size * 2 - 1); + assert(required_rom_size <= rom_size); return rom_size; } @@ -953,15 +946,23 @@ static void interface_set_client_capabilities(QXLInstance *sin, { PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + if (qxl->revision < 4) { + trace_qxl_set_client_capabilities_unsupported_by_revision(qxl->id, + qxl->revision); + return; + } + if (runstate_check(RUN_STATE_INMIGRATE) || runstate_check(RUN_STATE_POSTMIGRATE)) { return; } qxl->shadow_rom.client_present = client_present; - memcpy(qxl->shadow_rom.client_capabilities, caps, sizeof(caps)); + memcpy(qxl->shadow_rom.client_capabilities, caps, + sizeof(qxl->shadow_rom.client_capabilities)); qxl->rom->client_present = client_present; - memcpy(qxl->rom->client_capabilities, caps, sizeof(caps)); + memcpy(qxl->rom->client_capabilities, caps, + sizeof(qxl->rom->client_capabilities)); qxl_rom_set_dirty(qxl); qxl_send_events(qxl, QXL_INTERRUPT_CLIENT); @@ -985,6 +986,11 @@ static int interface_client_monitors_config(QXLInstance *sin, QXLRom *rom = memory_region_get_ram_ptr(&qxl->rom_bar); int i; + if (qxl->revision < 4) { + trace_qxl_client_monitors_config_unsupported_by_device(qxl->id, + qxl->revision); + return 0; + } /* * Older windows drivers set int_mask to 0 when their ISR is called, * then later set it to ~0. So it doesn't relate to the actual interrupts @@ -2030,7 +2036,7 @@ static int qxl_init_common(PCIQXLDevice *qxl) qxl->ssd.qxl.base.sif = &qxl_interface.base; qxl->ssd.qxl.id = qxl->id; if (qemu_spice_add_interface(&qxl->ssd.qxl.base) != 0) { - error_report("qxl interface %d.%d not supported by spice-server\n", + error_report("qxl interface %d.%d not supported by spice-server", SPICE_INTERFACE_QXL_MAJOR, SPICE_INTERFACE_QXL_MINOR); return -1; } @@ -2310,7 +2316,7 @@ static void qxl_primary_class_init(ObjectClass *klass, void *data) dc->props = qxl_properties; } -static TypeInfo qxl_primary_info = { +static const TypeInfo qxl_primary_info = { .name = "qxl-vga", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIQXLDevice), @@ -2332,7 +2338,7 @@ static void qxl_secondary_class_init(ObjectClass *klass, void *data) dc->props = qxl_properties; } -static TypeInfo qxl_secondary_info = { +static const TypeInfo qxl_secondary_info = { .name = "qxl", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIQXLDevice), diff --git a/hw/qxl.h b/hw/qxl.h index f867a1d0ac..36f1a2502b 100644 --- a/hw/qxl.h +++ b/hw/qxl.h @@ -4,9 +4,9 @@ #include "qemu-common.h" #include "ui/console.h" -#include "hw.h" -#include "pci/pci.h" -#include "vga_int.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/vga_int.h" #include "qemu/thread.h" #include "ui/qemu-spice.h" diff --git a/hw/rc4030.c b/hw/rc4030.c index a0358a319c..b065515e67 100644 --- a/hw/rc4030.c +++ b/hw/rc4030.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "mips.h" +#include "hw/hw.h" +#include "hw/mips.h" #include "qemu/timer.h" /********************************************************/ diff --git a/hw/realview_gic.c b/hw/realview_gic.c index 5bc37a7120..0ec30caa06 100644 --- a/hw/realview_gic.c +++ b/hw/realview_gic.c @@ -7,7 +7,7 @@ * This code is licensed under the GPL. */ -#include "sysbus.h" +#include "hw/sysbus.h" typedef struct { SysBusDevice busdev; @@ -35,7 +35,7 @@ static int realview_gic_init(SysBusDevice *dev) qdev_prop_set_uint32(s->gic, "num-cpu", 1); qdev_prop_set_uint32(s->gic, "num-irq", numirq); qdev_init_nofail(s->gic); - busdev = sysbus_from_qdev(s->gic); + busdev = SYS_BUS_DEVICE(s->gic); /* Pass through outbound IRQ lines from the GIC */ sysbus_pass_irq(dev, busdev); @@ -59,7 +59,7 @@ static void realview_gic_class_init(ObjectClass *klass, void *data) sdc->init = realview_gic_init; } -static TypeInfo realview_gic_info = { +static const TypeInfo realview_gic_info = { .name = "realview_gic", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(RealViewGICState), diff --git a/hw/rtl8139.c b/hw/rtl8139.c index c59ec6b6df..786b875c58 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -51,12 +51,12 @@ /* For crc32 */ #include -#include "hw.h" -#include "pci/pci.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" #include "sysemu/dma.h" #include "qemu/timer.h" #include "net/net.h" -#include "loader.h" +#include "hw/loader.h" #include "sysemu/sysemu.h" #include "qemu/iov.h" @@ -786,7 +786,7 @@ static bool rtl8139_cp_rx_valid(RTL8139State *s) static int rtl8139_can_receive(NetClientState *nc) { - RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; + RTL8139State *s = qemu_get_nic_opaque(nc); int avail; /* Receive (drop) packets if card is disabled. */ @@ -808,7 +808,7 @@ static int rtl8139_can_receive(NetClientState *nc) static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt) { - RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; + RTL8139State *s = qemu_get_nic_opaque(nc); /* size is the length of the buffer passed to the driver */ int size = size_; const uint8_t *dot1q_buf = NULL; @@ -1258,7 +1258,8 @@ static void rtl8139_reset(DeviceState *d) s->BasicModeStatus = 0x7809; //s->BasicModeStatus |= 0x0040; /* UTP medium */ s->BasicModeStatus |= 0x0020; /* autonegotiation completed */ - s->BasicModeStatus |= 0x0004; /* link is up */ + /* preserve link state */ + s->BasicModeStatus |= qemu_get_queue(s->nic)->link_down ? 0 : 0x04; s->NWayAdvert = 0x05e1; /* all modes, full duplex */ s->NWayLPAR = 0x05e1; /* all modes, full duplex */ @@ -1786,7 +1787,7 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, } DPRINTF("+++ transmit loopback mode\n"); - rtl8139_do_receive(&s->nic->nc, buf, size, do_interrupt); + rtl8139_do_receive(qemu_get_queue(s->nic), buf, size, do_interrupt); if (iov) { g_free(buf2); @@ -1795,9 +1796,9 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, else { if (iov) { - qemu_sendv_packet(&s->nic->nc, iov, 3); + qemu_sendv_packet(qemu_get_queue(s->nic), iov, 3); } else { - qemu_send_packet(&s->nic->nc, buf, size); + qemu_send_packet(qemu_get_queue(s->nic), buf, size); } } } @@ -3229,7 +3230,7 @@ static int rtl8139_post_load(void *opaque, int version_id) /* nc.link_down can't be migrated, so infer link_down according * to link status bit in BasicModeStatus */ - s->nic->nc.link_down = (s->BasicModeStatus & 0x04) == 0; + qemu_get_queue(s->nic)->link_down = (s->BasicModeStatus & 0x04) == 0; return 0; } @@ -3428,7 +3429,7 @@ static void rtl8139_timer(void *opaque) static void rtl8139_cleanup(NetClientState *nc) { - RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; + RTL8139State *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -3445,12 +3446,12 @@ static void pci_rtl8139_uninit(PCIDevice *dev) } qemu_del_timer(s->timer); qemu_free_timer(s->timer); - qemu_del_net_client(&s->nic->nc); + qemu_del_nic(s->nic); } static void rtl8139_set_link_status(NetClientState *nc) { - RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; + RTL8139State *s = qemu_get_nic_opaque(nc); if (nc->link_down) { s->BasicModeStatus &= ~0x04; @@ -3502,7 +3503,7 @@ static int pci_rtl8139_init(PCIDevice *dev) s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->cplus_txbuffer = NULL; s->cplus_txbuffer_len = 0; @@ -3539,7 +3540,7 @@ static void rtl8139_class_init(ObjectClass *klass, void *data) dc->props = rtl8139_properties; } -static TypeInfo rtl8139_info = { +static const TypeInfo rtl8139_info = { .name = "rtl8139", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(RTL8139State), diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c deleted file mode 100644 index 20827761d0..0000000000 --- a/hw/s390-virtio.c +++ /dev/null @@ -1,341 +0,0 @@ -/* - * QEMU S390 virtio target - * - * Copyright (c) 2009 Alexander Graf - * - * 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 . - */ - -#include "hw.h" -#include "block/block.h" -#include "sysemu/blockdev.h" -#include "sysemu/sysemu.h" -#include "net/net.h" -#include "boards.h" -#include "monitor/monitor.h" -#include "loader.h" -#include "elf.h" -#include "hw/virtio.h" -#include "hw/sysbus.h" -#include "sysemu/kvm.h" -#include "exec/address-spaces.h" - -#include "hw/s390-virtio-bus.h" -#include "hw/s390x/sclp.h" - -//#define DEBUG_S390 - -#ifdef DEBUG_S390 -#define dprintf(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define dprintf(fmt, ...) \ - do { } while (0) -#endif - -#define KVM_S390_VIRTIO_NOTIFY 0 -#define KVM_S390_VIRTIO_RESET 1 -#define KVM_S390_VIRTIO_SET_STATUS 2 - -#define KERN_IMAGE_START 0x010000UL -#define KERN_PARM_AREA 0x010480UL -#define INITRD_START 0x800000UL -#define INITRD_PARM_START 0x010408UL -#define INITRD_PARM_SIZE 0x010410UL -#define PARMFILE_START 0x001000UL - -#define ZIPL_START 0x009000UL -#define ZIPL_LOAD_ADDR 0x009000UL -#define ZIPL_FILENAME "s390-zipl.rom" - -#define MAX_BLK_DEVS 10 - -static VirtIOS390Bus *s390_bus; -static S390CPU **ipi_states; - -S390CPU *s390_cpu_addr2state(uint16_t cpu_addr) -{ - if (cpu_addr >= smp_cpus) { - return NULL; - } - - return ipi_states[cpu_addr]; -} - -int s390_virtio_hypercall(CPUS390XState *env, uint64_t mem, uint64_t hypercall) -{ - int r = 0, i; - - dprintf("KVM hypercall: %ld\n", hypercall); - switch (hypercall) { - case KVM_S390_VIRTIO_NOTIFY: - if (mem > ram_size) { - VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus, - mem, &i); - if (dev) { - virtio_queue_notify(dev->vdev, i); - } else { - r = -EINVAL; - } - } else { - /* Early printk */ - } - break; - case KVM_S390_VIRTIO_RESET: - { - VirtIOS390Device *dev; - - dev = s390_virtio_bus_find_mem(s390_bus, mem); - virtio_reset(dev->vdev); - stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0); - s390_virtio_device_sync(dev); - s390_virtio_reset_idx(dev); - break; - } - case KVM_S390_VIRTIO_SET_STATUS: - { - VirtIOS390Device *dev; - - dev = s390_virtio_bus_find_mem(s390_bus, mem); - if (dev) { - s390_virtio_device_update_status(dev); - } else { - r = -EINVAL; - } - break; - } - default: - r = -EINVAL; - break; - } - - return r; -} - -/* - * The number of running CPUs. On s390 a shutdown is the state of all CPUs - * being either stopped or disabled (for interrupts) waiting. We have to - * track this number to call the shutdown sequence accordingly. This - * number is modified either on startup or while holding the big qemu lock. - */ -static unsigned s390_running_cpus; - -void s390_add_running_cpu(CPUS390XState *env) -{ - if (env->halted) { - s390_running_cpus++; - env->halted = 0; - env->exception_index = -1; - } -} - -unsigned s390_del_running_cpu(CPUS390XState *env) -{ - if (env->halted == 0) { - assert(s390_running_cpus >= 1); - s390_running_cpus--; - env->halted = 1; - env->exception_index = EXCP_HLT; - } - return s390_running_cpus; -} - -/* PC hardware initialisation */ -static void s390_init(QEMUMachineInitArgs *args) -{ - ram_addr_t my_ram_size = args->ram_size; - const char *cpu_model = args->cpu_model; - const char *kernel_filename = args->kernel_filename; - const char *kernel_cmdline = args->kernel_cmdline; - const char *initrd_filename = args->initrd_filename; - CPUS390XState *env = NULL; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - ram_addr_t kernel_size = 0; - ram_addr_t initrd_offset; - ram_addr_t initrd_size = 0; - int shift = 0; - uint8_t *storage_keys; - void *virtio_region; - hwaddr virtio_region_len; - hwaddr virtio_region_start; - int i; - - /* s390x ram size detection needs a 16bit multiplier + an increment. So - guests > 64GB can be specified in 2MB steps etc. */ - while ((my_ram_size >> (20 + shift)) > 65535) { - shift++; - } - my_ram_size = my_ram_size >> (20 + shift) << (20 + shift); - - /* lets propagate the changed ram size into the global variable. */ - ram_size = my_ram_size; - - /* get a BUS */ - s390_bus = s390_virtio_bus_init(&my_ram_size); - s390_sclp_init(); - - /* allocate RAM */ - memory_region_init_ram(ram, "s390.ram", my_ram_size); - vmstate_register_ram_global(ram); - memory_region_add_subregion(sysmem, 0, ram); - - /* clear virtio region */ - virtio_region_len = my_ram_size - ram_size; - virtio_region_start = ram_size; - virtio_region = cpu_physical_memory_map(virtio_region_start, - &virtio_region_len, true); - memset(virtio_region, 0, virtio_region_len); - cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1, - virtio_region_len); - - /* allocate storage keys */ - storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); - - /* init CPUs */ - if (cpu_model == NULL) { - cpu_model = "host"; - } - - ipi_states = g_malloc(sizeof(S390CPU *) * smp_cpus); - - for (i = 0; i < smp_cpus; i++) { - S390CPU *cpu; - CPUS390XState *tmp_env; - - cpu = cpu_s390x_init(cpu_model); - tmp_env = &cpu->env; - if (!env) { - env = tmp_env; - } - ipi_states[i] = cpu; - tmp_env->halted = 1; - tmp_env->exception_index = EXCP_HLT; - tmp_env->storage_keys = storage_keys; - } - - /* One CPU has to run */ - s390_add_running_cpu(env); - - if (kernel_filename) { - - kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, NULL, - NULL, 1, ELF_MACHINE, 0); - if (kernel_size == -1UL) { - kernel_size = load_image_targphys(kernel_filename, 0, ram_size); - } - if (kernel_size == -1UL) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); - exit(1); - } - /* - * we can not rely on the ELF entry point, since up to 3.2 this - * value was 0x800 (the SALIPL loader) and it wont work. For - * all (Linux) cases 0x10000 (KERN_IMAGE_START) should be fine. - */ - env->psw.addr = KERN_IMAGE_START; - env->psw.mask = 0x0000000180000000ULL; - } else { - ram_addr_t bios_size = 0; - char *bios_filename; - - /* Load zipl bootloader */ - if (bios_name == NULL) { - bios_name = ZIPL_FILENAME; - } - - bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - bios_size = load_image_targphys(bios_filename, ZIPL_LOAD_ADDR, 4096); - g_free(bios_filename); - - if ((long)bios_size < 0) { - hw_error("could not load bootloader '%s'\n", bios_name); - } - - if (bios_size > 4096) { - hw_error("stage1 bootloader is > 4k\n"); - } - - env->psw.addr = ZIPL_START; - env->psw.mask = 0x0000000180000000ULL; - } - - if (initrd_filename) { - initrd_offset = INITRD_START; - while (kernel_size + 0x100000 > initrd_offset) { - initrd_offset += 0x100000; - } - initrd_size = load_image_targphys(initrd_filename, initrd_offset, - ram_size - initrd_offset); - if (initrd_size == -1UL) { - fprintf(stderr, "qemu: could not load initrd '%s'\n", - initrd_filename); - exit(1); - } - - /* we have to overwrite values in the kernel image, which are "rom" */ - stq_p(rom_ptr(INITRD_PARM_START), initrd_offset); - stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size); - } - - if (rom_ptr(KERN_PARM_AREA)) { - /* we have to overwrite values in the kernel image, which are "rom" */ - memcpy(rom_ptr(KERN_PARM_AREA), kernel_cmdline, - strlen(kernel_cmdline) + 1); - } - - /* Create VirtIO network adapters */ - for(i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - DeviceState *dev; - - if (!nd->model) { - nd->model = g_strdup("virtio"); - } - - if (strcmp(nd->model, "virtio")) { - fprintf(stderr, "S390 only supports VirtIO nics\n"); - exit(1); - } - - dev = qdev_create((BusState *)s390_bus, "virtio-net-s390"); - qdev_set_nic_properties(dev, nd); - qdev_init_nofail(dev); - } -} - -static QEMUMachine s390_machine = { - .name = "s390-virtio", - .alias = "s390", - .desc = "VirtIO based S390 machine", - .init = s390_init, - .block_default_type = IF_VIRTIO, - .no_cdrom = 1, - .no_floppy = 1, - .no_serial = 1, - .no_parallel = 1, - .no_sdcard = 1, - .use_virtcon = 1, - .max_cpus = 255, - .is_default = 1, -}; - -static void s390_machine_init(void) -{ - qemu_register_machine(&s390_machine); -} - -machine_init(s390_machine_init); - diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 096dfcd6a1..9f2f41989c 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -1,6 +1,9 @@ obj-y = s390-virtio-bus.o s390-virtio.o - -obj-y := $(addprefix ../,$(obj-y)) +obj-y += s390-virtio-hcall.o obj-y += sclp.o obj-y += event-facility.o obj-y += sclpquiesce.o sclpconsole.o +obj-y += ipl.o +obj-y += css.o +obj-y += s390-virtio-ccw.o +obj-y += virtio-ccw.o diff --git a/hw/s390x/css.c b/hw/s390x/css.c new file mode 100644 index 0000000000..e526a1c86c --- /dev/null +++ b/hw/s390x/css.c @@ -0,0 +1,1280 @@ +/* + * Channel subsystem base support. + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include +#include "qemu/bitops.h" +#include "cpu.h" +#include "ioinst.h" +#include "css.h" +#include "trace.h" + +typedef struct CrwContainer { + CRW crw; + QTAILQ_ENTRY(CrwContainer) sibling; +} CrwContainer; + +typedef struct ChpInfo { + uint8_t in_use; + uint8_t type; + uint8_t is_virtual; +} ChpInfo; + +typedef struct SubchSet { + SubchDev *sch[MAX_SCHID + 1]; + unsigned long schids_used[BITS_TO_LONGS(MAX_SCHID + 1)]; + unsigned long devnos_used[BITS_TO_LONGS(MAX_SCHID + 1)]; +} SubchSet; + +typedef struct CssImage { + SubchSet *sch_set[MAX_SSID + 1]; + ChpInfo chpids[MAX_CHPID + 1]; +} CssImage; + +typedef struct ChannelSubSys { + QTAILQ_HEAD(, CrwContainer) pending_crws; + bool do_crw_mchk; + bool crws_lost; + uint8_t max_cssid; + uint8_t max_ssid; + bool chnmon_active; + uint64_t chnmon_area; + CssImage *css[MAX_CSSID + 1]; + uint8_t default_cssid; +} ChannelSubSys; + +static ChannelSubSys *channel_subsys; + +int css_create_css_image(uint8_t cssid, bool default_image) +{ + trace_css_new_image(cssid, default_image ? "(default)" : ""); + if (cssid > MAX_CSSID) { + return -EINVAL; + } + if (channel_subsys->css[cssid]) { + return -EBUSY; + } + channel_subsys->css[cssid] = g_malloc0(sizeof(CssImage)); + if (default_image) { + channel_subsys->default_cssid = cssid; + } + return 0; +} + +static uint16_t css_build_subchannel_id(SubchDev *sch) +{ + if (channel_subsys->max_cssid > 0) { + return (sch->cssid << 8) | (1 << 3) | (sch->ssid << 1) | 1; + } + return (sch->ssid << 1) | 1; +} + +static void css_inject_io_interrupt(SubchDev *sch) +{ + S390CPU *cpu = s390_cpu_addr2state(0); + uint8_t isc = (sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ISC) >> 11; + + trace_css_io_interrupt(sch->cssid, sch->ssid, sch->schid, + sch->curr_status.pmcw.intparm, isc, ""); + s390_io_interrupt(cpu, + css_build_subchannel_id(sch), + sch->schid, + sch->curr_status.pmcw.intparm, + isc << 27); +} + +void css_conditional_io_interrupt(SubchDev *sch) +{ + /* + * If the subchannel is not currently status pending, make it pending + * with alert status. + */ + if (!(sch->curr_status.scsw.ctrl & SCSW_STCTL_STATUS_PEND)) { + S390CPU *cpu = s390_cpu_addr2state(0); + uint8_t isc = (sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ISC) >> 11; + + trace_css_io_interrupt(sch->cssid, sch->ssid, sch->schid, + sch->curr_status.pmcw.intparm, isc, + "(unsolicited)"); + sch->curr_status.scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; + sch->curr_status.scsw.ctrl |= + SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + /* Inject an I/O interrupt. */ + s390_io_interrupt(cpu, + css_build_subchannel_id(sch), + sch->schid, + sch->curr_status.pmcw.intparm, + isc << 27); + } +} + +static void sch_handle_clear_func(SubchDev *sch) +{ + PMCW *p = &sch->curr_status.pmcw; + SCSW *s = &sch->curr_status.scsw; + int path; + + /* Path management: In our simple css, we always choose the only path. */ + path = 0x80; + + /* Reset values prior to 'issueing the clear signal'. */ + p->lpum = 0; + p->pom = 0xff; + s->flags &= ~SCSW_FLAGS_MASK_PNO; + + /* We always 'attempt to issue the clear signal', and we always succeed. */ + sch->orb = NULL; + sch->channel_prog = 0x0; + sch->last_cmd_valid = false; + s->ctrl &= ~SCSW_ACTL_CLEAR_PEND; + s->ctrl |= SCSW_STCTL_STATUS_PEND; + + s->dstat = 0; + s->cstat = 0; + p->lpum = path; + +} + +static void sch_handle_halt_func(SubchDev *sch) +{ + + PMCW *p = &sch->curr_status.pmcw; + SCSW *s = &sch->curr_status.scsw; + int path; + + /* Path management: In our simple css, we always choose the only path. */ + path = 0x80; + + /* We always 'attempt to issue the halt signal', and we always succeed. */ + sch->orb = NULL; + sch->channel_prog = 0x0; + sch->last_cmd_valid = false; + s->ctrl &= ~SCSW_ACTL_HALT_PEND; + s->ctrl |= SCSW_STCTL_STATUS_PEND; + + if ((s->ctrl & (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) || + !((s->ctrl & SCSW_ACTL_START_PEND) || + (s->ctrl & SCSW_ACTL_SUSP))) { + s->dstat = SCSW_DSTAT_DEVICE_END; + } + s->cstat = 0; + p->lpum = path; + +} + +static void copy_sense_id_to_guest(SenseId *dest, SenseId *src) +{ + int i; + + dest->reserved = src->reserved; + dest->cu_type = cpu_to_be16(src->cu_type); + dest->cu_model = src->cu_model; + dest->dev_type = cpu_to_be16(src->dev_type); + dest->dev_model = src->dev_model; + dest->unused = src->unused; + for (i = 0; i < ARRAY_SIZE(dest->ciw); i++) { + dest->ciw[i].type = src->ciw[i].type; + dest->ciw[i].command = src->ciw[i].command; + dest->ciw[i].count = cpu_to_be16(src->ciw[i].count); + } +} + +static CCW1 copy_ccw_from_guest(hwaddr addr) +{ + CCW1 tmp; + CCW1 ret; + + cpu_physical_memory_read(addr, &tmp, sizeof(tmp)); + ret.cmd_code = tmp.cmd_code; + ret.flags = tmp.flags; + ret.count = be16_to_cpu(tmp.count); + ret.cda = be32_to_cpu(tmp.cda); + + return ret; +} + +static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr) +{ + int ret; + bool check_len; + int len; + CCW1 ccw; + + if (!ccw_addr) { + return -EIO; + } + + ccw = copy_ccw_from_guest(ccw_addr); + + /* Check for invalid command codes. */ + if ((ccw.cmd_code & 0x0f) == 0) { + return -EINVAL; + } + if (((ccw.cmd_code & 0x0f) == CCW_CMD_TIC) && + ((ccw.cmd_code & 0xf0) != 0)) { + return -EINVAL; + } + + if (ccw.flags & CCW_FLAG_SUSPEND) { + return -EINPROGRESS; + } + + check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC)); + + /* Look at the command. */ + switch (ccw.cmd_code) { + case CCW_CMD_NOOP: + /* Nothing to do. */ + ret = 0; + break; + case CCW_CMD_BASIC_SENSE: + if (check_len) { + if (ccw.count != sizeof(sch->sense_data)) { + ret = -EINVAL; + break; + } + } + len = MIN(ccw.count, sizeof(sch->sense_data)); + cpu_physical_memory_write(ccw.cda, sch->sense_data, len); + sch->curr_status.scsw.count = ccw.count - len; + memset(sch->sense_data, 0, sizeof(sch->sense_data)); + ret = 0; + break; + case CCW_CMD_SENSE_ID: + { + SenseId sense_id; + + copy_sense_id_to_guest(&sense_id, &sch->id); + /* Sense ID information is device specific. */ + if (check_len) { + if (ccw.count != sizeof(sense_id)) { + ret = -EINVAL; + break; + } + } + len = MIN(ccw.count, sizeof(sense_id)); + /* + * Only indicate 0xff in the first sense byte if we actually + * have enough place to store at least bytes 0-3. + */ + if (len >= 4) { + sense_id.reserved = 0xff; + } else { + sense_id.reserved = 0; + } + cpu_physical_memory_write(ccw.cda, &sense_id, len); + sch->curr_status.scsw.count = ccw.count - len; + ret = 0; + break; + } + case CCW_CMD_TIC: + if (sch->last_cmd_valid && (sch->last_cmd.cmd_code == CCW_CMD_TIC)) { + ret = -EINVAL; + break; + } + if (ccw.flags & (CCW_FLAG_CC | CCW_FLAG_DC)) { + ret = -EINVAL; + break; + } + sch->channel_prog = ccw.cda; + ret = -EAGAIN; + break; + default: + if (sch->ccw_cb) { + /* Handle device specific commands. */ + ret = sch->ccw_cb(sch, ccw); + } else { + ret = -ENOSYS; + } + break; + } + sch->last_cmd = ccw; + sch->last_cmd_valid = true; + if (ret == 0) { + if (ccw.flags & CCW_FLAG_CC) { + sch->channel_prog += 8; + ret = -EAGAIN; + } + } + + return ret; +} + +static void sch_handle_start_func(SubchDev *sch) +{ + + PMCW *p = &sch->curr_status.pmcw; + SCSW *s = &sch->curr_status.scsw; + ORB *orb = sch->orb; + int path; + int ret; + + /* Path management: In our simple css, we always choose the only path. */ + path = 0x80; + + if (!(s->ctrl & SCSW_ACTL_SUSP)) { + /* Look at the orb and try to execute the channel program. */ + p->intparm = orb->intparm; + if (!(orb->lpm & path)) { + /* Generate a deferred cc 3 condition. */ + s->flags |= SCSW_FLAGS_MASK_CC; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND); + return; + } + } else { + s->ctrl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND); + } + sch->last_cmd_valid = false; + do { + ret = css_interpret_ccw(sch, sch->channel_prog); + switch (ret) { + case -EAGAIN: + /* ccw chain, continue processing */ + break; + case 0: + /* success */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + SCSW_STCTL_STATUS_PEND; + s->dstat = SCSW_DSTAT_CHANNEL_END | SCSW_DSTAT_DEVICE_END; + break; + case -ENOSYS: + /* unsupported command, generate unit check (command reject) */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->dstat = SCSW_DSTAT_UNIT_CHECK; + /* Set sense bit 0 in ecw0. */ + sch->sense_data[0] = 0x80; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + break; + case -EFAULT: + /* memory problem, generate channel data check */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->cstat = SCSW_CSTAT_DATA_CHECK; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + break; + case -EBUSY: + /* subchannel busy, generate deferred cc 1 */ + s->flags &= ~SCSW_FLAGS_MASK_CC; + s->flags |= (1 << 8); + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + break; + case -EINPROGRESS: + /* channel program has been suspended */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->ctrl |= SCSW_ACTL_SUSP; + break; + default: + /* error, generate channel program check */ + s->ctrl &= ~SCSW_ACTL_START_PEND; + s->cstat = SCSW_CSTAT_PROG_CHECK; + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; + break; + } + } while (ret == -EAGAIN); + +} + +/* + * On real machines, this would run asynchronously to the main vcpus. + * We might want to make some parts of the ssch handling (interpreting + * read/writes) asynchronous later on if we start supporting more than + * our current very simple devices. + */ +static void do_subchannel_work(SubchDev *sch) +{ + + SCSW *s = &sch->curr_status.scsw; + + if (s->ctrl & SCSW_FCTL_CLEAR_FUNC) { + sch_handle_clear_func(sch); + } else if (s->ctrl & SCSW_FCTL_HALT_FUNC) { + sch_handle_halt_func(sch); + } else if (s->ctrl & SCSW_FCTL_START_FUNC) { + sch_handle_start_func(sch); + } else { + /* Cannot happen. */ + return; + } + css_inject_io_interrupt(sch); +} + +static void copy_pmcw_to_guest(PMCW *dest, const PMCW *src) +{ + int i; + + dest->intparm = cpu_to_be32(src->intparm); + dest->flags = cpu_to_be16(src->flags); + dest->devno = cpu_to_be16(src->devno); + dest->lpm = src->lpm; + dest->pnom = src->pnom; + dest->lpum = src->lpum; + dest->pim = src->pim; + dest->mbi = cpu_to_be16(src->mbi); + dest->pom = src->pom; + dest->pam = src->pam; + for (i = 0; i < ARRAY_SIZE(dest->chpid); i++) { + dest->chpid[i] = src->chpid[i]; + } + dest->chars = cpu_to_be32(src->chars); +} + +static void copy_scsw_to_guest(SCSW *dest, const SCSW *src) +{ + dest->flags = cpu_to_be16(src->flags); + dest->ctrl = cpu_to_be16(src->ctrl); + dest->cpa = cpu_to_be32(src->cpa); + dest->dstat = src->dstat; + dest->cstat = src->cstat; + dest->count = cpu_to_be16(src->count); +} + +static void copy_schib_to_guest(SCHIB *dest, const SCHIB *src) +{ + int i; + + copy_pmcw_to_guest(&dest->pmcw, &src->pmcw); + copy_scsw_to_guest(&dest->scsw, &src->scsw); + dest->mba = cpu_to_be64(src->mba); + for (i = 0; i < ARRAY_SIZE(dest->mda); i++) { + dest->mda[i] = src->mda[i]; + } +} + +int css_do_stsch(SubchDev *sch, SCHIB *schib) +{ + /* Use current status. */ + copy_schib_to_guest(schib, &sch->curr_status); + return 0; +} + +static void copy_pmcw_from_guest(PMCW *dest, const PMCW *src) +{ + int i; + + dest->intparm = be32_to_cpu(src->intparm); + dest->flags = be16_to_cpu(src->flags); + dest->devno = be16_to_cpu(src->devno); + dest->lpm = src->lpm; + dest->pnom = src->pnom; + dest->lpum = src->lpum; + dest->pim = src->pim; + dest->mbi = be16_to_cpu(src->mbi); + dest->pom = src->pom; + dest->pam = src->pam; + for (i = 0; i < ARRAY_SIZE(dest->chpid); i++) { + dest->chpid[i] = src->chpid[i]; + } + dest->chars = be32_to_cpu(src->chars); +} + +static void copy_scsw_from_guest(SCSW *dest, const SCSW *src) +{ + dest->flags = be16_to_cpu(src->flags); + dest->ctrl = be16_to_cpu(src->ctrl); + dest->cpa = be32_to_cpu(src->cpa); + dest->dstat = src->dstat; + dest->cstat = src->cstat; + dest->count = be16_to_cpu(src->count); +} + +static void copy_schib_from_guest(SCHIB *dest, const SCHIB *src) +{ + int i; + + copy_pmcw_from_guest(&dest->pmcw, &src->pmcw); + copy_scsw_from_guest(&dest->scsw, &src->scsw); + dest->mba = be64_to_cpu(src->mba); + for (i = 0; i < ARRAY_SIZE(dest->mda); i++) { + dest->mda[i] = src->mda[i]; + } +} + +int css_do_msch(SubchDev *sch, SCHIB *orig_schib) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + int ret; + SCHIB schib; + + if (!(sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_DNV)) { + ret = 0; + goto out; + } + + if (s->ctrl & SCSW_STCTL_STATUS_PEND) { + ret = -EINPROGRESS; + goto out; + } + + if (s->ctrl & + (SCSW_FCTL_START_FUNC|SCSW_FCTL_HALT_FUNC|SCSW_FCTL_CLEAR_FUNC)) { + ret = -EBUSY; + goto out; + } + + copy_schib_from_guest(&schib, orig_schib); + /* Only update the program-modifiable fields. */ + p->intparm = schib.pmcw.intparm; + p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | + PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | + PMCW_FLAGS_MASK_MP); + p->flags |= schib.pmcw.flags & + (PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | + PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | + PMCW_FLAGS_MASK_MP); + p->lpm = schib.pmcw.lpm; + p->mbi = schib.pmcw.mbi; + p->pom = schib.pmcw.pom; + p->chars &= ~(PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); + p->chars |= schib.pmcw.chars & + (PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); + sch->curr_status.mba = schib.mba; + + ret = 0; + +out: + return ret; +} + +int css_do_xsch(SubchDev *sch) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + int ret; + + if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { + ret = -ENODEV; + goto out; + } + + if (!(s->ctrl & SCSW_CTRL_MASK_FCTL) || + ((s->ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) || + (!(s->ctrl & + (SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND | SCSW_ACTL_SUSP))) || + (s->ctrl & SCSW_ACTL_SUBCH_ACTIVE)) { + ret = -EINPROGRESS; + goto out; + } + + if (s->ctrl & SCSW_CTRL_MASK_STCTL) { + ret = -EBUSY; + goto out; + } + + /* Cancel the current operation. */ + s->ctrl &= ~(SCSW_FCTL_START_FUNC | + SCSW_ACTL_RESUME_PEND | + SCSW_ACTL_START_PEND | + SCSW_ACTL_SUSP); + sch->channel_prog = 0x0; + sch->last_cmd_valid = false; + sch->orb = NULL; + s->dstat = 0; + s->cstat = 0; + ret = 0; + +out: + return ret; +} + +int css_do_csch(SubchDev *sch) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + int ret; + + if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { + ret = -ENODEV; + goto out; + } + + /* Trigger the clear function. */ + s->ctrl &= ~(SCSW_CTRL_MASK_FCTL | SCSW_CTRL_MASK_ACTL); + s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_CLEAR_FUNC; + + do_subchannel_work(sch); + ret = 0; + +out: + return ret; +} + +int css_do_hsch(SubchDev *sch) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + int ret; + + if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { + ret = -ENODEV; + goto out; + } + + if (((s->ctrl & SCSW_CTRL_MASK_STCTL) == SCSW_STCTL_STATUS_PEND) || + (s->ctrl & (SCSW_STCTL_PRIMARY | + SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT))) { + ret = -EINPROGRESS; + goto out; + } + + if (s->ctrl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { + ret = -EBUSY; + goto out; + } + + /* Trigger the halt function. */ + s->ctrl |= SCSW_FCTL_HALT_FUNC; + s->ctrl &= ~SCSW_FCTL_START_FUNC; + if (((s->ctrl & SCSW_CTRL_MASK_ACTL) == + (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) && + ((s->ctrl & SCSW_CTRL_MASK_STCTL) == SCSW_STCTL_INTERMEDIATE)) { + s->ctrl &= ~SCSW_STCTL_STATUS_PEND; + } + s->ctrl |= SCSW_ACTL_HALT_PEND; + + do_subchannel_work(sch); + ret = 0; + +out: + return ret; +} + +static void css_update_chnmon(SubchDev *sch) +{ + if (!(sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_MME)) { + /* Not active. */ + return; + } + /* The counter is conveniently located at the beginning of the struct. */ + if (sch->curr_status.pmcw.chars & PMCW_CHARS_MASK_MBFC) { + /* Format 1, per-subchannel area. */ + uint32_t count; + + count = ldl_phys(sch->curr_status.mba); + count++; + stl_phys(sch->curr_status.mba, count); + } else { + /* Format 0, global area. */ + uint32_t offset; + uint16_t count; + + offset = sch->curr_status.pmcw.mbi << 5; + count = lduw_phys(channel_subsys->chnmon_area + offset); + count++; + stw_phys(channel_subsys->chnmon_area + offset, count); + } +} + +int css_do_ssch(SubchDev *sch, ORB *orb) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + int ret; + + if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { + ret = -ENODEV; + goto out; + } + + if (s->ctrl & SCSW_STCTL_STATUS_PEND) { + ret = -EINPROGRESS; + goto out; + } + + if (s->ctrl & (SCSW_FCTL_START_FUNC | + SCSW_FCTL_HALT_FUNC | + SCSW_FCTL_CLEAR_FUNC)) { + ret = -EBUSY; + goto out; + } + + /* If monitoring is active, update counter. */ + if (channel_subsys->chnmon_active) { + css_update_chnmon(sch); + } + sch->orb = orb; + sch->channel_prog = orb->cpa; + /* Trigger the start function. */ + s->ctrl |= (SCSW_FCTL_START_FUNC | SCSW_ACTL_START_PEND); + s->flags &= ~SCSW_FLAGS_MASK_PNO; + + do_subchannel_work(sch); + ret = 0; + +out: + return ret; +} + +static void copy_irb_to_guest(IRB *dest, const IRB *src) +{ + int i; + + copy_scsw_to_guest(&dest->scsw, &src->scsw); + + for (i = 0; i < ARRAY_SIZE(dest->esw); i++) { + dest->esw[i] = cpu_to_be32(src->esw[i]); + } + for (i = 0; i < ARRAY_SIZE(dest->ecw); i++) { + dest->ecw[i] = cpu_to_be32(src->ecw[i]); + } + for (i = 0; i < ARRAY_SIZE(dest->emw); i++) { + dest->emw[i] = cpu_to_be32(src->emw[i]); + } +} + +int css_do_tsch(SubchDev *sch, IRB *target_irb) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + uint16_t stctl; + uint16_t fctl; + uint16_t actl; + IRB irb; + int ret; + + if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { + ret = 3; + goto out; + } + + stctl = s->ctrl & SCSW_CTRL_MASK_STCTL; + fctl = s->ctrl & SCSW_CTRL_MASK_FCTL; + actl = s->ctrl & SCSW_CTRL_MASK_ACTL; + + /* Prepare the irb for the guest. */ + memset(&irb, 0, sizeof(IRB)); + + /* Copy scsw from current status. */ + memcpy(&irb.scsw, s, sizeof(SCSW)); + if (stctl & SCSW_STCTL_STATUS_PEND) { + if (s->cstat & (SCSW_CSTAT_DATA_CHECK | + SCSW_CSTAT_CHN_CTRL_CHK | + SCSW_CSTAT_INTF_CTRL_CHK)) { + irb.scsw.flags |= SCSW_FLAGS_MASK_ESWF; + irb.esw[0] = 0x04804000; + } else { + irb.esw[0] = 0x00800000; + } + /* If a unit check is pending, copy sense data. */ + if ((s->dstat & SCSW_DSTAT_UNIT_CHECK) && + (p->chars & PMCW_CHARS_MASK_CSENSE)) { + irb.scsw.flags |= SCSW_FLAGS_MASK_ESWF | SCSW_FLAGS_MASK_ECTL; + memcpy(irb.ecw, sch->sense_data, sizeof(sch->sense_data)); + irb.esw[1] = 0x02000000 | (sizeof(sch->sense_data) << 8); + } + } + /* Store the irb to the guest. */ + copy_irb_to_guest(target_irb, &irb); + + /* Clear conditions on subchannel, if applicable. */ + if (stctl & SCSW_STCTL_STATUS_PEND) { + s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + if ((stctl != (SCSW_STCTL_INTERMEDIATE | SCSW_STCTL_STATUS_PEND)) || + ((fctl & SCSW_FCTL_HALT_FUNC) && + (actl & SCSW_ACTL_SUSP))) { + s->ctrl &= ~SCSW_CTRL_MASK_FCTL; + } + if (stctl != (SCSW_STCTL_INTERMEDIATE | SCSW_STCTL_STATUS_PEND)) { + s->flags &= ~SCSW_FLAGS_MASK_PNO; + s->ctrl &= ~(SCSW_ACTL_RESUME_PEND | + SCSW_ACTL_START_PEND | + SCSW_ACTL_HALT_PEND | + SCSW_ACTL_CLEAR_PEND | + SCSW_ACTL_SUSP); + } else { + if ((actl & SCSW_ACTL_SUSP) && + (fctl & SCSW_FCTL_START_FUNC)) { + s->flags &= ~SCSW_FLAGS_MASK_PNO; + if (fctl & SCSW_FCTL_HALT_FUNC) { + s->ctrl &= ~(SCSW_ACTL_RESUME_PEND | + SCSW_ACTL_START_PEND | + SCSW_ACTL_HALT_PEND | + SCSW_ACTL_CLEAR_PEND | + SCSW_ACTL_SUSP); + } else { + s->ctrl &= ~SCSW_ACTL_RESUME_PEND; + } + } + } + /* Clear pending sense data. */ + if (p->chars & PMCW_CHARS_MASK_CSENSE) { + memset(sch->sense_data, 0 , sizeof(sch->sense_data)); + } + } + + ret = ((stctl & SCSW_STCTL_STATUS_PEND) == 0); + +out: + return ret; +} + +static void copy_crw_to_guest(CRW *dest, const CRW *src) +{ + dest->flags = cpu_to_be16(src->flags); + dest->rsid = cpu_to_be16(src->rsid); +} + +int css_do_stcrw(CRW *crw) +{ + CrwContainer *crw_cont; + int ret; + + crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws); + if (crw_cont) { + QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling); + copy_crw_to_guest(crw, &crw_cont->crw); + g_free(crw_cont); + ret = 0; + } else { + /* List was empty, turn crw machine checks on again. */ + memset(crw, 0, sizeof(*crw)); + channel_subsys->do_crw_mchk = true; + ret = 1; + } + + return ret; +} + +int css_do_tpi(IOIntCode *int_code, int lowcore) +{ + /* No pending interrupts for !KVM. */ + return 0; + } + +int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid, + int rfmt, void *buf) +{ + int i, desc_size; + uint32_t words[8]; + uint32_t chpid_type_word; + CssImage *css; + + if (!m && !cssid) { + css = channel_subsys->css[channel_subsys->default_cssid]; + } else { + css = channel_subsys->css[cssid]; + } + if (!css) { + return 0; + } + desc_size = 0; + for (i = f_chpid; i <= l_chpid; i++) { + if (css->chpids[i].in_use) { + chpid_type_word = 0x80000000 | (css->chpids[i].type << 8) | i; + if (rfmt == 0) { + words[0] = cpu_to_be32(chpid_type_word); + words[1] = 0; + memcpy(buf + desc_size, words, 8); + desc_size += 8; + } else if (rfmt == 1) { + words[0] = cpu_to_be32(chpid_type_word); + words[1] = 0; + words[2] = 0; + words[3] = 0; + words[4] = 0; + words[5] = 0; + words[6] = 0; + words[7] = 0; + memcpy(buf + desc_size, words, 32); + desc_size += 32; + } + } + } + return desc_size; +} + +void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo) +{ + /* dct is currently ignored (not really meaningful for our devices) */ + /* TODO: Don't ignore mbk. */ + if (update && !channel_subsys->chnmon_active) { + /* Enable measuring. */ + channel_subsys->chnmon_area = mbo; + channel_subsys->chnmon_active = true; + } + if (!update && channel_subsys->chnmon_active) { + /* Disable measuring. */ + channel_subsys->chnmon_area = 0; + channel_subsys->chnmon_active = false; + } +} + +int css_do_rsch(SubchDev *sch) +{ + SCSW *s = &sch->curr_status.scsw; + PMCW *p = &sch->curr_status.pmcw; + int ret; + + if (!(p->flags & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA))) { + ret = -ENODEV; + goto out; + } + + if (s->ctrl & SCSW_STCTL_STATUS_PEND) { + ret = -EINPROGRESS; + goto out; + } + + if (((s->ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) || + (s->ctrl & SCSW_ACTL_RESUME_PEND) || + (!(s->ctrl & SCSW_ACTL_SUSP))) { + ret = -EINVAL; + goto out; + } + + /* If monitoring is active, update counter. */ + if (channel_subsys->chnmon_active) { + css_update_chnmon(sch); + } + + s->ctrl |= SCSW_ACTL_RESUME_PEND; + do_subchannel_work(sch); + ret = 0; + +out: + return ret; +} + +int css_do_rchp(uint8_t cssid, uint8_t chpid) +{ + uint8_t real_cssid; + + if (cssid > channel_subsys->max_cssid) { + return -EINVAL; + } + if (channel_subsys->max_cssid == 0) { + real_cssid = channel_subsys->default_cssid; + } else { + real_cssid = cssid; + } + if (!channel_subsys->css[real_cssid]) { + return -EINVAL; + } + + if (!channel_subsys->css[real_cssid]->chpids[chpid].in_use) { + return -ENODEV; + } + + if (!channel_subsys->css[real_cssid]->chpids[chpid].is_virtual) { + fprintf(stderr, + "rchp unsupported for non-virtual chpid %x.%02x!\n", + real_cssid, chpid); + return -ENODEV; + } + + /* We don't really use a channel path, so we're done here. */ + css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, + channel_subsys->max_cssid > 0 ? 1 : 0, chpid); + if (channel_subsys->max_cssid > 0) { + css_queue_crw(CRW_RSC_CHP, CRW_ERC_INIT, 0, real_cssid << 8); + } + return 0; +} + +bool css_schid_final(int m, uint8_t cssid, uint8_t ssid, uint16_t schid) +{ + SubchSet *set; + uint8_t real_cssid; + + real_cssid = (!m && (cssid == 0)) ? channel_subsys->default_cssid : cssid; + if (real_cssid > MAX_CSSID || ssid > MAX_SSID || + !channel_subsys->css[real_cssid] || + !channel_subsys->css[real_cssid]->sch_set[ssid]) { + return true; + } + set = channel_subsys->css[real_cssid]->sch_set[ssid]; + return schid > find_last_bit(set->schids_used, + (MAX_SCHID + 1) / sizeof(unsigned long)); +} + +static int css_add_virtual_chpid(uint8_t cssid, uint8_t chpid, uint8_t type) +{ + CssImage *css; + + trace_css_chpid_add(cssid, chpid, type); + if (cssid > MAX_CSSID) { + return -EINVAL; + } + css = channel_subsys->css[cssid]; + if (!css) { + return -EINVAL; + } + if (css->chpids[chpid].in_use) { + return -EEXIST; + } + css->chpids[chpid].in_use = 1; + css->chpids[chpid].type = type; + css->chpids[chpid].is_virtual = 1; + + css_generate_chp_crws(cssid, chpid); + + return 0; +} + +void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type) +{ + PMCW *p = &sch->curr_status.pmcw; + SCSW *s = &sch->curr_status.scsw; + int i; + CssImage *css = channel_subsys->css[sch->cssid]; + + assert(css != NULL); + memset(p, 0, sizeof(PMCW)); + p->flags |= PMCW_FLAGS_MASK_DNV; + p->devno = sch->devno; + /* single path */ + p->pim = 0x80; + p->pom = 0xff; + p->pam = 0x80; + p->chpid[0] = chpid; + if (!css->chpids[chpid].in_use) { + css_add_virtual_chpid(sch->cssid, chpid, type); + } + + memset(s, 0, sizeof(SCSW)); + sch->curr_status.mba = 0; + for (i = 0; i < ARRAY_SIZE(sch->curr_status.mda); i++) { + sch->curr_status.mda[i] = 0; + } +} + +SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid) +{ + uint8_t real_cssid; + + real_cssid = (!m && (cssid == 0)) ? channel_subsys->default_cssid : cssid; + + if (!channel_subsys->css[real_cssid]) { + return NULL; + } + + if (!channel_subsys->css[real_cssid]->sch_set[ssid]) { + return NULL; + } + + return channel_subsys->css[real_cssid]->sch_set[ssid]->sch[schid]; +} + +bool css_subch_visible(SubchDev *sch) +{ + if (sch->ssid > channel_subsys->max_ssid) { + return false; + } + + if (sch->cssid != channel_subsys->default_cssid) { + return (channel_subsys->max_cssid > 0); + } + + return true; +} + +bool css_present(uint8_t cssid) +{ + return (channel_subsys->css[cssid] != NULL); +} + +bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno) +{ + if (!channel_subsys->css[cssid]) { + return false; + } + if (!channel_subsys->css[cssid]->sch_set[ssid]) { + return false; + } + + return !!test_bit(devno, + channel_subsys->css[cssid]->sch_set[ssid]->devnos_used); +} + +void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, + uint16_t devno, SubchDev *sch) +{ + CssImage *css; + SubchSet *s_set; + + trace_css_assign_subch(sch ? "assign" : "deassign", cssid, ssid, schid, + devno); + if (!channel_subsys->css[cssid]) { + fprintf(stderr, + "Suspicious call to %s (%x.%x.%04x) for non-existing css!\n", + __func__, cssid, ssid, schid); + return; + } + css = channel_subsys->css[cssid]; + + if (!css->sch_set[ssid]) { + css->sch_set[ssid] = g_malloc0(sizeof(SubchSet)); + } + s_set = css->sch_set[ssid]; + + s_set->sch[schid] = sch; + if (sch) { + set_bit(schid, s_set->schids_used); + set_bit(devno, s_set->devnos_used); + } else { + clear_bit(schid, s_set->schids_used); + clear_bit(devno, s_set->devnos_used); + } +} + +void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid) +{ + CrwContainer *crw_cont; + + trace_css_crw(rsc, erc, rsid, chain ? "(chained)" : ""); + /* TODO: Maybe use a static crw pool? */ + crw_cont = g_try_malloc0(sizeof(CrwContainer)); + if (!crw_cont) { + channel_subsys->crws_lost = true; + return; + } + crw_cont->crw.flags = (rsc << 8) | erc; + if (chain) { + crw_cont->crw.flags |= CRW_FLAGS_MASK_C; + } + crw_cont->crw.rsid = rsid; + if (channel_subsys->crws_lost) { + crw_cont->crw.flags |= CRW_FLAGS_MASK_R; + channel_subsys->crws_lost = false; + } + + QTAILQ_INSERT_TAIL(&channel_subsys->pending_crws, crw_cont, sibling); + + if (channel_subsys->do_crw_mchk) { + S390CPU *cpu = s390_cpu_addr2state(0); + + channel_subsys->do_crw_mchk = false; + /* Inject crw pending machine check. */ + s390_crw_mchk(cpu); + } +} + +void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, + int hotplugged, int add) +{ + uint8_t guest_cssid; + bool chain_crw; + + if (add && !hotplugged) { + return; + } + if (channel_subsys->max_cssid == 0) { + /* Default cssid shows up as 0. */ + guest_cssid = (cssid == channel_subsys->default_cssid) ? 0 : cssid; + } else { + /* Show real cssid to the guest. */ + guest_cssid = cssid; + } + /* + * Only notify for higher subchannel sets/channel subsystems if the + * guest has enabled it. + */ + if ((ssid > channel_subsys->max_ssid) || + (guest_cssid > channel_subsys->max_cssid) || + ((channel_subsys->max_cssid == 0) && + (cssid != channel_subsys->default_cssid))) { + return; + } + chain_crw = (channel_subsys->max_ssid > 0) || + (channel_subsys->max_cssid > 0); + css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, chain_crw ? 1 : 0, schid); + if (chain_crw) { + css_queue_crw(CRW_RSC_SUBCH, CRW_ERC_IPI, 0, + (guest_cssid << 8) | (ssid << 4)); + } +} + +void css_generate_chp_crws(uint8_t cssid, uint8_t chpid) +{ + /* TODO */ +} + +int css_enable_mcsse(void) +{ + trace_css_enable_facility("mcsse"); + channel_subsys->max_cssid = MAX_CSSID; + return 0; +} + +int css_enable_mss(void) +{ + trace_css_enable_facility("mss"); + channel_subsys->max_ssid = MAX_SSID; + return 0; +} + +static void css_init(void) +{ + channel_subsys = g_malloc0(sizeof(*channel_subsys)); + QTAILQ_INIT(&channel_subsys->pending_crws); + channel_subsys->do_crw_mchk = true; + channel_subsys->crws_lost = false; + channel_subsys->chnmon_active = false; +} +machine_init(css_init); + +void css_reset_sch(SubchDev *sch) +{ + PMCW *p = &sch->curr_status.pmcw; + + p->intparm = 0; + p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | + PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | + PMCW_FLAGS_MASK_MP | PMCW_FLAGS_MASK_TF); + p->flags |= PMCW_FLAGS_MASK_DNV; + p->devno = sch->devno; + p->pim = 0x80; + p->lpm = p->pim; + p->pnom = 0; + p->lpum = 0; + p->mbi = 0; + p->pom = 0xff; + p->pam = 0x80; + p->chars &= ~(PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_XMWME | + PMCW_CHARS_MASK_CSENSE); + + memset(&sch->curr_status.scsw, 0, sizeof(sch->curr_status.scsw)); + sch->curr_status.mba = 0; + + sch->channel_prog = 0x0; + sch->last_cmd_valid = false; + sch->orb = NULL; +} + +void css_reset(void) +{ + CrwContainer *crw_cont; + + /* Clean up monitoring. */ + channel_subsys->chnmon_active = false; + channel_subsys->chnmon_area = 0; + + /* Clear pending CRWs. */ + while ((crw_cont = QTAILQ_FIRST(&channel_subsys->pending_crws))) { + QTAILQ_REMOVE(&channel_subsys->pending_crws, crw_cont, sibling); + g_free(crw_cont); + } + channel_subsys->do_crw_mchk = true; + channel_subsys->crws_lost = false; + + /* Reset maximum ids. */ + channel_subsys->max_cssid = 0; + channel_subsys->max_ssid = 0; +} diff --git a/hw/s390x/css.h b/hw/s390x/css.h new file mode 100644 index 0000000000..85ed05d0f5 --- /dev/null +++ b/hw/s390x/css.h @@ -0,0 +1,99 @@ +/* + * Channel subsystem structures and definitions. + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef CSS_H +#define CSS_H + +#include "ioinst.h" + +/* Channel subsystem constants. */ +#define MAX_SCHID 65535 +#define MAX_SSID 3 +#define MAX_CSSID 254 /* 255 is reserved */ +#define MAX_CHPID 255 + +#define MAX_CIWS 62 + +typedef struct CIW { + uint8_t type; + uint8_t command; + uint16_t count; +} QEMU_PACKED CIW; + +typedef struct SenseId { + /* common part */ + uint8_t reserved; /* always 0x'FF' */ + uint16_t cu_type; /* control unit type */ + uint8_t cu_model; /* control unit model */ + uint16_t dev_type; /* device type */ + uint8_t dev_model; /* device model */ + uint8_t unused; /* padding byte */ + /* extended part */ + CIW ciw[MAX_CIWS]; /* variable # of CIWs */ +} QEMU_PACKED SenseId; + +/* Channel measurements, from linux/drivers/s390/cio/cmf.c. */ +typedef struct CMB { + uint16_t ssch_rsch_count; + uint16_t sample_count; + uint32_t device_connect_time; + uint32_t function_pending_time; + uint32_t device_disconnect_time; + uint32_t control_unit_queuing_time; + uint32_t device_active_only_time; + uint32_t reserved[2]; +} QEMU_PACKED CMB; + +typedef struct CMBE { + uint32_t ssch_rsch_count; + uint32_t sample_count; + uint32_t device_connect_time; + uint32_t function_pending_time; + uint32_t device_disconnect_time; + uint32_t control_unit_queuing_time; + uint32_t device_active_only_time; + uint32_t device_busy_time; + uint32_t initial_command_response_time; + uint32_t reserved[7]; +} QEMU_PACKED CMBE; + +struct SubchDev { + /* channel-subsystem related things: */ + uint8_t cssid; + uint8_t ssid; + uint16_t schid; + uint16_t devno; + SCHIB curr_status; + uint8_t sense_data[32]; + hwaddr channel_prog; + CCW1 last_cmd; + bool last_cmd_valid; + ORB *orb; + /* transport-provided data: */ + int (*ccw_cb) (SubchDev *, CCW1); + SenseId id; + void *driver_data; +}; + +typedef SubchDev *(*css_subch_cb_func)(uint8_t m, uint8_t cssid, uint8_t ssid, + uint16_t schid); +int css_create_css_image(uint8_t cssid, bool default_image); +bool css_devno_used(uint8_t cssid, uint8_t ssid, uint16_t devno); +void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, + uint16_t devno, SubchDev *sch); +void css_sch_build_virtual_schib(SubchDev *sch, uint8_t chpid, uint8_t type); +void css_reset(void); +void css_reset_sch(SubchDev *sch); +void css_queue_crw(uint8_t rsc, uint8_t erc, int chain, uint16_t rsid); +void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, + int hotplugged, int add); +void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); +#endif diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index 89b1b66bd2..0faade0766 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -18,8 +18,8 @@ #include "monitor/monitor.h" #include "sysemu/sysemu.h" -#include "sclp.h" -#include "event-facility.h" +#include "hw/s390x/sclp.h" +#include "hw/s390x/event-facility.h" typedef struct EventTypesBus { BusState qbus; @@ -345,7 +345,7 @@ static void init_event_facility_class(ObjectClass *klass, void *data) k->init = init_event_facility; } -static TypeInfo s390_sclp_event_facility_info = { +static const TypeInfo s390_sclp_event_facility_info = { .name = "s390-sclp-event-facility", .parent = TYPE_DEVICE_S390_SCLP, .instance_size = sizeof(S390SCLPDevice), @@ -380,7 +380,7 @@ static void event_class_init(ObjectClass *klass, void *data) dc->exit = event_qdev_exit; } -static TypeInfo s390_sclp_event_type_info = { +static const TypeInfo s390_sclp_event_type_info = { .name = TYPE_SCLP_EVENT, .parent = TYPE_DEVICE, .instance_size = sizeof(SCLPEvent), diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c new file mode 100644 index 0000000000..206d552e16 --- /dev/null +++ b/hw/s390x/ipl.c @@ -0,0 +1,176 @@ +/* + * bootloader support + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Christian Borntraeger + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#include "sysemu/sysemu.h" +#include "cpu.h" +#include "elf.h" +#include "hw/loader.h" +#include "hw/sysbus.h" + +#define KERN_IMAGE_START 0x010000UL +#define KERN_PARM_AREA 0x010480UL +#define INITRD_START 0x800000UL +#define INITRD_PARM_START 0x010408UL +#define INITRD_PARM_SIZE 0x010410UL +#define PARMFILE_START 0x001000UL +#define ZIPL_FILENAME "s390-zipl.rom" +#define ZIPL_IMAGE_START 0x009000UL +#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) + +#define TYPE_S390_IPL "s390-ipl" +#define S390_IPL(obj) \ + OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL) +#if 0 +#define S390_IPL_CLASS(klass) \ + OBJECT_CLASS_CHECK(S390IPLState, (klass), TYPE_S390_IPL) +#define S390_IPL_GET_CLASS(obj) \ + OBJECT_GET_CLASS(S390IPLState, (obj), TYPE_S390_IPL) +#endif + +typedef struct S390IPLClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + + void (*parent_reset) (SysBusDevice *dev); +} S390IPLClass; + +typedef struct S390IPLState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + char *kernel; + char *initrd; + char *cmdline; +} S390IPLState; + + +static void s390_ipl_cpu(uint64_t pswaddr) +{ + S390CPU *cpu = S390_CPU(qemu_get_cpu(0)); + CPUS390XState *env = &cpu->env; + + env->psw.addr = pswaddr; + env->psw.mask = IPL_PSW_MASK; + s390_add_running_cpu(cpu); +} + +static int s390_ipl_init(SysBusDevice *dev) +{ + S390IPLState *ipl = S390_IPL(dev); + ram_addr_t kernel_size = 0; + + if (!ipl->kernel) { + ram_addr_t bios_size = 0; + char *bios_filename; + + /* Load zipl bootloader */ + if (bios_name == NULL) { + bios_name = ZIPL_FILENAME; + } + + bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START, 4096); + g_free(bios_filename); + + if ((long)bios_size < 0) { + hw_error("could not load bootloader '%s'\n", bios_name); + } + + if (bios_size > 4096) { + hw_error("stage1 bootloader is > 4k\n"); + } + return 0; + } else { + kernel_size = load_elf(ipl->kernel, NULL, NULL, NULL, NULL, + NULL, 1, ELF_MACHINE, 0); + if (kernel_size == -1UL) { + kernel_size = load_image_targphys(ipl->kernel, 0, ram_size); + } + if (kernel_size == -1UL) { + fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel); + return -1; + } + /* we have to overwrite values in the kernel image, which are "rom" */ + strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline); + } + if (ipl->initrd) { + ram_addr_t initrd_offset, initrd_size; + + initrd_offset = INITRD_START; + while (kernel_size + 0x100000 > initrd_offset) { + initrd_offset += 0x100000; + } + initrd_size = load_image_targphys(ipl->initrd, initrd_offset, + ram_size - initrd_offset); + if (initrd_size == -1UL) { + fprintf(stderr, "qemu: could not load initrd '%s'\n", ipl->initrd); + exit(1); + } + + /* we have to overwrite values in the kernel image, which are "rom" */ + stq_p(rom_ptr(INITRD_PARM_START), initrd_offset); + stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size); + } + + return 0; +} + +static Property s390_ipl_properties[] = { + DEFINE_PROP_STRING("kernel", S390IPLState, kernel), + DEFINE_PROP_STRING("initrd", S390IPLState, initrd), + DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline), + DEFINE_PROP_END_OF_LIST(), +}; + +static void s390_ipl_reset(DeviceState *dev) +{ + S390IPLState *ipl = S390_IPL(dev); + + if (ipl->kernel) { + /* + * we can not rely on the ELF entry point, since up to 3.2 this + * value was 0x800 (the SALIPL loader) and it wont work. For + * all (Linux) cases 0x10000 (KERN_IMAGE_START) should be fine. + */ + return s390_ipl_cpu(KERN_IMAGE_START); + } else { + return s390_ipl_cpu(ZIPL_IMAGE_START); + } +} + +static void s390_ipl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = s390_ipl_init; + dc->props = s390_ipl_properties; + dc->reset = s390_ipl_reset; + dc->no_user = 1; +} + +static const TypeInfo s390_ipl_info = { + .class_init = s390_ipl_class_init, + .parent = TYPE_SYS_BUS_DEVICE, + .name = "s390-ipl", + .instance_size = sizeof(S390IPLState), +}; + +static void s390_ipl_register_types(void) +{ + type_register_static(&s390_ipl_info); +} + +type_init(s390_ipl_register_types) diff --git a/hw/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c similarity index 84% rename from hw/s390-virtio-bus.c rename to hw/s390x/s390-virtio-bus.c index 963b4f0dc2..d9b7f83878 100644 --- a/hw/s390-virtio-bus.c +++ b/hw/s390x/s390-virtio-bus.c @@ -17,12 +17,12 @@ * License along with this library; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "block/block.h" #include "sysemu/sysemu.h" -#include "boards.h" +#include "hw/boards.h" #include "monitor/monitor.h" -#include "loader.h" +#include "hw/loader.h" #include "elf.h" #include "hw/virtio.h" #include "hw/virtio-rng.h" @@ -31,7 +31,8 @@ #include "hw/sysbus.h" #include "sysemu/kvm.h" -#include "hw/s390-virtio-bus.h" +#include "hw/s390x/s390-virtio-bus.h" +#include "hw/virtio-bus.h" /* #define DEBUG_S390 */ @@ -110,12 +111,12 @@ VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size) return bus; } -static void s390_virtio_irq(CPUS390XState *env, int config_change, uint64_t token) +static void s390_virtio_irq(S390CPU *cpu, int config_change, uint64_t token) { if (kvm_enabled()) { - kvm_s390_virtio_irq(env, config_change, token); + kvm_s390_virtio_irq(cpu, config_change, token); } else { - cpu_inject_ext(env, VIRTIO_EXT_CODE, config_change, token); + cpu_inject_ext(cpu, VIRTIO_EXT_CODE, config_change, token); } } @@ -136,14 +137,13 @@ static int s390_virtio_device_init(VirtIOS390Device *dev, VirtIODevice *vdev) bus->dev_offs += dev_len; - virtio_bind_device(vdev, &virtio_s390_bindings, dev); + virtio_bind_device(vdev, &virtio_s390_bindings, DEVICE(dev)); dev->host_features = vdev->get_features(vdev, dev->host_features); s390_virtio_device_sync(dev); s390_virtio_reset_idx(dev); if (dev->qdev.hotplugged) { S390CPU *cpu = s390_cpu_addr2state(0); - CPUS390XState *env = &cpu->env; - s390_virtio_irq(env, VIRTIO_PARAM_DEV_ADD, dev->dev_offs); + s390_virtio_irq(cpu, VIRTIO_PARAM_DEV_ADD, dev->dev_offs); } return 0; @@ -153,7 +153,8 @@ static int s390_virtio_net_init(VirtIOS390Device *dev) { VirtIODevice *vdev; - vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net); + vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net, + dev->host_features); if (!vdev) { return -1; } @@ -363,19 +364,32 @@ VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem) return NULL; } -static void virtio_s390_notify(void *opaque, uint16_t vector) +/* DeviceState to VirtIOS390Device. Note: used on datapath, + * be careful and test performance if you change this. + */ +static inline VirtIOS390Device *to_virtio_s390_device_fast(DeviceState *d) { - VirtIOS390Device *dev = (VirtIOS390Device*)opaque; - uint64_t token = s390_virtio_device_vq_token(dev, vector); - S390CPU *cpu = s390_cpu_addr2state(0); - CPUS390XState *env = &cpu->env; - - s390_virtio_irq(env, 0, token); + return container_of(d, VirtIOS390Device, qdev); } -static unsigned virtio_s390_get_features(void *opaque) +/* DeviceState to VirtIOS390Device. TODO: use QOM. */ +static inline VirtIOS390Device *to_virtio_s390_device(DeviceState *d) { - VirtIOS390Device *dev = (VirtIOS390Device*)opaque; + return container_of(d, VirtIOS390Device, qdev); +} + +static void virtio_s390_notify(DeviceState *d, uint16_t vector) +{ + VirtIOS390Device *dev = to_virtio_s390_device_fast(d); + uint64_t token = s390_virtio_device_vq_token(dev, vector); + S390CPU *cpu = s390_cpu_addr2state(0); + + s390_virtio_irq(cpu, 0, token); +} + +static unsigned virtio_s390_get_features(DeviceState *d) +{ + VirtIOS390Device *dev = to_virtio_s390_device(d); return dev->host_features; } @@ -388,6 +402,7 @@ static const VirtIOBindings virtio_s390_bindings = { static Property s390_virtio_net_properties[] = { DEFINE_NIC_PROPERTIES(VirtIOS390Device, nic), + DEFINE_VIRTIO_NET_FEATURES(VirtIOS390Device, host_features), DEFINE_PROP_UINT32("x-txtimer", VirtIOS390Device, net.txtimer, TX_TIMER_INTERVAL), DEFINE_PROP_INT32("x-txburst", VirtIOS390Device, @@ -405,7 +420,7 @@ static void s390_virtio_net_class_init(ObjectClass *klass, void *data) dc->props = s390_virtio_net_properties; } -static TypeInfo s390_virtio_net = { +static const TypeInfo s390_virtio_net = { .name = "virtio-net-s390", .parent = TYPE_VIRTIO_S390_DEVICE, .instance_size = sizeof(VirtIOS390Device), @@ -431,7 +446,7 @@ static void s390_virtio_blk_class_init(ObjectClass *klass, void *data) dc->props = s390_virtio_blk_properties; } -static TypeInfo s390_virtio_blk = { +static const TypeInfo s390_virtio_blk = { .name = "virtio-blk-s390", .parent = TYPE_VIRTIO_S390_DEVICE, .instance_size = sizeof(VirtIOS390Device), @@ -453,7 +468,7 @@ static void s390_virtio_serial_class_init(ObjectClass *klass, void *data) dc->props = s390_virtio_serial_properties; } -static TypeInfo s390_virtio_serial = { +static const TypeInfo s390_virtio_serial = { .name = "virtio-serial-s390", .parent = TYPE_VIRTIO_S390_DEVICE, .instance_size = sizeof(VirtIOS390Device), @@ -475,7 +490,7 @@ static void s390_virtio_rng_class_init(ObjectClass *klass, void *data) k->init = s390_virtio_rng_init; } -static TypeInfo s390_virtio_rng = { +static const TypeInfo s390_virtio_rng = { .name = "virtio-rng-s390", .parent = TYPE_VIRTIO_S390_DEVICE, .instance_size = sizeof(VirtIOS390Device), @@ -488,9 +503,18 @@ static int s390_virtio_busdev_init(DeviceState *dev) VirtIOS390Device *_dev = (VirtIOS390Device *)dev; VirtIOS390DeviceClass *_info = VIRTIO_S390_DEVICE_GET_CLASS(dev); + virtio_s390_bus_new(&_dev->bus, _dev); + return _info->init(_dev); } +static void s390_virtio_busdev_reset(DeviceState *dev) +{ + VirtIOS390Device *_dev = (VirtIOS390Device *)dev; + + virtio_reset(_dev->vdev); +} + static void virtio_s390_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -498,9 +522,10 @@ static void virtio_s390_device_class_init(ObjectClass *klass, void *data) dc->init = s390_virtio_busdev_init; dc->bus_type = TYPE_S390_VIRTIO_BUS; dc->unplug = qdev_simple_unplug_cb; + dc->reset = s390_virtio_busdev_reset; } -static TypeInfo virtio_s390_device_info = { +static const TypeInfo virtio_s390_device_info = { .name = TYPE_VIRTIO_S390_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(VirtIOS390Device), @@ -523,7 +548,7 @@ static void s390_virtio_scsi_class_init(ObjectClass *klass, void *data) dc->props = s390_virtio_scsi_properties; } -static TypeInfo s390_virtio_scsi = { +static const TypeInfo s390_virtio_scsi = { .name = "virtio-scsi-s390", .parent = TYPE_VIRTIO_S390_DEVICE, .instance_size = sizeof(VirtIOS390Device), @@ -548,15 +573,43 @@ static void s390_virtio_bridge_class_init(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo s390_virtio_bridge_info = { +static const TypeInfo s390_virtio_bridge_info = { .name = "s390-virtio-bridge", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SysBusDevice), .class_init = s390_virtio_bridge_class_init, }; +/* virtio-s390-bus */ + +void virtio_s390_bus_new(VirtioBusState *bus, VirtIOS390Device *dev) +{ + DeviceState *qdev = DEVICE(dev); + BusState *qbus; + qbus_create_inplace((BusState *)bus, TYPE_VIRTIO_S390_BUS, qdev, NULL); + qbus = BUS(bus); + qbus->allow_hotplug = 0; +} + +static void virtio_s390_bus_class_init(ObjectClass *klass, void *data) +{ + VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); + BusClass *bus_class = BUS_CLASS(klass); + bus_class->max_dev = 1; + k->notify = virtio_s390_notify; + k->get_features = virtio_s390_get_features; +} + +static const TypeInfo virtio_s390_bus_info = { + .name = TYPE_VIRTIO_S390_BUS, + .parent = TYPE_VIRTIO_BUS, + .instance_size = sizeof(VirtioS390BusState), + .class_init = virtio_s390_bus_class_init, +}; + static void s390_virtio_register_types(void) { + type_register_static(&virtio_s390_bus_info); type_register_static(&s390_virtio_bus_info); type_register_static(&virtio_s390_device_info); type_register_static(&s390_virtio_serial); diff --git a/hw/s390-virtio-bus.h b/hw/s390x/s390-virtio-bus.h similarity index 81% rename from hw/s390-virtio-bus.h rename to hw/s390x/s390-virtio-bus.h index 23fedd5be8..4aacf83998 100644 --- a/hw/s390-virtio-bus.h +++ b/hw/s390x/s390-virtio-bus.h @@ -19,11 +19,12 @@ #ifndef HW_S390_VIRTIO_BUS_H #define HW_S390_VIRTIO_BUS_H 1 -#include "virtio-blk.h" -#include "virtio-net.h" -#include "virtio-rng.h" -#include "virtio-serial.h" -#include "virtio-scsi.h" +#include "hw/virtio-blk.h" +#include "hw/virtio-net.h" +#include "hw/virtio-rng.h" +#include "hw/virtio-serial.h" +#include "hw/virtio-scsi.h" +#include "hw/virtio-bus.h" #define VIRTIO_DEV_OFFS_TYPE 0 /* 8 bits */ #define VIRTIO_DEV_OFFS_NUM_VQ 1 /* 8 bits */ @@ -59,8 +60,24 @@ #define S390_VIRTIO_BUS(obj) \ OBJECT_CHECK(VirtIOS390Bus, (obj), TYPE_S390_VIRTIO_BUS) +/* virtio-s390-bus */ + +typedef struct VirtioBusState VirtioS390BusState; +typedef struct VirtioBusClass VirtioS390BusClass; + +#define TYPE_VIRTIO_S390_BUS "virtio-s390-bus" +#define VIRTIO_S390_BUS(obj) \ + OBJECT_CHECK(VirtioS390BusState, (obj), TYPE_VIRTIO_S390_BUS) +#define VIRTIO_S390_BUS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtioS390BusClass, obj, TYPE_VIRTIO_S390_BUS) +#define VIRTIO_S390_BUS_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtioS390BusClass, klass, TYPE_VIRTIO_S390_BUS) + + typedef struct VirtIOS390Device VirtIOS390Device; +void virtio_s390_bus_new(VirtioBusState *bus, VirtIOS390Device *dev); + typedef struct VirtIOS390DeviceClass { DeviceClass qdev; int (*init)(VirtIOS390Device *dev); @@ -79,6 +96,7 @@ struct VirtIOS390Device { virtio_net_conf net; VirtIOSCSIConf scsi; VirtIORNGConf rng; + VirtioBusState bus; }; typedef struct VirtIOS390Bus { diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c new file mode 100644 index 0000000000..d4364143ea --- /dev/null +++ b/hw/s390x/s390-virtio-ccw.c @@ -0,0 +1,134 @@ +/* + * virtio ccw machine + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "hw/boards.h" +#include "exec/address-spaces.h" +#include "s390-virtio.h" +#include "hw/s390x/sclp.h" +#include "ioinst.h" +#include "css.h" +#include "virtio-ccw.h" + +static int virtio_ccw_hcall_notify(const uint64_t *args) +{ + uint64_t subch_id = args[0]; + uint64_t queue = args[1]; + SubchDev *sch; + int cssid, ssid, schid, m; + + if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) { + return -EINVAL; + } + sch = css_find_subch(m, cssid, ssid, schid); + if (!sch || !css_subch_visible(sch)) { + return -EINVAL; + } + virtio_queue_notify(virtio_ccw_get_vdev(sch), queue); + return 0; + +} + +static int virtio_ccw_hcall_early_printk(const uint64_t *args) +{ + uint64_t mem = args[0]; + + if (mem < ram_size) { + /* Early printk */ + return 0; + } + return -EINVAL; +} + +static void virtio_ccw_register_hcalls(void) +{ + s390_register_virtio_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, + virtio_ccw_hcall_notify); + /* Tolerate early printk. */ + s390_register_virtio_hypercall(KVM_S390_VIRTIO_NOTIFY, + virtio_ccw_hcall_early_printk); +} + +static void ccw_init(QEMUMachineInitArgs *args) +{ + ram_addr_t my_ram_size = args->ram_size; + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + int shift = 0; + uint8_t *storage_keys; + int ret; + VirtualCssBus *css_bus; + + /* s390x ram size detection needs a 16bit multiplier + an increment. So + guests > 64GB can be specified in 2MB steps etc. */ + while ((my_ram_size >> (20 + shift)) > 65535) { + shift++; + } + my_ram_size = my_ram_size >> (20 + shift) << (20 + shift); + + /* lets propagate the changed ram size into the global variable. */ + ram_size = my_ram_size; + + /* get a BUS */ + css_bus = virtual_css_bus_init(); + s390_sclp_init(); + s390_init_ipl_dev(args->kernel_filename, args->kernel_cmdline, + args->initrd_filename); + + /* register hypercalls */ + virtio_ccw_register_hcalls(); + + /* allocate RAM */ + memory_region_init_ram(ram, "s390.ram", my_ram_size); + vmstate_register_ram_global(ram); + memory_region_add_subregion(sysmem, 0, ram); + + /* allocate storage keys */ + storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); + + /* init CPUs */ + s390_init_cpus(args->cpu_model, storage_keys); + + if (kvm_enabled()) { + kvm_s390_enable_css_support(s390_cpu_addr2state(0)); + } + /* + * Create virtual css and set it as default so that non mcss-e + * enabled guests only see virtio devices. + */ + ret = css_create_css_image(VIRTUAL_CSSID, true); + assert(ret == 0); + + /* Create VirtIO network adapters */ + s390_create_virtio_net(BUS(css_bus), "virtio-net-ccw"); +} + +static QEMUMachine ccw_machine = { + .name = "s390-ccw-virtio", + .alias = "s390-ccw", + .desc = "VirtIO-ccw based S390 machine", + .init = ccw_init, + .block_default_type = IF_VIRTIO, + .no_cdrom = 1, + .no_floppy = 1, + .no_serial = 1, + .no_parallel = 1, + .no_sdcard = 1, + .use_sclp = 1, + .max_cpus = 255, + DEFAULT_MACHINE_OPTIONS, +}; + +static void ccw_machine_init(void) +{ + qemu_register_machine(&ccw_machine); +} + +machine_init(ccw_machine_init) diff --git a/hw/s390x/s390-virtio-hcall.c b/hw/s390x/s390-virtio-hcall.c new file mode 100644 index 0000000000..ee626493c6 --- /dev/null +++ b/hw/s390x/s390-virtio-hcall.c @@ -0,0 +1,36 @@ +/* + * Support for virtio hypercalls on s390 + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "cpu.h" +#include "hw/s390x/s390-virtio.h" + +#define MAX_DIAG_SUBCODES 255 + +static s390_virtio_fn s390_diag500_table[MAX_DIAG_SUBCODES]; + +void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn) +{ + assert(code < MAX_DIAG_SUBCODES); + assert(!s390_diag500_table[code]); + + s390_diag500_table[code] = fn; +} + +int s390_virtio_hypercall(CPUS390XState *env) +{ + s390_virtio_fn fn = s390_diag500_table[env->regs[1]]; + + if (!fn) { + return -EINVAL; + } + + return fn(&env->regs[2]); +} diff --git a/hw/s390x/s390-virtio.c b/hw/s390x/s390-virtio.c new file mode 100644 index 0000000000..ca275bd9d7 --- /dev/null +++ b/hw/s390x/s390-virtio.c @@ -0,0 +1,301 @@ +/* + * QEMU S390 virtio target + * + * Copyright (c) 2009 Alexander Graf + * Copyright IBM Corp 2012 + * + * 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. + * + * Contributions after 2012-10-29 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + * + * You should have received a copy of the GNU (Lesser) General Public + * License along with this library; if not, see . + */ + +#include "hw/hw.h" +#include "block/block.h" +#include "sysemu/blockdev.h" +#include "sysemu/sysemu.h" +#include "net/net.h" +#include "hw/boards.h" +#include "monitor/monitor.h" +#include "hw/loader.h" +#include "hw/virtio.h" +#include "hw/sysbus.h" +#include "sysemu/kvm.h" +#include "exec/address-spaces.h" + +#include "hw/s390x/s390-virtio-bus.h" +#include "hw/s390x/sclp.h" +#include "hw/s390x/s390-virtio.h" + +//#define DEBUG_S390 + +#ifdef DEBUG_S390 +#define dprintf(fmt, ...) \ + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define dprintf(fmt, ...) \ + do { } while (0) +#endif + +#define MAX_BLK_DEVS 10 + +static VirtIOS390Bus *s390_bus; +static S390CPU **ipi_states; + +S390CPU *s390_cpu_addr2state(uint16_t cpu_addr) +{ + if (cpu_addr >= smp_cpus) { + return NULL; + } + + return ipi_states[cpu_addr]; +} + +static int s390_virtio_hcall_notify(const uint64_t *args) +{ + uint64_t mem = args[0]; + int r = 0, i; + + if (mem > ram_size) { + VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus, mem, &i); + if (dev) { + virtio_queue_notify(dev->vdev, i); + } else { + r = -EINVAL; + } + } else { + /* Early printk */ + } + return r; +} + +static int s390_virtio_hcall_reset(const uint64_t *args) +{ + uint64_t mem = args[0]; + VirtIOS390Device *dev; + + dev = s390_virtio_bus_find_mem(s390_bus, mem); + if (dev == NULL) { + return -EINVAL; + } + virtio_reset(dev->vdev); + stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0); + s390_virtio_device_sync(dev); + s390_virtio_reset_idx(dev); + + return 0; +} + +static int s390_virtio_hcall_set_status(const uint64_t *args) +{ + uint64_t mem = args[0]; + int r = 0; + VirtIOS390Device *dev; + + dev = s390_virtio_bus_find_mem(s390_bus, mem); + if (dev) { + s390_virtio_device_update_status(dev); + } else { + r = -EINVAL; + } + return r; +} + +static void s390_virtio_register_hcalls(void) +{ + s390_register_virtio_hypercall(KVM_S390_VIRTIO_NOTIFY, + s390_virtio_hcall_notify); + s390_register_virtio_hypercall(KVM_S390_VIRTIO_RESET, + s390_virtio_hcall_reset); + s390_register_virtio_hypercall(KVM_S390_VIRTIO_SET_STATUS, + s390_virtio_hcall_set_status); +} + +/* + * The number of running CPUs. On s390 a shutdown is the state of all CPUs + * being either stopped or disabled (for interrupts) waiting. We have to + * track this number to call the shutdown sequence accordingly. This + * number is modified either on startup or while holding the big qemu lock. + */ +static unsigned s390_running_cpus; + +void s390_add_running_cpu(S390CPU *cpu) +{ + CPUState *cs = CPU(cpu); + CPUS390XState *env = &cpu->env; + + if (cs->halted) { + s390_running_cpus++; + cs->halted = 0; + env->exception_index = -1; + } +} + +unsigned s390_del_running_cpu(S390CPU *cpu) +{ + CPUState *cs = CPU(cpu); + CPUS390XState *env = &cpu->env; + + if (cs->halted == 0) { + assert(s390_running_cpus >= 1); + s390_running_cpus--; + cs->halted = 1; + env->exception_index = EXCP_HLT; + } + return s390_running_cpus; +} + +void s390_init_ipl_dev(const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "s390-ipl"); + if (kernel_filename) { + qdev_prop_set_string(dev, "kernel", kernel_filename); + } + if (initrd_filename) { + qdev_prop_set_string(dev, "initrd", initrd_filename); + } + qdev_prop_set_string(dev, "cmdline", kernel_cmdline); + qdev_init_nofail(dev); +} + +void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys) +{ + int i; + + if (cpu_model == NULL) { + cpu_model = "host"; + } + + ipi_states = g_malloc(sizeof(S390CPU *) * smp_cpus); + + for (i = 0; i < smp_cpus; i++) { + S390CPU *cpu; + CPUState *cs; + + cpu = cpu_s390x_init(cpu_model); + cs = CPU(cpu); + + ipi_states[i] = cpu; + cs->halted = 1; + cpu->env.exception_index = EXCP_HLT; + cpu->env.storage_keys = storage_keys; + } +} + + +void s390_create_virtio_net(BusState *bus, const char *name) +{ + int i; + + for (i = 0; i < nb_nics; i++) { + NICInfo *nd = &nd_table[i]; + DeviceState *dev; + + if (!nd->model) { + nd->model = g_strdup("virtio"); + } + + if (strcmp(nd->model, "virtio")) { + fprintf(stderr, "S390 only supports VirtIO nics\n"); + exit(1); + } + + dev = qdev_create(bus, name); + qdev_set_nic_properties(dev, nd); + qdev_init_nofail(dev); + } +} + +/* PC hardware initialisation */ +static void s390_init(QEMUMachineInitArgs *args) +{ + ram_addr_t my_ram_size = args->ram_size; + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + int shift = 0; + uint8_t *storage_keys; + void *virtio_region; + hwaddr virtio_region_len; + hwaddr virtio_region_start; + + /* s390x ram size detection needs a 16bit multiplier + an increment. So + guests > 64GB can be specified in 2MB steps etc. */ + while ((my_ram_size >> (20 + shift)) > 65535) { + shift++; + } + my_ram_size = my_ram_size >> (20 + shift) << (20 + shift); + + /* lets propagate the changed ram size into the global variable. */ + ram_size = my_ram_size; + + /* get a BUS */ + s390_bus = s390_virtio_bus_init(&my_ram_size); + s390_sclp_init(); + s390_init_ipl_dev(args->kernel_filename, args->kernel_cmdline, + args->initrd_filename); + + /* register hypercalls */ + s390_virtio_register_hcalls(); + + /* allocate RAM */ + memory_region_init_ram(ram, "s390.ram", my_ram_size); + vmstate_register_ram_global(ram); + memory_region_add_subregion(sysmem, 0, ram); + + /* clear virtio region */ + virtio_region_len = my_ram_size - ram_size; + virtio_region_start = ram_size; + virtio_region = cpu_physical_memory_map(virtio_region_start, + &virtio_region_len, true); + memset(virtio_region, 0, virtio_region_len); + cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1, + virtio_region_len); + + /* allocate storage keys */ + storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE); + + /* init CPUs */ + s390_init_cpus(args->cpu_model, storage_keys); + + /* Create VirtIO network adapters */ + s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390"); +} + +static QEMUMachine s390_machine = { + .name = "s390-virtio", + .alias = "s390", + .desc = "VirtIO based S390 machine", + .init = s390_init, + .block_default_type = IF_VIRTIO, + .no_cdrom = 1, + .no_floppy = 1, + .no_serial = 1, + .no_parallel = 1, + .no_sdcard = 1, + .use_virtcon = 1, + .max_cpus = 255, + .is_default = 1, + DEFAULT_MACHINE_OPTIONS, +}; + +static void s390_machine_init(void) +{ + qemu_register_machine(&s390_machine); +} + +machine_init(s390_machine_init); diff --git a/hw/s390x/s390-virtio.h b/hw/s390x/s390-virtio.h new file mode 100644 index 0000000000..a6c4c19895 --- /dev/null +++ b/hw/s390x/s390-virtio.h @@ -0,0 +1,28 @@ +/* + * Virtio interfaces for s390 + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef HW_S390_VIRTIO_H +#define HW_S390_VIRTIO_H 1 + +#define KVM_S390_VIRTIO_NOTIFY 0 +#define KVM_S390_VIRTIO_RESET 1 +#define KVM_S390_VIRTIO_SET_STATUS 2 +#define KVM_S390_VIRTIO_CCW_NOTIFY 3 + +typedef int (*s390_virtio_fn)(const uint64_t *args); +void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn); + +void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys); +void s390_init_ipl_dev(const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename); +void s390_create_virtio_net(BusState *bus, const char *name); +#endif diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 7ad791d5e3..86d6ae0023 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -16,7 +16,7 @@ #include "sysemu/kvm.h" #include "exec/memory.h" -#include "sclp.h" +#include "hw/s390x/sclp.h" static inline S390SCLPDevice *get_event_facility(void) { @@ -146,7 +146,7 @@ static void s390_sclp_device_class_init(ObjectClass *klass, void *data) dc->init = s390_sclp_dev_init; } -static TypeInfo s390_sclp_device_info = { +static const TypeInfo s390_sclp_device_info = { .name = TYPE_DEVICE_S390_SCLP, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(S390SCLPDevice), diff --git a/hw/s390x/sclpconsole.c b/hw/s390x/sclpconsole.c index aa70e16665..5c881e5f3f 100644 --- a/hw/s390x/sclpconsole.c +++ b/hw/s390x/sclpconsole.c @@ -14,9 +14,10 @@ #include #include "qemu/thread.h" +#include "qemu/error-report.h" -#include "sclp.h" -#include "event-facility.h" +#include "hw/s390x/sclp.h" +#include "hw/s390x/event-facility.h" #include "char/char.h" typedef struct ASCIIConsoleData { @@ -44,12 +45,9 @@ typedef struct SCLPConsole { /* Return number of bytes that fit into iov buffer */ static int chr_can_read(void *opaque) { - int can_read; SCLPConsole *scon = opaque; - can_read = SIZE_BUFFER_VT220 - scon->iov_data_len; - - return can_read; + return scon->iov ? SIZE_BUFFER_VT220 - scon->iov_data_len : 0; } /* Receive n bytes from character layer, save in iov buffer, @@ -291,7 +289,7 @@ static void console_class_init(ObjectClass *klass, void *data) ec->write_event_data = write_event_data; } -static TypeInfo sclp_console_info = { +static const TypeInfo sclp_console_info = { .name = "sclpconsole", .parent = TYPE_SCLP_EVENT, .instance_size = sizeof(SCLPConsole), diff --git a/hw/s390x/sclpquiesce.c b/hw/s390x/sclpquiesce.c index 6e6f5624df..5fadc86d42 100644 --- a/hw/s390x/sclpquiesce.c +++ b/hw/s390x/sclpquiesce.c @@ -13,8 +13,8 @@ */ #include #include "sysemu/sysemu.h" -#include "sclp.h" -#include "event-facility.h" +#include "hw/s390x/sclp.h" +#include "hw/s390x/event-facility.h" typedef struct SignalQuiesce { EventBufferHeader ebh; @@ -107,7 +107,7 @@ static void quiesce_class_init(ObjectClass *klass, void *data) k->write_event_data = NULL; } -static TypeInfo sclp_quiesce_info = { +static const TypeInfo sclp_quiesce_info = { .name = "sclpquiesce", .parent = TYPE_SCLP_EVENT, .instance_size = sizeof(SCLPEvent), diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c new file mode 100644 index 0000000000..d4361f6e3e --- /dev/null +++ b/hw/s390x/virtio-ccw.c @@ -0,0 +1,1019 @@ +/* + * virtio ccw target implementation + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include "hw/hw.h" +#include "block/block.h" +#include "sysemu/blockdev.h" +#include "sysemu/sysemu.h" +#include "net/net.h" +#include "monitor/monitor.h" +#include "hw/virtio.h" +#include "hw/virtio-serial.h" +#include "hw/virtio-net.h" +#include "hw/sysbus.h" +#include "qemu/bitops.h" +#include "hw/virtio-bus.h" + +#include "ioinst.h" +#include "css.h" +#include "virtio-ccw.h" +#include "trace.h" + +static int virtual_css_bus_reset(BusState *qbus) +{ + /* This should actually be modelled via the generic css */ + css_reset(); + + /* we dont traverse ourself, return 0 */ + return 0; +} + + +static void virtual_css_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + k->reset = virtual_css_bus_reset; +} + +static const TypeInfo virtual_css_bus_info = { + .name = TYPE_VIRTUAL_CSS_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(VirtualCssBus), + .class_init = virtual_css_bus_class_init, +}; + +static const VirtIOBindings virtio_ccw_bindings; + +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch) +{ + VirtIODevice *vdev = NULL; + + if (sch->driver_data) { + vdev = ((VirtioCcwDevice *)sch->driver_data)->vdev; + } + return vdev; +} + +VirtualCssBus *virtual_css_bus_init(void) +{ + VirtualCssBus *cbus; + BusState *bus; + DeviceState *dev; + + /* Create bridge device */ + dev = qdev_create(NULL, "virtual-css-bridge"); + qdev_init_nofail(dev); + + /* Create bus on bridge device */ + bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); + cbus = VIRTUAL_CSS_BUS(bus); + + /* Enable hotplugging */ + bus->allow_hotplug = 1; + + return cbus; +} + +/* Communication blocks used by several channel commands. */ +typedef struct VqInfoBlock { + uint64_t queue; + uint32_t align; + uint16_t index; + uint16_t num; +} QEMU_PACKED VqInfoBlock; + +typedef struct VqConfigBlock { + uint16_t index; + uint16_t num_max; +} QEMU_PACKED VqConfigBlock; + +typedef struct VirtioFeatDesc { + uint32_t features; + uint8_t index; +} QEMU_PACKED VirtioFeatDesc; + +/* Specify where the virtqueues for the subchannel are in guest memory. */ +static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, + uint16_t index, uint16_t num) +{ + VirtioCcwDevice *dev = sch->driver_data; + + if (index > VIRTIO_PCI_QUEUE_MAX) { + return -EINVAL; + } + + /* Current code in virtio.c relies on 4K alignment. */ + if (addr && (align != 4096)) { + return -EINVAL; + } + + if (!dev) { + return -EINVAL; + } + + virtio_queue_set_addr(dev->vdev, index, addr); + if (!addr) { + virtio_queue_set_vector(dev->vdev, index, 0); + } else { + /* Fail if we don't have a big enough queue. */ + /* TODO: Add interface to handle vring.num changing */ + if (virtio_queue_get_num(dev->vdev, index) > num) { + return -EINVAL; + } + virtio_queue_set_vector(dev->vdev, index, index); + } + /* tell notify handler in case of config change */ + dev->vdev->config_vector = VIRTIO_PCI_QUEUE_MAX; + return 0; +} + +static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) +{ + int ret; + VqInfoBlock info; + uint8_t status; + VirtioFeatDesc features; + void *config; + hwaddr indicators; + VqConfigBlock vq_config; + VirtioCcwDevice *dev = sch->driver_data; + bool check_len; + int len; + hwaddr hw_len; + + if (!dev) { + return -EINVAL; + } + + trace_virtio_ccw_interpret_ccw(sch->cssid, sch->ssid, sch->schid, + ccw.cmd_code); + check_len = !((ccw.flags & CCW_FLAG_SLI) && !(ccw.flags & CCW_FLAG_DC)); + + /* Look at the command. */ + switch (ccw.cmd_code) { + case CCW_CMD_SET_VQ: + if (check_len) { + if (ccw.count != sizeof(info)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(info)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + if (!ccw.cda) { + ret = -EFAULT; + } else { + info.queue = ldq_phys(ccw.cda); + info.align = ldl_phys(ccw.cda + sizeof(info.queue)); + info.index = lduw_phys(ccw.cda + sizeof(info.queue) + + sizeof(info.align)); + info.num = lduw_phys(ccw.cda + sizeof(info.queue) + + sizeof(info.align) + + sizeof(info.index)); + ret = virtio_ccw_set_vqs(sch, info.queue, info.align, info.index, + info.num); + sch->curr_status.scsw.count = 0; + } + break; + case CCW_CMD_VDEV_RESET: + virtio_reset(dev->vdev); + ret = 0; + break; + case CCW_CMD_READ_FEAT: + if (check_len) { + if (ccw.count != sizeof(features)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(features)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + if (!ccw.cda) { + ret = -EFAULT; + } else { + features.index = ldub_phys(ccw.cda + sizeof(features.features)); + if (features.index < ARRAY_SIZE(dev->host_features)) { + features.features = dev->host_features[features.index]; + } else { + /* Return zeroes if the guest supports more feature bits. */ + features.features = 0; + } + stl_le_phys(ccw.cda, features.features); + sch->curr_status.scsw.count = ccw.count - sizeof(features); + ret = 0; + } + break; + case CCW_CMD_WRITE_FEAT: + if (check_len) { + if (ccw.count != sizeof(features)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(features)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + if (!ccw.cda) { + ret = -EFAULT; + } else { + features.index = ldub_phys(ccw.cda + sizeof(features.features)); + features.features = ldl_le_phys(ccw.cda); + if (features.index < ARRAY_SIZE(dev->host_features)) { + if (dev->vdev->set_features) { + dev->vdev->set_features(dev->vdev, features.features); + } + dev->vdev->guest_features = features.features; + } else { + /* + * If the guest supports more feature bits, assert that it + * passes us zeroes for those we don't support. + */ + if (features.features) { + fprintf(stderr, "Guest bug: features[%i]=%x (expected 0)\n", + features.index, features.features); + /* XXX: do a unit check here? */ + } + } + sch->curr_status.scsw.count = ccw.count - sizeof(features); + ret = 0; + } + break; + case CCW_CMD_READ_CONF: + if (check_len) { + if (ccw.count > dev->vdev->config_len) { + ret = -EINVAL; + break; + } + } + len = MIN(ccw.count, dev->vdev->config_len); + if (!ccw.cda) { + ret = -EFAULT; + } else { + dev->vdev->get_config(dev->vdev, dev->vdev->config); + /* XXX config space endianness */ + cpu_physical_memory_write(ccw.cda, dev->vdev->config, len); + sch->curr_status.scsw.count = ccw.count - len; + ret = 0; + } + break; + case CCW_CMD_WRITE_CONF: + if (check_len) { + if (ccw.count > dev->vdev->config_len) { + ret = -EINVAL; + break; + } + } + len = MIN(ccw.count, dev->vdev->config_len); + hw_len = len; + if (!ccw.cda) { + ret = -EFAULT; + } else { + config = cpu_physical_memory_map(ccw.cda, &hw_len, 0); + if (!config) { + ret = -EFAULT; + } else { + len = hw_len; + /* XXX config space endianness */ + memcpy(dev->vdev->config, config, len); + cpu_physical_memory_unmap(config, hw_len, 0, hw_len); + if (dev->vdev->set_config) { + dev->vdev->set_config(dev->vdev, dev->vdev->config); + } + sch->curr_status.scsw.count = ccw.count - len; + ret = 0; + } + } + break; + case CCW_CMD_WRITE_STATUS: + if (check_len) { + if (ccw.count != sizeof(status)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(status)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + if (!ccw.cda) { + ret = -EFAULT; + } else { + status = ldub_phys(ccw.cda); + virtio_set_status(dev->vdev, status); + if (dev->vdev->status == 0) { + virtio_reset(dev->vdev); + } + sch->curr_status.scsw.count = ccw.count - sizeof(status); + ret = 0; + } + break; + case CCW_CMD_SET_IND: + if (check_len) { + if (ccw.count != sizeof(indicators)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(indicators)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + indicators = ldq_phys(ccw.cda); + if (!indicators) { + ret = -EFAULT; + } else { + dev->indicators = indicators; + sch->curr_status.scsw.count = ccw.count - sizeof(indicators); + ret = 0; + } + break; + case CCW_CMD_SET_CONF_IND: + if (check_len) { + if (ccw.count != sizeof(indicators)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(indicators)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + indicators = ldq_phys(ccw.cda); + if (!indicators) { + ret = -EFAULT; + } else { + dev->indicators2 = indicators; + sch->curr_status.scsw.count = ccw.count - sizeof(indicators); + ret = 0; + } + break; + case CCW_CMD_READ_VQ_CONF: + if (check_len) { + if (ccw.count != sizeof(vq_config)) { + ret = -EINVAL; + break; + } + } else if (ccw.count < sizeof(vq_config)) { + /* Can't execute command. */ + ret = -EINVAL; + break; + } + if (!ccw.cda) { + ret = -EFAULT; + } else { + vq_config.index = lduw_phys(ccw.cda); + vq_config.num_max = virtio_queue_get_num(dev->vdev, + vq_config.index); + stw_phys(ccw.cda + sizeof(vq_config.index), vq_config.num_max); + sch->curr_status.scsw.count = ccw.count - sizeof(vq_config); + ret = 0; + } + break; + default: + ret = -ENOSYS; + break; + } + return ret; +} + +static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) +{ + unsigned int cssid = 0; + unsigned int ssid = 0; + unsigned int schid; + unsigned int devno; + bool have_devno = false; + bool found = false; + SubchDev *sch; + int ret; + int num; + DeviceState *parent = DEVICE(dev); + + sch = g_malloc0(sizeof(SubchDev)); + + sch->driver_data = dev; + dev->sch = sch; + + dev->vdev = vdev; + dev->indicators = 0; + + /* Initialize subchannel structure. */ + sch->channel_prog = 0x0; + sch->last_cmd_valid = false; + sch->orb = NULL; + /* + * Use a device number if provided. Otherwise, fall back to subchannel + * number. + */ + if (dev->bus_id) { + num = sscanf(dev->bus_id, "%x.%x.%04x", &cssid, &ssid, &devno); + if (num == 3) { + if ((cssid > MAX_CSSID) || (ssid > MAX_SSID)) { + ret = -EINVAL; + error_report("Invalid cssid or ssid: cssid %x, ssid %x", + cssid, ssid); + goto out_err; + } + /* Enforce use of virtual cssid. */ + if (cssid != VIRTUAL_CSSID) { + ret = -EINVAL; + error_report("cssid %x not valid for virtio devices", cssid); + goto out_err; + } + if (css_devno_used(cssid, ssid, devno)) { + ret = -EEXIST; + error_report("Device %x.%x.%04x already exists", cssid, ssid, + devno); + goto out_err; + } + sch->cssid = cssid; + sch->ssid = ssid; + sch->devno = devno; + have_devno = true; + } else { + ret = -EINVAL; + error_report("Malformed devno parameter '%s'", dev->bus_id); + goto out_err; + } + } + + /* Find the next free id. */ + if (have_devno) { + for (schid = 0; schid <= MAX_SCHID; schid++) { + if (!css_find_subch(1, cssid, ssid, schid)) { + sch->schid = schid; + css_subch_assign(cssid, ssid, schid, devno, sch); + found = true; + break; + } + } + if (!found) { + ret = -ENODEV; + error_report("No free subchannel found for %x.%x.%04x", cssid, ssid, + devno); + goto out_err; + } + trace_virtio_ccw_new_device(cssid, ssid, schid, devno, + "user-configured"); + } else { + cssid = VIRTUAL_CSSID; + for (ssid = 0; ssid <= MAX_SSID; ssid++) { + for (schid = 0; schid <= MAX_SCHID; schid++) { + if (!css_find_subch(1, cssid, ssid, schid)) { + sch->cssid = cssid; + sch->ssid = ssid; + sch->schid = schid; + devno = schid; + /* + * If the devno is already taken, look further in this + * subchannel set. + */ + while (css_devno_used(cssid, ssid, devno)) { + if (devno == MAX_SCHID) { + devno = 0; + } else if (devno == schid - 1) { + ret = -ENODEV; + error_report("No free devno found"); + goto out_err; + } else { + devno++; + } + } + sch->devno = devno; + css_subch_assign(cssid, ssid, schid, devno, sch); + found = true; + break; + } + } + if (found) { + break; + } + } + if (!found) { + ret = -ENODEV; + error_report("Virtual channel subsystem is full!"); + goto out_err; + } + trace_virtio_ccw_new_device(cssid, ssid, schid, devno, + "auto-configured"); + } + + /* Build initial schib. */ + css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); + + sch->ccw_cb = virtio_ccw_cb; + + /* Build senseid data. */ + memset(&sch->id, 0, sizeof(SenseId)); + sch->id.reserved = 0xff; + sch->id.cu_type = VIRTIO_CCW_CU_TYPE; + sch->id.cu_model = dev->vdev->device_id; + + virtio_bind_device(vdev, &virtio_ccw_bindings, DEVICE(dev)); + /* Only the first 32 feature bits are used. */ + dev->host_features[0] = vdev->get_features(vdev, dev->host_features[0]); + dev->host_features[0] |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; + dev->host_features[0] |= 0x1 << VIRTIO_F_BAD_FEATURE; + + css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, + parent->hotplugged, 1); + return 0; + +out_err: + dev->sch = NULL; + g_free(sch); + return ret; +} + +static int virtio_ccw_exit(VirtioCcwDevice *dev) +{ + SubchDev *sch = dev->sch; + + if (sch) { + css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); + g_free(sch); + } + dev->indicators = 0; + return 0; +} + +static int virtio_ccw_net_init(VirtioCcwDevice *dev) +{ + VirtIODevice *vdev; + + vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net, + dev->host_features[0]); + if (!vdev) { + return -1; + } + + return virtio_ccw_device_init(dev, vdev); +} + +static int virtio_ccw_net_exit(VirtioCcwDevice *dev) +{ + virtio_net_exit(dev->vdev); + return virtio_ccw_exit(dev); +} + +static int virtio_ccw_blk_init(VirtioCcwDevice *dev) +{ + VirtIODevice *vdev; + + vdev = virtio_blk_init((DeviceState *)dev, &dev->blk); + if (!vdev) { + return -1; + } + + return virtio_ccw_device_init(dev, vdev); +} + +static int virtio_ccw_blk_exit(VirtioCcwDevice *dev) +{ + virtio_blk_exit(dev->vdev); + return virtio_ccw_exit(dev); +} + +static int virtio_ccw_serial_init(VirtioCcwDevice *dev) +{ + VirtIODevice *vdev; + + vdev = virtio_serial_init((DeviceState *)dev, &dev->serial); + if (!vdev) { + return -1; + } + + return virtio_ccw_device_init(dev, vdev); +} + +static int virtio_ccw_serial_exit(VirtioCcwDevice *dev) +{ + virtio_serial_exit(dev->vdev); + return virtio_ccw_exit(dev); +} + +static int virtio_ccw_balloon_init(VirtioCcwDevice *dev) +{ + VirtIODevice *vdev; + + vdev = virtio_balloon_init((DeviceState *)dev); + if (!vdev) { + return -1; + } + + return virtio_ccw_device_init(dev, vdev); +} + +static int virtio_ccw_balloon_exit(VirtioCcwDevice *dev) +{ + virtio_balloon_exit(dev->vdev); + return virtio_ccw_exit(dev); +} + +static int virtio_ccw_scsi_init(VirtioCcwDevice *dev) +{ + VirtIODevice *vdev; + + vdev = virtio_scsi_init((DeviceState *)dev, &dev->scsi); + if (!vdev) { + return -1; + } + + return virtio_ccw_device_init(dev, vdev); +} + +static int virtio_ccw_scsi_exit(VirtioCcwDevice *dev) +{ + virtio_scsi_exit(dev->vdev); + return virtio_ccw_exit(dev); +} + +static int virtio_ccw_rng_init(VirtioCcwDevice *dev) +{ + VirtIODevice *vdev; + + if (dev->rng.rng == NULL) { + dev->rng.default_backend = RNG_RANDOM(object_new(TYPE_RNG_RANDOM)); + object_property_add_child(OBJECT(dev), "default-backend", + OBJECT(dev->rng.default_backend), NULL); + object_property_set_link(OBJECT(dev), OBJECT(dev->rng.default_backend), + "rng", NULL); + } + vdev = virtio_rng_init((DeviceState *)dev, &dev->rng); + if (!vdev) { + return -1; + } + return virtio_ccw_device_init(dev, vdev); +} + +static int virtio_ccw_rng_exit(VirtioCcwDevice *dev) +{ + virtio_rng_exit(dev->vdev); + return virtio_ccw_exit(dev); +} + +/* DeviceState to VirtioCcwDevice. Note: used on datapath, + * be careful and test performance if you change this. + */ +static inline VirtioCcwDevice *to_virtio_ccw_dev_fast(DeviceState *d) +{ + return container_of(d, VirtioCcwDevice, parent_obj); +} + +static void virtio_ccw_notify(DeviceState *d, uint16_t vector) +{ + VirtioCcwDevice *dev = to_virtio_ccw_dev_fast(d); + SubchDev *sch = dev->sch; + uint64_t indicators; + + if (vector >= 128) { + return; + } + + if (vector < VIRTIO_PCI_QUEUE_MAX) { + indicators = ldq_phys(dev->indicators); + indicators |= 1ULL << vector; + stq_phys(dev->indicators, indicators); + } else { + vector = 0; + indicators = ldq_phys(dev->indicators2); + indicators |= 1ULL << vector; + stq_phys(dev->indicators2, indicators); + } + + css_conditional_io_interrupt(sch); + +} + +static unsigned virtio_ccw_get_features(DeviceState *d) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + + /* Only the first 32 feature bits are used. */ + return dev->host_features[0]; +} + +static void virtio_ccw_reset(DeviceState *d) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + + virtio_reset(dev->vdev); + css_reset_sch(dev->sch); +} + +/**************** Virtio-ccw Bus Device Descriptions *******************/ + +static const VirtIOBindings virtio_ccw_bindings = { + .notify = virtio_ccw_notify, + .get_features = virtio_ccw_get_features, +}; + +static Property virtio_ccw_net_properties[] = { + DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_VIRTIO_NET_FEATURES(VirtioCcwDevice, host_features[0]), + DEFINE_NIC_PROPERTIES(VirtioCcwDevice, nic), + DEFINE_PROP_UINT32("x-txtimer", VirtioCcwDevice, + net.txtimer, TX_TIMER_INTERVAL), + DEFINE_PROP_INT32("x-txburst", VirtioCcwDevice, + net.txburst, TX_BURST), + DEFINE_PROP_STRING("tx", VirtioCcwDevice, net.tx), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->init = virtio_ccw_net_init; + k->exit = virtio_ccw_net_exit; + dc->reset = virtio_ccw_reset; + dc->props = virtio_ccw_net_properties; +} + +static const TypeInfo virtio_ccw_net = { + .name = "virtio-net-ccw", + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtioCcwDevice), + .class_init = virtio_ccw_net_class_init, +}; + +static Property virtio_ccw_blk_properties[] = { + DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_BLOCK_PROPERTIES(VirtioCcwDevice, blk.conf), + DEFINE_PROP_STRING("serial", VirtioCcwDevice, blk.serial), +#ifdef __linux__ + DEFINE_PROP_BIT("scsi", VirtioCcwDevice, blk.scsi, 0, true), +#endif + DEFINE_VIRTIO_BLK_FEATURES(VirtioCcwDevice, host_features[0]), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->init = virtio_ccw_blk_init; + k->exit = virtio_ccw_blk_exit; + dc->reset = virtio_ccw_reset; + dc->props = virtio_ccw_blk_properties; +} + +static const TypeInfo virtio_ccw_blk = { + .name = "virtio-blk-ccw", + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtioCcwDevice), + .class_init = virtio_ccw_blk_class_init, +}; + +static Property virtio_ccw_serial_properties[] = { + DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_PROP_UINT32("max_ports", VirtioCcwDevice, + serial.max_virtserial_ports, 31), + DEFINE_VIRTIO_COMMON_FEATURES(VirtioCcwDevice, host_features[0]), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->init = virtio_ccw_serial_init; + k->exit = virtio_ccw_serial_exit; + dc->reset = virtio_ccw_reset; + dc->props = virtio_ccw_serial_properties; +} + +static const TypeInfo virtio_ccw_serial = { + .name = "virtio-serial-ccw", + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtioCcwDevice), + .class_init = virtio_ccw_serial_class_init, +}; + +static Property virtio_ccw_balloon_properties[] = { + DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_VIRTIO_COMMON_FEATURES(VirtioCcwDevice, host_features[0]), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->init = virtio_ccw_balloon_init; + k->exit = virtio_ccw_balloon_exit; + dc->reset = virtio_ccw_reset; + dc->props = virtio_ccw_balloon_properties; +} + +static const TypeInfo virtio_ccw_balloon = { + .name = "virtio-balloon-ccw", + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtioCcwDevice), + .class_init = virtio_ccw_balloon_class_init, +}; + +static Property virtio_ccw_scsi_properties[] = { + DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_VIRTIO_SCSI_PROPERTIES(VirtioCcwDevice, host_features[0], scsi), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->init = virtio_ccw_scsi_init; + k->exit = virtio_ccw_scsi_exit; + dc->reset = virtio_ccw_reset; + dc->props = virtio_ccw_scsi_properties; +} + +static const TypeInfo virtio_ccw_scsi = { + .name = "virtio-scsi-ccw", + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtioCcwDevice), + .class_init = virtio_ccw_scsi_class_init, +}; + +static void virtio_ccw_rng_initfn(Object *obj) +{ + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(obj); + + object_property_add_link(obj, "rng", TYPE_RNG_BACKEND, + (Object **)&dev->rng.rng, NULL); +} + +static Property virtio_ccw_rng_properties[] = { + DEFINE_PROP_STRING("devno", VirtioCcwDevice, bus_id), + DEFINE_VIRTIO_COMMON_FEATURES(VirtioCcwDevice, host_features[0]), + DEFINE_PROP_UINT64("max-bytes", VirtioCcwDevice, rng.max_bytes, INT64_MAX), + DEFINE_PROP_UINT32("period", VirtioCcwDevice, rng.period_ms, 1 << 16), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->init = virtio_ccw_rng_init; + k->exit = virtio_ccw_rng_exit; + dc->reset = virtio_ccw_reset; + dc->props = virtio_ccw_rng_properties; +} + +static const TypeInfo virtio_ccw_rng = { + .name = "virtio-rng-ccw", + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtioCcwDevice), + .instance_init = virtio_ccw_rng_initfn, + .class_init = virtio_ccw_rng_class_init, +}; + +static int virtio_ccw_busdev_init(DeviceState *dev) +{ + VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; + VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev); + + virtio_ccw_bus_new(&_dev->bus, _dev); + + return _info->init(_dev); +} + +static int virtio_ccw_busdev_exit(DeviceState *dev) +{ + VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; + VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev); + + return _info->exit(_dev); +} + +static int virtio_ccw_busdev_unplug(DeviceState *dev) +{ + VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; + SubchDev *sch = _dev->sch; + + /* + * We should arrive here only for device_del, since we don't support + * direct hot(un)plug of channels, but only through virtio. + */ + assert(sch != NULL); + /* Subchannel is now disabled and no longer valid. */ + sch->curr_status.pmcw.flags &= ~(PMCW_FLAGS_MASK_ENA | + PMCW_FLAGS_MASK_DNV); + + css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); + + qdev_free(dev); + return 0; +} + +static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->init = virtio_ccw_busdev_init; + dc->exit = virtio_ccw_busdev_exit; + dc->unplug = virtio_ccw_busdev_unplug; + dc->bus_type = TYPE_VIRTUAL_CSS_BUS; + +} + +static const TypeInfo virtio_ccw_device_info = { + .name = TYPE_VIRTIO_CCW_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(VirtioCcwDevice), + .class_init = virtio_ccw_device_class_init, + .class_size = sizeof(VirtIOCCWDeviceClass), + .abstract = true, +}; + +/***************** Virtual-css Bus Bridge Device ********************/ +/* Only required to have the virtio bus as child in the system bus */ + +static int virtual_css_bridge_init(SysBusDevice *dev) +{ + /* nothing */ + return 0; +} + +static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = virtual_css_bridge_init; + dc->no_user = 1; +} + +static const TypeInfo virtual_css_bridge_info = { + .name = "virtual-css-bridge", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SysBusDevice), + .class_init = virtual_css_bridge_class_init, +}; + +/* virtio-ccw-bus */ + +void virtio_ccw_bus_new(VirtioBusState *bus, VirtioCcwDevice *dev) +{ + DeviceState *qdev = DEVICE(dev); + BusState *qbus; + + qbus_create_inplace((BusState *)bus, TYPE_VIRTIO_CCW_BUS, qdev, NULL); + qbus = BUS(bus); + qbus->allow_hotplug = 0; +} + +static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data) +{ + VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); + BusClass *bus_class = BUS_CLASS(klass); + + bus_class->max_dev = 1; + k->notify = virtio_ccw_notify; + k->get_features = virtio_ccw_get_features; +} + +static const TypeInfo virtio_ccw_bus_info = { + .name = TYPE_VIRTIO_CCW_BUS, + .parent = TYPE_VIRTIO_BUS, + .instance_size = sizeof(VirtioCcwBusState), + .class_init = virtio_ccw_bus_class_init, +}; + +static void virtio_ccw_register(void) +{ + type_register_static(&virtio_ccw_bus_info); + type_register_static(&virtual_css_bus_info); + type_register_static(&virtio_ccw_device_info); + type_register_static(&virtio_ccw_serial); + type_register_static(&virtio_ccw_blk); + type_register_static(&virtio_ccw_net); + type_register_static(&virtio_ccw_balloon); + type_register_static(&virtio_ccw_scsi); + type_register_static(&virtio_ccw_rng); + type_register_static(&virtual_css_bridge_info); +} + +type_init(virtio_ccw_register) diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h new file mode 100644 index 0000000000..88c46c081b --- /dev/null +++ b/hw/s390x/virtio-ccw.h @@ -0,0 +1,100 @@ +/* + * virtio ccw target definitions + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#ifndef HW_S390X_VIRTIO_CCW_H +#define HW_S390X_VIRTIO_CCW_H + +#include +#include +#include +#include +#include +#include + +#define VIRTUAL_CSSID 0xfe + +#define VIRTIO_CCW_CU_TYPE 0x3832 +#define VIRTIO_CCW_CHPID_TYPE 0x32 + +#define CCW_CMD_SET_VQ 0x13 +#define CCW_CMD_VDEV_RESET 0x33 +#define CCW_CMD_READ_FEAT 0x12 +#define CCW_CMD_WRITE_FEAT 0x11 +#define CCW_CMD_READ_CONF 0x22 +#define CCW_CMD_WRITE_CONF 0x21 +#define CCW_CMD_WRITE_STATUS 0x31 +#define CCW_CMD_SET_IND 0x43 +#define CCW_CMD_SET_CONF_IND 0x53 +#define CCW_CMD_READ_VQ_CONF 0x32 + +#define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device" +#define VIRTIO_CCW_DEVICE(obj) \ + OBJECT_CHECK(VirtioCcwDevice, (obj), TYPE_VIRTIO_CCW_DEVICE) +#define VIRTIO_CCW_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtIOCCWDeviceClass, (klass), TYPE_VIRTIO_CCW_DEVICE) +#define VIRTIO_CCW_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtIOCCWDeviceClass, (obj), TYPE_VIRTIO_CCW_DEVICE) + +typedef struct VirtioBusState VirtioCcwBusState; +typedef struct VirtioBusClass VirtioCcwBusClass; + +#define TYPE_VIRTIO_CCW_BUS "virtio-ccw-bus" +#define VIRTIO_CCW_BUS(obj) \ + OBJECT_CHECK(VirtioCcwBus, (obj), TYPE_VIRTIO_CCW_BUS) +#define VIRTIO_CCW_BUS_GET_CLASS(obj) \ + OBJECT_CHECK(VirtioCcwBusState, (obj), TYPE_VIRTIO_CCW_BUS) +#define VIRTIO_CCW_BUS_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtioCcwBusClass, klass, TYPE_VIRTIO_CCW_BUS) + +typedef struct VirtioCcwDevice VirtioCcwDevice; + +void virtio_ccw_bus_new(VirtioBusState *bus, VirtioCcwDevice *dev); + +typedef struct VirtIOCCWDeviceClass { + DeviceClass parent_class; + int (*init)(VirtioCcwDevice *dev); + int (*exit)(VirtioCcwDevice *dev); +} VirtIOCCWDeviceClass; + +/* Change here if we want to support more feature bits. */ +#define VIRTIO_CCW_FEATURE_SIZE 1 + +struct VirtioCcwDevice { + DeviceState parent_obj; + SubchDev *sch; + VirtIODevice *vdev; + char *bus_id; + VirtIOBlkConf blk; + NICConf nic; + uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE]; + virtio_serial_conf serial; + virtio_net_conf net; + VirtIOSCSIConf scsi; + VirtIORNGConf rng; + VirtioBusState bus; + /* Guest provided values: */ + hwaddr indicators; + hwaddr indicators2; +}; + +/* virtual css bus type */ +typedef struct VirtualCssBus { + BusState parent_obj; +} VirtualCssBus; + +#define TYPE_VIRTUAL_CSS_BUS "virtual-css-bus" +#define VIRTUAL_CSS_BUS(obj) \ + OBJECT_CHECK(VirtualCssBus, (obj), TYPE_VIRTUAL_CSS_BUS) + +VirtualCssBus *virtual_css_bus_init(void); +void virtio_ccw_device_update_status(SubchDev *sch); +VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch); +#endif diff --git a/hw/sb16.c b/hw/sb16.c index bb460ccb60..bd51cebfd8 100644 --- a/hw/sb16.c +++ b/hw/sb16.c @@ -21,11 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "audiodev.h" +#include "hw/hw.h" +#include "hw/audiodev.h" #include "audio/audio.h" -#include "isa.h" -#include "qdev.h" +#include "hw/isa.h" +#include "hw/qdev.h" #include "qemu/timer.h" #include "qemu/host-utils.h" @@ -1409,7 +1409,7 @@ static void sb16_class_initfn (ObjectClass *klass, void *data) dc->props = sb16_properties; } -static TypeInfo sb16_info = { +static const TypeInfo sb16_info = { .name = "sb16", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof (SB16State), diff --git a/hw/sbi.c b/hw/sbi.c index ca78a384c7..8795749de8 100644 --- a/hw/sbi.c +++ b/hw/sbi.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" //#define DEBUG_IRQ @@ -141,7 +141,7 @@ static void sbi_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_sbi; } -static TypeInfo sbi_info = { +static const TypeInfo sbi_info = { .name = "sbi", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SBIState), diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 970c1fc01b..08787c2a9b 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -1,8 +1,8 @@ -#include "hw.h" +#include "hw/hw.h" #include "qemu/error-report.h" -#include "scsi.h" -#include "scsi-defs.h" -#include "qdev.h" +#include "hw/scsi.h" +#include "hw/scsi-defs.h" +#include "hw/qdev.h" #include "sysemu/blockdev.h" #include "trace.h" #include "sysemu/dma.h" @@ -282,7 +282,7 @@ static const struct SCSIReqOps reqops_invalid_opcode = { static int32_t scsi_unit_attention(SCSIRequest *req, uint8_t *buf) { - if (req->dev && req->dev->unit_attention.key == UNIT_ATTENTION) { + if (req->dev->unit_attention.key == UNIT_ATTENTION) { scsi_req_build_sense(req, req->dev->unit_attention); } else if (req->bus->unit_attention.key == UNIT_ATTENTION) { scsi_req_build_sense(req, req->bus->unit_attention); @@ -1508,6 +1508,10 @@ void scsi_req_unref(SCSIRequest *req) will start the next chunk or complete the command. */ void scsi_req_continue(SCSIRequest *req) { + if (req->io_canceled) { + trace_scsi_req_continue_canceled(req->dev->id, req->lun, req->tag); + return; + } trace_scsi_req_continue(req->dev->id, req->lun, req->tag); if (req->cmd.mode == SCSI_XFER_TO_DEV) { req->ops->write_data(req); @@ -1863,7 +1867,7 @@ static void scsi_device_class_init(ObjectClass *klass, void *data) k->props = scsi_props; } -static TypeInfo scsi_device_type_info = { +static const TypeInfo scsi_device_type_info = { .name = TYPE_SCSI_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(SCSIDevice), diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index a69735b0a6..c5c7bf3dfa 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -30,8 +30,8 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include "qemu-common.h" #include "qemu/error-report.h" -#include "scsi.h" -#include "scsi-defs.h" +#include "hw/scsi.h" +#include "hw/scsi-defs.h" #include "sysemu/sysemu.h" #include "sysemu/blockdev.h" #include "hw/block-common.h" @@ -41,9 +41,11 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include #endif -#define SCSI_DMA_BUF_SIZE 131072 -#define SCSI_MAX_INQUIRY_LEN 256 -#define SCSI_MAX_MODE_LEN 256 +#define SCSI_DMA_BUF_SIZE 131072 +#define SCSI_MAX_INQUIRY_LEN 256 +#define SCSI_MAX_MODE_LEN 256 + +#define DEFAULT_DISCARD_GRANULARITY 4096 typedef struct SCSIDiskState SCSIDiskState; @@ -85,9 +87,7 @@ static void scsi_free_request(SCSIRequest *req) { SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - if (r->iov.iov_base) { - qemu_vfree(r->iov.iov_base); - } + qemu_vfree(r->iov.iov_base); } /* Helper function for command completion with sense. */ @@ -178,6 +178,9 @@ static void scsi_aio_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; bdrv_acct_done(s->qdev.conf.bs, &r->acct); + if (r->req.io_canceled) { + goto done; + } if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { @@ -223,6 +226,10 @@ static void scsi_write_do_fua(SCSIDiskReq *r) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + if (r->req.io_canceled) { + goto done; + } + if (scsi_is_cmd_fua(&r->req.cmd)) { bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH); r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r); @@ -230,6 +237,8 @@ static void scsi_write_do_fua(SCSIDiskReq *r) } scsi_req_complete(&r->req, GOOD); + +done: if (!r->req.io_canceled) { scsi_req_unref(&r->req); } @@ -243,6 +252,9 @@ static void scsi_dma_complete(void *opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; bdrv_acct_done(s->qdev.conf.bs, &r->acct); + if (r->req.io_canceled) { + goto done; + } if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { @@ -274,6 +286,9 @@ static void scsi_read_complete(void * opaque, int ret) assert(r->req.aiocb != NULL); r->req.aiocb = NULL; bdrv_acct_done(s->qdev.conf.bs, &r->acct); + if (r->req.io_canceled) { + goto done; + } if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { @@ -305,6 +320,9 @@ static void scsi_do_read(void *opaque, int ret) r->req.aiocb = NULL; bdrv_acct_done(s->qdev.conf.bs, &r->acct); } + if (r->req.io_canceled) { + goto done; + } if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { @@ -312,10 +330,6 @@ static void scsi_do_read(void *opaque, int ret) } } - if (r->req.io_canceled) { - return; - } - /* The request is used as the AIO opaque value, so add a ref. */ scsi_req_ref(&r->req); @@ -423,6 +437,9 @@ static void scsi_write_complete(void * opaque, int ret) r->req.aiocb = NULL; bdrv_acct_done(s->qdev.conf.bs, &r->acct); } + if (r->req.io_canceled) { + goto done; + } if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { @@ -1478,13 +1495,17 @@ static void scsi_unmap_complete(void *opaque, int ret) uint32_t nb_sectors; r->req.aiocb = NULL; + if (r->req.io_canceled) { + goto done; + } + if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { goto done; } } - if (data->count > 0 && !r->req.io_canceled) { + if (data->count > 0) { sector_num = ldq_be_p(&data->inbuf[0]); nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL; if (!check_lba_range(s, sector_num, nb_sectors)) { @@ -1501,10 +1522,9 @@ static void scsi_unmap_complete(void *opaque, int ret) return; } + scsi_req_complete(&r->req, GOOD); + done: - if (data->count == 0) { - scsi_req_complete(&r->req, GOOD); - } if (!r->req.io_canceled) { scsi_req_unref(&r->req); } @@ -1682,7 +1702,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); if (!nb_sectors) { scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); - return -1; + return 0; } if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) { goto illegal_request; @@ -1751,7 +1771,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); if (!nb_sectors) { scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); - return -1; + return 0; } if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) { goto illegal_request; @@ -2061,6 +2081,11 @@ static int scsi_initfn(SCSIDevice *dev) return -1; } + if (s->qdev.conf.discard_granularity == -1) { + s->qdev.conf.discard_granularity = + MAX(s->qdev.conf.logical_block_size, DEFAULT_DISCARD_GRANULARITY); + } + if (!s->version) { s->version = g_strdup(qemu_get_version()); } @@ -2389,7 +2414,7 @@ static void scsi_hd_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_scsi_disk_state; } -static TypeInfo scsi_hd_info = { +static const TypeInfo scsi_hd_info = { .name = "scsi-hd", .parent = TYPE_SCSI_DEVICE, .instance_size = sizeof(SCSIDiskState), @@ -2418,7 +2443,7 @@ static void scsi_cd_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_scsi_disk_state; } -static TypeInfo scsi_cd_info = { +static const TypeInfo scsi_cd_info = { .name = "scsi-cd", .parent = TYPE_SCSI_DEVICE, .instance_size = sizeof(SCSIDiskState), @@ -2447,7 +2472,7 @@ static void scsi_block_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_scsi_disk_state; } -static TypeInfo scsi_block_info = { +static const TypeInfo scsi_block_info = { .name = "scsi-block", .parent = TYPE_SCSI_DEVICE, .instance_size = sizeof(SCSIDiskState), @@ -2481,7 +2506,7 @@ static void scsi_disk_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_scsi_disk_state; } -static TypeInfo scsi_disk_info = { +static const TypeInfo scsi_disk_info = { .name = "scsi-disk", .parent = TYPE_SCSI_DEVICE, .instance_size = sizeof(SCSIDiskState), diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 4c702be19f..4d04caccce 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -13,7 +13,7 @@ #include "qemu-common.h" #include "qemu/error-report.h" -#include "scsi.h" +#include "hw/scsi.h" #include "sysemu/blockdev.h" #ifdef __linux__ @@ -35,7 +35,7 @@ do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) #include #include #include -#include "scsi-defs.h" +#include "hw/scsi-defs.h" #define SCSI_SENSE_BUF_SIZE 96 @@ -499,7 +499,7 @@ static void scsi_generic_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_scsi_device; } -static TypeInfo scsi_generic_info = { +static const TypeInfo scsi_generic_info = { .name = "scsi-generic", .parent = TYPE_SCSI_DEVICE, .instance_size = sizeof(SCSIDevice), diff --git a/hw/scsi.h b/hw/scsi.h index a5b5b2ec0d..33e2e0bdf1 100644 --- a/hw/scsi.h +++ b/hw/scsi.h @@ -1,7 +1,7 @@ #ifndef QEMU_HW_SCSI_H #define QEMU_HW_SCSI_H -#include "qdev.h" +#include "hw/qdev.h" #include "block/block.h" #include "hw/block-common.h" #include "sysemu/sysemu.h" diff --git a/hw/sd.c b/hw/sd.c index 428bd78e32..a895123867 100644 --- a/hw/sd.c +++ b/hw/sd.c @@ -29,9 +29,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "hw.h" +#include "hw/hw.h" #include "block/block.h" -#include "sd.h" +#include "hw/sd.h" #include "qemu/bitmap.h" //#define DEBUG_SD 1 diff --git a/hw/sdhci.c b/hw/sdhci.c new file mode 100644 index 0000000000..93feada049 --- /dev/null +++ b/hw/sdhci.c @@ -0,0 +1,1300 @@ +/* + * SD Association Host Standard Specification v2.0 controller emulation + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Mitsyanko Igor + * Peter A.G. Crosthwaite + * + * Based on MMC controller for Samsung S5PC1xx-based board emulation + * by Alexey Merkulov and Vladimir Monakhov. + * + * 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) 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 . + */ + +#include "hw/hw.h" +#include "sysemu/blockdev.h" +#include "sysemu/dma.h" +#include "qemu/timer.h" +#include "block/block_int.h" +#include "qemu/bitops.h" + +#include "hw/sdhci.h" + +/* host controller debug messages */ +#ifndef SDHC_DEBUG +#define SDHC_DEBUG 0 +#endif + +#if SDHC_DEBUG == 0 + #define DPRINT_L1(fmt, args...) do { } while (0) + #define DPRINT_L2(fmt, args...) do { } while (0) + #define ERRPRINT(fmt, args...) do { } while (0) +#elif SDHC_DEBUG == 1 + #define DPRINT_L1(fmt, args...) \ + do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0) + #define DPRINT_L2(fmt, args...) do { } while (0) + #define ERRPRINT(fmt, args...) \ + do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0) +#else + #define DPRINT_L1(fmt, args...) \ + do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0) + #define DPRINT_L2(fmt, args...) \ + do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0) + #define ERRPRINT(fmt, args...) \ + do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0) +#endif + +/* Default SD/MMC host controller features information, which will be + * presented in CAPABILITIES register of generic SD host controller at reset. + * If not stated otherwise: + * 0 - not supported, 1 - supported, other - prohibited. + */ +#define SDHC_CAPAB_64BITBUS 0ul /* 64-bit System Bus Support */ +#define SDHC_CAPAB_18V 1ul /* Voltage support 1.8v */ +#define SDHC_CAPAB_30V 0ul /* Voltage support 3.0v */ +#define SDHC_CAPAB_33V 1ul /* Voltage support 3.3v */ +#define SDHC_CAPAB_SUSPRESUME 0ul /* Suspend/resume support */ +#define SDHC_CAPAB_SDMA 1ul /* SDMA support */ +#define SDHC_CAPAB_HIGHSPEED 1ul /* High speed support */ +#define SDHC_CAPAB_ADMA1 1ul /* ADMA1 support */ +#define SDHC_CAPAB_ADMA2 1ul /* ADMA2 support */ +/* Maximum host controller R/W buffers size + * Possible values: 512, 1024, 2048 bytes */ +#define SDHC_CAPAB_MAXBLOCKLENGTH 512ul +/* Maximum clock frequency for SDclock in MHz + * value in range 10-63 MHz, 0 - not defined */ +#define SDHC_CAPAB_BASECLKFREQ 0ul +#define SDHC_CAPAB_TOUNIT 1ul /* Timeout clock unit 0 - kHz, 1 - MHz */ +/* Timeout clock frequency 1-63, 0 - not defined */ +#define SDHC_CAPAB_TOCLKFREQ 0ul + +/* Now check all parameters and calculate CAPABILITIES REGISTER value */ +#if SDHC_CAPAB_64BITBUS > 1 || SDHC_CAPAB_18V > 1 || SDHC_CAPAB_30V > 1 || \ + SDHC_CAPAB_33V > 1 || SDHC_CAPAB_SUSPRESUME > 1 || SDHC_CAPAB_SDMA > 1 || \ + SDHC_CAPAB_HIGHSPEED > 1 || SDHC_CAPAB_ADMA2 > 1 || SDHC_CAPAB_ADMA1 > 1 ||\ + SDHC_CAPAB_TOUNIT > 1 +#error Capabilities features can have value 0 or 1 only! +#endif + +#if SDHC_CAPAB_MAXBLOCKLENGTH == 512 +#define MAX_BLOCK_LENGTH 0ul +#elif SDHC_CAPAB_MAXBLOCKLENGTH == 1024 +#define MAX_BLOCK_LENGTH 1ul +#elif SDHC_CAPAB_MAXBLOCKLENGTH == 2048 +#define MAX_BLOCK_LENGTH 2ul +#else +#error Max host controller block size can have value 512, 1024 or 2048 only! +#endif + +#if (SDHC_CAPAB_BASECLKFREQ > 0 && SDHC_CAPAB_BASECLKFREQ < 10) || \ + SDHC_CAPAB_BASECLKFREQ > 63 +#error SDclock frequency can have value in range 0, 10-63 only! +#endif + +#if SDHC_CAPAB_TOCLKFREQ > 63 +#error Timeout clock frequency can have value in range 0-63 only! +#endif + +#define SDHC_CAPAB_REG_DEFAULT \ + ((SDHC_CAPAB_64BITBUS << 28) | (SDHC_CAPAB_18V << 26) | \ + (SDHC_CAPAB_30V << 25) | (SDHC_CAPAB_33V << 24) | \ + (SDHC_CAPAB_SUSPRESUME << 23) | (SDHC_CAPAB_SDMA << 22) | \ + (SDHC_CAPAB_HIGHSPEED << 21) | (SDHC_CAPAB_ADMA1 << 20) | \ + (SDHC_CAPAB_ADMA2 << 19) | (MAX_BLOCK_LENGTH << 16) | \ + (SDHC_CAPAB_BASECLKFREQ << 8) | (SDHC_CAPAB_TOUNIT << 7) | \ + (SDHC_CAPAB_TOCLKFREQ)) + +#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val)) + +static uint8_t sdhci_slotint(SDHCIState *s) +{ + return (s->norintsts & s->norintsigen) || (s->errintsts & s->errintsigen) || + ((s->norintsts & SDHC_NIS_INSERT) && (s->wakcon & SDHC_WKUP_ON_INS)) || + ((s->norintsts & SDHC_NIS_REMOVE) && (s->wakcon & SDHC_WKUP_ON_RMV)); +} + +static inline void sdhci_update_irq(SDHCIState *s) +{ + qemu_set_irq(s->irq, sdhci_slotint(s)); +} + +static void sdhci_raise_insertion_irq(void *opaque) +{ + SDHCIState *s = (SDHCIState *)opaque; + + if (s->norintsts & SDHC_NIS_REMOVE) { + qemu_mod_timer(s->insert_timer, + qemu_get_clock_ns(vm_clock) + SDHC_INSERTION_DELAY); + } else { + s->prnsts = 0x1ff0000; + if (s->norintstsen & SDHC_NISEN_INSERT) { + s->norintsts |= SDHC_NIS_INSERT; + } + sdhci_update_irq(s); + } +} + +static void sdhci_insert_eject_cb(void *opaque, int irq, int level) +{ + SDHCIState *s = (SDHCIState *)opaque; + DPRINT_L1("Card state changed: %s!\n", level ? "insert" : "eject"); + + if ((s->norintsts & SDHC_NIS_REMOVE) && level) { + /* Give target some time to notice card ejection */ + qemu_mod_timer(s->insert_timer, + qemu_get_clock_ns(vm_clock) + SDHC_INSERTION_DELAY); + } else { + if (level) { + s->prnsts = 0x1ff0000; + if (s->norintstsen & SDHC_NISEN_INSERT) { + s->norintsts |= SDHC_NIS_INSERT; + } + } else { + s->prnsts = 0x1fa0000; + s->pwrcon &= ~SDHC_POWER_ON; + s->clkcon &= ~SDHC_CLOCK_SDCLK_EN; + if (s->norintstsen & SDHC_NISEN_REMOVE) { + s->norintsts |= SDHC_NIS_REMOVE; + } + } + sdhci_update_irq(s); + } +} + +static void sdhci_card_readonly_cb(void *opaque, int irq, int level) +{ + SDHCIState *s = (SDHCIState *)opaque; + + if (level) { + s->prnsts &= ~SDHC_WRITE_PROTECT; + } else { + /* Write enabled */ + s->prnsts |= SDHC_WRITE_PROTECT; + } +} + +static void sdhci_reset(SDHCIState *s) +{ + qemu_del_timer(s->insert_timer); + qemu_del_timer(s->transfer_timer); + /* Set all registers to 0. Capabilities registers are not cleared + * and assumed to always preserve their value, given to them during + * initialization */ + memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad); + + sd_set_cb(s->card, s->ro_cb, s->eject_cb); + s->data_count = 0; + s->stopped_state = sdhc_not_stopped; +} + +static void sdhci_do_data_transfer(void *opaque) +{ + SDHCIState *s = (SDHCIState *)opaque; + + SDHCI_GET_CLASS(s)->data_transfer(s); +} + +static void sdhci_send_command(SDHCIState *s) +{ + SDRequest request; + uint8_t response[16]; + int rlen; + + s->errintsts = 0; + s->acmd12errsts = 0; + request.cmd = s->cmdreg >> 8; + request.arg = s->argument; + DPRINT_L1("sending CMD%u ARG[0x%08x]\n", request.cmd, request.arg); + rlen = sd_do_command(s->card, &request, response); + + if (s->cmdreg & SDHC_CMD_RESPONSE) { + if (rlen == 4) { + s->rspreg[0] = (response[0] << 24) | (response[1] << 16) | + (response[2] << 8) | response[3]; + s->rspreg[1] = s->rspreg[2] = s->rspreg[3] = 0; + DPRINT_L1("Response: RSPREG[31..0]=0x%08x\n", s->rspreg[0]); + } else if (rlen == 16) { + s->rspreg[0] = (response[11] << 24) | (response[12] << 16) | + (response[13] << 8) | response[14]; + s->rspreg[1] = (response[7] << 24) | (response[8] << 16) | + (response[9] << 8) | response[10]; + s->rspreg[2] = (response[3] << 24) | (response[4] << 16) | + (response[5] << 8) | response[6]; + s->rspreg[3] = (response[0] << 16) | (response[1] << 8) | + response[2]; + DPRINT_L1("Response received:\n RSPREG[127..96]=0x%08x, RSPREG[95.." + "64]=0x%08x,\n RSPREG[63..32]=0x%08x, RSPREG[31..0]=0x%08x\n", + s->rspreg[3], s->rspreg[2], s->rspreg[1], s->rspreg[0]); + } else { + ERRPRINT("Timeout waiting for command response\n"); + if (s->errintstsen & SDHC_EISEN_CMDTIMEOUT) { + s->errintsts |= SDHC_EIS_CMDTIMEOUT; + s->norintsts |= SDHC_NIS_ERR; + } + } + + if ((s->norintstsen & SDHC_NISEN_TRSCMP) && + (s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY) { + s->norintsts |= SDHC_NIS_TRSCMP; + } + } else if (rlen != 0 && (s->errintstsen & SDHC_EISEN_CMDIDX)) { + s->errintsts |= SDHC_EIS_CMDIDX; + s->norintsts |= SDHC_NIS_ERR; + } + + if (s->norintstsen & SDHC_NISEN_CMDCMP) { + s->norintsts |= SDHC_NIS_CMDCMP; + } + + sdhci_update_irq(s); + + if (s->blksize && (s->cmdreg & SDHC_CMD_DATA_PRESENT)) { + sdhci_do_data_transfer(s); + } +} + +static void sdhci_end_transfer(SDHCIState *s) +{ + /* Automatically send CMD12 to stop transfer if AutoCMD12 enabled */ + if ((s->trnmod & SDHC_TRNS_ACMD12) != 0) { + SDRequest request; + uint8_t response[16]; + + request.cmd = 0x0C; + request.arg = 0; + DPRINT_L1("Automatically issue CMD%d %08x\n", request.cmd, request.arg); + sd_do_command(s->card, &request, response); + /* Auto CMD12 response goes to the upper Response register */ + s->rspreg[3] = (response[0] << 24) | (response[1] << 16) | + (response[2] << 8) | response[3]; + } + + s->prnsts &= ~(SDHC_DOING_READ | SDHC_DOING_WRITE | + SDHC_DAT_LINE_ACTIVE | SDHC_DATA_INHIBIT | + SDHC_SPACE_AVAILABLE | SDHC_DATA_AVAILABLE); + + if (s->norintstsen & SDHC_NISEN_TRSCMP) { + s->norintsts |= SDHC_NIS_TRSCMP; + } + + sdhci_update_irq(s); +} + +/* + * Programmed i/o data transfer + */ + +/* Fill host controller's read buffer with BLKSIZE bytes of data from card */ +static void sdhci_read_block_from_card(SDHCIState *s) +{ + int index = 0; + + if ((s->trnmod & SDHC_TRNS_MULTI) && + (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0)) { + return; + } + + for (index = 0; index < (s->blksize & 0x0fff); index++) { + s->fifo_buffer[index] = sd_read_data(s->card); + } + + /* New data now available for READ through Buffer Port Register */ + s->prnsts |= SDHC_DATA_AVAILABLE; + if (s->norintstsen & SDHC_NISEN_RBUFRDY) { + s->norintsts |= SDHC_NIS_RBUFRDY; + } + + /* Clear DAT line active status if that was the last block */ + if ((s->trnmod & SDHC_TRNS_MULTI) == 0 || + ((s->trnmod & SDHC_TRNS_MULTI) && s->blkcnt == 1)) { + s->prnsts &= ~SDHC_DAT_LINE_ACTIVE; + } + + /* If stop at block gap request was set and it's not the last block of + * data - generate Block Event interrupt */ + if (s->stopped_state == sdhc_gap_read && (s->trnmod & SDHC_TRNS_MULTI) && + s->blkcnt != 1) { + s->prnsts &= ~SDHC_DAT_LINE_ACTIVE; + if (s->norintstsen & SDHC_EISEN_BLKGAP) { + s->norintsts |= SDHC_EIS_BLKGAP; + } + } + + sdhci_update_irq(s); +} + +/* Read @size byte of data from host controller @s BUFFER DATA PORT register */ +static uint32_t sdhci_read_dataport(SDHCIState *s, unsigned size) +{ + uint32_t value = 0; + int i; + + /* first check that a valid data exists in host controller input buffer */ + if ((s->prnsts & SDHC_DATA_AVAILABLE) == 0) { + ERRPRINT("Trying to read from empty buffer\n"); + return 0; + } + + for (i = 0; i < size; i++) { + value |= s->fifo_buffer[s->data_count] << i * 8; + s->data_count++; + /* check if we've read all valid data (blksize bytes) from buffer */ + if ((s->data_count) >= (s->blksize & 0x0fff)) { + DPRINT_L2("All %u bytes of data have been read from input buffer\n", + s->data_count); + s->prnsts &= ~SDHC_DATA_AVAILABLE; /* no more data in a buffer */ + s->data_count = 0; /* next buff read must start at position [0] */ + + if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { + s->blkcnt--; + } + + /* if that was the last block of data */ + if ((s->trnmod & SDHC_TRNS_MULTI) == 0 || + ((s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0)) || + /* stop at gap request */ + (s->stopped_state == sdhc_gap_read && + !(s->prnsts & SDHC_DAT_LINE_ACTIVE))) { + SDHCI_GET_CLASS(s)->end_data_transfer(s); + } else { /* if there are more data, read next block from card */ + SDHCI_GET_CLASS(s)->read_block_from_card(s); + } + break; + } + } + + return value; +} + +/* Write data from host controller FIFO to card */ +static void sdhci_write_block_to_card(SDHCIState *s) +{ + int index = 0; + + if (s->prnsts & SDHC_SPACE_AVAILABLE) { + if (s->norintstsen & SDHC_NISEN_WBUFRDY) { + s->norintsts |= SDHC_NIS_WBUFRDY; + } + sdhci_update_irq(s); + return; + } + + if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { + if (s->blkcnt == 0) { + return; + } else { + s->blkcnt--; + } + } + + for (index = 0; index < (s->blksize & 0x0fff); index++) { + sd_write_data(s->card, s->fifo_buffer[index]); + } + + /* Next data can be written through BUFFER DATORT register */ + s->prnsts |= SDHC_SPACE_AVAILABLE; + if (s->norintstsen & SDHC_NISEN_WBUFRDY) { + s->norintsts |= SDHC_NIS_WBUFRDY; + } + + /* Finish transfer if that was the last block of data */ + if ((s->trnmod & SDHC_TRNS_MULTI) == 0 || + ((s->trnmod & SDHC_TRNS_MULTI) && + (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0))) { + SDHCI_GET_CLASS(s)->end_data_transfer(s); + } + + /* Generate Block Gap Event if requested and if not the last block */ + if (s->stopped_state == sdhc_gap_write && (s->trnmod & SDHC_TRNS_MULTI) && + s->blkcnt > 0) { + s->prnsts &= ~SDHC_DOING_WRITE; + if (s->norintstsen & SDHC_EISEN_BLKGAP) { + s->norintsts |= SDHC_EIS_BLKGAP; + } + SDHCI_GET_CLASS(s)->end_data_transfer(s); + } + + sdhci_update_irq(s); +} + +/* Write @size bytes of @value data to host controller @s Buffer Data Port + * register */ +static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size) +{ + unsigned i; + + /* Check that there is free space left in a buffer */ + if (!(s->prnsts & SDHC_SPACE_AVAILABLE)) { + ERRPRINT("Can't write to data buffer: buffer full\n"); + return; + } + + for (i = 0; i < size; i++) { + s->fifo_buffer[s->data_count] = value & 0xFF; + s->data_count++; + value >>= 8; + if (s->data_count >= (s->blksize & 0x0fff)) { + DPRINT_L2("write buffer filled with %u bytes of data\n", + s->data_count); + s->data_count = 0; + s->prnsts &= ~SDHC_SPACE_AVAILABLE; + if (s->prnsts & SDHC_DOING_WRITE) { + SDHCI_GET_CLASS(s)->write_block_to_card(s); + } + } + } +} + +/* + * Single DMA data transfer + */ + +/* Multi block SDMA transfer */ +static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) +{ + bool page_aligned = false; + unsigned int n, begin; + const uint16_t block_size = s->blksize & 0x0fff; + uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12); + uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk); + + /* XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for + * possible stop at page boundary if initial address is not page aligned, + * allow them to work properly */ + if ((s->sdmasysad % boundary_chk) == 0) { + page_aligned = true; + } + + if (s->trnmod & SDHC_TRNS_READ) { + s->prnsts |= SDHC_DOING_READ | SDHC_DATA_INHIBIT | + SDHC_DAT_LINE_ACTIVE; + while (s->blkcnt) { + if (s->data_count == 0) { + for (n = 0; n < block_size; n++) { + s->fifo_buffer[n] = sd_read_data(s->card); + } + } + begin = s->data_count; + if (((boundary_count + begin) < block_size) && page_aligned) { + s->data_count = boundary_count + begin; + boundary_count = 0; + } else { + s->data_count = block_size; + boundary_count -= block_size - begin; + if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { + s->blkcnt--; + } + } + dma_memory_write(&dma_context_memory, s->sdmasysad, + &s->fifo_buffer[begin], s->data_count - begin); + s->sdmasysad += s->data_count - begin; + if (s->data_count == block_size) { + s->data_count = 0; + } + if (page_aligned && boundary_count == 0) { + break; + } + } + } else { + s->prnsts |= SDHC_DOING_WRITE | SDHC_DATA_INHIBIT | + SDHC_DAT_LINE_ACTIVE; + while (s->blkcnt) { + begin = s->data_count; + if (((boundary_count + begin) < block_size) && page_aligned) { + s->data_count = boundary_count + begin; + boundary_count = 0; + } else { + s->data_count = block_size; + boundary_count -= block_size - begin; + } + dma_memory_read(&dma_context_memory, s->sdmasysad, + &s->fifo_buffer[begin], s->data_count); + s->sdmasysad += s->data_count - begin; + if (s->data_count == block_size) { + for (n = 0; n < block_size; n++) { + sd_write_data(s->card, s->fifo_buffer[n]); + } + s->data_count = 0; + if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { + s->blkcnt--; + } + } + if (page_aligned && boundary_count == 0) { + break; + } + } + } + + if (s->blkcnt == 0) { + SDHCI_GET_CLASS(s)->end_data_transfer(s); + } else { + if (s->norintstsen & SDHC_NISEN_DMA) { + s->norintsts |= SDHC_NIS_DMA; + } + sdhci_update_irq(s); + } +} + +/* single block SDMA transfer */ + +static void sdhci_sdma_transfer_single_block(SDHCIState *s) +{ + int n; + uint32_t datacnt = s->blksize & 0x0fff; + + if (s->trnmod & SDHC_TRNS_READ) { + for (n = 0; n < datacnt; n++) { + s->fifo_buffer[n] = sd_read_data(s->card); + } + dma_memory_write(&dma_context_memory, s->sdmasysad, s->fifo_buffer, + datacnt); + } else { + dma_memory_read(&dma_context_memory, s->sdmasysad, s->fifo_buffer, + datacnt); + for (n = 0; n < datacnt; n++) { + sd_write_data(s->card, s->fifo_buffer[n]); + } + } + + if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { + s->blkcnt--; + } + + SDHCI_GET_CLASS(s)->end_data_transfer(s); +} + +typedef struct ADMADescr { + hwaddr addr; + uint16_t length; + uint8_t attr; + uint8_t incr; +} ADMADescr; + +static void get_adma_description(SDHCIState *s, ADMADescr *dscr) +{ + uint32_t adma1 = 0; + uint64_t adma2 = 0; + hwaddr entry_addr = (hwaddr)s->admasysaddr; + switch (SDHC_DMA_TYPE(s->hostctl)) { + case SDHC_CTRL_ADMA2_32: + dma_memory_read(&dma_context_memory, entry_addr, (uint8_t *)&adma2, + sizeof(adma2)); + adma2 = le64_to_cpu(adma2); + /* The spec does not specify endianness of descriptor table. + * We currently assume that it is LE. + */ + dscr->addr = (hwaddr)extract64(adma2, 32, 32) & ~0x3ull; + dscr->length = (uint16_t)extract64(adma2, 16, 16); + dscr->attr = (uint8_t)extract64(adma2, 0, 7); + dscr->incr = 8; + break; + case SDHC_CTRL_ADMA1_32: + dma_memory_read(&dma_context_memory, entry_addr, (uint8_t *)&adma1, + sizeof(adma1)); + adma1 = le32_to_cpu(adma1); + dscr->addr = (hwaddr)(adma1 & 0xFFFFF000); + dscr->attr = (uint8_t)extract32(adma1, 0, 7); + dscr->incr = 4; + if ((dscr->attr & SDHC_ADMA_ATTR_ACT_MASK) == SDHC_ADMA_ATTR_SET_LEN) { + dscr->length = (uint16_t)extract32(adma1, 12, 16); + } else { + dscr->length = 4096; + } + break; + case SDHC_CTRL_ADMA2_64: + dma_memory_read(&dma_context_memory, entry_addr, + (uint8_t *)(&dscr->attr), 1); + dma_memory_read(&dma_context_memory, entry_addr + 2, + (uint8_t *)(&dscr->length), 2); + dscr->length = le16_to_cpu(dscr->length); + dma_memory_read(&dma_context_memory, entry_addr + 4, + (uint8_t *)(&dscr->addr), 8); + dscr->attr = le64_to_cpu(dscr->attr); + dscr->attr &= 0xfffffff8; + dscr->incr = 12; + break; + } +} + +/* Advanced DMA data transfer */ + +static void sdhci_do_adma(SDHCIState *s) +{ + unsigned int n, begin, length; + const uint16_t block_size = s->blksize & 0x0fff; + ADMADescr dscr; + int i; + + for (i = 0; i < SDHC_ADMA_DESCS_PER_DELAY; ++i) { + s->admaerr &= ~SDHC_ADMAERR_LENGTH_MISMATCH; + + get_adma_description(s, &dscr); + DPRINT_L2("ADMA loop: addr=" TARGET_FMT_plx ", len=%d, attr=%x\n", + dscr.addr, dscr.length, dscr.attr); + + if ((dscr.attr & SDHC_ADMA_ATTR_VALID) == 0) { + /* Indicate that error occurred in ST_FDS state */ + s->admaerr &= ~SDHC_ADMAERR_STATE_MASK; + s->admaerr |= SDHC_ADMAERR_STATE_ST_FDS; + + /* Generate ADMA error interrupt */ + if (s->errintstsen & SDHC_EISEN_ADMAERR) { + s->errintsts |= SDHC_EIS_ADMAERR; + s->norintsts |= SDHC_NIS_ERR; + } + + sdhci_update_irq(s); + return; + } + + length = dscr.length ? dscr.length : 65536; + + switch (dscr.attr & SDHC_ADMA_ATTR_ACT_MASK) { + case SDHC_ADMA_ATTR_ACT_TRAN: /* data transfer */ + + if (s->trnmod & SDHC_TRNS_READ) { + while (length) { + if (s->data_count == 0) { + for (n = 0; n < block_size; n++) { + s->fifo_buffer[n] = sd_read_data(s->card); + } + } + begin = s->data_count; + if ((length + begin) < block_size) { + s->data_count = length + begin; + length = 0; + } else { + s->data_count = block_size; + length -= block_size - begin; + } + dma_memory_write(&dma_context_memory, dscr.addr, + &s->fifo_buffer[begin], + s->data_count - begin); + dscr.addr += s->data_count - begin; + if (s->data_count == block_size) { + s->data_count = 0; + if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { + s->blkcnt--; + if (s->blkcnt == 0) { + break; + } + } + } + } + } else { + while (length) { + begin = s->data_count; + if ((length + begin) < block_size) { + s->data_count = length + begin; + length = 0; + } else { + s->data_count = block_size; + length -= block_size - begin; + } + dma_memory_read(&dma_context_memory, dscr.addr, + &s->fifo_buffer[begin], s->data_count); + dscr.addr += s->data_count - begin; + if (s->data_count == block_size) { + for (n = 0; n < block_size; n++) { + sd_write_data(s->card, s->fifo_buffer[n]); + } + s->data_count = 0; + if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { + s->blkcnt--; + if (s->blkcnt == 0) { + break; + } + } + } + } + } + s->admasysaddr += dscr.incr; + break; + case SDHC_ADMA_ATTR_ACT_LINK: /* link to next descriptor table */ + s->admasysaddr = dscr.addr; + DPRINT_L1("ADMA link: admasysaddr=0x%lx\n", s->admasysaddr); + break; + default: + s->admasysaddr += dscr.incr; + break; + } + + /* ADMA transfer terminates if blkcnt == 0 or by END attribute */ + if (((s->trnmod & SDHC_TRNS_BLK_CNT_EN) && + (s->blkcnt == 0)) || (dscr.attr & SDHC_ADMA_ATTR_END)) { + DPRINT_L2("ADMA transfer completed\n"); + if (length || ((dscr.attr & SDHC_ADMA_ATTR_END) && + (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && + s->blkcnt != 0)) { + ERRPRINT("SD/MMC host ADMA length mismatch\n"); + s->admaerr |= SDHC_ADMAERR_LENGTH_MISMATCH | + SDHC_ADMAERR_STATE_ST_TFR; + if (s->errintstsen & SDHC_EISEN_ADMAERR) { + ERRPRINT("Set ADMA error flag\n"); + s->errintsts |= SDHC_EIS_ADMAERR; + s->norintsts |= SDHC_NIS_ERR; + } + + sdhci_update_irq(s); + } + SDHCI_GET_CLASS(s)->end_data_transfer(s); + return; + } + + if (dscr.attr & SDHC_ADMA_ATTR_INT) { + DPRINT_L1("ADMA interrupt: admasysaddr=0x%lx\n", s->admasysaddr); + if (s->norintstsen & SDHC_NISEN_DMA) { + s->norintsts |= SDHC_NIS_DMA; + } + + sdhci_update_irq(s); + return; + } + } + + /* we have unfinished bussiness - reschedule to continue ADMA */ + qemu_mod_timer(s->transfer_timer, + qemu_get_clock_ns(vm_clock) + SDHC_TRANSFER_DELAY); +} + +/* Perform data transfer according to controller configuration */ + +static void sdhci_data_transfer(SDHCIState *s) +{ + SDHCIClass *k = SDHCI_GET_CLASS(s); + s->data_count = 0; + + if (s->trnmod & SDHC_TRNS_DMA) { + switch (SDHC_DMA_TYPE(s->hostctl)) { + case SDHC_CTRL_SDMA: + if ((s->trnmod & SDHC_TRNS_MULTI) && + (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || s->blkcnt == 0)) { + break; + } + + if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) { + k->do_sdma_single(s); + } else { + k->do_sdma_multi(s); + } + + break; + case SDHC_CTRL_ADMA1_32: + if (!(s->capareg & SDHC_CAN_DO_ADMA1)) { + ERRPRINT("ADMA1 not supported\n"); + break; + } + + k->do_adma(s); + break; + case SDHC_CTRL_ADMA2_32: + if (!(s->capareg & SDHC_CAN_DO_ADMA2)) { + ERRPRINT("ADMA2 not supported\n"); + break; + } + + k->do_adma(s); + break; + case SDHC_CTRL_ADMA2_64: + if (!(s->capareg & SDHC_CAN_DO_ADMA2) || + !(s->capareg & SDHC_64_BIT_BUS_SUPPORT)) { + ERRPRINT("64 bit ADMA not supported\n"); + break; + } + + k->do_adma(s); + break; + default: + ERRPRINT("Unsupported DMA type\n"); + break; + } + } else { + if ((s->trnmod & SDHC_TRNS_READ) && sd_data_ready(s->card)) { + s->prnsts |= SDHC_DOING_READ | SDHC_DATA_INHIBIT | + SDHC_DAT_LINE_ACTIVE; + SDHCI_GET_CLASS(s)->read_block_from_card(s); + } else { + s->prnsts |= SDHC_DOING_WRITE | SDHC_DAT_LINE_ACTIVE | + SDHC_SPACE_AVAILABLE | SDHC_DATA_INHIBIT; + SDHCI_GET_CLASS(s)->write_block_to_card(s); + } + } +} + +static bool sdhci_can_issue_command(SDHCIState *s) +{ + if (!SDHC_CLOCK_IS_ON(s->clkcon) || !(s->pwrcon & SDHC_POWER_ON) || + (((s->prnsts & SDHC_DATA_INHIBIT) || s->stopped_state) && + ((s->cmdreg & SDHC_CMD_DATA_PRESENT) || + ((s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY && + !(SDHC_COMMAND_TYPE(s->cmdreg) == SDHC_CMD_ABORT))))) { + return false; + } + + return true; +} + +/* The Buffer Data Port register must be accessed in sequential and + * continuous manner */ +static inline bool +sdhci_buff_access_is_sequential(SDHCIState *s, unsigned byte_num) +{ + if ((s->data_count & 0x3) != byte_num) { + ERRPRINT("Non-sequential access to Buffer Data Port register" + "is prohibited\n"); + return false; + } + return true; +} + +static uint32_t sdhci_read(SDHCIState *s, unsigned int offset, unsigned size) +{ + uint32_t ret = 0; + + switch (offset & ~0x3) { + case SDHC_SYSAD: + ret = s->sdmasysad; + break; + case SDHC_BLKSIZE: + ret = s->blksize | (s->blkcnt << 16); + break; + case SDHC_ARGUMENT: + ret = s->argument; + break; + case SDHC_TRNMOD: + ret = s->trnmod | (s->cmdreg << 16); + break; + case SDHC_RSPREG0 ... SDHC_RSPREG3: + ret = s->rspreg[((offset & ~0x3) - SDHC_RSPREG0) >> 2]; + break; + case SDHC_BDATA: + if (sdhci_buff_access_is_sequential(s, offset - SDHC_BDATA)) { + ret = SDHCI_GET_CLASS(s)->bdata_read(s, size); + DPRINT_L2("read %ub: addr[0x%04x] -> %u\n", size, offset, ret); + return ret; + } + break; + case SDHC_PRNSTS: + ret = s->prnsts; + break; + case SDHC_HOSTCTL: + ret = s->hostctl | (s->pwrcon << 8) | (s->blkgap << 16) | + (s->wakcon << 24); + break; + case SDHC_CLKCON: + ret = s->clkcon | (s->timeoutcon << 16); + break; + case SDHC_NORINTSTS: + ret = s->norintsts | (s->errintsts << 16); + break; + case SDHC_NORINTSTSEN: + ret = s->norintstsen | (s->errintstsen << 16); + break; + case SDHC_NORINTSIGEN: + ret = s->norintsigen | (s->errintsigen << 16); + break; + case SDHC_ACMD12ERRSTS: + ret = s->acmd12errsts; + break; + case SDHC_CAPAREG: + ret = s->capareg; + break; + case SDHC_MAXCURR: + ret = s->maxcurr; + break; + case SDHC_ADMAERR: + ret = s->admaerr; + break; + case SDHC_ADMASYSADDR: + ret = (uint32_t)s->admasysaddr; + break; + case SDHC_ADMASYSADDR + 4: + ret = (uint32_t)(s->admasysaddr >> 32); + break; + case SDHC_SLOT_INT_STATUS: + ret = (SD_HOST_SPECv2_VERS << 16) | sdhci_slotint(s); + break; + default: + ERRPRINT("bad %ub read: addr[0x%04x]\n", size, offset); + break; + } + + ret >>= (offset & 0x3) * 8; + ret &= (1ULL << (size * 8)) - 1; + DPRINT_L2("read %ub: addr[0x%04x] -> %u(0x%x)\n", size, offset, ret, ret); + return ret; +} + +static inline void sdhci_blkgap_write(SDHCIState *s, uint8_t value) +{ + if ((value & SDHC_STOP_AT_GAP_REQ) && (s->blkgap & SDHC_STOP_AT_GAP_REQ)) { + return; + } + s->blkgap = value & SDHC_STOP_AT_GAP_REQ; + + if ((value & SDHC_CONTINUE_REQ) && s->stopped_state && + (s->blkgap & SDHC_STOP_AT_GAP_REQ) == 0) { + if (s->stopped_state == sdhc_gap_read) { + s->prnsts |= SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ; + SDHCI_GET_CLASS(s)->read_block_from_card(s); + } else { + s->prnsts |= SDHC_DAT_LINE_ACTIVE | SDHC_DOING_WRITE; + SDHCI_GET_CLASS(s)->write_block_to_card(s); + } + s->stopped_state = sdhc_not_stopped; + } else if (!s->stopped_state && (value & SDHC_STOP_AT_GAP_REQ)) { + if (s->prnsts & SDHC_DOING_READ) { + s->stopped_state = sdhc_gap_read; + } else if (s->prnsts & SDHC_DOING_WRITE) { + s->stopped_state = sdhc_gap_write; + } + } +} + +static inline void sdhci_reset_write(SDHCIState *s, uint8_t value) +{ + switch (value) { + case SDHC_RESET_ALL: + DEVICE_GET_CLASS(s)->reset(DEVICE(s)); + break; + case SDHC_RESET_CMD: + s->prnsts &= ~SDHC_CMD_INHIBIT; + s->norintsts &= ~SDHC_NIS_CMDCMP; + break; + case SDHC_RESET_DATA: + s->data_count = 0; + s->prnsts &= ~(SDHC_SPACE_AVAILABLE | SDHC_DATA_AVAILABLE | + SDHC_DOING_READ | SDHC_DOING_WRITE | + SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE); + s->blkgap &= ~(SDHC_STOP_AT_GAP_REQ | SDHC_CONTINUE_REQ); + s->stopped_state = sdhc_not_stopped; + s->norintsts &= ~(SDHC_NIS_WBUFRDY | SDHC_NIS_RBUFRDY | + SDHC_NIS_DMA | SDHC_NIS_TRSCMP | SDHC_NIS_BLKGAP); + break; + } +} + +static void +sdhci_write(SDHCIState *s, unsigned int offset, uint32_t value, unsigned size) +{ + unsigned shift = 8 * (offset & 0x3); + uint32_t mask = ~(((1ULL << (size * 8)) - 1) << shift); + value <<= shift; + + switch (offset & ~0x3) { + case SDHC_SYSAD: + s->sdmasysad = (s->sdmasysad & mask) | value; + MASKED_WRITE(s->sdmasysad, mask, value); + /* Writing to last byte of sdmasysad might trigger transfer */ + if (!(mask & 0xFF000000) && TRANSFERRING_DATA(s->prnsts) && s->blkcnt && + s->blksize && SDHC_DMA_TYPE(s->hostctl) == SDHC_CTRL_SDMA) { + SDHCI_GET_CLASS(s)->do_sdma_multi(s); + } + break; + case SDHC_BLKSIZE: + if (!TRANSFERRING_DATA(s->prnsts)) { + MASKED_WRITE(s->blksize, mask, value); + MASKED_WRITE(s->blkcnt, mask >> 16, value >> 16); + } + break; + case SDHC_ARGUMENT: + MASKED_WRITE(s->argument, mask, value); + break; + case SDHC_TRNMOD: + /* DMA can be enabled only if it is supported as indicated by + * capabilities register */ + if (!(s->capareg & SDHC_CAN_DO_DMA)) { + value &= ~SDHC_TRNS_DMA; + } + MASKED_WRITE(s->trnmod, mask, value); + MASKED_WRITE(s->cmdreg, mask >> 16, value >> 16); + + /* Writing to the upper byte of CMDREG triggers SD command generation */ + if ((mask & 0xFF000000) || !SDHCI_GET_CLASS(s)->can_issue_command(s)) { + break; + } + + SDHCI_GET_CLASS(s)->send_command(s); + break; + case SDHC_BDATA: + if (sdhci_buff_access_is_sequential(s, offset - SDHC_BDATA)) { + SDHCI_GET_CLASS(s)->bdata_write(s, value >> shift, size); + } + break; + case SDHC_HOSTCTL: + if (!(mask & 0xFF0000)) { + sdhci_blkgap_write(s, value >> 16); + } + MASKED_WRITE(s->hostctl, mask, value); + MASKED_WRITE(s->pwrcon, mask >> 8, value >> 8); + MASKED_WRITE(s->wakcon, mask >> 24, value >> 24); + if (!(s->prnsts & SDHC_CARD_PRESENT) || ((s->pwrcon >> 1) & 0x7) < 5 || + !(s->capareg & (1 << (31 - ((s->pwrcon >> 1) & 0x7))))) { + s->pwrcon &= ~SDHC_POWER_ON; + } + break; + case SDHC_CLKCON: + if (!(mask & 0xFF000000)) { + sdhci_reset_write(s, value >> 24); + } + MASKED_WRITE(s->clkcon, mask, value); + MASKED_WRITE(s->timeoutcon, mask >> 16, value >> 16); + if (s->clkcon & SDHC_CLOCK_INT_EN) { + s->clkcon |= SDHC_CLOCK_INT_STABLE; + } else { + s->clkcon &= ~SDHC_CLOCK_INT_STABLE; + } + break; + case SDHC_NORINTSTS: + if (s->norintstsen & SDHC_NISEN_CARDINT) { + value &= ~SDHC_NIS_CARDINT; + } + s->norintsts &= mask | ~value; + s->errintsts &= (mask >> 16) | ~(value >> 16); + if (s->errintsts) { + s->norintsts |= SDHC_NIS_ERR; + } else { + s->norintsts &= ~SDHC_NIS_ERR; + } + sdhci_update_irq(s); + break; + case SDHC_NORINTSTSEN: + MASKED_WRITE(s->norintstsen, mask, value); + MASKED_WRITE(s->errintstsen, mask >> 16, value >> 16); + s->norintsts &= s->norintstsen; + s->errintsts &= s->errintstsen; + if (s->errintsts) { + s->norintsts |= SDHC_NIS_ERR; + } else { + s->norintsts &= ~SDHC_NIS_ERR; + } + sdhci_update_irq(s); + break; + case SDHC_NORINTSIGEN: + MASKED_WRITE(s->norintsigen, mask, value); + MASKED_WRITE(s->errintsigen, mask >> 16, value >> 16); + sdhci_update_irq(s); + break; + case SDHC_ADMAERR: + MASKED_WRITE(s->admaerr, mask, value); + break; + case SDHC_ADMASYSADDR: + s->admasysaddr = (s->admasysaddr & (0xFFFFFFFF00000000ULL | + (uint64_t)mask)) | (uint64_t)value; + break; + case SDHC_ADMASYSADDR + 4: + s->admasysaddr = (s->admasysaddr & (0x00000000FFFFFFFFULL | + ((uint64_t)mask << 32))) | ((uint64_t)value << 32); + break; + case SDHC_FEAER: + s->acmd12errsts |= value; + s->errintsts |= (value >> 16) & s->errintstsen; + if (s->acmd12errsts) { + s->errintsts |= SDHC_EIS_CMD12ERR; + } + if (s->errintsts) { + s->norintsts |= SDHC_NIS_ERR; + } + sdhci_update_irq(s); + break; + default: + ERRPRINT("bad %ub write offset: addr[0x%04x] <- %u(0x%x)\n", + size, offset, value >> shift, value >> shift); + break; + } + DPRINT_L2("write %ub: addr[0x%04x] <- %u(0x%x)\n", + size, offset, value >> shift, value >> shift); +} + +static uint64_t +sdhci_readfn(void *opaque, hwaddr offset, unsigned size) +{ + SDHCIState *s = (SDHCIState *)opaque; + + return SDHCI_GET_CLASS(s)->mem_read(s, offset, size); +} + +static void +sdhci_writefn(void *opaque, hwaddr off, uint64_t val, unsigned sz) +{ + SDHCIState *s = (SDHCIState *)opaque; + + SDHCI_GET_CLASS(s)->mem_write(s, off, val, sz); +} + +static const MemoryRegionOps sdhci_mmio_ops = { + .read = sdhci_readfn, + .write = sdhci_writefn, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + .unaligned = false + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static inline unsigned int sdhci_get_fifolen(SDHCIState *s) +{ + switch (SDHC_CAPAB_BLOCKSIZE(s->capareg)) { + case 0: + return 512; + case 1: + return 1024; + case 2: + return 2048; + default: + hw_error("SDHC: unsupported value for maximum block size\n"); + return 0; + } +} + +static void sdhci_initfn(Object *obj) +{ + SDHCIState *s = SDHCI(obj); + DriveInfo *di; + + di = drive_get_next(IF_SD); + s->card = sd_init(di ? di->bdrv : NULL, 0); + s->eject_cb = qemu_allocate_irqs(sdhci_insert_eject_cb, s, 1)[0]; + s->ro_cb = qemu_allocate_irqs(sdhci_card_readonly_cb, s, 1)[0]; + sd_set_cb(s->card, s->ro_cb, s->eject_cb); + + s->insert_timer = qemu_new_timer_ns(vm_clock, sdhci_raise_insertion_irq, s); + s->transfer_timer = qemu_new_timer_ns(vm_clock, sdhci_do_data_transfer, s); +} + +static void sdhci_uninitfn(Object *obj) +{ + SDHCIState *s = SDHCI(obj); + + qemu_del_timer(s->insert_timer); + qemu_free_timer(s->insert_timer); + qemu_del_timer(s->transfer_timer); + qemu_free_timer(s->transfer_timer); + qemu_free_irqs(&s->eject_cb); + qemu_free_irqs(&s->ro_cb); + + if (s->fifo_buffer) { + g_free(s->fifo_buffer); + s->fifo_buffer = NULL; + } +} + +const VMStateDescription sdhci_vmstate = { + .name = "sdhci", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(sdmasysad, SDHCIState), + VMSTATE_UINT16(blksize, SDHCIState), + VMSTATE_UINT16(blkcnt, SDHCIState), + VMSTATE_UINT32(argument, SDHCIState), + VMSTATE_UINT16(trnmod, SDHCIState), + VMSTATE_UINT16(cmdreg, SDHCIState), + VMSTATE_UINT32_ARRAY(rspreg, SDHCIState, 4), + VMSTATE_UINT32(prnsts, SDHCIState), + VMSTATE_UINT8(hostctl, SDHCIState), + VMSTATE_UINT8(pwrcon, SDHCIState), + VMSTATE_UINT8(blkgap, SDHCIState), + VMSTATE_UINT8(wakcon, SDHCIState), + VMSTATE_UINT16(clkcon, SDHCIState), + VMSTATE_UINT8(timeoutcon, SDHCIState), + VMSTATE_UINT8(admaerr, SDHCIState), + VMSTATE_UINT16(norintsts, SDHCIState), + VMSTATE_UINT16(errintsts, SDHCIState), + VMSTATE_UINT16(norintstsen, SDHCIState), + VMSTATE_UINT16(errintstsen, SDHCIState), + VMSTATE_UINT16(norintsigen, SDHCIState), + VMSTATE_UINT16(errintsigen, SDHCIState), + VMSTATE_UINT16(acmd12errsts, SDHCIState), + VMSTATE_UINT16(data_count, SDHCIState), + VMSTATE_UINT64(admasysaddr, SDHCIState), + VMSTATE_UINT8(stopped_state, SDHCIState), + VMSTATE_VBUFFER_UINT32(fifo_buffer, SDHCIState, 1, NULL, 0, buf_maxsz), + VMSTATE_TIMER(insert_timer, SDHCIState), + VMSTATE_TIMER(transfer_timer, SDHCIState), + VMSTATE_END_OF_LIST() + } +}; + +/* Capabilities registers provide information on supported features of this + * specific host controller implementation */ +static Property sdhci_properties[] = { + DEFINE_PROP_HEX32("capareg", SDHCIState, capareg, + SDHC_CAPAB_REG_DEFAULT), + DEFINE_PROP_HEX32("maxcurr", SDHCIState, maxcurr, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sdhci_realize(DeviceState *dev, Error ** errp) +{ + SDHCIState *s = SDHCI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + s->buf_maxsz = sdhci_get_fifolen(s); + s->fifo_buffer = g_malloc0(s->buf_maxsz); + sysbus_init_irq(sbd, &s->irq); + memory_region_init_io(&s->iomem, &sdhci_mmio_ops, s, "sdhci", + SDHC_REGISTERS_MAP_SIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void sdhci_generic_reset(DeviceState *ds) +{ + SDHCIState *s = SDHCI(ds); + SDHCI_GET_CLASS(s)->reset(s); +} + +static void sdhci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SDHCIClass *k = SDHCI_CLASS(klass); + + dc->vmsd = &sdhci_vmstate; + dc->props = sdhci_properties; + dc->reset = sdhci_generic_reset; + dc->realize = sdhci_realize; + + k->reset = sdhci_reset; + k->mem_read = sdhci_read; + k->mem_write = sdhci_write; + k->send_command = sdhci_send_command; + k->can_issue_command = sdhci_can_issue_command; + k->data_transfer = sdhci_data_transfer; + k->end_data_transfer = sdhci_end_transfer; + k->do_sdma_single = sdhci_sdma_transfer_single_block; + k->do_sdma_multi = sdhci_sdma_transfer_multi_blocks; + k->do_adma = sdhci_do_adma; + k->read_block_from_card = sdhci_read_block_from_card; + k->write_block_to_card = sdhci_write_block_to_card; + k->bdata_read = sdhci_read_dataport; + k->bdata_write = sdhci_write_dataport; +} + +static const TypeInfo sdhci_type_info = { + .name = TYPE_SDHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SDHCIState), + .instance_init = sdhci_initfn, + .instance_finalize = sdhci_uninitfn, + .class_init = sdhci_class_init, + .class_size = sizeof(SDHCIClass) +}; + +static void sdhci_register_types(void) +{ + type_register_static(&sdhci_type_info); +} + +type_init(sdhci_register_types) diff --git a/hw/sdhci.h b/hw/sdhci.h new file mode 100644 index 0000000000..a560c3c93f --- /dev/null +++ b/hw/sdhci.h @@ -0,0 +1,312 @@ +/* + * SD Association Host Standard Specification v2.0 controller emulation + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Mitsyanko Igor + * Peter A.G. Crosthwaite + * + * Based on MMC controller for Samsung S5PC1xx-based board emulation + * by Alexey Merkulov and Vladimir Monakhov. + * + * 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) 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 . + */ + +#ifndef SDHCI_H +#define SDHCI_H + +#include "qemu-common.h" +#include "hw/sysbus.h" +#include "hw/sd.h" + +/* R/W SDMA System Address register 0x0 */ +#define SDHC_SYSAD 0x00 + +/* R/W Host DMA Buffer Boundary and Transfer Block Size Register 0x0 */ +#define SDHC_BLKSIZE 0x04 + +/* R/W Blocks count for current transfer 0x0 */ +#define SDHC_BLKCNT 0x06 + +/* R/W Command Argument Register 0x0 */ +#define SDHC_ARGUMENT 0x08 + +/* R/W Transfer Mode Setting Register 0x0 */ +#define SDHC_TRNMOD 0x0C +#define SDHC_TRNS_DMA 0x0001 +#define SDHC_TRNS_BLK_CNT_EN 0x0002 +#define SDHC_TRNS_ACMD12 0x0004 +#define SDHC_TRNS_READ 0x0010 +#define SDHC_TRNS_MULTI 0x0020 + +/* R/W Command Register 0x0 */ +#define SDHC_CMDREG 0x0E +#define SDHC_CMD_RSP_WITH_BUSY (3 << 0) +#define SDHC_CMD_DATA_PRESENT (1 << 5) +#define SDHC_CMD_SUSPEND (1 << 6) +#define SDHC_CMD_RESUME (1 << 7) +#define SDHC_CMD_ABORT ((1 << 6)|(1 << 7)) +#define SDHC_CMD_TYPE_MASK ((1 << 6)|(1 << 7)) +#define SDHC_COMMAND_TYPE(x) ((x) & SDHC_CMD_TYPE_MASK) + +/* ROC Response Register 0 0x0 */ +#define SDHC_RSPREG0 0x10 +/* ROC Response Register 1 0x0 */ +#define SDHC_RSPREG1 0x14 +/* ROC Response Register 2 0x0 */ +#define SDHC_RSPREG2 0x18 +/* ROC Response Register 3 0x0 */ +#define SDHC_RSPREG3 0x1C + +/* R/W Buffer Data Register 0x0 */ +#define SDHC_BDATA 0x20 + +/* R/ROC Present State Register 0x000A0000 */ +#define SDHC_PRNSTS 0x24 +#define SDHC_CMD_INHIBIT 0x00000001 +#define SDHC_DATA_INHIBIT 0x00000002 +#define SDHC_DAT_LINE_ACTIVE 0x00000004 +#define SDHC_DOING_WRITE 0x00000100 +#define SDHC_DOING_READ 0x00000200 +#define SDHC_SPACE_AVAILABLE 0x00000400 +#define SDHC_DATA_AVAILABLE 0x00000800 +#define SDHC_CARD_PRESENT 0x00010000 +#define SDHC_CARD_DETECT 0x00040000 +#define SDHC_WRITE_PROTECT 0x00080000 +#define TRANSFERRING_DATA(x) \ + ((x) & (SDHC_DOING_READ | SDHC_DOING_WRITE)) + +/* R/W Host control Register 0x0 */ +#define SDHC_HOSTCTL 0x28 +#define SDHC_CTRL_DMA_CHECK_MASK 0x18 +#define SDHC_CTRL_SDMA 0x00 +#define SDHC_CTRL_ADMA1_32 0x08 +#define SDHC_CTRL_ADMA2_32 0x10 +#define SDHC_CTRL_ADMA2_64 0x18 +#define SDHC_DMA_TYPE(x) ((x) & SDHC_CTRL_DMA_CHECK_MASK) + +/* R/W Power Control Register 0x0 */ +#define SDHC_PWRCON 0x29 +#define SDHC_POWER_ON (1 << 0) + +/* R/W Block Gap Control Register 0x0 */ +#define SDHC_BLKGAP 0x2A +#define SDHC_STOP_AT_GAP_REQ 0x01 +#define SDHC_CONTINUE_REQ 0x02 + +/* R/W WakeUp Control Register 0x0 */ +#define SDHC_WAKCON 0x2B +#define SDHC_WKUP_ON_INS (1 << 1) +#define SDHC_WKUP_ON_RMV (1 << 2) + +/* CLKCON */ +#define SDHC_CLKCON 0x2C +#define SDHC_CLOCK_INT_STABLE 0x0002 +#define SDHC_CLOCK_INT_EN 0x0001 +#define SDHC_CLOCK_SDCLK_EN (1 << 2) +#define SDHC_CLOCK_CHK_MASK 0x0007 +#define SDHC_CLOCK_IS_ON(x) \ + (((x) & SDHC_CLOCK_CHK_MASK) == SDHC_CLOCK_CHK_MASK) + +/* R/W Timeout Control Register 0x0 */ +#define SDHC_TIMEOUTCON 0x2E + +/* R/W Software Reset Register 0x0 */ +#define SDHC_SWRST 0x2F +#define SDHC_RESET_ALL 0x01 +#define SDHC_RESET_CMD 0x02 +#define SDHC_RESET_DATA 0x04 + +/* ROC/RW1C Normal Interrupt Status Register 0x0 */ +#define SDHC_NORINTSTS 0x30 +#define SDHC_NIS_ERR 0x8000 +#define SDHC_NIS_CMDCMP 0x0001 +#define SDHC_NIS_TRSCMP 0x0002 +#define SDHC_NIS_BLKGAP 0x0004 +#define SDHC_NIS_DMA 0x0008 +#define SDHC_NIS_WBUFRDY 0x0010 +#define SDHC_NIS_RBUFRDY 0x0020 +#define SDHC_NIS_INSERT 0x0040 +#define SDHC_NIS_REMOVE 0x0080 +#define SDHC_NIS_CARDINT 0x0100 + +/* ROC/RW1C Error Interrupt Status Register 0x0 */ +#define SDHC_ERRINTSTS 0x32 +#define SDHC_EIS_CMDTIMEOUT 0x0001 +#define SDHC_EIS_BLKGAP 0x0004 +#define SDHC_EIS_CMDIDX 0x0008 +#define SDHC_EIS_CMD12ERR 0x0100 +#define SDHC_EIS_ADMAERR 0x0200 + +/* R/W Normal Interrupt Status Enable Register 0x0 */ +#define SDHC_NORINTSTSEN 0x34 +#define SDHC_NISEN_CMDCMP 0x0001 +#define SDHC_NISEN_TRSCMP 0x0002 +#define SDHC_NISEN_DMA 0x0008 +#define SDHC_NISEN_WBUFRDY 0x0010 +#define SDHC_NISEN_RBUFRDY 0x0020 +#define SDHC_NISEN_INSERT 0x0040 +#define SDHC_NISEN_REMOVE 0x0080 +#define SDHC_NISEN_CARDINT 0x0100 + +/* R/W Error Interrupt Status Enable Register 0x0 */ +#define SDHC_ERRINTSTSEN 0x36 +#define SDHC_EISEN_CMDTIMEOUT 0x0001 +#define SDHC_EISEN_BLKGAP 0x0004 +#define SDHC_EISEN_CMDIDX 0x0008 +#define SDHC_EISEN_ADMAERR 0x0200 + +/* R/W Normal Interrupt Signal Enable Register 0x0 */ +#define SDHC_NORINTSIGEN 0x38 +#define SDHC_NORINTSIG_INSERT (1 << 6) +#define SDHC_NORINTSIG_REMOVE (1 << 7) + +/* R/W Error Interrupt Signal Enable Register 0x0 */ +#define SDHC_ERRINTSIGEN 0x3A + +/* ROC Auto CMD12 error status register 0x0 */ +#define SDHC_ACMD12ERRSTS 0x3C + +/* HWInit Capabilities Register 0x05E80080 */ +#define SDHC_CAPAREG 0x40 +#define SDHC_CAN_DO_DMA 0x00400000 +#define SDHC_CAN_DO_ADMA2 0x00080000 +#define SDHC_CAN_DO_ADMA1 0x00100000 +#define SDHC_64_BIT_BUS_SUPPORT (1 << 28) +#define SDHC_CAPAB_BLOCKSIZE(x) (((x) >> 16) & 0x3) + +/* HWInit Maximum Current Capabilities Register 0x0 */ +#define SDHC_MAXCURR 0x48 + +/* W Force Event Auto CMD12 Error Interrupt Register 0x0000 */ +#define SDHC_FEAER 0x50 +/* W Force Event Error Interrupt Register Error Interrupt 0x0000 */ +#define SDHC_FEERR 0x52 + +/* R/W ADMA Error Status Register 0x00 */ +#define SDHC_ADMAERR 0x54 +#define SDHC_ADMAERR_LENGTH_MISMATCH (1 << 2) +#define SDHC_ADMAERR_STATE_ST_STOP (0 << 0) +#define SDHC_ADMAERR_STATE_ST_FDS (1 << 0) +#define SDHC_ADMAERR_STATE_ST_TFR (3 << 0) +#define SDHC_ADMAERR_STATE_MASK (3 << 0) + +/* R/W ADMA System Address Register 0x00 */ +#define SDHC_ADMASYSADDR 0x58 +#define SDHC_ADMA_ATTR_SET_LEN (1 << 4) +#define SDHC_ADMA_ATTR_ACT_TRAN (1 << 5) +#define SDHC_ADMA_ATTR_ACT_LINK (3 << 4) +#define SDHC_ADMA_ATTR_INT (1 << 2) +#define SDHC_ADMA_ATTR_END (1 << 1) +#define SDHC_ADMA_ATTR_VALID (1 << 0) +#define SDHC_ADMA_ATTR_ACT_MASK ((1 << 4)|(1 << 5)) + +/* Slot interrupt status */ +#define SDHC_SLOT_INT_STATUS 0xFC + +/* HWInit Host Controller Version Register 0x0401 */ +#define SDHC_HCVER 0xFE +#define SD_HOST_SPECv2_VERS 0x2401 + +#define SDHC_REGISTERS_MAP_SIZE 0x100 +#define SDHC_INSERTION_DELAY (get_ticks_per_sec()) +#define SDHC_TRANSFER_DELAY 100 +#define SDHC_ADMA_DESCS_PER_DELAY 5 +#define SDHC_CMD_RESPONSE (3 << 0) + +enum { + sdhc_not_stopped = 0, /* normal SDHC state */ + sdhc_gap_read = 1, /* SDHC stopped at block gap during read operation */ + sdhc_gap_write = 2 /* SDHC stopped at block gap during write operation */ +}; + +/* SD/MMC host controller state */ +typedef struct SDHCIState { + SysBusDevice busdev; + SDState *card; + MemoryRegion iomem; + + QEMUTimer *insert_timer; /* timer for 'changing' sd card. */ + QEMUTimer *transfer_timer; + qemu_irq eject_cb; + qemu_irq ro_cb; + qemu_irq irq; + + uint32_t sdmasysad; /* SDMA System Address register */ + uint16_t blksize; /* Host DMA Buff Boundary and Transfer BlkSize Reg */ + uint16_t blkcnt; /* Blocks count for current transfer */ + uint32_t argument; /* Command Argument Register */ + uint16_t trnmod; /* Transfer Mode Setting Register */ + uint16_t cmdreg; /* Command Register */ + uint32_t rspreg[4]; /* Response Registers 0-3 */ + uint32_t prnsts; /* Present State Register */ + uint8_t hostctl; /* Host Control Register */ + uint8_t pwrcon; /* Power control Register */ + uint8_t blkgap; /* Block Gap Control Register */ + uint8_t wakcon; /* WakeUp Control Register */ + uint16_t clkcon; /* Clock control Register */ + uint8_t timeoutcon; /* Timeout Control Register */ + uint8_t admaerr; /* ADMA Error Status Register */ + uint16_t norintsts; /* Normal Interrupt Status Register */ + uint16_t errintsts; /* Error Interrupt Status Register */ + uint16_t norintstsen; /* Normal Interrupt Status Enable Register */ + uint16_t errintstsen; /* Error Interrupt Status Enable Register */ + uint16_t norintsigen; /* Normal Interrupt Signal Enable Register */ + uint16_t errintsigen; /* Error Interrupt Signal Enable Register */ + uint16_t acmd12errsts; /* Auto CMD12 error status register */ + uint64_t admasysaddr; /* ADMA System Address Register */ + + uint32_t capareg; /* Capabilities Register */ + uint32_t maxcurr; /* Maximum Current Capabilities Register */ + uint8_t *fifo_buffer; /* SD host i/o FIFO buffer */ + uint32_t buf_maxsz; + uint16_t data_count; /* current element in FIFO buffer */ + uint8_t stopped_state;/* Current SDHC state */ + /* Buffer Data Port Register - virtual access point to R and W buffers */ + /* Software Reset Register - always reads as 0 */ + /* Force Event Auto CMD12 Error Interrupt Reg - write only */ + /* Force Event Error Interrupt Register- write only */ + /* RO Host Controller Version Register always reads as 0x2401 */ +} SDHCIState; + +typedef struct SDHCIClass { + SysBusDeviceClass busdev_class; + + void (*reset)(SDHCIState *s); + uint32_t (*mem_read)(SDHCIState *s, unsigned int offset, unsigned size); + void (*mem_write)(SDHCIState *s, unsigned int offset, uint32_t value, + unsigned size); + void (*send_command)(SDHCIState *s); + bool (*can_issue_command)(SDHCIState *s); + void (*data_transfer)(SDHCIState *s); + void (*end_data_transfer)(SDHCIState *s); + void (*do_sdma_single)(SDHCIState *s); + void (*do_sdma_multi)(SDHCIState *s); + void (*do_adma)(SDHCIState *s); + void (*read_block_from_card)(SDHCIState *s); + void (*write_block_to_card)(SDHCIState *s); + uint32_t (*bdata_read)(SDHCIState *s, unsigned size); + void (*bdata_write)(SDHCIState *s, uint32_t value, unsigned size); +} SDHCIClass; + +extern const VMStateDescription sdhci_vmstate; + +#define TYPE_SDHCI "generic-sdhci" +#define SDHCI(obj) \ + OBJECT_CHECK(SDHCIState, (obj), TYPE_SDHCI) +#define SDHCI_CLASS(klass) \ + OBJECT_CLASS_CHECK(SDHCIClass, (klass), TYPE_SDHCI) +#define SDHCI_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SDHCIClass, (obj), TYPE_SDHCI) + +#endif /* SDHCI_H */ diff --git a/hw/serial-isa.c b/hw/serial-isa.c index 96c78f7f8d..a630a7d506 100644 --- a/hw/serial-isa.c +++ b/hw/serial-isa.c @@ -23,8 +23,8 @@ * THE SOFTWARE. */ -#include "serial.h" -#include "isa.h" +#include "hw/serial.h" +#include "hw/isa.h" typedef struct ISASerialState { ISADevice dev; @@ -99,7 +99,7 @@ static void serial_isa_class_initfn(ObjectClass *klass, void *data) dc->props = serial_isa_properties; } -static TypeInfo serial_isa_info = { +static const TypeInfo serial_isa_info = { .name = "isa-serial", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISASerialState), diff --git a/hw/serial-pci.c b/hw/serial-pci.c index 6a2548a515..954657ba32 100644 --- a/hw/serial-pci.c +++ b/hw/serial-pci.c @@ -25,8 +25,8 @@ /* see docs/specs/pci-serial.txt */ -#include "serial.h" -#include "pci/pci.h" +#include "hw/serial.h" +#include "hw/pci/pci.h" #define PCI_SERIAL_MAX_PORTS 4 @@ -185,8 +185,8 @@ static void serial_pci_class_initfn(ObjectClass *klass, void *data) PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); pc->init = serial_pci_init; pc->exit = serial_pci_exit; - pc->vendor_id = 0x1b36; /* Red Hat */ - pc->device_id = 0x0002; + pc->vendor_id = PCI_VENDOR_ID_REDHAT; + pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL; pc->revision = 1; pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; dc->vmsd = &vmstate_pci_serial; @@ -199,8 +199,8 @@ static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data) PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); pc->init = multi_serial_pci_init; pc->exit = multi_serial_pci_exit; - pc->vendor_id = 0x1b36; /* Red Hat */ - pc->device_id = 0x0003; + pc->vendor_id = PCI_VENDOR_ID_REDHAT; + pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL2; pc->revision = 1; pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; dc->vmsd = &vmstate_pci_multi_serial; @@ -213,29 +213,29 @@ static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, void *data) PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); pc->init = multi_serial_pci_init; pc->exit = multi_serial_pci_exit; - pc->vendor_id = 0x1b36; /* Red Hat */ - pc->device_id = 0x0004; + pc->vendor_id = PCI_VENDOR_ID_REDHAT; + pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL4; pc->revision = 1; pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; dc->vmsd = &vmstate_pci_multi_serial; dc->props = multi_4x_serial_pci_properties; } -static TypeInfo serial_pci_info = { +static const TypeInfo serial_pci_info = { .name = "pci-serial", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCISerialState), .class_init = serial_pci_class_initfn, }; -static TypeInfo multi_2x_serial_pci_info = { +static const TypeInfo multi_2x_serial_pci_info = { .name = "pci-serial-2x", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIMultiSerialState), .class_init = multi_2x_serial_pci_class_initfn, }; -static TypeInfo multi_4x_serial_pci_info = { +static const TypeInfo multi_4x_serial_pci_info = { .name = "pci-serial-4x", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIMultiSerialState), diff --git a/hw/serial.c b/hw/serial.c index a5b2a0c609..48a5eb62b9 100644 --- a/hw/serial.c +++ b/hw/serial.c @@ -23,7 +23,7 @@ * THE SOFTWARE. */ -#include "serial.h" +#include "hw/serial.h" #include "char/char.h" #include "qemu/timer.h" #include "exec/address-spaces.h" @@ -256,10 +256,9 @@ static void serial_update_msl(SerialState *s) qemu_mod_timer(s->modem_status_poll, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 100); } -static void serial_xmit(void *opaque) +static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque) { SerialState *s = opaque; - uint64_t new_xmit_ts = qemu_get_clock_ns(vm_clock); if (s->tsr_retry <= 0) { if (s->fcr & UART_FCR_FE) { @@ -267,7 +266,7 @@ static void serial_xmit(void *opaque) if (!s->xmit_fifo.count) s->lsr |= UART_LSR_THRE; } else if ((s->lsr & UART_LSR_THRE)) { - return; + return FALSE; } else { s->tsr = s->thr; s->lsr |= UART_LSR_THRE; @@ -279,30 +278,25 @@ static void serial_xmit(void *opaque) /* in loopback mode, say that we just received a char */ serial_receive1(s, &s->tsr, 1); } else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1) { - if ((s->tsr_retry >= 0) && (s->tsr_retry <= MAX_XMIT_RETRY)) { + if (s->tsr_retry >= 0 && s->tsr_retry < MAX_XMIT_RETRY && + qemu_chr_fe_add_watch(s->chr, G_IO_OUT, serial_xmit, s) > 0) { s->tsr_retry++; - qemu_mod_timer(s->transmit_timer, new_xmit_ts + s->char_transmit_time); - return; - } else if (s->poll_msl < 0) { - /* If we exceed MAX_XMIT_RETRY and the backend is not a real serial port, then - drop any further failed writes instantly, until we get one that goes through. - This is to prevent guests that log to unconnected pipes or pty's from stalling. */ - s->tsr_retry = -1; + return FALSE; } - } - else { + s->tsr_retry = 0; + } else { s->tsr_retry = 0; } s->last_xmit_ts = qemu_get_clock_ns(vm_clock); - if (!(s->lsr & UART_LSR_THRE)) - qemu_mod_timer(s->transmit_timer, s->last_xmit_ts + s->char_transmit_time); if (s->lsr & UART_LSR_THRE) { s->lsr |= UART_LSR_TEMT; s->thr_ipending = 1; serial_update_irq(s); } + + return FALSE; } @@ -332,7 +326,7 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, s->lsr &= ~UART_LSR_THRE; serial_update_irq(s); } - serial_xmit(s); + serial_xmit(NULL, G_IO_OUT, s); } break; case 1: @@ -686,8 +680,6 @@ void serial_init_core(SerialState *s) s->modem_status_poll = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) serial_update_msl, s); s->fifo_timeout_timer = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) fifo_timeout_int, s); - s->transmit_timer = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) serial_xmit, s); - qemu_register_reset(serial_reset, s); qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1, diff --git a/hw/serial.h b/hw/serial.h index 98ee4241be..e884499607 100644 --- a/hw/serial.h +++ b/hw/serial.h @@ -25,7 +25,7 @@ #ifndef HW_SERIAL_H #define HW_SERIAL_H 1 -#include "hw.h" +#include "hw/hw.h" #include "sysemu/sysemu.h" #include "exec/memory.h" @@ -72,8 +72,6 @@ struct SerialState { struct QEMUTimer *fifo_timeout_timer; int timeout_ipending; /* timeout interrupt pending state */ - struct QEMUTimer *transmit_timer; - uint64_t char_transmit_time; /* time to transmit a char in ticks */ int poll_msl; diff --git a/hw/sga.c b/hw/sga.c index d5c91ed98e..4b1d4e5369 100644 --- a/hw/sga.c +++ b/hw/sga.c @@ -24,9 +24,9 @@ * sgabios code originally available at code.google.com/p/sgabios * */ -#include "pci/pci.h" -#include "pc.h" -#include "loader.h" +#include "hw/pci/pci.h" +#include "hw/pc.h" +#include "hw/loader.h" #include "sysemu/sysemu.h" #define SGABIOS_FILENAME "sgabios.bin" @@ -48,7 +48,7 @@ static void sga_class_initfn(ObjectClass *klass, void *data) dc->desc = "Serial Graphics Adapter"; } -static TypeInfo sga_info = { +static const TypeInfo sga_info = { .name = "sga", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISASGAState), diff --git a/hw/sh.h b/hw/sh.h index 77bf8aad2c..6230954eac 100644 --- a/hw/sh.h +++ b/hw/sh.h @@ -2,7 +2,7 @@ #define QEMU_SH_H /* Definitions for SH board emulation. */ -#include "sh_intc.h" +#include "hw/sh_intc.h" #define A7ADDR(x) ((x) & 0x1fffffff) #define P4ADDR(x) ((x) | 0xe0000000) diff --git a/hw/sh4/Makefile.objs b/hw/sh4/Makefile.objs index 68c5921790..72b6a1fcb4 100644 --- a/hw/sh4/Makefile.objs +++ b/hw/sh4/Makefile.objs @@ -1,5 +1,9 @@ -obj-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o +obj-y = tc58128.o obj-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o obj-y += ide/mmio.o obj-y := $(addprefix ../,$(obj-y)) + +obj-y += shix.o r2d.o + +obj-y += sh7750.o sh7750_regnames.o diff --git a/hw/r2d.c b/hw/sh4/r2d.c similarity index 93% rename from hw/r2d.c rename to hw/sh4/r2d.c index 7cf1893d19..faa03d2069 100644 --- a/hw/r2d.c +++ b/hw/sh4/r2d.c @@ -23,19 +23,19 @@ * THE SOFTWARE. */ -#include "sysbus.h" -#include "hw.h" -#include "sh.h" -#include "devices.h" +#include "hw/sysbus.h" +#include "hw/hw.h" +#include "hw/sh.h" +#include "hw/devices.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "pci/pci.h" +#include "hw/boards.h" +#include "hw/pci/pci.h" #include "net/net.h" -#include "sh7750_regs.h" -#include "ide.h" -#include "loader.h" -#include "usb.h" -#include "flash.h" +#include "hw/sh7750_regs.h" +#include "hw/ide.h" +#include "hw/loader.h" +#include "hw/usb.h" +#include "hw/flash.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" @@ -262,7 +262,7 @@ static void r2d_init(QEMUMachineInitArgs *args) irq = r2d_fpga_init(address_space_mem, 0x04000000, sh7750_irl(s)); dev = qdev_create(NULL, "sh_pci"); - busdev = sysbus_from_qdev(dev); + busdev = SYS_BUS_DEVICE(dev); qdev_init_nofail(dev); sysbus_mmio_map(busdev, 0, P4ADDR(0x1e200000)); sysbus_mmio_map(busdev, 1, A7ADDR(0x1e200000)); @@ -276,8 +276,14 @@ static void r2d_init(QEMUMachineInitArgs *args) /* onboard CF (True IDE mode, Master only). */ dinfo = drive_get(IF_IDE, 0, 0); - mmio_ide_init(0x14001000, 0x1400080c, address_space_mem, irq[CF_IDE], 1, - dinfo, NULL); + dev = qdev_create(NULL, "mmio-ide"); + busdev = SYS_BUS_DEVICE(dev); + sysbus_connect_irq(busdev, 0, irq[CF_IDE]); + qdev_prop_set_uint32(dev, "shift", 1); + qdev_init_nofail(dev); + sysbus_mmio_map(busdev, 0, 0x14001000); + sysbus_mmio_map(busdev, 1, 0x1400080c); + mmio_ide_init_drives(dev, dinfo, NULL); /* onboard flash memory */ dinfo = drive_get(IF_PFLASH, 0, 0); @@ -347,6 +353,7 @@ static QEMUMachine r2d_machine = { .name = "r2d", .desc = "r2d-plus board", .init = r2d_init, + DEFAULT_MACHINE_OPTIONS, }; static void r2d_machine_init(void) diff --git a/hw/sh7750.c b/hw/sh4/sh7750.c similarity index 98% rename from hw/sh7750.c rename to hw/sh4/sh7750.c index 666f8655ed..e4d37ad6ac 100644 --- a/hw/sh7750.c +++ b/hw/sh4/sh7750.c @@ -23,12 +23,12 @@ * THE SOFTWARE. */ #include -#include "hw.h" -#include "sh.h" +#include "hw/hw.h" +#include "hw/sh.h" #include "sysemu/sysemu.h" -#include "sh7750_regs.h" -#include "sh7750_regnames.h" -#include "sh_intc.h" +#include "hw/sh7750_regs.h" +#include "hw/sh7750_regnames.h" +#include "hw/sh_intc.h" #include "cpu.h" #include "exec/address-spaces.h" @@ -255,6 +255,7 @@ static uint32_t sh7750_mem_readw(void *opaque, hwaddr addr) static uint32_t sh7750_mem_readl(void *opaque, hwaddr addr) { SH7750State *s = opaque; + SuperHCPUClass *scc; switch (addr) { case SH7750_BCR1_A7: @@ -288,11 +289,14 @@ static uint32_t sh7750_mem_readl(void *opaque, hwaddr addr) case SH7750_CCR_A7: return s->ccr; case 0x1f000030: /* Processor version */ - return s->cpu->pvr; + scc = SUPERH_CPU_GET_CLASS(s->cpu); + return scc->pvr; case 0x1f000040: /* Cache version */ - return s->cpu->cvr; + scc = SUPERH_CPU_GET_CLASS(s->cpu); + return scc->cvr; case 0x1f000044: /* Processor revision */ - return s->cpu->prr; + scc = SUPERH_CPU_GET_CLASS(s->cpu); + return scc->prr; default: error_access("long read", addr); abort(); diff --git a/hw/sh7750_regnames.c b/hw/sh4/sh7750_regnames.c similarity index 95% rename from hw/sh7750_regnames.c rename to hw/sh4/sh7750_regnames.c index 5a5a2d80de..389698d24a 100644 --- a/hw/sh7750_regnames.c +++ b/hw/sh4/sh7750_regnames.c @@ -1,7 +1,7 @@ -#include "hw.h" -#include "sh.h" -#include "sh7750_regs.h" -#include "sh7750_regnames.h" +#include "hw/hw.h" +#include "hw/sh.h" +#include "hw/sh7750_regs.h" +#include "hw/sh7750_regnames.h" #define REGNAME(r) {r, #r}, diff --git a/hw/shix.c b/hw/sh4/shix.c similarity index 96% rename from hw/shix.c rename to hw/sh4/shix.c index 86d703ad70..192579d065 100644 --- a/hw/shix.c +++ b/hw/sh4/shix.c @@ -27,11 +27,11 @@ More information in target-sh4/README.sh4 */ -#include "hw.h" -#include "sh.h" +#include "hw/hw.h" +#include "hw/sh.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "loader.h" +#include "hw/boards.h" +#include "hw/loader.h" #include "exec/address-spaces.h" #define BIOS_FILENAME "shix_bios.bin" @@ -92,6 +92,7 @@ static QEMUMachine shix_machine = { .desc = "shix card", .init = shix_init, .is_default = 1, + DEFAULT_MACHINE_OPTIONS, }; static void shix_machine_init(void) diff --git a/hw/sh_intc.c b/hw/sh_intc.c index c3f77d5092..29e3d8f127 100644 --- a/hw/sh_intc.c +++ b/hw/sh_intc.c @@ -8,9 +8,9 @@ * This code is licensed under the GPL. */ -#include "sh_intc.h" -#include "hw.h" -#include "sh.h" +#include "hw/sh_intc.h" +#include "hw/hw.h" +#include "hw/sh.h" //#define DEBUG_INTC //#define DEBUG_INTC_SOURCES @@ -42,15 +42,17 @@ void sh_intc_toggle_source(struct intc_source *source, pending_changed = 1; if (pending_changed) { + CPUState *cpu = CPU(sh_env_get_cpu(first_cpu)); if (source->pending) { source->parent->pending++; - if (source->parent->pending == 1) - cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD); - } - else { + if (source->parent->pending == 1) { + cpu_interrupt(cpu, CPU_INTERRUPT_HARD); + } + } else { source->parent->pending--; - if (source->parent->pending == 0) - cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD); + if (source->parent->pending == 0) { + cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); + } } } diff --git a/hw/sh_intc.h b/hw/sh_intc.h index 6f11beeddd..b7ddcb096a 100644 --- a/hw/sh_intc.h +++ b/hw/sh_intc.h @@ -2,7 +2,7 @@ #define __SH_INTC_H__ #include "qemu-common.h" -#include "irq.h" +#include "hw/irq.h" #include "exec/address-spaces.h" typedef unsigned char intc_enum; diff --git a/hw/sh_pci.c b/hw/sh_pci.c index 018b1c198b..96535dbe01 100644 --- a/hw/sh_pci.c +++ b/hw/sh_pci.c @@ -21,10 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "sysbus.h" -#include "sh.h" -#include "pci/pci.h" -#include "pci/pci_host.h" +#include "hw/sysbus.h" +#include "hw/sh.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" #include "qemu/bswap.h" #include "exec/address-spaces.h" @@ -156,7 +156,7 @@ static void sh_pci_host_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_HITACHI_SH7751R; } -static TypeInfo sh_pci_host_info = { +static const TypeInfo sh_pci_host_info = { .name = "sh_pci_host", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), @@ -170,7 +170,7 @@ static void sh_pci_device_class_init(ObjectClass *klass, void *data) sdc->init = sh_pci_device_init; } -static TypeInfo sh_pci_device_info = { +static const TypeInfo sh_pci_device_info = { .name = "sh_pci", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SHPCIState), diff --git a/hw/sh_serial.c b/hw/sh_serial.c index 21c5b1362d..40e797c5a2 100644 --- a/hw/sh_serial.c +++ b/hw/sh_serial.c @@ -24,8 +24,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "sh.h" +#include "hw/hw.h" +#include "hw/sh.h" #include "char/char.h" #include "exec/address-spaces.h" diff --git a/hw/sh_timer.c b/hw/sh_timer.c index 64ea23fce6..b4503230a9 100644 --- a/hw/sh_timer.c +++ b/hw/sh_timer.c @@ -8,11 +8,11 @@ * This code is licensed under the GPL. */ -#include "hw.h" -#include "sh.h" +#include "hw/hw.h" +#include "hw/sh.h" #include "qemu/timer.h" #include "exec/address-spaces.h" -#include "ptimer.h" +#include "hw/ptimer.h" //#define DEBUG_TIMER diff --git a/hw/slavio_intctl.c b/hw/slavio_intctl.c index a44ce95c1f..b60592b35d 100644 --- a/hw/slavio_intctl.c +++ b/hw/slavio_intctl.c @@ -22,9 +22,9 @@ * THE SOFTWARE. */ -#include "sun4m.h" +#include "hw/sun4m.h" #include "monitor/monitor.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "trace.h" //#define DEBUG_IRQ_COUNT @@ -210,7 +210,7 @@ void slavio_pic_info(Monitor *mon, DeviceState *dev) SLAVIO_INTCTLState *s; int i; - sd = sysbus_from_qdev(dev); + sd = SYS_BUS_DEVICE(dev); s = FROM_SYSBUS(SLAVIO_INTCTLState, sd); for (i = 0; i < MAX_CPUS; i++) { monitor_printf(mon, "per-cpu %d: pending 0x%08x\n", i, @@ -230,7 +230,7 @@ void slavio_irq_info(Monitor *mon, DeviceState *dev) int i; int64_t count; - sd = sysbus_from_qdev(dev); + sd = SYS_BUS_DEVICE(dev); s = FROM_SYSBUS(SLAVIO_INTCTLState, sd); monitor_printf(mon, "IRQ statistics:\n"); for (i = 0; i < 32; i++) { @@ -456,7 +456,7 @@ static void slavio_intctl_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_intctl; } -static TypeInfo slavio_intctl_info = { +static const TypeInfo slavio_intctl_info = { .name = "slavio_intctl", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SLAVIO_INTCTLState), diff --git a/hw/slavio_misc.c b/hw/slavio_misc.c index 704f2b173b..a7a9368864 100644 --- a/hw/slavio_misc.c +++ b/hw/slavio_misc.c @@ -23,7 +23,7 @@ */ #include "sysemu/sysemu.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "trace.h" /* @@ -478,7 +478,7 @@ static void slavio_misc_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_misc; } -static TypeInfo slavio_misc_info = { +static const TypeInfo slavio_misc_info = { .name = "slavio_misc", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MiscState), @@ -492,7 +492,7 @@ static void apc_class_init(ObjectClass *klass, void *data) k->init = apc_init1; } -static TypeInfo apc_info = { +static const TypeInfo apc_info = { .name = "apc", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MiscState), diff --git a/hw/slavio_timer.c b/hw/slavio_timer.c index 584629f1a5..83f22a0366 100644 --- a/hw/slavio_timer.c +++ b/hw/slavio_timer.c @@ -22,10 +22,10 @@ * THE SOFTWARE. */ -#include "sun4m.h" +#include "hw/sun4m.h" #include "qemu/timer.h" -#include "ptimer.h" -#include "sysbus.h" +#include "hw/ptimer.h" +#include "hw/sysbus.h" #include "trace.h" /* @@ -420,7 +420,7 @@ static void slavio_timer_class_init(ObjectClass *klass, void *data) dc->props = slavio_timer_properties; } -static TypeInfo slavio_timer_info = { +static const TypeInfo slavio_timer_info = { .name = "slavio_timer", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SLAVIO_TIMERState), diff --git a/hw/sm501.c b/hw/sm501.c index dd186aa7f2..0e019111bb 100644 --- a/hw/sm501.c +++ b/hw/sm501.c @@ -23,12 +23,12 @@ */ #include -#include "hw.h" -#include "serial.h" +#include "hw/hw.h" +#include "hw/serial.h" #include "ui/console.h" -#include "devices.h" -#include "sysbus.h" -#include "qdev-addr.h" +#include "hw/devices.h" +#include "hw/sysbus.h" +#include "hw/qdev-addr.h" #include "qemu/range.h" #include "ui/pixel_ops.h" @@ -1171,28 +1171,28 @@ typedef void draw_hwc_line_func(SM501State * s, int crt, uint8_t * palette, int c_y, uint8_t *d, int width); #define DEPTH 8 -#include "sm501_template.h" +#include "hw/sm501_template.h" #define DEPTH 15 -#include "sm501_template.h" +#include "hw/sm501_template.h" #define BGR_FORMAT #define DEPTH 15 -#include "sm501_template.h" +#include "hw/sm501_template.h" #define DEPTH 16 -#include "sm501_template.h" +#include "hw/sm501_template.h" #define BGR_FORMAT #define DEPTH 16 -#include "sm501_template.h" +#include "hw/sm501_template.h" #define DEPTH 32 -#include "sm501_template.h" +#include "hw/sm501_template.h" #define BGR_FORMAT #define DEPTH 32 -#include "sm501_template.h" +#include "hw/sm501_template.h" static draw_line_func * draw_line8_funcs[] = { draw_line8_8, @@ -1428,9 +1428,9 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base, qdev_prop_set_uint32(dev, "num-ports", 2); qdev_prop_set_taddr(dev, "dma-offset", base); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base + MMIO_BASE_OFFSET + SM501_USB_HOST); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); /* bridge to serial emulation module */ if (chr) { diff --git a/hw/smbus.c b/hw/smbus.c index e3cf6a2cc8..9626415bca 100644 --- a/hw/smbus.c +++ b/hw/smbus.c @@ -9,9 +9,9 @@ /* TODO: Implement PEC. */ -#include "hw.h" -#include "i2c.h" -#include "smbus.h" +#include "hw/hw.h" +#include "hw/i2c.h" +#include "hw/smbus.h" //#define DEBUG_SMBUS 1 @@ -318,7 +318,7 @@ static void smbus_device_class_init(ObjectClass *klass, void *data) sc->send = smbus_i2c_send; } -static TypeInfo smbus_device_type_info = { +static const TypeInfo smbus_device_type_info = { .name = TYPE_SMBUS_DEVICE, .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(SMBusDevice), diff --git a/hw/smbus.h b/hw/smbus.h index 91fdfd0f24..4d46467a95 100644 --- a/hw/smbus.h +++ b/hw/smbus.h @@ -25,7 +25,7 @@ * THE SOFTWARE. */ -#include "i2c.h" +#include "hw/i2c.h" #define TYPE_SMBUS_DEVICE "smbus-device" #define SMBUS_DEVICE(obj) \ diff --git a/hw/smbus_eeprom.c b/hw/smbus_eeprom.c index a8bf8cbb02..c63e75f0bb 100644 --- a/hw/smbus_eeprom.c +++ b/hw/smbus_eeprom.c @@ -22,9 +22,9 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "i2c.h" -#include "smbus.h" +#include "hw/hw.h" +#include "hw/i2c.h" +#include "hw/smbus.h" //#define DEBUG @@ -123,7 +123,7 @@ static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data) dc->props = smbus_eeprom_properties; } -static TypeInfo smbus_eeprom_info = { +static const TypeInfo smbus_eeprom_info = { .name = "smbus-eeprom", .parent = TYPE_SMBUS_DEVICE, .instance_size = sizeof(SMBusEEPROMDevice), diff --git a/hw/smbus_ich9.c b/hw/smbus_ich9.c index 16db3a743c..732ebd3bb0 100644 --- a/hw/smbus_ich9.c +++ b/hw/smbus_ich9.c @@ -24,15 +24,15 @@ * GNU GPL, version 2 or (at your option) any later version. * */ -#include "hw.h" -#include "pc.h" -#include "pm_smbus.h" -#include "pci/pci.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/pm_smbus.h" +#include "hw/pci/pci.h" #include "sysemu/sysemu.h" -#include "i2c.h" -#include "smbus.h" +#include "hw/i2c.h" +#include "hw/smbus.h" -#include "ich9.h" +#include "hw/ich9.h" #define TYPE_ICH9_SMB_DEVICE "ICH9 SMB" #define ICH9_SMB_DEVICE(obj) \ diff --git a/hw/smc91c111.c b/hw/smc91c111.c index 2161b4af7a..c2feae6eb8 100644 --- a/hw/smc91c111.c +++ b/hw/smc91c111.c @@ -7,9 +7,9 @@ * This code is licensed under the GPL */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "net/net.h" -#include "devices.h" +#include "hw/devices.h" /* For crc32 */ #include @@ -237,7 +237,7 @@ static void smc91c111_do_tx(smc91c111_state *s) smc91c111_release_packet(s, packetnum); else if (s->tx_fifo_done_len < NUM_PACKETS) s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum; - qemu_send_packet(&s->nic->nc, p, len); + qemu_send_packet(qemu_get_queue(s->nic), p, len); } s->tx_fifo_len = 0; smc91c111_update(s); @@ -254,7 +254,7 @@ static void smc91c111_queue_tx(smc91c111_state *s, int packet) static void smc91c111_reset(DeviceState *dev) { - smc91c111_state *s = FROM_SYSBUS(smc91c111_state, sysbus_from_qdev(dev)); + smc91c111_state *s = FROM_SYSBUS(smc91c111_state, SYS_BUS_DEVICE(dev)); s->bank = 0; s->tx_fifo_len = 0; s->tx_fifo_done_len = 0; @@ -442,6 +442,7 @@ static void smc91c111_writeb(void *opaque, hwaddr offset, return; case 12: /* Early receive. */ s->ercv = value & 0x1f; + return; case 13: /* Ignore. */ return; @@ -630,7 +631,7 @@ static uint32_t smc91c111_readl(void *opaque, hwaddr offset) static int smc91c111_can_receive(NetClientState *nc) { - smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + smc91c111_state *s = qemu_get_nic_opaque(nc); if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) return 1; @@ -641,7 +642,7 @@ static int smc91c111_can_receive(NetClientState *nc) static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + smc91c111_state *s = qemu_get_nic_opaque(nc); int status; int packetsize; uint32_t crc; @@ -730,7 +731,7 @@ static const MemoryRegionOps smc91c111_mem_ops = { static void smc91c111_cleanup(NetClientState *nc) { - smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + smc91c111_state *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -753,7 +754,7 @@ static int smc91c111_init1(SysBusDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); /* ??? Save/restore. */ return 0; } @@ -774,7 +775,7 @@ static void smc91c111_class_init(ObjectClass *klass, void *data) dc->props = smc91c111_properties; } -static TypeInfo smc91c111_info = { +static const TypeInfo smc91c111_info = { .name = "smc91c111", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(smc91c111_state), @@ -797,7 +798,7 @@ void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq) dev = qdev_create(NULL, "smc91c111"); qdev_set_nic_properties(dev, nd); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, base); sysbus_connect_irq(s, 0, irq); } diff --git a/hw/soc_dma.c b/hw/soc_dma.c index 64e8ee1d13..db5d609388 100644 --- a/hw/soc_dma.c +++ b/hw/soc_dma.c @@ -19,7 +19,7 @@ */ #include "qemu-common.h" #include "qemu/timer.h" -#include "soc_dma.h" +#include "hw/soc_dma.h" static void transfer_mem2mem(struct soc_dma_ch_s *ch) { diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c index 8077eb94bc..19701e7e40 100644 --- a/hw/spapr_llan.c +++ b/hw/spapr_llan.c @@ -24,7 +24,7 @@ * THE SOFTWARE. * */ -#include "hw.h" +#include "hw/hw.h" #include "net/net.h" #include "hw/qdev.h" #include "hw/spapr.h" @@ -85,7 +85,7 @@ typedef struct VIOsPAPRVLANDevice { static int spapr_vlan_can_receive(NetClientState *nc) { - VIOsPAPRVLANDevice *dev = DO_UPCAST(NICState, nc, nc)->opaque; + VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc); return (dev->isopen && dev->rx_bufs > 0); } @@ -93,7 +93,7 @@ static int spapr_vlan_can_receive(NetClientState *nc) static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - VIOsPAPRDevice *sdev = DO_UPCAST(NICState, nc, nc)->opaque; + VIOsPAPRDevice *sdev = qemu_get_nic_opaque(nc); VIOsPAPRVLANDevice *dev = (VIOsPAPRVLANDevice *)sdev; vlan_bd_t rxq_bd = vio_ldq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF); vlan_bd_t bd; @@ -175,11 +175,19 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, return size; } +static void spapr_vlan_cleanup(NetClientState *nc) +{ + VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc); + + dev->nic = NULL; +} + static NetClientInfo net_spapr_vlan_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), .can_receive = spapr_vlan_can_receive, .receive = spapr_vlan_receive, + .cleanup = spapr_vlan_cleanup, }; static void spapr_vlan_reset(VIOsPAPRDevice *sdev) @@ -199,7 +207,7 @@ static int spapr_vlan_init(VIOsPAPRDevice *sdev) dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf, object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev); - qemu_format_nic_info_str(&dev->nic->nc, dev->nicconf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a); return 0; } @@ -462,7 +470,7 @@ static target_ulong h_send_logical_lan(PowerPCCPU *cpu, sPAPREnvironment *spapr, p += VLAN_BD_LEN(bufs[i]); } - qemu_send_packet(&dev->nic->nc, lbuf, total_len); + qemu_send_packet(qemu_get_queue(dev->nic), lbuf, total_len); return H_SUCCESS; } @@ -502,7 +510,7 @@ static void spapr_vlan_class_init(ObjectClass *klass, void *data) k->rtce_window_size = 0x10000000; } -static TypeInfo spapr_vlan_info = { +static const TypeInfo spapr_vlan_info = { .name = "spapr-vlan", .parent = TYPE_VIO_SPAPR_DEVICE, .instance_size = sizeof(VIOsPAPRVLANDevice), diff --git a/hw/spapr_nvram.c b/hw/spapr_nvram.c index f20f6b4fdd..680cdba928 100644 --- a/hw/spapr_nvram.c +++ b/hw/spapr_nvram.c @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include + #include #include "sysemu/device_tree.h" diff --git a/hw/spapr_pci.c b/hw/spapr_pci.c index 27b3ad3d60..36adbc5592 100644 --- a/hw/spapr_pci.c +++ b/hw/spapr_pci.c @@ -22,11 +22,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "pci/pci.h" -#include "pci/msi.h" -#include "pci/msix.h" -#include "pci/pci_host.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" +#include "hw/pci/pci_host.h" #include "hw/spapr.h" #include "hw/spapr_pci.h" #include "exec/address-spaces.h" @@ -435,7 +435,7 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level) */ sPAPRPHBState *phb = opaque; - trace_spapr_pci_lsi_set(phb->busname, irq_num, phb->lsi_table[irq_num].irq); + trace_spapr_pci_lsi_set(phb->dtbusname, irq_num, phb->lsi_table[irq_num].irq); qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level); } @@ -522,7 +522,63 @@ static int spapr_phb_init(SysBusDevice *s) int i; PCIBus *bus; + if (sphb->index != -1) { + hwaddr windows_base; + + if ((sphb->buid != -1) || (sphb->dma_liobn != -1) + || (sphb->mem_win_addr != -1) + || (sphb->io_win_addr != -1) + || (sphb->msi_win_addr != -1)) { + fprintf(stderr, "Either \"index\" or other parameters must" + " be specified for PAPR PHB, not both\n"); + return -1; + } + + sphb->buid = SPAPR_PCI_BASE_BUID + sphb->index; + sphb->dma_liobn = SPAPR_PCI_BASE_LIOBN + sphb->index; + + windows_base = SPAPR_PCI_WINDOW_BASE + + sphb->index * SPAPR_PCI_WINDOW_SPACING; + sphb->mem_win_addr = windows_base + SPAPR_PCI_MMIO_WIN_OFF; + sphb->io_win_addr = windows_base + SPAPR_PCI_IO_WIN_OFF; + sphb->msi_win_addr = windows_base + SPAPR_PCI_MSI_WIN_OFF; + } + + if (sphb->buid == -1) { + fprintf(stderr, "BUID not specified for PHB\n"); + return -1; + } + + if (sphb->dma_liobn == -1) { + fprintf(stderr, "LIOBN not specified for PHB\n"); + return -1; + } + + if (sphb->mem_win_addr == -1) { + fprintf(stderr, "Memory window address not specified for PHB\n"); + return -1; + } + + if (sphb->io_win_addr == -1) { + fprintf(stderr, "IO window address not specified for PHB\n"); + return -1; + } + + if (sphb->msi_win_addr == -1) { + fprintf(stderr, "MSI window address not specified for PHB\n"); + return -1; + } + + if (find_phb(spapr, sphb->buid)) { + fprintf(stderr, "PCI host bridges must have unique BUIDs\n"); + return -1; + } + sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid); + if (!sphb->busname) { + sphb->busname = sphb->dtbusname; + } + namebuf = alloca(strlen(sphb->dtbusname) + 32); /* Initialize memory regions */ @@ -565,17 +621,19 @@ static int spapr_phb_init(SysBusDevice *s) &sphb->msiwindow); } - bus = pci_register_bus(DEVICE(s), - sphb->busname ? sphb->busname : sphb->dtbusname, + bus = pci_register_bus(DEVICE(s), sphb->busname, pci_spapr_set_irq, pci_spapr_map_irq, sphb, &sphb->memspace, &sphb->iospace, PCI_DEVFN(0, 0), PCI_NUM_PINS); phb->bus = bus; - sphb->dma_liobn = SPAPR_PCI_BASE_LIOBN | (pci_find_domain(bus) << 16); sphb->dma_window_start = 0; sphb->dma_window_size = 0x40000000; sphb->dma = spapr_tce_new_dma_context(sphb->dma_liobn, sphb->dma_window_size); + if (!sphb->dma) { + fprintf(stderr, "Unable to create TCE table for %s\n", sphb->dtbusname); + return -1; + } pci_setup_iommu(bus, spapr_pci_dma_context_fn, sphb); QLIST_INSERT_HEAD(&spapr->phbs, sphb, list); @@ -597,7 +655,7 @@ static int spapr_phb_init(SysBusDevice *s) static void spapr_phb_reset(DeviceState *qdev) { - SysBusDevice *s = sysbus_from_qdev(qdev); + SysBusDevice *s = SYS_BUS_DEVICE(qdev); sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); /* Reset the IOMMU state */ @@ -605,13 +663,17 @@ static void spapr_phb_reset(DeviceState *qdev) } static Property spapr_phb_properties[] = { - DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, 0), DEFINE_PROP_STRING("busname", sPAPRPHBState, busname), - DEFINE_PROP_HEX64("mem_win_addr", sPAPRPHBState, mem_win_addr, 0), - DEFINE_PROP_HEX64("mem_win_size", sPAPRPHBState, mem_win_size, 0x20000000), - DEFINE_PROP_HEX64("io_win_addr", sPAPRPHBState, io_win_addr, 0), - DEFINE_PROP_HEX64("io_win_size", sPAPRPHBState, io_win_size, 0x10000), - DEFINE_PROP_HEX64("msi_win_addr", sPAPRPHBState, msi_win_addr, 0), + DEFINE_PROP_INT32("index", sPAPRPHBState, index, -1), + DEFINE_PROP_HEX64("buid", sPAPRPHBState, buid, -1), + DEFINE_PROP_HEX32("liobn", sPAPRPHBState, dma_liobn, -1), + DEFINE_PROP_HEX64("mem_win_addr", sPAPRPHBState, mem_win_addr, -1), + DEFINE_PROP_HEX64("mem_win_size", sPAPRPHBState, mem_win_size, + SPAPR_PCI_MMIO_WIN_SIZE), + DEFINE_PROP_HEX64("io_win_addr", sPAPRPHBState, io_win_addr, -1), + DEFINE_PROP_HEX64("io_win_size", sPAPRPHBState, io_win_size, + SPAPR_PCI_IO_WIN_SIZE), + DEFINE_PROP_HEX64("msi_win_addr", sPAPRPHBState, msi_win_addr, -1), DEFINE_PROP_END_OF_LIST(), }; @@ -632,25 +694,17 @@ static const TypeInfo spapr_phb_info = { .class_init = spapr_phb_class_init, }; -void spapr_create_phb(sPAPREnvironment *spapr, - const char *busname, uint64_t buid, - uint64_t mem_win_addr, uint64_t mem_win_size, - uint64_t io_win_addr, uint64_t msi_win_addr) +PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index, + const char *busname) { DeviceState *dev; dev = qdev_create(NULL, TYPE_SPAPR_PCI_HOST_BRIDGE); - - if (busname) { - qdev_prop_set_string(dev, "busname", g_strdup(busname)); - } - qdev_prop_set_uint64(dev, "buid", buid); - qdev_prop_set_uint64(dev, "mem_win_addr", mem_win_addr); - qdev_prop_set_uint64(dev, "mem_win_size", mem_win_size); - qdev_prop_set_uint64(dev, "io_win_addr", io_win_addr); - qdev_prop_set_uint64(dev, "msi_win_addr", msi_win_addr); - + qdev_prop_set_uint32(dev, "index", index); + qdev_prop_set_string(dev, "busname", busname); qdev_init_nofail(dev); + + return PCI_HOST_BRIDGE(dev); } /* Macros to operate with address in OF binding to PCI */ diff --git a/hw/spapr_pci.h b/hw/spapr_pci.h index 7b26ba1561..8bb3c62c3d 100644 --- a/hw/spapr_pci.h +++ b/hw/spapr_pci.h @@ -37,6 +37,7 @@ typedef struct sPAPRPHBState { PCIHostState parent_obj; + int32_t index; uint64_t buid; char *busname; char *dtbusname; @@ -64,18 +65,25 @@ typedef struct sPAPRPHBState { QLIST_ENTRY(sPAPRPHBState) list; } sPAPRPHBState; +#define SPAPR_PCI_BASE_BUID 0x800000020000000ULL + +#define SPAPR_PCI_WINDOW_BASE 0x10000000000ULL +#define SPAPR_PCI_WINDOW_SPACING 0x1000000000ULL +#define SPAPR_PCI_MMIO_WIN_OFF 0xA0000000 +#define SPAPR_PCI_MMIO_WIN_SIZE 0x20000000 +#define SPAPR_PCI_IO_WIN_OFF 0x80000000 +#define SPAPR_PCI_IO_WIN_SIZE 0x10000 +#define SPAPR_PCI_MSI_WIN_OFF 0x90000000 + +#define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL + static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) { return xics_get_qirq(spapr->icp, phb->lsi_table[pin].irq); } -#define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL -#define SPAPR_PCI_IO_WIN_SIZE 0x10000 - -void spapr_create_phb(sPAPREnvironment *spapr, - const char *busname, uint64_t buid, - uint64_t mem_win_addr, uint64_t mem_win_size, - uint64_t io_win_addr, uint64_t msi_win_addr); +PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index, + const char *busname); int spapr_populate_pci_dt(sPAPRPHBState *phb, uint32_t xics_phandle, diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 2d811320ca..27940949ce 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -31,10 +31,10 @@ * - Add indirect descriptors support * - Maybe do autosense (PAPR seems to mandate it, linux doesn't care) */ -#include "hw.h" -#include "scsi.h" -#include "scsi-defs.h" -#include "srp.h" +#include "hw/hw.h" +#include "hw/scsi.h" +#include "hw/scsi-defs.h" +#include "hw/srp.h" #include "hw/qdev.h" #include "hw/spapr.h" #include "hw/spapr_vio.h" @@ -967,7 +967,7 @@ static void spapr_vscsi_class_init(ObjectClass *klass, void *data) k->rtce_window_size = 0x10000000; } -static TypeInfo spapr_vscsi_info = { +static const TypeInfo spapr_vscsi_info = { .name = "spapr-vscsi", .parent = TYPE_VIO_SPAPR_DEVICE, .instance_size = sizeof(VSCSIState), diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c index ec81a7e6e8..be08571d23 100644 --- a/hw/spapr_vty.c +++ b/hw/spapr_vty.c @@ -1,4 +1,4 @@ -#include "qdev.h" +#include "hw/qdev.h" #include "char/char.h" #include "hw/spapr.h" #include "hw/spapr_vio.h" @@ -150,7 +150,7 @@ static void spapr_vty_class_init(ObjectClass *klass, void *data) dc->props = spapr_vty_properties; } -static TypeInfo spapr_vty_info = { +static const TypeInfo spapr_vty_info = { .name = "spapr-vty", .parent = TYPE_VIO_SPAPR_DEVICE, .instance_size = sizeof(VIOsPAPRVTYDevice), diff --git a/hw/sparc/Makefile.objs b/hw/sparc/Makefile.objs index a39a511c52..71bbddf8c3 100644 --- a/hw/sparc/Makefile.objs +++ b/hw/sparc/Makefile.objs @@ -1,8 +1,10 @@ -obj-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o +obj-y = lance.o tcx.o sun4m_iommu.o slavio_intctl.o obj-y += slavio_timer.o slavio_misc.o sparc32_dma.o -obj-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o +obj-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o # GRLIB obj-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o obj-y := $(addprefix ../,$(obj-y)) + +obj-y += sun4m.o leon3.o diff --git a/hw/leon3.c b/hw/sparc/leon3.c similarity index 93% rename from hw/leon3.c rename to hw/sparc/leon3.c index 79b3a41def..bf06bf4b51 100644 --- a/hw/leon3.c +++ b/hw/sparc/leon3.c @@ -21,18 +21,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" -#include "ptimer.h" +#include "hw/ptimer.h" #include "char/char.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "loader.h" +#include "hw/boards.h" +#include "hw/loader.h" #include "elf.h" #include "trace.h" #include "exec/address-spaces.h" -#include "grlib.h" +#include "hw/grlib.h" /* Default system clock. */ #define CPU_CLK (40 * 1000 * 1000) @@ -49,11 +49,12 @@ typedef struct ResetData { static void main_cpu_reset(void *opaque) { ResetData *s = (ResetData *)opaque; + CPUState *cpu = CPU(s->cpu); CPUSPARCState *env = &s->cpu->env; - cpu_reset(CPU(s->cpu)); + cpu_reset(cpu); - env->halted = 0; + cpu->halted = 0; env->pc = s->entry; env->npc = s->entry + 4; } @@ -66,6 +67,7 @@ void leon3_irq_ack(void *irq_manager, int intno) static void leon3_set_pil_in(void *opaque, uint32_t pil_in) { CPUSPARCState *env = (CPUSPARCState *)opaque; + CPUState *cs; assert(env != NULL); @@ -81,16 +83,18 @@ static void leon3_set_pil_in(void *opaque, uint32_t pil_in) env->interrupt_index = TT_EXTINT | i; if (old_interrupt != env->interrupt_index) { + cs = CPU(sparc_env_get_cpu(env)); trace_leon3_set_irq(i); - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); } break; } } } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) { + cs = CPU(sparc_env_get_cpu(env)); trace_leon3_reset_irq(env->interrupt_index & 15); env->interrupt_index = 0; - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } @@ -212,6 +216,7 @@ static QEMUMachine leon3_generic_machine = { .name = "leon3_generic", .desc = "Leon-3 generic", .init = leon3_generic_hw_init, + DEFAULT_MACHINE_OPTIONS, }; static void leon3_machine_init(void) diff --git a/hw/sun4m.c b/hw/sparc/sun4m.c similarity index 96% rename from hw/sun4m.c rename to hw/sparc/sun4m.c index 0d84b373b1..2f214da557 100644 --- a/hw/sun4m.c +++ b/hw/sparc/sun4m.c @@ -21,24 +21,24 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/timer.h" -#include "sun4m.h" -#include "nvram.h" -#include "sparc32_dma.h" -#include "fdc.h" +#include "hw/sun4m.h" +#include "hw/nvram.h" +#include "hw/sparc32_dma.h" +#include "hw/fdc.h" #include "sysemu/sysemu.h" #include "net/net.h" -#include "boards.h" -#include "firmware_abi.h" -#include "esp.h" -#include "pc.h" -#include "isa.h" -#include "fw_cfg.h" -#include "escc.h" -#include "empty_slot.h" -#include "qdev-addr.h" -#include "loader.h" +#include "hw/boards.h" +#include "hw/firmware_abi.h" +#include "hw/esp.h" +#include "hw/pc.h" +#include "hw/isa.h" +#include "hw/fw_cfg.h" +#include "hw/escc.h" +#include "hw/empty_slot.h" +#include "hw/qdev-addr.h" +#include "hw/loader.h" #include "elf.h" #include "sysemu/blockdev.h" #include "trace.h" @@ -216,13 +216,13 @@ static void nvram_init(M48t59State *nvram, uint8_t *macaddr, static DeviceState *slavio_intctl; -void sun4m_pic_info(Monitor *mon) +void sun4m_pic_info(Monitor *mon, const QDict *qdict) { if (slavio_intctl) slavio_pic_info(mon, slavio_intctl); } -void sun4m_irq_info(Monitor *mon) +void sun4m_irq_info(Monitor *mon, const QDict *qdict) { if (slavio_intctl) slavio_irq_info(mon, slavio_intctl); @@ -230,6 +230,8 @@ void sun4m_irq_info(Monitor *mon) void cpu_check_irqs(CPUSPARCState *env) { + CPUState *cs; + if (env->pil_in && (env->interrupt_index == 0 || (env->interrupt_index & ~15) == TT_EXTINT)) { unsigned int i; @@ -240,26 +242,29 @@ void cpu_check_irqs(CPUSPARCState *env) env->interrupt_index = TT_EXTINT | i; if (old_interrupt != env->interrupt_index) { + cs = CPU(sparc_env_get_cpu(env)); trace_sun4m_cpu_interrupt(i); - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); } break; } } } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) { + cs = CPU(sparc_env_get_cpu(env)); trace_sun4m_cpu_reset_interrupt(env->interrupt_index & 15); env->interrupt_index = 0; - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } static void cpu_kick_irq(SPARCCPU *cpu) { CPUSPARCState *env = &cpu->env; + CPUState *cs = CPU(cpu); - env->halted = 0; + cs->halted = 0; cpu_check_irqs(env); - qemu_cpu_kick(CPU(cpu)); + qemu_cpu_kick(cs); } static void cpu_set_irq(void *opaque, int irq, int level) @@ -285,25 +290,27 @@ static void dummy_cpu_set_irq(void *opaque, int irq, int level) static void main_cpu_reset(void *opaque) { SPARCCPU *cpu = opaque; - CPUSPARCState *env = &cpu->env; + CPUState *cs = CPU(cpu); - cpu_reset(CPU(cpu)); - env->halted = 0; + cpu_reset(cs); + cs->halted = 0; } static void secondary_cpu_reset(void *opaque) { SPARCCPU *cpu = opaque; - CPUSPARCState *env = &cpu->env; + CPUState *cs = CPU(cpu); - cpu_reset(CPU(cpu)); - env->halted = 1; + cpu_reset(cs); + cs->halted = 1; } static void cpu_halt_signal(void *opaque, int irq, int level) { - if (level && cpu_single_env) - cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT); + if (level && cpu_single_env) { + cpu_interrupt(CPU(sparc_env_get_cpu(cpu_single_env)), + CPU_INTERRUPT_HALT); + } } static uint64_t translate_kernel_address(void *opaque, uint64_t addr) @@ -381,7 +388,7 @@ static void *iommu_init(hwaddr addr, uint32_t version, qemu_irq irq) dev = qdev_create(NULL, "iommu"); qdev_prop_set_uint32(dev, "version", version); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_connect_irq(s, 0, irq); sysbus_mmio_map(s, 0, addr); @@ -398,7 +405,7 @@ static void *sparc32_dma_init(hwaddr daddr, qemu_irq parent_irq, qdev_prop_set_ptr(dev, "iommu_opaque", iommu); qdev_prop_set_uint32(dev, "is_ledma", is_ledma); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_connect_irq(s, 0, parent_irq); *dev_irq = qdev_get_gpio_in(dev, 0); sysbus_mmio_map(s, 0, daddr); @@ -419,7 +426,7 @@ static void lance_init(NICInfo *nd, hwaddr leaddr, qdev_set_nic_properties(dev, nd); qdev_prop_set_ptr(dev, "dma", dma_opaque); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, leaddr); sysbus_connect_irq(s, 0, irq); reset = qdev_get_gpio_in(dev, 0); @@ -437,7 +444,7 @@ static DeviceState *slavio_intctl_init(hwaddr addr, dev = qdev_create(NULL, "slavio_intctl"); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); for (i = 0; i < MAX_CPUS; i++) { for (j = 0; j < MAX_PILS; j++) { @@ -465,7 +472,7 @@ static void slavio_timer_init_all(hwaddr addr, qemu_irq master_irq, dev = qdev_create(NULL, "slavio_timer"); qdev_prop_set_uint32(dev, "num_cpus", num_cpus); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_connect_irq(s, 0, master_irq); sysbus_mmio_map(s, 0, addr + SYS_TIMER_OFFSET); @@ -502,7 +509,7 @@ static void slavio_misc_init(hwaddr base, dev = qdev_create(NULL, "slavio_misc"); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); if (base) { /* 8 bit registers */ /* Slavio control */ @@ -540,7 +547,7 @@ static void ecc_init(hwaddr base, qemu_irq irq, uint32_t version) dev = qdev_create(NULL, "eccmemctl"); qdev_prop_set_uint32(dev, "version", version); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_connect_irq(s, 0, irq); sysbus_mmio_map(s, 0, base); if (version == 0) { // SS-600MP only @@ -555,7 +562,7 @@ static void apc_init(hwaddr power_base, qemu_irq cpu_halt) dev = qdev_create(NULL, "apc"); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); /* Power management (APC) XXX: not a Slavio device */ sysbus_mmio_map(s, 0, power_base); sysbus_connect_irq(s, 0, cpu_halt); @@ -574,7 +581,7 @@ static void tcx_init(hwaddr addr, int vram_size, int width, qdev_prop_set_uint16(dev, "height", height); qdev_prop_set_uint16(dev, "depth", depth); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); /* 8-bit plane */ sysbus_mmio_map(s, 0, addr + 0x00800000ULL); /* DAC */ @@ -604,7 +611,7 @@ static void idreg_init(hwaddr addr) dev = qdev_create(NULL, "macio_idreg"); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, addr); cpu_physical_memory_write_rom(addr, idreg_data, sizeof(idreg_data)); @@ -633,7 +640,7 @@ static void idreg_class_init(ObjectClass *klass, void *data) k->init = idreg_init1; } -static TypeInfo idreg_info = { +static const TypeInfo idreg_info = { .name = "macio_idreg", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IDRegState), @@ -653,7 +660,7 @@ static void afx_init(hwaddr addr) dev = qdev_create(NULL, "tcx_afx"); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, addr); } @@ -675,7 +682,7 @@ static void afx_class_init(ObjectClass *klass, void *data) k->init = afx_init1; } -static TypeInfo afx_info = { +static const TypeInfo afx_info = { .name = "tcx_afx", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AFXState), @@ -703,7 +710,7 @@ static void prom_init(hwaddr addr, const char *bios_name) dev = qdev_create(NULL, "openprom"); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, addr); @@ -752,7 +759,7 @@ static void prom_class_init(ObjectClass *klass, void *data) dc->props = prom_properties; } -static TypeInfo prom_info = { +static const TypeInfo prom_info = { .name = "openprom", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PROMState), @@ -793,7 +800,7 @@ static void ram_init(hwaddr addr, ram_addr_t RAM_size, exit(1); } dev = qdev_create(NULL, "memory"); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); d = FROM_SYSBUS(RamDevice, s); d->size = RAM_size; @@ -816,7 +823,7 @@ static void ram_class_init(ObjectClass *klass, void *data) dc->props = ram_properties; } -static TypeInfo ram_info = { +static const TypeInfo ram_info = { .name = "memory", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(RamDevice), @@ -826,6 +833,7 @@ static TypeInfo ram_info = { static void cpu_devinit(const char *cpu_model, unsigned int id, uint64_t prom_addr, qemu_irq **cpu_irqs) { + CPUState *cs; SPARCCPU *cpu; CPUSPARCState *env; @@ -841,7 +849,8 @@ static void cpu_devinit(const char *cpu_model, unsigned int id, qemu_register_reset(main_cpu_reset, cpu); } else { qemu_register_reset(secondary_cpu_reset, cpu); - env->halted = 1; + cs = CPU(cpu); + cs->halted = 1; } *cpu_irqs = qemu_allocate_irqs(cpu_set_irq, cpu, MAX_PILS); env->prom_addr = prom_addr; @@ -1021,6 +1030,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size, hwdef->ecc_version); fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id); @@ -1030,9 +1040,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size, if (kernel_cmdline) { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR); pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline); - fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA, - (uint8_t*)strdup(kernel_cmdline), - strlen(kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1); } else { @@ -1428,6 +1436,7 @@ static QEMUMachine ss5_machine = { .init = ss5_init, .block_default_type = IF_SCSI, .is_default = 1, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine ss10_machine = { @@ -1436,6 +1445,7 @@ static QEMUMachine ss10_machine = { .init = ss10_init, .block_default_type = IF_SCSI, .max_cpus = 4, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine ss600mp_machine = { @@ -1444,6 +1454,7 @@ static QEMUMachine ss600mp_machine = { .init = ss600mp_init, .block_default_type = IF_SCSI, .max_cpus = 4, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine ss20_machine = { @@ -1452,6 +1463,7 @@ static QEMUMachine ss20_machine = { .init = ss20_init, .block_default_type = IF_SCSI, .max_cpus = 4, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine voyager_machine = { @@ -1459,6 +1471,7 @@ static QEMUMachine voyager_machine = { .desc = "Sun4m platform, SPARCstation Voyager", .init = vger_init, .block_default_type = IF_SCSI, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine ss_lx_machine = { @@ -1466,6 +1479,7 @@ static QEMUMachine ss_lx_machine = { .desc = "Sun4m platform, SPARCstation LX", .init = ss_lx_init, .block_default_type = IF_SCSI, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine ss4_machine = { @@ -1473,6 +1487,7 @@ static QEMUMachine ss4_machine = { .desc = "Sun4m platform, SPARCstation 4", .init = ss4_init, .block_default_type = IF_SCSI, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine scls_machine = { @@ -1480,6 +1495,7 @@ static QEMUMachine scls_machine = { .desc = "Sun4m platform, SPARCClassic", .init = scls_init, .block_default_type = IF_SCSI, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine sbook_machine = { @@ -1487,6 +1503,7 @@ static QEMUMachine sbook_machine = { .desc = "Sun4m platform, SPARCbook", .init = sbook_init, .block_default_type = IF_SCSI, + DEFAULT_MACHINE_OPTIONS, }; static const struct sun4d_hwdef sun4d_hwdefs[] = { @@ -1553,7 +1570,7 @@ static DeviceState *sbi_init(hwaddr addr, qemu_irq **parent_irq) dev = qdev_create(NULL, "sbi"); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); for (i = 0; i < MAX_CPUS; i++) { sysbus_connect_irq(s, i, *parent_irq[i]); @@ -1658,6 +1675,7 @@ static void sun4d_hw_init(const struct sun4d_hwdef *hwdef, ram_addr_t RAM_size, "Sun4d"); fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id); @@ -1667,9 +1685,7 @@ static void sun4d_hw_init(const struct sun4d_hwdef *hwdef, ram_addr_t RAM_size, if (kernel_cmdline) { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR); pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline); - fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA, - (uint8_t*)strdup(kernel_cmdline), - strlen(kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); } else { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); } @@ -1711,6 +1727,7 @@ static QEMUMachine ss1000_machine = { .init = ss1000_init, .block_default_type = IF_SCSI, .max_cpus = 8, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine ss2000_machine = { @@ -1719,6 +1736,7 @@ static QEMUMachine ss2000_machine = { .init = ss2000_init, .block_default_type = IF_SCSI, .max_cpus = 20, + DEFAULT_MACHINE_OPTIONS, }; static const struct sun4c_hwdef sun4c_hwdefs[] = { @@ -1754,7 +1772,7 @@ static DeviceState *sun4c_intctl_init(hwaddr addr, dev = qdev_create(NULL, "sun4c_intctl"); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); for (i = 0; i < MAX_PILS; i++) { sysbus_connect_irq(s, i, parent_irq[i]); @@ -1858,6 +1876,7 @@ static void sun4c_hw_init(const struct sun4c_hwdef *hwdef, ram_addr_t RAM_size, "Sun4c"); fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id); @@ -1867,9 +1886,7 @@ static void sun4c_hw_init(const struct sun4c_hwdef *hwdef, ram_addr_t RAM_size, if (kernel_cmdline) { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR); pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline); - fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA, - (uint8_t*)strdup(kernel_cmdline), - strlen(kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); } else { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); } @@ -1897,6 +1914,7 @@ static QEMUMachine ss2_machine = { .desc = "Sun4c platform, SPARCstation 2", .init = ss2_init, .block_default_type = IF_SCSI, + DEFAULT_MACHINE_OPTIONS, }; static void sun4m_register_types(void) diff --git a/hw/sparc32_dma.c b/hw/sparc32_dma.c index d11a302f20..18e368ec98 100644 --- a/hw/sparc32_dma.c +++ b/hw/sparc32_dma.c @@ -25,10 +25,10 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "sparc32_dma.h" -#include "sun4m.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sparc32_dma.h" +#include "hw/sun4m.h" +#include "hw/sysbus.h" #include "trace.h" /* @@ -300,7 +300,7 @@ static void sparc32_dma_class_init(ObjectClass *klass, void *data) dc->props = sparc32_dma_properties; } -static TypeInfo sparc32_dma_info = { +static const TypeInfo sparc32_dma_info = { .name = "sparc32_dma", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(DMAState), diff --git a/hw/sparc64/Makefile.objs b/hw/sparc64/Makefile.objs index 8c65fc4215..4df0d90ec2 100644 --- a/hw/sparc64/Makefile.objs +++ b/hw/sparc64/Makefile.objs @@ -1,4 +1,6 @@ -obj-y = sun4u.o apb_pci.o +obj-y = apb_pci.o obj-y += mc146818rtc.o obj-y := $(addprefix ../,$(obj-y)) + +obj-y += sun4u.o diff --git a/hw/sun4u.c b/hw/sparc64/sun4u.c similarity index 95% rename from hw/sun4u.c rename to hw/sparc64/sun4u.c index cbfd217587..4c39cf6607 100644 --- a/hw/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -21,22 +21,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "pci/pci.h" -#include "apb_pci.h" -#include "pc.h" -#include "serial.h" -#include "nvram.h" -#include "fdc.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/apb_pci.h" +#include "hw/pc.h" +#include "hw/serial.h" +#include "hw/nvram.h" +#include "hw/fdc.h" #include "net/net.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" -#include "boards.h" -#include "firmware_abi.h" -#include "fw_cfg.h" -#include "sysbus.h" -#include "ide.h" -#include "loader.h" +#include "hw/boards.h" +#include "hw/firmware_abi.h" +#include "hw/fw_cfg.h" +#include "hw/sysbus.h" +#include "hw/ide.h" +#include "hw/loader.h" #include "elf.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" @@ -254,6 +254,7 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, void cpu_check_irqs(CPUSPARCState *env) { + CPUState *cs; uint32_t pil = env->pil_in | (env->softint & ~(SOFTINT_TIMER | SOFTINT_STIMER)); @@ -261,6 +262,7 @@ void cpu_check_irqs(CPUSPARCState *env) if (env->ivec_status & 0x20) { return; } + cs = CPU(sparc_env_get_cpu(env)); /* check if TM or SM in SOFTINT are set setting these also causes interrupt 14 */ if (env->softint & (SOFTINT_TIMER | SOFTINT_STIMER)) { @@ -270,11 +272,11 @@ void cpu_check_irqs(CPUSPARCState *env) /* The bit corresponding to psrpil is (1<< psrpil), the next bit is (2 << psrpil). */ if (pil < (2 << env->psrpil)){ - if (env->interrupt_request & CPU_INTERRUPT_HARD) { + if (cs->interrupt_request & CPU_INTERRUPT_HARD) { CPUIRQ_DPRINTF("Reset CPU IRQ (current interrupt %x)\n", env->interrupt_index); env->interrupt_index = 0; - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } return; } @@ -297,50 +299,54 @@ void cpu_check_irqs(CPUSPARCState *env) env->interrupt_index = new_interrupt; CPUIRQ_DPRINTF("Set CPU IRQ %d old=%x new=%x\n", i, old_interrupt, new_interrupt); - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); } break; } } - } else if (env->interrupt_request & CPU_INTERRUPT_HARD) { + } else if (cs->interrupt_request & CPU_INTERRUPT_HARD) { CPUIRQ_DPRINTF("Interrupts disabled, pil=%08x pil_in=%08x softint=%08x " "current interrupt %x\n", pil, env->pil_in, env->softint, env->interrupt_index); env->interrupt_index = 0; - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } static void cpu_kick_irq(SPARCCPU *cpu) { + CPUState *cs = CPU(cpu); CPUSPARCState *env = &cpu->env; - env->halted = 0; + cs->halted = 0; cpu_check_irqs(env); - qemu_cpu_kick(CPU(cpu)); + qemu_cpu_kick(cs); } static void cpu_set_ivec_irq(void *opaque, int irq, int level) { SPARCCPU *cpu = opaque; CPUSPARCState *env = &cpu->env; + CPUState *cs; if (level) { if (!(env->ivec_status & 0x20)) { CPUIRQ_DPRINTF("Raise IVEC IRQ %d\n", irq); - env->halted = 0; + cs = CPU(cpu); + cs->halted = 0; env->interrupt_index = TT_IVEC; env->ivec_status |= 0x20; env->ivec_data[0] = (0x1f << 6) | irq; env->ivec_data[1] = 0; env->ivec_data[2] = 0; - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); } } else { if (env->ivec_status & 0x20) { CPUIRQ_DPRINTF("Lower IVEC IRQ %d\n", irq); + cs = CPU(cpu); env->ivec_status &= ~0x20; - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } } @@ -618,7 +624,7 @@ static void ebus_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_BRIDGE_OTHER; } -static TypeInfo ebus_info = { +static const TypeInfo ebus_info = { .name = "ebus", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(EbusState), @@ -646,7 +652,7 @@ static void prom_init(hwaddr addr, const char *bios_name) dev = qdev_create(NULL, "openprom"); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, addr); @@ -695,7 +701,7 @@ static void prom_class_init(ObjectClass *klass, void *data) dc->props = prom_properties; } -static TypeInfo prom_info = { +static const TypeInfo prom_info = { .name = "openprom", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PROMState), @@ -729,7 +735,7 @@ static void ram_init(hwaddr addr, ram_addr_t RAM_size) /* allocate RAM */ dev = qdev_create(NULL, "memory"); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); d = FROM_SYSBUS(RamDevice, s); d->size = RAM_size; @@ -752,7 +758,7 @@ static void ram_class_init(ObjectClass *klass, void *data) dc->props = ram_properties; } -static TypeInfo ram_info = { +static const TypeInfo ram_info = { .name = "memory", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(RamDevice), @@ -878,6 +884,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem, (uint8_t *)&nd_table[0].macaddr); fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id); @@ -886,9 +893,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem, if (kernel_cmdline) { fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1); - fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA, - (uint8_t*)strdup(kernel_cmdline), - strlen(kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); } else { fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, 0); } @@ -978,6 +983,7 @@ static QEMUMachine sun4u_machine = { .init = sun4u_init, .max_cpus = 1, // XXX for now .is_default = 1, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine sun4v_machine = { @@ -985,6 +991,7 @@ static QEMUMachine sun4v_machine = { .desc = "Sun4v platform", .init = sun4v_init, .max_cpus = 1, // XXX for now + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine niagara_machine = { @@ -992,6 +999,7 @@ static QEMUMachine niagara_machine = { .desc = "Sun4v platform, Niagara", .init = niagara_init, .max_cpus = 1, // XXX for now + DEFAULT_MACHINE_OPTIONS, }; static void sun4u_register_types(void) diff --git a/hw/ssd0303.c b/hw/ssd0303.c index cbdf49af57..db50909734 100644 --- a/hw/ssd0303.c +++ b/hw/ssd0303.c @@ -10,7 +10,7 @@ /* The controller can support a variety of different displays, but we only implement one. Most of the commends relating to brightness and geometry setup are ignored. */ -#include "i2c.h" +#include "hw/i2c.h" #include "ui/console.h" //#define DEBUG_SSD0303 1 @@ -306,7 +306,7 @@ static void ssd0303_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_ssd0303; } -static TypeInfo ssd0303_info = { +static const TypeInfo ssd0303_info = { .name = "ssd0303", .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(ssd0303_state), diff --git a/hw/ssd0323.c b/hw/ssd0323.c index fe6f801ae7..27b4151994 100644 --- a/hw/ssd0323.c +++ b/hw/ssd0323.c @@ -10,7 +10,7 @@ /* The controller can support a variety of different displays, but we only implement one. Most of the commends relating to brightness and geometry setup are ignored. */ -#include "ssi.h" +#include "hw/ssi.h" #include "ui/console.h" //#define DEBUG_SSD0323 1 @@ -357,7 +357,7 @@ static void ssd0323_class_init(ObjectClass *klass, void *data) k->cs_polarity = SSI_CS_HIGH; } -static TypeInfo ssd0323_info = { +static const TypeInfo ssd0323_info = { .name = "ssd0323", .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(ssd0323_state), diff --git a/hw/ssi-sd.c b/hw/ssi-sd.c index d61c3328d9..4d3c4f6445 100644 --- a/hw/ssi-sd.c +++ b/hw/ssi-sd.c @@ -11,8 +11,8 @@ */ #include "sysemu/blockdev.h" -#include "ssi.h" -#include "sd.h" +#include "hw/ssi.h" +#include "hw/sd.h" //#define DEBUG_SSI_SD 1 @@ -259,7 +259,7 @@ static void ssi_sd_class_init(ObjectClass *klass, void *data) k->cs_polarity = SSI_CS_LOW; } -static TypeInfo ssi_sd_info = { +static const TypeInfo ssi_sd_info = { .name = "ssi-sd", .parent = TYPE_SSI_SLAVE, .instance_size = sizeof(ssi_sd_state), diff --git a/hw/ssi.c b/hw/ssi.c index 2b56357153..1264d9da23 100644 --- a/hw/ssi.c +++ b/hw/ssi.c @@ -12,7 +12,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "ssi.h" +#include "hw/ssi.h" struct SSIBus { BusState qbus; @@ -78,7 +78,7 @@ static void ssi_slave_class_init(ObjectClass *klass, void *data) } } -static TypeInfo ssi_slave_info = { +static const TypeInfo ssi_slave_info = { .name = TYPE_SSI_SLAVE, .parent = TYPE_DEVICE, .class_init = ssi_slave_class_init, diff --git a/hw/ssi.h b/hw/ssi.h index a05d60beb4..fdae317295 100644 --- a/hw/ssi.h +++ b/hw/ssi.h @@ -11,7 +11,7 @@ #ifndef QEMU_SSI_H #define QEMU_SSI_H -#include "qdev.h" +#include "hw/qdev.h" typedef struct SSISlave SSISlave; diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c index d7e1e21ff9..59b84564a0 100644 --- a/hw/stellaris_enet.c +++ b/hw/stellaris_enet.c @@ -6,7 +6,7 @@ * * This code is licensed under the GPL. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "net/net.h" #include @@ -80,7 +80,7 @@ static void stellaris_enet_update(stellaris_enet_state *s) /* TODO: Implement MAC address filtering. */ static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + stellaris_enet_state *s = qemu_get_nic_opaque(nc); int n; uint8_t *p; uint32_t crc; @@ -122,7 +122,7 @@ static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, si static int stellaris_enet_can_receive(NetClientState *nc) { - stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + stellaris_enet_state *s = qemu_get_nic_opaque(nc); if ((s->rctl & SE_RCTL_RXEN) == 0) return 1; @@ -259,7 +259,8 @@ static void stellaris_enet_write(void *opaque, hwaddr offset, memset(&s->tx_fifo[s->tx_frame_len], 0, 60 - s->tx_frame_len); s->tx_fifo_len = 60; } - qemu_send_packet(&s->nic->nc, s->tx_fifo, s->tx_frame_len); + qemu_send_packet(qemu_get_queue(s->nic), s->tx_fifo, + s->tx_frame_len); s->tx_frame_len = -1; s->ris |= SE_INT_TXEMP; stellaris_enet_update(s); @@ -383,7 +384,7 @@ static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id) static void stellaris_enet_cleanup(NetClientState *nc) { - stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque; + stellaris_enet_state *s = qemu_get_nic_opaque(nc); unregister_savevm(&s->busdev.qdev, "stellaris_enet", s); @@ -412,7 +413,7 @@ static int stellaris_enet_init(SysBusDevice *dev) s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); stellaris_enet_reset(s); register_savevm(&s->busdev.qdev, "stellaris_enet", -1, 1, @@ -434,7 +435,7 @@ static void stellaris_enet_class_init(ObjectClass *klass, void *data) dc->props = stellaris_enet_properties; } -static TypeInfo stellaris_enet_info = { +static const TypeInfo stellaris_enet_info = { .name = "stellaris_enet", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(stellaris_enet_state), diff --git a/hw/stellaris_input.c b/hw/stellaris_input.c index 7a95c3fc88..4e407922a0 100644 --- a/hw/stellaris_input.c +++ b/hw/stellaris_input.c @@ -6,8 +6,8 @@ * * This code is licensed under the GPL. */ -#include "hw.h" -#include "devices.h" +#include "hw/hw.h" +#include "hw/devices.h" #include "ui/console.h" typedef struct { diff --git a/hw/stream.c b/hw/stream.c index be57e8b247..a07d6a56d3 100644 --- a/hw/stream.c +++ b/hw/stream.c @@ -1,4 +1,4 @@ -#include "stream.h" +#include "hw/stream.h" void stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app) @@ -8,7 +8,7 @@ stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app) k->push(sink, buf, len, app); } -static TypeInfo stream_slave_info = { +static const TypeInfo stream_slave_info = { .name = TYPE_STREAM_SLAVE, .parent = TYPE_INTERFACE, .class_size = sizeof(StreamSlaveClass), diff --git a/hw/strongarm.c b/hw/strongarm.c index 804c1a37a6..49f9577e32 100644 --- a/hw/strongarm.c +++ b/hw/strongarm.c @@ -26,13 +26,13 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ -#include "sysbus.h" -#include "strongarm.h" +#include "hw/sysbus.h" +#include "hw/strongarm.h" #include "qemu/error-report.h" -#include "arm-misc.h" +#include "hw/arm-misc.h" #include "char/char.h" #include "sysemu/sysemu.h" -#include "ssi.h" +#include "hw/ssi.h" //#define DEBUG @@ -212,7 +212,7 @@ static void strongarm_pic_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_strongarm_pic_regs; } -static TypeInfo strongarm_pic_info = { +static const TypeInfo strongarm_pic_info = { .name = "strongarm_pic", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMPICState), @@ -433,7 +433,7 @@ static void strongarm_rtc_sysbus_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_strongarm_rtc_regs; } -static TypeInfo strongarm_rtc_sysbus_info = { +static const TypeInfo strongarm_rtc_sysbus_info = { .name = "strongarm-rtc", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMRTCState), @@ -619,9 +619,9 @@ static DeviceState *strongarm_gpio_init(hwaddr base, dev = qdev_create(NULL, "strongarm-gpio"); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); for (i = 0; i < 12; i++) - sysbus_connect_irq(sysbus_from_qdev(dev), i, + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, qdev_get_gpio_in(pic, SA_PIC_GPIO0_EDGE + i)); return dev; @@ -674,7 +674,7 @@ static void strongarm_gpio_class_init(ObjectClass *klass, void *data) dc->desc = "StrongARM GPIO controller"; } -static TypeInfo strongarm_gpio_info = { +static const TypeInfo strongarm_gpio_info = { .name = "strongarm-gpio", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMGPIOInfo), @@ -840,7 +840,7 @@ static void strongarm_ppc_class_init(ObjectClass *klass, void *data) dc->desc = "StrongARM PPC controller"; } -static TypeInfo strongarm_ppc_info = { +static const TypeInfo strongarm_ppc_info = { .name = "strongarm-ppc", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMPPCInfo), @@ -1299,7 +1299,7 @@ static void strongarm_uart_class_init(ObjectClass *klass, void *data) dc->props = strongarm_uart_properties; } -static TypeInfo strongarm_uart_info = { +static const TypeInfo strongarm_uart_info = { .name = "strongarm-uart", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMUARTState), @@ -1538,7 +1538,7 @@ static void strongarm_ssp_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_strongarm_ssp_regs; } -static TypeInfo strongarm_ssp_info = { +static const TypeInfo strongarm_ssp_info = { .name = "strongarm-ssp", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(StrongARMSSPState), @@ -1597,9 +1597,9 @@ StrongARMState *sa1110_init(MemoryRegion *sysmem, DeviceState *dev = qdev_create(NULL, "strongarm-uart"); qdev_prop_set_chr(dev, "chardev", serial_hds[i]); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, sa_serial[i].io_base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(s->pic, sa_serial[i].irq)); } diff --git a/hw/sun4c_intctl.c b/hw/sun4c_intctl.c index b78d54f232..9d443d1b10 100644 --- a/hw/sun4c_intctl.c +++ b/hw/sun4c_intctl.c @@ -22,10 +22,10 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "sun4m.h" +#include "hw/hw.h" +#include "hw/sun4m.h" #include "monitor/monitor.h" -#include "sysbus.h" +#include "hw/sysbus.h" //#define DEBUG_IRQ_COUNT //#define DEBUG_IRQ @@ -193,7 +193,7 @@ static void sun4c_intctl_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_sun4c_intctl; } -static TypeInfo sun4c_intctl_info = { +static const TypeInfo sun4c_intctl_info = { .name = "sun4c_intctl", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Sun4c_INTCTLState), diff --git a/hw/sun4m.h b/hw/sun4m.h index 47eb945f07..0d2cfb807b 100644 --- a/hw/sun4m.h +++ b/hw/sun4m.h @@ -27,10 +27,10 @@ void slavio_pic_info(Monitor *mon, DeviceState *dev); void slavio_irq_info(Monitor *mon, DeviceState *dev); /* sun4m.c */ -void sun4m_pic_info(Monitor *mon); -void sun4m_irq_info(Monitor *mon); +void sun4m_pic_info(Monitor *mon, const QDict *qdict); +void sun4m_irq_info(Monitor *mon, const QDict *qdict); /* sparc32_dma.c */ -#include "sparc32_dma.h" +#include "hw/sparc32_dma.h" #endif diff --git a/hw/sun4m_iommu.c b/hw/sun4m_iommu.c index ce6819e10b..33e77b02a3 100644 --- a/hw/sun4m_iommu.c +++ b/hw/sun4m_iommu.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#include "sun4m.h" -#include "sysbus.h" +#include "hw/sun4m.h" +#include "hw/sysbus.h" #include "trace.h" /* @@ -373,7 +373,7 @@ static void iommu_class_init(ObjectClass *klass, void *data) dc->props = iommu_properties; } -static TypeInfo iommu_info = { +static const TypeInfo iommu_info = { .name = "iommu", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IOMMUState), diff --git a/hw/sysbus.c b/hw/sysbus.c index 49a41775f8..702fc728f4 100644 --- a/hw/sysbus.c +++ b/hw/sysbus.c @@ -17,7 +17,7 @@ * License along with this library; if not, see . */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "monitor/monitor.h" #include "exec/address-spaces.h" @@ -48,7 +48,8 @@ void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq) } } -void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr) +static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr, + bool may_overlap, unsigned priority) { assert(n >= 0 && n < dev->num_mmio); @@ -61,11 +62,29 @@ void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr) memory_region_del_subregion(get_system_memory(), dev->mmio[n].memory); } dev->mmio[n].addr = addr; - memory_region_add_subregion(get_system_memory(), - addr, - dev->mmio[n].memory); + if (may_overlap) { + memory_region_add_subregion_overlap(get_system_memory(), + addr, + dev->mmio[n].memory, + priority); + } + else { + memory_region_add_subregion(get_system_memory(), + addr, + dev->mmio[n].memory); + } } +void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr) +{ + sysbus_mmio_map_common(dev, n, addr, false, 0); +} + +void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr, + unsigned priority) +{ + sysbus_mmio_map_common(dev, n, addr, true, priority); +} /* Request an IRQ source. The actual IRQ object may be populated later. */ void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p) @@ -131,7 +150,7 @@ DeviceState *sysbus_create_varargs(const char *name, int n; dev = qdev_create(NULL, name); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); qdev_init_nofail(dev); if (addr != (hwaddr)-1) { sysbus_mmio_map(s, 0, addr); @@ -163,7 +182,7 @@ DeviceState *sysbus_try_create_varargs(const char *name, if (!dev) { return NULL; } - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); qdev_init_nofail(dev); if (addr != (hwaddr)-1) { sysbus_mmio_map(s, 0, addr); @@ -184,7 +203,7 @@ DeviceState *sysbus_try_create_varargs(const char *name, static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent) { - SysBusDevice *s = sysbus_from_qdev(dev); + SysBusDevice *s = SYS_BUS_DEVICE(dev); hwaddr size; int i; @@ -198,7 +217,7 @@ static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent) static char *sysbus_get_fw_dev_path(DeviceState *dev) { - SysBusDevice *s = sysbus_from_qdev(dev); + SysBusDevice *s = SYS_BUS_DEVICE(dev); char path[40]; int off; @@ -255,7 +274,7 @@ static void sysbus_device_class_init(ObjectClass *klass, void *data) k->bus_type = TYPE_SYSTEM_BUS; } -static TypeInfo sysbus_device_type_info = { +static const TypeInfo sysbus_device_type_info = { .name = TYPE_SYS_BUS_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(SysBusDevice), diff --git a/hw/sysbus.h b/hw/sysbus.h index 669cf87ae9..5d90a52af5 100644 --- a/hw/sysbus.h +++ b/hw/sysbus.h @@ -3,7 +3,7 @@ /* Devices attached directly to the main system bus. */ -#include "qdev.h" +#include "hw/qdev.h" #include "exec/memory.h" #define QDEV_MAX_MMIO 32 @@ -44,7 +44,6 @@ struct SysBusDevice { }; /* Macros to compensate for lack of type inheritance in C. */ -#define sysbus_from_qdev(dev) ((SysBusDevice *)(dev)) #define FROM_SYSBUS(type, dev) DO_UPCAST(type, busdev, dev) void *sysbus_new(void); @@ -57,6 +56,8 @@ void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size); void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq); void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr); +void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr, + unsigned priority); void sysbus_add_memory(SysBusDevice *dev, hwaddr addr, MemoryRegion *mem); void sysbus_add_memory_overlap(SysBusDevice *dev, hwaddr addr, diff --git a/hw/tc58128.c b/hw/tc58128.c index 4ce80b18f3..f76f96d9e7 100644 --- a/hw/tc58128.c +++ b/hw/tc58128.c @@ -1,6 +1,6 @@ -#include "hw.h" -#include "sh.h" -#include "loader.h" +#include "hw/hw.h" +#include "hw/sh.h" +#include "hw/loader.h" #define CE1 0x0100 #define CE2 0x0200 diff --git a/hw/tc6393xb.c b/hw/tc6393xb.c index e815f83198..0755463a1d 100644 --- a/hw/tc6393xb.c +++ b/hw/tc6393xb.c @@ -10,9 +10,9 @@ * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "devices.h" -#include "flash.h" +#include "hw/hw.h" +#include "hw/devices.h" +#include "hw/flash.h" #include "ui/console.h" #include "ui/pixel_ops.h" #include "sysemu/blockdev.h" @@ -421,15 +421,15 @@ static void tc6393xb_nand_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) } #define BITS 8 -#include "tc6393xb_template.h" +#include "hw/tc6393xb_template.h" #define BITS 15 -#include "tc6393xb_template.h" +#include "hw/tc6393xb_template.h" #define BITS 16 -#include "tc6393xb_template.h" +#include "hw/tc6393xb_template.h" #define BITS 24 -#include "tc6393xb_template.h" +#include "hw/tc6393xb_template.h" #define BITS 32 -#include "tc6393xb_template.h" +#include "hw/tc6393xb_template.h" static void tc6393xb_draw_graphic(TC6393xbState *s, int full_update) { diff --git a/hw/tcx.c b/hw/tcx.c index 185588b49c..896407d865 100644 --- a/hw/tcx.c +++ b/hw/tcx.c @@ -25,8 +25,8 @@ #include "qemu-common.h" #include "ui/console.h" #include "ui/pixel_ops.h" -#include "sysbus.h" -#include "qdev-addr.h" +#include "hw/sysbus.h" +#include "hw/qdev-addr.h" #define MAXX 1024 #define MAXY 768 @@ -716,7 +716,7 @@ static void tcx_class_init(ObjectClass *klass, void *data) dc->props = tcx_properties; } -static TypeInfo tcx_info = { +static const TypeInfo tcx_info = { .name = "SUNW,tcx", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(TCXState), diff --git a/hw/tmp105.c b/hw/tmp105.c index 8e8dbd94eb..47e5437e0d 100644 --- a/hw/tmp105.c +++ b/hw/tmp105.c @@ -18,22 +18,10 @@ * with this program; if not, see . */ -#include "hw.h" -#include "i2c.h" - -typedef struct { - I2CSlave i2c; - uint8_t len; - uint8_t buf[2]; - qemu_irq pin; - - uint8_t pointer; - uint8_t config; - int16_t temperature; - int16_t limit[2]; - int faults; - uint8_t alarm; -} TMP105State; +#include "hw/hw.h" +#include "hw/i2c.h" +#include "hw/tmp105.h" +#include "qapi/visitor.h" static void tmp105_interrupt_update(TMP105State *s) { @@ -64,15 +52,30 @@ static void tmp105_alarm_update(TMP105State *s) tmp105_interrupt_update(s); } -/* Units are 0.001 centigrades relative to 0 C. */ -void tmp105_set(I2CSlave *i2c, int temp) +static void tmp105_get_temperature(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) { - TMP105State *s = (TMP105State *) i2c; + TMP105State *s = TMP105(obj); + int64_t value = s->temperature; + visit_type_int(v, &value, name, errp); +} + +/* Units are 0.001 centigrades relative to 0 C. */ +static void tmp105_set_temperature(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + TMP105State *s = TMP105(obj); + int64_t temp; + + visit_type_int(v, &temp, name, errp); + if (error_is_set(errp)) { + return; + } if (temp >= 128000 || temp < -128000) { - fprintf(stderr, "%s: values is out of range (%i.%03i C)\n", - __FUNCTION__, temp / 1000, temp % 1000); - exit(-1); + error_setg(errp, "value %" PRId64 ".%03" PRIu64 " °C is out of range", + temp / 1000, temp % 1000); + return; } s->temperature = ((int16_t) (temp * 0x800 / 128000)) << 4; @@ -92,22 +95,22 @@ static void tmp105_read(TMP105State *s) } switch (s->pointer & 3) { - case 0: /* Temperature */ + case TMP105_REG_TEMPERATURE: s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8); s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) & (0xf0 << ((~s->config >> 5) & 3)); /* R */ break; - case 1: /* Configuration */ + case TMP105_REG_CONFIG: s->buf[s->len ++] = s->config; break; - case 2: /* T_LOW */ + case TMP105_REG_T_LOW: s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8; s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0; break; - case 3: /* T_HIGH */ + case TMP105_REG_T_HIGH: s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8; s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0; break; @@ -117,10 +120,10 @@ static void tmp105_read(TMP105State *s) static void tmp105_write(TMP105State *s) { switch (s->pointer & 3) { - case 0: /* Temperature */ + case TMP105_REG_TEMPERATURE: break; - case 1: /* Configuration */ + case TMP105_REG_CONFIG: if (s->buf[0] & ~s->config & (1 << 0)) /* SD */ printf("%s: TMP105 shutdown\n", __FUNCTION__); s->config = s->buf[0]; @@ -128,8 +131,8 @@ static void tmp105_write(TMP105State *s) tmp105_alarm_update(s); break; - case 2: /* T_LOW */ - case 3: /* T_HIGH */ + case TMP105_REG_T_LOW: + case TMP105_REG_T_HIGH: if (s->len >= 3) s->limit[s->pointer & 1] = (int16_t) ((((uint16_t) s->buf[0]) << 8) | s->buf[1]); @@ -140,23 +143,27 @@ static void tmp105_write(TMP105State *s) static int tmp105_rx(I2CSlave *i2c) { - TMP105State *s = (TMP105State *) i2c; + TMP105State *s = TMP105(i2c); - if (s->len < 2) + if (s->len < 2) { return s->buf[s->len ++]; - else + } else { return 0xff; + } } static int tmp105_tx(I2CSlave *i2c, uint8_t data) { - TMP105State *s = (TMP105State *) i2c; + TMP105State *s = TMP105(i2c); - if (!s->len ++) + if (s->len == 0) { s->pointer = data; - else { - if (s->len <= 2) + s->len++; + } else { + if (s->len <= 2) { s->buf[s->len - 1] = data; + } + s->len++; tmp105_write(s); } @@ -165,10 +172,11 @@ static int tmp105_tx(I2CSlave *i2c, uint8_t data) static void tmp105_event(I2CSlave *i2c, enum i2c_event event) { - TMP105State *s = (TMP105State *) i2c; + TMP105State *s = TMP105(i2c); - if (event == I2C_START_RECV) + if (event == I2C_START_RECV) { tmp105_read(s); + } s->len = 0; } @@ -204,7 +212,7 @@ static const VMStateDescription vmstate_tmp105 = { static void tmp105_reset(I2CSlave *i2c) { - TMP105State *s = (TMP105State *) i2c; + TMP105State *s = TMP105(i2c); s->temperature = 0; s->pointer = 0; @@ -217,7 +225,7 @@ static void tmp105_reset(I2CSlave *i2c) static int tmp105_init(I2CSlave *i2c) { - TMP105State *s = FROM_I2C_SLAVE(TMP105State, i2c); + TMP105State *s = TMP105(i2c); qdev_init_gpio_out(&i2c->qdev, &s->pin, 1); @@ -226,6 +234,13 @@ static int tmp105_init(I2CSlave *i2c) return 0; } +static void tmp105_initfn(Object *obj) +{ + object_property_add(obj, "temperature", "int", + tmp105_get_temperature, + tmp105_set_temperature, NULL, NULL, NULL); +} + static void tmp105_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -238,10 +253,11 @@ static void tmp105_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_tmp105; } -static TypeInfo tmp105_info = { - .name = "tmp105", +static const TypeInfo tmp105_info = { + .name = TYPE_TMP105, .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(TMP105State), + .instance_init = tmp105_initfn, .class_init = tmp105_class_init, }; diff --git a/hw/tmp105.h b/hw/tmp105.h new file mode 100644 index 0000000000..9a9632c54b --- /dev/null +++ b/hw/tmp105.h @@ -0,0 +1,47 @@ +/* + * Texas Instruments TMP105 Temperature Sensor + * + * Browse the data sheet: + * + * http://www.ti.com/lit/gpn/tmp105 + * + * Copyright (C) 2012 Alex Horn + * Copyright (C) 2008-2012 Andrzej Zaborowski + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ +#ifndef QEMU_TMP105_H +#define QEMU_TMP105_H + +#include "hw/i2c.h" +#include "hw/tmp105_regs.h" + +#define TYPE_TMP105 "tmp105" +#define TMP105(obj) OBJECT_CHECK(TMP105State, (obj), TYPE_TMP105) + +/** + * TMP105State: + * @config: Bits 5 and 6 (value 32 and 64) determine the precision of the + * temperature. See Table 8 in the data sheet. + * + * @see_also: http://www.ti.com/lit/gpn/tmp105 + */ +typedef struct TMP105State { + /*< private >*/ + I2CSlave i2c; + /*< public >*/ + + uint8_t len; + uint8_t buf[2]; + qemu_irq pin; + + uint8_t pointer; + uint8_t config; + int16_t temperature; + int16_t limit[2]; + int faults; + uint8_t alarm; +} TMP105State; + +#endif diff --git a/hw/tmp105_regs.h b/hw/tmp105_regs.h new file mode 100644 index 0000000000..9b55abaf90 --- /dev/null +++ b/hw/tmp105_regs.h @@ -0,0 +1,50 @@ +/* + * Texas Instruments TMP105 Temperature Sensor I2C messages + * + * Browse the data sheet: + * + * http://www.ti.com/lit/gpn/tmp105 + * + * Copyright (C) 2012 Alex Horn + * Copyright (C) 2008-2012 Andrzej Zaborowski + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ +#ifndef QEMU_TMP105_MSGS_H +#define QEMU_TMP105_MSGS_H + +/** + * TMP105Reg: + * @TMP105_REG_TEMPERATURE: Temperature register + * @TMP105_REG_CONFIG: Configuration register + * @TMP105_REG_T_LOW: Low temperature register (also known as T_hyst) + * @TMP105_REG_T_HIGH: High temperature register (also known as T_OS) + * + * The following temperature sensors are + * compatible with the TMP105 registers: + * - adt75 + * - ds1775 + * - ds75 + * - lm75 + * - lm75a + * - max6625 + * - max6626 + * - mcp980x + * - stds75 + * - tcn75 + * - tmp100 + * - tmp101 + * - tmp105 + * - tmp175 + * - tmp275 + * - tmp75 + **/ +typedef enum TMP105Reg { + TMP105_REG_TEMPERATURE = 0, + TMP105_REG_CONFIG, + TMP105_REG_T_LOW, + TMP105_REG_T_HIGH, +} TMP105Reg; + +#endif diff --git a/hw/tpci200.c b/hw/tpci200.c new file mode 100644 index 0000000000..e3408ef4ba --- /dev/null +++ b/hw/tpci200.c @@ -0,0 +1,671 @@ +/* + * QEMU TEWS TPCI200 IndustryPack carrier emulation + * + * Copyright (C) 2012 Igalia, S.L. + * Author: Alberto Garcia + * + * This code is licensed under the GNU GPL v2 or (at your option) any + * later version. + */ + +#include "hw/ipack.h" +#include "hw/pci/pci.h" +#include "qemu/bitops.h" +#include + +/* #define DEBUG_TPCI */ + +#ifdef DEBUG_TPCI +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, "TPCI200: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do { } while (0) +#endif + +#define N_MODULES 4 + +#define IP_ID_SPACE 2 +#define IP_INT_SPACE 3 +#define IP_IO_SPACE_ADDR_MASK 0x7F +#define IP_ID_SPACE_ADDR_MASK 0x3F +#define IP_INT_SPACE_ADDR_MASK 0x3F + +#define STATUS_INT(IP, INTNO) BIT((IP) * 2 + (INTNO)) +#define STATUS_TIME(IP) BIT((IP) + 12) +#define STATUS_ERR_ANY 0xF00 + +#define CTRL_CLKRATE BIT(0) +#define CTRL_RECOVER BIT(1) +#define CTRL_TIME_INT BIT(2) +#define CTRL_ERR_INT BIT(3) +#define CTRL_INT_EDGE(INTNO) BIT(4 + (INTNO)) +#define CTRL_INT(INTNO) BIT(6 + (INTNO)) + +#define REG_REV_ID 0x00 +#define REG_IP_A_CTRL 0x02 +#define REG_IP_B_CTRL 0x04 +#define REG_IP_C_CTRL 0x06 +#define REG_IP_D_CTRL 0x08 +#define REG_RESET 0x0A +#define REG_STATUS 0x0C +#define IP_N_FROM_REG(REG) ((REG) / 2 - 1) + +typedef struct { + PCIDevice dev; + IPackBus bus; + MemoryRegion mmio; + MemoryRegion io; + MemoryRegion las0; + MemoryRegion las1; + MemoryRegion las2; + MemoryRegion las3; + bool big_endian[3]; + uint8_t ctrl[N_MODULES]; + uint16_t status; + uint8_t int_set; +} TPCI200State; + +#define TYPE_TPCI200 "tpci200" + +#define TPCI200(obj) \ + OBJECT_CHECK(TPCI200State, (obj), TYPE_TPCI200) + +static const uint8_t local_config_regs[] = { + 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x08, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x60, 0x41, 0xD4, + 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x01, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x08, 0x01, 0x02, + 0x00, 0x04, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x02, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x7A, 0x00, 0x52, 0x92, 0x24, 0x02 +}; + +static void adjust_addr(bool big_endian, hwaddr *addr, unsigned size) +{ + /* During 8 bit access in big endian mode, + odd and even addresses are swapped */ + if (big_endian && size == 1) { + *addr ^= 1; + } +} + +static uint64_t adjust_value(bool big_endian, uint64_t *val, unsigned size) +{ + /* Local spaces only support 8/16 bit access, + * so there's no need to care for sizes > 2 */ + if (big_endian && size == 2) { + *val = bswap16(*val); + } + return *val; +} + +static void tpci200_set_irq(void *opaque, int intno, int level) +{ + IPackDevice *ip = opaque; + IPackBus *bus = IPACK_BUS(qdev_get_parent_bus(DEVICE(ip))); + PCIDevice *pcidev = PCI_DEVICE(BUS(bus)->parent); + TPCI200State *dev = TPCI200(pcidev); + unsigned ip_n = ip->slot; + uint16_t prev_status = dev->status; + + assert(ip->slot >= 0 && ip->slot < N_MODULES); + + /* The requested interrupt must be enabled in the IP CONTROL + * register */ + if (!(dev->ctrl[ip_n] & CTRL_INT(intno))) { + return; + } + + /* Update the interrupt status in the IP STATUS register */ + if (level) { + dev->status |= STATUS_INT(ip_n, intno); + } else { + dev->status &= ~STATUS_INT(ip_n, intno); + } + + /* Return if there are no changes */ + if (dev->status == prev_status) { + return; + } + + DPRINTF("IP %u INT%u#: %u\n", ip_n, intno, level); + + /* Check if the interrupt is edge sensitive */ + if (dev->ctrl[ip_n] & CTRL_INT_EDGE(intno)) { + if (level) { + qemu_set_irq(dev->dev.irq[0], !dev->int_set); + qemu_set_irq(dev->dev.irq[0], dev->int_set); + } + } else { + unsigned i, j; + uint16_t level_status = dev->status; + + /* Check if there are any level sensitive interrupts set by + removing the ones that are edge sensitive from the status + register */ + for (i = 0; i < N_MODULES; i++) { + for (j = 0; j < 2; j++) { + if (dev->ctrl[i] & CTRL_INT_EDGE(j)) { + level_status &= ~STATUS_INT(i, j); + } + } + } + + if (level_status && !dev->int_set) { + qemu_irq_raise(dev->dev.irq[0]); + dev->int_set = 1; + } else if (!level_status && dev->int_set) { + qemu_irq_lower(dev->dev.irq[0]); + dev->int_set = 0; + } + } +} + +static uint64_t tpci200_read_cfg(void *opaque, hwaddr addr, unsigned size) +{ + TPCI200State *s = opaque; + uint8_t ret = 0; + if (addr < ARRAY_SIZE(local_config_regs)) { + ret = local_config_regs[addr]; + } + /* Endianness is stored in the first bit of these registers */ + if ((addr == 0x2b && s->big_endian[0]) || + (addr == 0x2f && s->big_endian[1]) || + (addr == 0x33 && s->big_endian[2])) { + ret |= 1; + } + DPRINTF("Read from LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) ret); + return ret; +} + +static void tpci200_write_cfg(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + TPCI200State *s = opaque; + /* Endianness is stored in the first bit of these registers */ + if (addr == 0x2b || addr == 0x2f || addr == 0x33) { + unsigned las = (addr - 0x2b) / 4; + s->big_endian[las] = val & 1; + DPRINTF("LAS%u big endian mode: %u\n", las, (unsigned) val & 1); + } else { + DPRINTF("Write to LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) val); + } +} + +static uint64_t tpci200_read_las0(void *opaque, hwaddr addr, unsigned size) +{ + TPCI200State *s = opaque; + uint64_t ret = 0; + + switch (addr) { + + case REG_REV_ID: + DPRINTF("Read REVISION ID\n"); /* Current value is 0x00 */ + break; + + case REG_IP_A_CTRL: + case REG_IP_B_CTRL: + case REG_IP_C_CTRL: + case REG_IP_D_CTRL: + { + unsigned ip_n = IP_N_FROM_REG(addr); + ret = s->ctrl[ip_n]; + DPRINTF("Read IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) ret); + } + break; + + case REG_RESET: + DPRINTF("Read RESET\n"); /* Not implemented */ + break; + + case REG_STATUS: + ret = s->status; + DPRINTF("Read STATUS: 0x%x\n", (unsigned) ret); + break; + + /* Reserved */ + default: + DPRINTF("Unsupported read from LAS0 0x%x\n", (unsigned) addr); + break; + } + + return adjust_value(s->big_endian[0], &ret, size); +} + +static void tpci200_write_las0(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + TPCI200State *s = opaque; + + adjust_value(s->big_endian[0], &val, size); + + switch (addr) { + + case REG_REV_ID: + DPRINTF("Write Revision ID: 0x%x\n", (unsigned) val); /* No effect */ + break; + + case REG_IP_A_CTRL: + case REG_IP_B_CTRL: + case REG_IP_C_CTRL: + case REG_IP_D_CTRL: + { + unsigned ip_n = IP_N_FROM_REG(addr); + s->ctrl[ip_n] = val; + DPRINTF("Write IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) val); + } + break; + + case REG_RESET: + DPRINTF("Write RESET: 0x%x\n", (unsigned) val); /* Not implemented */ + break; + + case REG_STATUS: + { + unsigned i; + + for (i = 0; i < N_MODULES; i++) { + IPackDevice *ip = ipack_device_find(&s->bus, i); + + if (ip != NULL) { + if (val & STATUS_INT(i, 0)) { + DPRINTF("Clear IP %c INT0# status\n", 'A' + i); + qemu_irq_lower(ip->irq[0]); + } + if (val & STATUS_INT(i, 1)) { + DPRINTF("Clear IP %c INT1# status\n", 'A' + i); + qemu_irq_lower(ip->irq[1]); + } + } + + if (val & STATUS_TIME(i)) { + DPRINTF("Clear IP %c timeout\n", 'A' + i); + s->status &= ~STATUS_TIME(i); + } + } + + if (val & STATUS_ERR_ANY) { + DPRINTF("Unexpected write to STATUS register: 0x%x\n", + (unsigned) val); + } + } + break; + + /* Reserved */ + default: + DPRINTF("Unsupported write to LAS0 0x%x: 0x%x\n", + (unsigned) addr, (unsigned) val); + break; + } +} + +static uint64_t tpci200_read_las1(void *opaque, hwaddr addr, unsigned size) +{ + TPCI200State *s = opaque; + IPackDevice *ip; + uint64_t ret = 0; + unsigned ip_n, space; + uint8_t offset; + + adjust_addr(s->big_endian[1], &addr, size); + + /* + * The address is divided into the IP module number (0-4), the IP + * address space (I/O, ID, INT) and the offset within that space. + */ + ip_n = addr >> 8; + space = (addr >> 6) & 3; + ip = ipack_device_find(&s->bus, ip_n); + + if (ip == NULL) { + DPRINTF("Read LAS1: IP module %u not installed\n", ip_n); + } else { + IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); + switch (space) { + + case IP_ID_SPACE: + offset = addr & IP_ID_SPACE_ADDR_MASK; + if (k->id_read) { + ret = k->id_read(ip, offset); + } + break; + + case IP_INT_SPACE: + offset = addr & IP_INT_SPACE_ADDR_MASK; + + /* Read address 0 to ACK IP INT0# and address 2 to ACK IP INT1# */ + if (offset == 0 || offset == 2) { + unsigned intno = offset / 2; + bool int_set = s->status & STATUS_INT(ip_n, intno); + bool int_edge_sensitive = s->ctrl[ip_n] & CTRL_INT_EDGE(intno); + if (int_set && !int_edge_sensitive) { + qemu_irq_lower(ip->irq[intno]); + } + } + + if (k->int_read) { + ret = k->int_read(ip, offset); + } + break; + + default: + offset = addr & IP_IO_SPACE_ADDR_MASK; + if (k->io_read) { + ret = k->io_read(ip, offset); + } + break; + } + } + + return adjust_value(s->big_endian[1], &ret, size); +} + +static void tpci200_write_las1(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + TPCI200State *s = opaque; + IPackDevice *ip; + unsigned ip_n, space; + uint8_t offset; + + adjust_addr(s->big_endian[1], &addr, size); + adjust_value(s->big_endian[1], &val, size); + + /* + * The address is divided into the IP module number, the IP + * address space (I/O, ID, INT) and the offset within that space. + */ + ip_n = addr >> 8; + space = (addr >> 6) & 3; + ip = ipack_device_find(&s->bus, ip_n); + + if (ip == NULL) { + DPRINTF("Write LAS1: IP module %u not installed\n", ip_n); + } else { + IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); + switch (space) { + + case IP_ID_SPACE: + offset = addr & IP_ID_SPACE_ADDR_MASK; + if (k->id_write) { + k->id_write(ip, offset, val); + } + break; + + case IP_INT_SPACE: + offset = addr & IP_INT_SPACE_ADDR_MASK; + if (k->int_write) { + k->int_write(ip, offset, val); + } + break; + + default: + offset = addr & IP_IO_SPACE_ADDR_MASK; + if (k->io_write) { + k->io_write(ip, offset, val); + } + break; + } + } +} + +static uint64_t tpci200_read_las2(void *opaque, hwaddr addr, unsigned size) +{ + TPCI200State *s = opaque; + IPackDevice *ip; + uint64_t ret = 0; + unsigned ip_n; + uint32_t offset; + + adjust_addr(s->big_endian[2], &addr, size); + + /* + * The address is divided into the IP module number and the offset + * within the IP module MEM space. + */ + ip_n = addr >> 23; + offset = addr & 0x7fffff; + ip = ipack_device_find(&s->bus, ip_n); + + if (ip == NULL) { + DPRINTF("Read LAS2: IP module %u not installed\n", ip_n); + } else { + IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); + if (k->mem_read16) { + ret = k->mem_read16(ip, offset); + } + } + + return adjust_value(s->big_endian[2], &ret, size); +} + +static void tpci200_write_las2(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + TPCI200State *s = opaque; + IPackDevice *ip; + unsigned ip_n; + uint32_t offset; + + adjust_addr(s->big_endian[2], &addr, size); + adjust_value(s->big_endian[2], &val, size); + + /* + * The address is divided into the IP module number and the offset + * within the IP module MEM space. + */ + ip_n = addr >> 23; + offset = addr & 0x7fffff; + ip = ipack_device_find(&s->bus, ip_n); + + if (ip == NULL) { + DPRINTF("Write LAS2: IP module %u not installed\n", ip_n); + } else { + IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); + if (k->mem_write16) { + k->mem_write16(ip, offset, val); + } + } +} + +static uint64_t tpci200_read_las3(void *opaque, hwaddr addr, unsigned size) +{ + TPCI200State *s = opaque; + IPackDevice *ip; + uint64_t ret = 0; + /* + * The address is divided into the IP module number and the offset + * within the IP module MEM space. + */ + unsigned ip_n = addr >> 22; + uint32_t offset = addr & 0x3fffff; + + ip = ipack_device_find(&s->bus, ip_n); + + if (ip == NULL) { + DPRINTF("Read LAS3: IP module %u not installed\n", ip_n); + } else { + IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); + if (k->mem_read8) { + ret = k->mem_read8(ip, offset); + } + } + + return ret; +} + +static void tpci200_write_las3(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + TPCI200State *s = opaque; + IPackDevice *ip; + /* + * The address is divided into the IP module number and the offset + * within the IP module MEM space. + */ + unsigned ip_n = addr >> 22; + uint32_t offset = addr & 0x3fffff; + + ip = ipack_device_find(&s->bus, ip_n); + + if (ip == NULL) { + DPRINTF("Write LAS3: IP module %u not installed\n", ip_n); + } else { + IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); + if (k->mem_write8) { + k->mem_write8(ip, offset, val); + } + } +} + +static const MemoryRegionOps tpci200_cfg_ops = { + .read = tpci200_read_cfg, + .write = tpci200_write_cfg, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4 + }, + .impl = { + .min_access_size = 1, + .max_access_size = 1 + } +}; + +static const MemoryRegionOps tpci200_las0_ops = { + .read = tpci200_read_las0, + .write = tpci200_write_las0, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 2, + .max_access_size = 2 + } +}; + +static const MemoryRegionOps tpci200_las1_ops = { + .read = tpci200_read_las1, + .write = tpci200_write_las1, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 2 + } +}; + +static const MemoryRegionOps tpci200_las2_ops = { + .read = tpci200_read_las2, + .write = tpci200_write_las2, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 2 + } +}; + +static const MemoryRegionOps tpci200_las3_ops = { + .read = tpci200_read_las3, + .write = tpci200_write_las3, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 1 + } +}; + +static int tpci200_initfn(PCIDevice *pci_dev) +{ + TPCI200State *s = TPCI200(pci_dev); + uint8_t *c = s->dev.config; + + pci_set_word(c + PCI_COMMAND, 0x0003); + pci_set_word(c + PCI_STATUS, 0x0280); + + pci_set_byte(c + PCI_INTERRUPT_PIN, 0x01); /* Interrupt pin A */ + + pci_set_byte(c + PCI_CAPABILITY_LIST, 0x40); + pci_set_long(c + 0x40, 0x48014801); + pci_set_long(c + 0x48, 0x00024C06); + pci_set_long(c + 0x4C, 0x00000003); + + memory_region_init_io(&s->mmio, &tpci200_cfg_ops, + s, "tpci200_mmio", 128); + memory_region_init_io(&s->io, &tpci200_cfg_ops, + s, "tpci200_io", 128); + memory_region_init_io(&s->las0, &tpci200_las0_ops, + s, "tpci200_las0", 256); + memory_region_init_io(&s->las1, &tpci200_las1_ops, + s, "tpci200_las1", 1024); + memory_region_init_io(&s->las2, &tpci200_las2_ops, + s, "tpci200_las2", 1024*1024*32); + memory_region_init_io(&s->las3, &tpci200_las3_ops, + s, "tpci200_las3", 1024*1024*16); + pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); + pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io); + pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las0); + pci_register_bar(&s->dev, 3, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las1); + pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las2); + pci_register_bar(&s->dev, 5, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las3); + + ipack_bus_new_inplace(&s->bus, DEVICE(&s->dev), NULL, + N_MODULES, tpci200_set_irq); + + return 0; +} + +static void tpci200_exitfn(PCIDevice *pci_dev) +{ + TPCI200State *s = TPCI200(pci_dev); + + memory_region_destroy(&s->mmio); + memory_region_destroy(&s->io); + memory_region_destroy(&s->las0); + memory_region_destroy(&s->las1); + memory_region_destroy(&s->las2); + memory_region_destroy(&s->las3); +} + +static const VMStateDescription vmstate_tpci200 = { + .name = "tpci200", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, TPCI200State), + VMSTATE_BOOL_ARRAY(big_endian, TPCI200State, 3), + VMSTATE_UINT8_ARRAY(ctrl, TPCI200State, N_MODULES), + VMSTATE_UINT16(status, TPCI200State), + VMSTATE_UINT8(int_set, TPCI200State), + VMSTATE_END_OF_LIST() + } +}; + +static void tpci200_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = tpci200_initfn; + k->exit = tpci200_exitfn; + k->vendor_id = PCI_VENDOR_ID_TEWS; + k->device_id = PCI_DEVICE_ID_TEWS_TPCI200; + k->class_id = PCI_CLASS_BRIDGE_OTHER; + k->subsystem_vendor_id = PCI_VENDOR_ID_TEWS; + k->subsystem_id = 0x300A; + dc->desc = "TEWS TPCI200 IndustryPack carrier"; + dc->vmsd = &vmstate_tpci200; +} + +static const TypeInfo tpci200_info = { + .name = TYPE_TPCI200, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(TPCI200State), + .class_init = tpci200_class_init, +}; + +static void tpci200_register_types(void) +{ + type_register_static(&tpci200_info); +} + +type_init(tpci200_register_types) diff --git a/hw/tsc2005.c b/hw/tsc2005.c index 740ff86aa8..a771cd5e52 100644 --- a/hw/tsc2005.c +++ b/hw/tsc2005.c @@ -18,10 +18,10 @@ * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" #include "ui/console.h" -#include "devices.h" +#include "hw/devices.h" #define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - (p ? 12 : 10))) diff --git a/hw/tsc210x.c b/hw/tsc210x.c index 2076c355d2..b93e502e05 100644 --- a/hw/tsc210x.c +++ b/hw/tsc210x.c @@ -19,12 +19,12 @@ * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "audio/audio.h" #include "qemu/timer.h" #include "ui/console.h" -#include "omap.h" /* For I2SCodec and uWireSlave */ -#include "devices.h" +#include "hw/omap.h" /* For I2SCodec and uWireSlave */ +#include "hw/devices.h" #define TSC_DATA_REGISTERS_PAGE 0x0 #define TSC_CONTROL_REGISTERS_PAGE 0x1 diff --git a/hw/tusb6010.c b/hw/tusb6010.c index 990d50619d..a5251a34ac 100644 --- a/hw/tusb6010.c +++ b/hw/tusb6010.c @@ -20,11 +20,11 @@ */ #include "qemu-common.h" #include "qemu/timer.h" -#include "usb.h" -#include "omap.h" -#include "irq.h" -#include "devices.h" -#include "sysbus.h" +#include "hw/usb.h" +#include "hw/omap.h" +#include "hw/irq.h" +#include "hw/devices.h" +#include "hw/sysbus.h" typedef struct TUSBState { SysBusDevice busdev; @@ -740,7 +740,7 @@ static void tusb6010_irq(void *opaque, int source, int level) static void tusb6010_reset(DeviceState *dev) { - TUSBState *s = FROM_SYSBUS(TUSBState, sysbus_from_qdev(dev)); + TUSBState *s = FROM_SYSBUS(TUSBState, SYS_BUS_DEVICE(dev)); int i; s->test_reset = TUSB_PROD_TEST_RESET_VAL; @@ -798,7 +798,7 @@ static void tusb6010_class_init(ObjectClass *klass, void *data) dc->reset = tusb6010_reset; } -static TypeInfo tusb6010_info = { +static const TypeInfo tusb6010_info = { .name = "tusb6010", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(TUSBState), diff --git a/hw/twl92230.c b/hw/twl92230.c index c71e4a2af0..7d020c4cba 100644 --- a/hw/twl92230.c +++ b/hw/twl92230.c @@ -19,9 +19,9 @@ * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" -#include "i2c.h" +#include "hw/i2c.h" #include "sysemu/sysemu.h" #include "ui/console.h" @@ -867,7 +867,7 @@ static void twl92230_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_menelaus; } -static TypeInfo twl92230_info = { +static const TypeInfo twl92230_info = { .name = "twl92230", .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(MenelausState), diff --git a/hw/unicore32/Makefile.objs b/hw/unicore32/Makefile.objs index 0725ce3ca7..e0fd628523 100644 --- a/hw/unicore32/Makefile.objs +++ b/hw/unicore32/Makefile.objs @@ -2,5 +2,3 @@ # PKUnity-v3 SoC and board information obj-${CONFIG_PUV3} += puv3.o - -obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/puv3.c b/hw/unicore32/puv3.c similarity index 89% rename from hw/puv3.c rename to hw/unicore32/puv3.c index 7814bc5051..78ab13f9ed 100644 --- a/hw/puv3.c +++ b/hw/unicore32/puv3.c @@ -13,26 +13,27 @@ #include "ui/console.h" #include "elf.h" #include "exec/address-spaces.h" -#include "sysbus.h" -#include "boards.h" -#include "loader.h" -#include "pc.h" +#include "hw/sysbus.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/pc.h" #undef DEBUG_PUV3 -#include "puv3.h" +#include "hw/puv3.h" #define KERNEL_LOAD_ADDR 0x03000000 #define KERNEL_MAX_SIZE 0x00800000 /* Just a guess */ static void puv3_intc_cpu_handler(void *opaque, int irq, int level) { - CPUUniCore32State *env = opaque; + UniCore32CPU *cpu = opaque; + CPUState *cs = CPU(cpu); assert(irq == 0); if (level) { - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } @@ -44,7 +45,8 @@ static void puv3_soc_init(CPUUniCore32State *env) int i; /* Initialize interrupt controller */ - cpu_intc = qemu_allocate_irqs(puv3_intc_cpu_handler, env, 1); + cpu_intc = qemu_allocate_irqs(puv3_intc_cpu_handler, + uc32_env_get_cpu(env), 1); dev = sysbus_create_simple("puv3_intc", PUV3_INTC_BASE, *cpu_intc); for (i = 0; i < PUV3_IRQS_NR; i++) { irqs[i] = qdev_get_gpio_in(dev, i); @@ -124,6 +126,7 @@ static QEMUMachine puv3_machine = { .desc = "PKUnity Version-3 based on UniCore32", .init = puv3_init, .is_default = 1, + DEFAULT_MACHINE_OPTIONS, }; static void puv3_machine_init(void) diff --git a/hw/unin_pci.c b/hw/unin_pci.c index 46757924b6..cb95ad1f5e 100644 --- a/hw/unin_pci.c +++ b/hw/unin_pci.c @@ -21,10 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "ppc_mac.h" -#include "pci/pci.h" -#include "pci/pci_host.h" +#include "hw/hw.h" +#include "hw/ppc/mac.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" /* debug UniNorth */ //#define DEBUG_UNIN diff --git a/hw/usb.h b/hw/usb.h index 81e265c4fd..1b10684dde 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -25,7 +25,7 @@ * THE SOFTWARE. */ -#include "qdev.h" +#include "hw/qdev.h" #include "qemu/queue.h" /* Constants related to the USB / PCI interaction */ @@ -307,6 +307,12 @@ typedef struct USBDeviceClass { */ void (*flush_ep_queue)(USBDevice *dev, USBEndpoint *ep); + /* + * Called by the hcd to let the device know the queue for an endpoint + * has been unlinked / stopped. Optional may be NULL. + */ + void (*ep_stopped)(USBDevice *dev, USBEndpoint *ep); + const char *product_desc; const USBDesc *usb_desc; } USBDeviceClass; @@ -355,6 +361,7 @@ struct USBPacket { int pid; uint64_t id; USBEndpoint *ep; + unsigned int stream; QEMUIOVector iov; uint64_t parameter; /* control transfers */ bool short_not_ok; @@ -377,13 +384,15 @@ struct USBCombinedPacket { void usb_packet_init(USBPacket *p); void usb_packet_set_state(USBPacket *p, USBPacketState state); void usb_packet_check_state(USBPacket *p, USBPacketState expected); -void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id, - bool short_not_ok, bool int_req); +void usb_packet_setup(USBPacket *p, int pid, + USBEndpoint *ep, unsigned int stream, + uint64_t id, bool short_not_ok, bool int_req); void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len); int usb_packet_map(USBPacket *p, QEMUSGList *sgl); void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl); void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes); void usb_packet_skip(USBPacket *p, size_t bytes); +size_t usb_packet_size(USBPacket *p); void usb_packet_cleanup(USBPacket *p); static inline bool usb_packet_is_inflight(USBPacket *p) @@ -411,6 +420,7 @@ void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep, uint16_t raw); int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep); void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled); +void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted); USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, uint64_t id); @@ -422,14 +432,13 @@ void usb_attach(USBPort *port); void usb_detach(USBPort *port); void usb_port_reset(USBPort *port); void usb_device_reset(USBDevice *dev); -void usb_wakeup(USBEndpoint *ep); +void usb_wakeup(USBEndpoint *ep, unsigned int stream); void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); int set_usb_string(uint8_t *buf, const char *str); /* usb-linux.c */ USBDevice *usb_host_device_open(USBBus *bus, const char *devname); -int usb_host_device_close(const char *devname); -void usb_host_info(Monitor *mon); +void usb_host_info(Monitor *mon, const QDict *qdict); /* usb-bt.c */ USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci); @@ -482,7 +491,7 @@ struct USBBus { struct USBBusOps { int (*register_companion)(USBBus *bus, USBPort *ports[], uint32_t portcount, uint32_t firstport); - void (*wakeup_endpoint)(USBBus *bus, USBEndpoint *ep); + void (*wakeup_endpoint)(USBBus *bus, USBEndpoint *ep, unsigned int stream); }; void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host); @@ -539,11 +548,23 @@ void usb_device_set_interface(USBDevice *dev, int interface, void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep); +void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep); + const char *usb_device_get_product_desc(USBDevice *dev); const USBDesc *usb_device_get_usb_desc(USBDevice *dev); int ehci_create_ich9_with_companions(PCIBus *bus, int slot); -#endif +/* quirks.c */ +/* In bulk endpoints are streaming data sources (iow behave like isoc eps) */ +#define USB_QUIRK_BUFFER_BULK_IN 0x01 +/* Bulk pkts in FTDI format, need special handling when combining packets */ +#define USB_QUIRK_IS_FTDI 0x02 + +int usb_get_quirks(uint16_t vendor_id, uint16_t product_id, + uint8_t interface_class, uint8_t interface_subclass, + uint8_t interface_protocol); + +#endif diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index 5a4eeb6d13..e63e287ce0 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -1,14 +1,30 @@ +# usb subsystem core +common-obj-y += core.o combined-packet.o bus.o desc.o +common-obj-y += libhw.o + +# usb host adapters common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o hcd-ehci-sysbus.o common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o -common-obj-y += libhw.o -common-obj-$(CONFIG_SMARTCARD) += dev-smartcard-reader.o -common-obj-$(CONFIG_USB_REDIR) += redirect.o +# emulated usb devices +common-obj-y += dev-hub.o +common-obj-y += dev-hid.o +common-obj-$(CONFIG_USB_TABLET_WACOM) += dev-wacom.o +common-obj-$(CONFIG_USB_STORAGE_BOT) += dev-storage.o +common-obj-$(CONFIG_USB_STORAGE_UAS) += dev-uas.o +common-obj-$(CONFIG_USB_AUDIO) += dev-audio.o +common-obj-$(CONFIG_USB_SERIAL) += dev-serial.o +common-obj-$(CONFIG_USB_NETWORK) += dev-network.o -common-obj-y += core.o combined-packet.o bus.o desc.o dev-hub.o -common-obj-y += host-$(HOST_USB).o dev-bluetooth.o -common-obj-y += dev-hid.o dev-storage.o dev-wacom.o -common-obj-y += dev-serial.o dev-network.o dev-audio.o -common-obj-y += dev-uas.o +# FIXME: make configurable too +CONFIG_USB_BLUETOOTH := y +common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o +common-obj-$(CONFIG_USB_SMARTCARD) += dev-smartcard-reader.o + +# usb redirection +common-obj-$(CONFIG_USB_REDIR) += redirect.o quirks.o + +# usb pass-through +common-obj-y += $(patsubst %,host-%.o,$(HOST_USB)) diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 10260a13ac..e58cd9ade2 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -189,6 +189,14 @@ void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep) } } +void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep) +{ + USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); + if (klass->ep_stopped) { + klass->ep_stopped(dev, ep); + } +} + static int usb_qdev_init(DeviceState *qdev) { USBDevice *dev = USB_DEVICE(qdev); @@ -534,7 +542,7 @@ static char *usb_get_fw_dev_path(DeviceState *qdev) return fw_path; } -void usb_info(Monitor *mon) +void usb_info(Monitor *mon, const QDict *qdict) { USBBus *bus; USBDevice *dev; @@ -620,7 +628,7 @@ static void usb_device_class_init(ObjectClass *klass, void *data) k->props = usb_props; } -static TypeInfo usb_device_type_info = { +static const TypeInfo usb_device_type_info = { .name = TYPE_USB_DEVICE, .parent = TYPE_DEVICE, .instance_size = sizeof(USBDevice), diff --git a/hw/usb/core.c b/hw/usb/core.c index e315fc1021..15a150aea0 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -71,7 +71,7 @@ void usb_device_reset(USBDevice *dev) usb_device_handle_reset(dev); } -void usb_wakeup(USBEndpoint *ep) +void usb_wakeup(USBEndpoint *ep, unsigned int stream) { USBDevice *dev = ep->dev; USBBus *bus = usb_bus_from_device(dev); @@ -80,7 +80,7 @@ void usb_wakeup(USBEndpoint *ep) dev->port->ops->wakeup(dev->port); } if (bus->ops->wakeup_endpoint) { - bus->ops->wakeup_endpoint(bus, ep); + bus->ops->wakeup_endpoint(bus, ep, stream); } } @@ -545,14 +545,16 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state) p->state = state; } -void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id, - bool short_not_ok, bool int_req) +void usb_packet_setup(USBPacket *p, int pid, + USBEndpoint *ep, unsigned int stream, + uint64_t id, bool short_not_ok, bool int_req) { assert(!usb_packet_is_inflight(p)); assert(p->iov.iov != NULL); p->id = id; p->pid = pid; p->ep = ep; + p->stream = stream; p->status = USB_RET_SUCCESS; p->actual_length = 0; p->parameter = 0; @@ -570,15 +572,17 @@ void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len) void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes) { + QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov; + assert(p->actual_length >= 0); - assert(p->actual_length + bytes <= p->iov.size); + assert(p->actual_length + bytes <= iov->size); switch (p->pid) { case USB_TOKEN_SETUP: case USB_TOKEN_OUT: - iov_to_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes); + iov_to_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes); break; case USB_TOKEN_IN: - iov_from_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes); + iov_from_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes); break; default: fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid); @@ -589,14 +593,21 @@ void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes) void usb_packet_skip(USBPacket *p, size_t bytes) { + QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov; + assert(p->actual_length >= 0); - assert(p->actual_length + bytes <= p->iov.size); + assert(p->actual_length + bytes <= iov->size); if (p->pid == USB_TOKEN_IN) { - iov_memset(p->iov.iov, p->iov.niov, p->actual_length, 0, bytes); + iov_memset(iov->iov, iov->niov, p->actual_length, 0, bytes); } p->actual_length += bytes; } +size_t usb_packet_size(USBPacket *p) +{ + return p->combined ? p->combined->iov.size : p->iov.size; +} + void usb_packet_cleanup(USBPacket *p) { assert(!usb_packet_is_inflight(p)); @@ -755,13 +766,19 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled) uep->pipeline = enabled; } +void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted) +{ + struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); + uep->halted = halted; +} + USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, uint64_t id) { struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); USBPacket *p; - while ((p = QTAILQ_FIRST(&uep->queue)) != NULL) { + QTAILQ_FOREACH(p, &uep->queue, queue) { if (p->id == id) { return p; } diff --git a/hw/usb/desc.c b/hw/usb/desc.c index b7c32333d7..b389381326 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -225,12 +225,9 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, int flags, d->u.endpoint.bRefresh = ep->bRefresh; d->u.endpoint.bSynchAddress = ep->bSynchAddress; } - if (ep->extra) { - memcpy(dest + bLength, ep->extra, extralen); - } if (superlen) { - USBDescriptor *d = (void *)(dest + bLength + extralen); + USBDescriptor *d = (void *)(dest + bLength); d->bLength = 0x06; d->bDescriptorType = USB_DT_ENDPOINT_COMPANION; @@ -243,6 +240,10 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, int flags, usb_hi(ep->wBytesPerInterval); } + if (ep->extra) { + memcpy(dest + bLength + superlen, ep->extra, extralen); + } + return bLength + extralen + superlen; } diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c index b669601c92..b8c79b85e9 100644 --- a/hw/usb/dev-audio.c +++ b/hw/usb/dev-audio.c @@ -684,7 +684,7 @@ static void usb_audio_class_init(ObjectClass *klass, void *data) k->set_interface = usb_audio_set_interface; } -static TypeInfo usb_audio_info = { +static const TypeInfo usb_audio_info = { .name = "usb-audio", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBAudioState), diff --git a/hw/usb/dev-bluetooth.c b/hw/usb/dev-bluetooth.c index a0d7a88d91..0f8aa48c85 100644 --- a/hw/usb/dev-bluetooth.c +++ b/hw/usb/dev-bluetooth.c @@ -478,7 +478,7 @@ static void usb_bt_out_hci_packet_event(void *opaque, struct USBBtState *s = (struct USBBtState *) opaque; if (s->evt.len == 0) { - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } usb_bt_fifo_enqueue(&s->evt, data, len); } @@ -555,7 +555,7 @@ static void usb_bt_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_usb_bt; } -static TypeInfo bt_info = { +static const TypeInfo bt_info = { .name = "usb-bt-dongle", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(struct USBBtState), diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index ce38fef9f6..9701048887 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -423,7 +423,7 @@ static void usb_hid_changed(HIDState *hs) { USBHIDState *us = container_of(hs, USBHIDState, hid); - usb_wakeup(us->intr); + usb_wakeup(us->intr, 0); } static void usb_hid_handle_reset(USBDevice *dev) @@ -501,7 +501,7 @@ static void usb_hid_handle_control(USBDevice *dev, USBPacket *p, break; case SET_IDLE: hs->idle = (uint8_t) (value >> 8); - hid_set_next_idle(hs, qemu_get_clock_ns(vm_clock)); + hid_set_next_idle(hs); if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { hid_pointer_activate(hs); } @@ -523,16 +523,14 @@ static void usb_hid_handle_data(USBDevice *dev, USBPacket *p) switch (p->pid) { case USB_TOKEN_IN: if (p->ep->nr == 1) { - int64_t curtime = qemu_get_clock_ns(vm_clock); if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { hid_pointer_activate(hs); } - if (!hid_has_events(hs) && - (!hs->idle || hs->next_idle_clock - curtime > 0)) { + if (!hid_has_events(hs)) { p->status = USB_RET_NAK; return; } - hid_set_next_idle(hs, curtime); + hid_set_next_idle(hs); if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { len = hid_pointer_poll(hs, buf, p->iov.size); } else if (hs->kind == HID_KEYBOARD) { @@ -659,7 +657,7 @@ static void usb_tablet_class_initfn(ObjectClass *klass, void *data) dc->props = usb_tablet_properties; } -static TypeInfo usb_tablet_info = { +static const TypeInfo usb_tablet_info = { .name = "usb-tablet", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBHIDState), @@ -678,7 +676,7 @@ static void usb_mouse_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_usb_ptr; } -static TypeInfo usb_mouse_info = { +static const TypeInfo usb_mouse_info = { .name = "usb-mouse", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBHIDState), @@ -697,7 +695,7 @@ static void usb_keyboard_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_usb_kbd; } -static TypeInfo usb_keyboard_info = { +static const TypeInfo usb_keyboard_info = { .name = "usb-kbd", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBHIDState), diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 470fbbb86c..504c98c350 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -164,7 +164,7 @@ static void usb_hub_attach(USBPort *port1) } else { port->wPortStatus &= ~PORT_STAT_LOW_SPEED; } - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } static void usb_hub_detach(USBPort *port1) @@ -173,7 +173,7 @@ static void usb_hub_detach(USBPort *port1) USBHubPort *port = &s->ports[port1->index]; trace_usb_hub_detach(s->dev.addr, port1->index + 1); - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); /* Let upstream know the device on this port is gone */ s->dev.port->ops->child_detach(s->dev.port, port1->dev); @@ -184,7 +184,7 @@ static void usb_hub_detach(USBPort *port1) port->wPortStatus &= ~PORT_STAT_ENABLE; port->wPortChange |= PORT_STAT_C_ENABLE; } - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } static void usb_hub_child_detach(USBPort *port1, USBDevice *child) @@ -202,7 +202,7 @@ static void usb_hub_wakeup(USBPort *port1) if (port->wPortStatus & PORT_STAT_SUSPEND) { port->wPortChange |= PORT_STAT_C_SUSPEND; - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } } @@ -364,7 +364,7 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, port->wPortChange |= PORT_STAT_C_RESET; /* set enable bit */ port->wPortStatus |= PORT_STAT_ENABLE; - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } break; case PORT_POWER: @@ -568,7 +568,7 @@ static void usb_hub_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_usb_hub; } -static TypeInfo hub_info = { +static const TypeInfo hub_info = { .name = "usb-hub", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBHubState), diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 1c54863452..5473ac2cd5 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -27,6 +27,7 @@ #include "hw/usb.h" #include "hw/usb/desc.h" #include "net/net.h" +#include "qapi/qmp/qerror.h" #include "qemu/queue.h" #include "qemu/config-file.h" #include "sysemu/sysemu.h" @@ -855,7 +856,7 @@ static void *rndis_queue_response(USBNetState *s, unsigned int length) g_malloc0(sizeof(struct rndis_response) + length); if (QTAILQ_EMPTY(&s->rndis_resp)) { - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } QTAILQ_INSERT_TAIL(&s->rndis_resp, r, entries); @@ -1012,7 +1013,7 @@ static int rndis_keepalive_response(USBNetState *s, static void usb_net_reset_in_buf(USBNetState *s) { s->in_ptr = s->in_len = 0; - qemu_flush_queued_packets(&s->nic->nc); + qemu_flush_queued_packets(qemu_get_queue(s->nic)); } static int rndis_parse(USBNetState *s, uint8_t *data, int length) @@ -1196,7 +1197,7 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p) if (!is_rndis(s)) { if (p->iov.size < 64) { - qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr); + qemu_send_packet(qemu_get_queue(s->nic), s->out_buf, s->out_ptr); s->out_ptr = 0; } return; @@ -1209,7 +1210,7 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p) uint32_t offs = 8 + le32_to_cpu(msg->DataOffset); uint32_t size = le32_to_cpu(msg->DataLength); if (offs + size <= len) - qemu_send_packet(&s->nic->nc, s->out_buf + offs, size); + qemu_send_packet(qemu_get_queue(s->nic), s->out_buf + offs, size); } s->out_ptr -= len; memmove(s->out_buf, &s->out_buf[len], s->out_ptr); @@ -1261,7 +1262,7 @@ static void usb_net_handle_data(USBDevice *dev, USBPacket *p) static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + USBNetState *s = qemu_get_nic_opaque(nc); uint8_t *in_buf = s->in_buf; size_t total_size = size; @@ -1308,7 +1309,7 @@ static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t siz static int usbnet_can_receive(NetClientState *nc) { - USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + USBNetState *s = qemu_get_nic_opaque(nc); if (is_rndis(s) && s->rndis_state != RNDIS_DATA_INITIALIZED) { return 1; @@ -1319,7 +1320,7 @@ static int usbnet_can_receive(NetClientState *nc) static void usbnet_cleanup(NetClientState *nc) { - USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; + USBNetState *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -1330,7 +1331,7 @@ static void usb_net_handle_destroy(USBDevice *dev) /* TODO: remove the nd_table[] entry */ rndis_clear_responsequeue(s); - qemu_del_net_client(&s->nic->nc); + qemu_del_nic(s->nic); } static NetClientInfo net_usbnet_info = { @@ -1361,7 +1362,7 @@ static int usb_net_initfn(USBDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_usbnet_info, &s->conf, object_get_typename(OBJECT(s)), s->dev.qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); snprintf(s->usbstring_mac, sizeof(s->usbstring_mac), "%02x%02x%02x%02x%02x%02x", 0x40, @@ -1433,7 +1434,7 @@ static void usb_net_class_initfn(ObjectClass *klass, void *data) dc->props = net_properties; } -static TypeInfo net_info = { +static const TypeInfo net_info = { .name = "usb-net", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBNetState), diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 20cf5337b7..47ac8c9b69 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -600,7 +600,7 @@ static void usb_serial_class_initfn(ObjectClass *klass, void *data) dc->props = serial_properties; } -static TypeInfo serial_info = { +static const TypeInfo serial_info = { .name = "usb-serial", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBSerialState), @@ -628,7 +628,7 @@ static void usb_braille_class_initfn(ObjectClass *klass, void *data) dc->props = braille_properties; } -static TypeInfo braille_info = { +static const TypeInfo braille_info = { .name = "usb-braille", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBSerialState), diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index f26bb341f7..caebc1c3ff 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -839,7 +839,7 @@ static void ccid_on_slot_change(USBCCIDState *s, bool full) s->bmSlotICCState |= SLOT_0_CHANGED_MASK; } s->notify_slot_change = true; - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } static void ccid_write_data_block_error( @@ -1322,7 +1322,7 @@ static void ccid_class_initfn(ObjectClass *klass, void *data) dc->props = ccid_properties; } -static TypeInfo ccid_info = { +static const TypeInfo ccid_info = { .name = CCID_DEV_NAME, .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBCCIDState), @@ -1338,7 +1338,7 @@ static void ccid_card_class_init(ObjectClass *klass, void *data) k->props = ccid_props; } -static TypeInfo ccid_card_type_info = { +static const TypeInfo ccid_card_type_info = { .name = TYPE_CCID_CARD, .parent = TYPE_DEVICE, .instance_size = sizeof(CCIDCardState), diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 5025597673..d3f01aa2a7 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -54,12 +54,12 @@ typedef struct { struct usb_msd_csw csw; SCSIRequest *req; SCSIBus bus; - BlockConf conf; - char *serial; - SCSIDevice *scsi_dev; - uint32_t removable; /* For async completion. */ USBPacket *packet; + /* usb-storage only */ + BlockConf conf; + char *serial; + uint32_t removable; } MSDState; struct usb_msd_cbw { @@ -343,7 +343,8 @@ static void usb_msd_handle_control(USBDevice *dev, USBPacket *p, int request, int value, int index, int length, uint8_t *data) { MSDState *s = (MSDState *)dev; - int ret; + SCSIDevice *scsi_dev; + int ret, maxlun; ret = usb_desc_handle_control(dev, p, request, value, index, length, data); if (ret >= 0) { @@ -359,7 +360,19 @@ static void usb_msd_handle_control(USBDevice *dev, USBPacket *p, s->mode = USB_MSDM_CBW; break; case ClassInterfaceRequest | GetMaxLun: - data[0] = 0; + maxlun = 0; + for (;;) { + scsi_dev = scsi_device_find(&s->bus, 0, 0, maxlun+1); + if (scsi_dev == NULL) { + break; + } + if (scsi_dev->lun != maxlun+1) { + break; + } + maxlun++; + } + DPRINTF("MaxLun %d\n", maxlun); + data[0] = maxlun; p->actual_length = 1; break; default: @@ -386,6 +399,8 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) uint32_t tag; struct usb_msd_cbw cbw; uint8_t devep = p->ep->nr; + SCSIDevice *scsi_dev; + uint32_t len; switch (p->pid) { case USB_TOKEN_OUT: @@ -405,7 +420,8 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) goto fail; } DPRINTF("Command on LUN %d\n", cbw.lun); - if (cbw.lun != 0) { + scsi_dev = scsi_device_find(&s->bus, 0, 0, cbw.lun); + if (scsi_dev == NULL) { fprintf(stderr, "usb-msd: Bad LUN %d\n", cbw.lun); goto fail; } @@ -422,12 +438,12 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) tag, cbw.flags, cbw.cmd_len, s->data_len); assert(le32_to_cpu(s->csw.residue) == 0); s->scsi_len = 0; - s->req = scsi_req_new(s->scsi_dev, tag, 0, cbw.cmd, NULL); + s->req = scsi_req_new(scsi_dev, tag, cbw.lun, cbw.cmd, NULL); #ifdef DEBUG_MSD scsi_req_print(s->req); #endif - scsi_req_enqueue(s->req); - if (s->req && s->req->cmd.xfer != SCSI_XFER_NONE) { + len = scsi_req_enqueue(s->req); + if (len) { scsi_req_continue(s->req); } break; @@ -553,7 +569,7 @@ static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req) return NULL; } -static const struct SCSIBusInfo usb_msd_scsi_info = { +static const struct SCSIBusInfo usb_msd_scsi_info_storage = { .tcq = false, .max_target = 0, .max_lun = 0, @@ -564,10 +580,22 @@ static const struct SCSIBusInfo usb_msd_scsi_info = { .load_request = usb_msd_load_request, }; -static int usb_msd_initfn(USBDevice *dev) +static const struct SCSIBusInfo usb_msd_scsi_info_bot = { + .tcq = false, + .max_target = 0, + .max_lun = 15, + + .transfer_data = usb_msd_transfer_data, + .complete = usb_msd_command_complete, + .cancel = usb_msd_request_cancelled, + .load_request = usb_msd_load_request, +}; + +static int usb_msd_initfn_storage(USBDevice *dev) { MSDState *s = DO_UPCAST(MSDState, dev, dev); BlockDriverState *bs = s->conf.bs; + SCSIDevice *scsi_dev; if (!bs) { error_report("drive property not set"); @@ -595,10 +623,10 @@ static int usb_msd_initfn(USBDevice *dev) } usb_desc_init(dev); - scsi_bus_new(&s->bus, &s->dev.qdev, &usb_msd_scsi_info); - s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable, + scsi_bus_new(&s->bus, &s->dev.qdev, &usb_msd_scsi_info_storage); + scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable, s->conf.bootindex); - if (!s->scsi_dev) { + if (!scsi_dev) { return -1; } s->bus.qbus.allow_hotplug = 0; @@ -616,6 +644,19 @@ static int usb_msd_initfn(USBDevice *dev) return 0; } +static int usb_msd_initfn_bot(USBDevice *dev) +{ + MSDState *s = DO_UPCAST(MSDState, dev, dev); + + usb_desc_create_serial(dev); + usb_desc_init(dev); + scsi_bus_new(&s->bus, &s->dev.qdev, &usb_msd_scsi_info_bot); + s->bus.qbus.allow_hotplug = 0; + usb_msd_handle_reset(dev); + + return 0; +} + static USBDevice *usb_msd_init(USBBus *bus, const char *filename) { static int nr=0; @@ -698,12 +739,11 @@ static Property msd_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void usb_msd_class_initfn(ObjectClass *klass, void *data) +static void usb_msd_class_initfn_common(ObjectClass *klass) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); - uc->init = usb_msd_initfn; uc->product_desc = "QEMU USB MSD"; uc->usb_desc = &desc; uc->cancel_packet = usb_msd_cancel_io; @@ -713,19 +753,44 @@ static void usb_msd_class_initfn(ObjectClass *klass, void *data) uc->handle_data = usb_msd_handle_data; dc->fw_name = "storage"; dc->vmsd = &vmstate_usb_msd; - dc->props = msd_properties; } -static TypeInfo msd_info = { +static void usb_msd_class_initfn_storage(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + + uc->init = usb_msd_initfn_storage; + dc->props = msd_properties; + usb_msd_class_initfn_common(klass); +} + +static void usb_msd_class_initfn_bot(ObjectClass *klass, void *data) +{ + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + + uc->init = usb_msd_initfn_bot; + usb_msd_class_initfn_common(klass); +} + +static const TypeInfo msd_info = { .name = "usb-storage", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(MSDState), - .class_init = usb_msd_class_initfn, + .class_init = usb_msd_class_initfn_storage, +}; + +static const TypeInfo bot_info = { + .name = "usb-bot", + .parent = TYPE_USB_DEVICE, + .instance_size = sizeof(MSDState), + .class_init = usb_msd_class_initfn_bot, }; static void usb_msd_register_types(void) { type_register_static(&msd_info); + type_register_static(&bot_info); usb_legacy_register("usb-storage", "disk", usb_msd_init); } diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index 9a0088928f..1ac5117ba7 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -99,6 +99,9 @@ typedef struct { /* --------------------------------------------------------------------- */ +#define UAS_STREAM_BM_ATTR 4 +#define UAS_MAX_STREAMS (1 << UAS_STREAM_BM_ATTR) + typedef struct UASDevice UASDevice; typedef struct UASRequest UASRequest; typedef struct UASStatus UASStatus; @@ -106,12 +109,18 @@ typedef struct UASStatus UASStatus; struct UASDevice { USBDevice dev; SCSIBus bus; - UASRequest *datain; - UASRequest *dataout; - USBPacket *status; QEMUBH *status_bh; QTAILQ_HEAD(, UASStatus) results; QTAILQ_HEAD(, UASRequest) requests; + + /* usb 2.0 only */ + USBPacket *status2; + UASRequest *datain2; + UASRequest *dataout2; + + /* usb 3.0 only */ + USBPacket *data3[UAS_MAX_STREAMS]; + USBPacket *status3[UAS_MAX_STREAMS]; }; struct UASRequest { @@ -132,6 +141,7 @@ struct UASRequest { }; struct UASStatus { + uint32_t stream; uas_ui status; uint32_t length; QTAILQ_ENTRY(UASStatus) next; @@ -144,6 +154,7 @@ enum { STR_PRODUCT, STR_SERIALNUMBER, STR_CONFIG_HIGH, + STR_CONFIG_SUPER, }; static const USBDescStrings desc_strings = { @@ -151,6 +162,7 @@ static const USBDescStrings desc_strings = { [STR_PRODUCT] = "USB Attached SCSI HBA", [STR_SERIALNUMBER] = "27842", [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", + [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)", }; static const USBDescIface desc_iface_high = { @@ -204,6 +216,64 @@ static const USBDescIface desc_iface_high = { } }; +static const USBDescIface desc_iface_super = { + .bInterfaceNumber = 0, + .bNumEndpoints = 4, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = 0x06, /* SCSI */ + .bInterfaceProtocol = 0x62, /* UAS */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_COMMAND, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 1024, + .bMaxBurst = 15, + .extra = (uint8_t[]) { + 0x04, /* u8 bLength */ + 0x24, /* u8 bDescriptorType */ + UAS_PIPE_ID_COMMAND, + 0x00, /* u8 bReserved */ + }, + },{ + .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_STATUS, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 1024, + .bMaxBurst = 15, + .bmAttributes_super = UAS_STREAM_BM_ATTR, + .extra = (uint8_t[]) { + 0x04, /* u8 bLength */ + 0x24, /* u8 bDescriptorType */ + UAS_PIPE_ID_STATUS, + 0x00, /* u8 bReserved */ + }, + },{ + .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_DATA_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 1024, + .bMaxBurst = 15, + .bmAttributes_super = UAS_STREAM_BM_ATTR, + .extra = (uint8_t[]) { + 0x04, /* u8 bLength */ + 0x24, /* u8 bDescriptorType */ + UAS_PIPE_ID_DATA_IN, + 0x00, /* u8 bReserved */ + }, + },{ + .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 1024, + .bMaxBurst = 15, + .bmAttributes_super = UAS_STREAM_BM_ATTR, + .extra = (uint8_t[]) { + 0x04, /* u8 bLength */ + 0x24, /* u8 bDescriptorType */ + UAS_PIPE_ID_DATA_OUT, + 0x00, /* u8 bReserved */ + }, + }, + } +}; + static const USBDescDevice desc_device_high = { .bcdUSB = 0x0200, .bMaxPacketSize0 = 64, @@ -220,6 +290,22 @@ static const USBDescDevice desc_device_high = { }, }; +static const USBDescDevice desc_device_super = { + .bcdUSB = 0x0300, + .bMaxPacketSize0 = 64, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_SUPER, + .bmAttributes = 0xc0, + .nif = 1, + .ifs = &desc_iface_super, + }, + }, +}; + static const USBDesc desc = { .id = { .idVendor = 0x46f4, /* CRC16() of "QEMU" */ @@ -229,45 +315,68 @@ static const USBDesc desc = { .iProduct = STR_PRODUCT, .iSerialNumber = STR_SERIALNUMBER, }, - .high = &desc_device_high, - .str = desc_strings, + .high = &desc_device_high, + .super = &desc_device_super, + .str = desc_strings, }; /* --------------------------------------------------------------------- */ -static UASStatus *usb_uas_alloc_status(uint8_t id, uint16_t tag) +static bool uas_using_streams(UASDevice *uas) +{ + return uas->dev.speed == USB_SPEED_SUPER; +} + +/* --------------------------------------------------------------------- */ + +static UASStatus *usb_uas_alloc_status(UASDevice *uas, uint8_t id, uint16_t tag) { UASStatus *st = g_new0(UASStatus, 1); st->status.hdr.id = id; st->status.hdr.tag = cpu_to_be16(tag); st->length = sizeof(uas_ui_header); + if (uas_using_streams(uas)) { + st->stream = tag; + } return st; } static void usb_uas_send_status_bh(void *opaque) { UASDevice *uas = opaque; - UASStatus *st = QTAILQ_FIRST(&uas->results); - USBPacket *p = uas->status; + UASStatus *st; + USBPacket *p; - assert(p != NULL); - assert(st != NULL); + while ((st = QTAILQ_FIRST(&uas->results)) != NULL) { + if (uas_using_streams(uas)) { + p = uas->status3[st->stream]; + uas->status3[st->stream] = NULL; + } else { + p = uas->status2; + uas->status2 = NULL; + } + if (p == NULL) { + break; + } - uas->status = NULL; - usb_packet_copy(p, &st->status, st->length); - QTAILQ_REMOVE(&uas->results, st, next); - g_free(st); + usb_packet_copy(p, &st->status, st->length); + QTAILQ_REMOVE(&uas->results, st, next); + g_free(st); - p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ - usb_packet_complete(&uas->dev, p); + p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ + usb_packet_complete(&uas->dev, p); + } } static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length) { + USBPacket *p = uas_using_streams(uas) ? + uas->status3[st->stream] : uas->status2; + st->length += length; QTAILQ_INSERT_TAIL(&uas->results, st, next); - if (uas->status) { + if (p) { /* * Just schedule bh make sure any in-flight data transaction * is finished before completing (sending) the status packet. @@ -276,14 +385,14 @@ static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length) } else { USBEndpoint *ep = usb_ep_get(&uas->dev, USB_TOKEN_IN, UAS_PIPE_ID_STATUS); - usb_wakeup(ep); + usb_wakeup(ep, st->stream); } } static void usb_uas_queue_response(UASDevice *uas, uint16_t tag, uint8_t code, uint16_t add_info) { - UASStatus *st = usb_uas_alloc_status(UAS_UI_RESPONSE, tag); + UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_RESPONSE, tag); trace_usb_uas_response(uas->dev.addr, tag, code); st->status.response.response_code = code; @@ -293,7 +402,7 @@ static void usb_uas_queue_response(UASDevice *uas, uint16_t tag, static void usb_uas_queue_sense(UASRequest *req, uint8_t status) { - UASStatus *st = usb_uas_alloc_status(UAS_UI_SENSE, req->tag); + UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_SENSE, req->tag); int len, slen = 0; trace_usb_uas_sense(req->uas->dev.addr, req->tag, status); @@ -310,7 +419,8 @@ static void usb_uas_queue_sense(UASRequest *req, uint8_t status) static void usb_uas_queue_read_ready(UASRequest *req) { - UASStatus *st = usb_uas_alloc_status(UAS_UI_READ_READY, req->tag); + UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_READ_READY, + req->tag); trace_usb_uas_read_ready(req->uas->dev.addr, req->tag); usb_uas_queue_status(req->uas, st, 0); @@ -318,7 +428,8 @@ static void usb_uas_queue_read_ready(UASRequest *req) static void usb_uas_queue_write_ready(UASRequest *req) { - UASStatus *st = usb_uas_alloc_status(UAS_UI_WRITE_READY, req->tag); + UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_WRITE_READY, + req->tag); trace_usb_uas_write_ready(req->uas->dev.addr, req->tag); usb_uas_queue_status(req->uas, st, 0); @@ -381,18 +492,22 @@ static void usb_uas_start_next_transfer(UASDevice *uas) { UASRequest *req; + if (uas_using_streams(uas)) { + return; + } + QTAILQ_FOREACH(req, &uas->requests, next) { if (req->active || req->complete) { continue; } - if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain == NULL) { - uas->datain = req; + if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain2 == NULL) { + uas->datain2 = req; usb_uas_queue_read_ready(req); req->active = true; return; } - if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout == NULL) { - uas->dataout = req; + if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout2 == NULL) { + uas->dataout2 = req; usb_uas_queue_write_ready(req); req->active = true; return; @@ -417,11 +532,11 @@ static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv) UASRequest *req = priv; UASDevice *uas = req->uas; - if (req == uas->datain) { - uas->datain = NULL; + if (req == uas->datain2) { + uas->datain2 = NULL; } - if (req == uas->dataout) { - uas->dataout = NULL; + if (req == uas->dataout2) { + uas->dataout2 = NULL; } QTAILQ_REMOVE(&uas->requests, req, next); g_free(req); @@ -522,12 +637,25 @@ static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p) { UASDevice *uas = DO_UPCAST(UASDevice, dev, dev); UASRequest *req, *nreq; + int i; - if (uas->status == p) { - uas->status = NULL; + if (uas->status2 == p) { + uas->status2 = NULL; qemu_bh_cancel(uas->status_bh); return; } + if (uas_using_streams(uas)) { + for (i = 0; i < UAS_MAX_STREAMS; i++) { + if (uas->status3[i] == p) { + uas->status3[i] = NULL; + return; + } + if (uas->data3[i] == p) { + uas->data3[i] = NULL; + return; + } + } + } QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) { if (req->data == p) { req->data = NULL; @@ -555,9 +683,18 @@ static void usb_uas_command(UASDevice *uas, uas_ui *ui) usb_uas_get_lun(req->lun), req->lun >> 32, req->lun & 0xffffffff); QTAILQ_INSERT_TAIL(&uas->requests, req, next); + if (uas_using_streams(uas) && uas->data3[req->tag] != NULL) { + req->data = uas->data3[req->tag]; + req->data_async = true; + uas->data3[req->tag] = NULL; + } + req->req = scsi_req_new(req->dev, req->tag, usb_uas_get_lun(req->lun), ui->command.cdb, req); +#if 1 + scsi_req_print(req->req); +#endif len = scsi_req_enqueue(req->req); if (len) { req->data_size = len; @@ -669,12 +806,26 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p) } break; case UAS_PIPE_ID_STATUS: - st = QTAILQ_FIRST(&uas->results); - if (st == NULL) { - assert(uas->status == NULL); - uas->status = p; - p->status = USB_RET_ASYNC; - break; + if (p->stream) { + QTAILQ_FOREACH(st, &uas->results, next) { + if (st->stream == p->stream) { + break; + } + } + if (st == NULL) { + assert(uas->status3[p->stream] == NULL); + uas->status3[p->stream] = p; + p->status = USB_RET_ASYNC; + break; + } + } else { + st = QTAILQ_FIRST(&uas->results); + if (st == NULL) { + assert(uas->status2 == NULL); + uas->status2 = p; + p->status = USB_RET_ASYNC; + break; + } } usb_packet_copy(p, &st->status, st->length); QTAILQ_REMOVE(&uas->results, st, next); @@ -682,11 +833,23 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p) break; case UAS_PIPE_ID_DATA_IN: case UAS_PIPE_ID_DATA_OUT: - req = (p->ep->nr == UAS_PIPE_ID_DATA_IN) ? uas->datain : uas->dataout; + if (p->stream) { + req = usb_uas_find_request(uas, p->stream); + } else { + req = (p->ep->nr == UAS_PIPE_ID_DATA_IN) + ? uas->datain2 : uas->dataout2; + } if (req == NULL) { - fprintf(stderr, "%s: no inflight request\n", __func__); - p->status = USB_RET_STALL; - break; + if (p->stream) { + assert(uas->data3[p->stream] == NULL); + uas->data3[p->stream] = p; + p->status = USB_RET_ASYNC; + break; + } else { + fprintf(stderr, "%s: no inflight request\n", __func__); + p->status = USB_RET_STALL; + break; + } } scsi_req_ref(req->req); req->data = p; @@ -757,7 +920,7 @@ static void usb_uas_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_usb_uas; } -static TypeInfo uas_info = { +static const TypeInfo uas_info = { .name = "usb-uas", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(UASDevice), diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c index 9ab368a6c5..3be5cdefda 100644 --- a/hw/usb/dev-wacom.c +++ b/hw/usb/dev-wacom.c @@ -138,7 +138,7 @@ static void usb_mouse_event(void *opaque, s->dz += dz1; s->buttons_state = buttons_state; s->changed = 1; - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } static void usb_wacom_event(void *opaque, @@ -152,7 +152,7 @@ static void usb_wacom_event(void *opaque, s->dz += dz; s->buttons_state = buttons_state; s->changed = 1; - usb_wakeup(s->intr); + usb_wakeup(s->intr, 0); } static inline int int_clamp(int val, int vmin, int vmax) @@ -366,7 +366,7 @@ static void usb_wacom_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_usb_wacom; } -static TypeInfo wacom_info = { +static const TypeInfo wacom_info = { .name = "usb-wacom-tablet", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBWacomState), diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index ee77d41db5..0eb78269f7 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -16,14 +16,8 @@ */ #include "hw/usb/hcd-ehci.h" -#include "hw/pci/pci.h" #include "qemu/range.h" -typedef struct EHCIPCIState { - PCIDevice pcidev; - EHCIState ehci; -} EHCIPCIState; - typedef struct EHCIPCIInfo { const char *name; uint16_t vendor_id; @@ -33,7 +27,7 @@ typedef struct EHCIPCIInfo { static int usb_ehci_pci_initfn(PCIDevice *dev) { - EHCIPCIState *i = DO_UPCAST(EHCIPCIState, pcidev, dev); + EHCIPCIState *i = PCI_EHCI(dev); EHCIState *s = &i->ehci; uint8_t *pci_conf = dev->config; @@ -83,7 +77,7 @@ static int usb_ehci_pci_initfn(PCIDevice *dev) static void usb_ehci_pci_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int l) { - EHCIPCIState *i = DO_UPCAST(EHCIPCIState, pcidev, dev); + EHCIPCIState *i = PCI_EHCI(dev); bool busmaster; pci_default_write_config(dev, addr, val, l); @@ -115,12 +109,8 @@ static void ehci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - EHCIPCIInfo *i = data; k->init = usb_ehci_pci_initfn; - k->vendor_id = i->vendor_id; - k->device_id = i->device_id; - k->revision = i->revision; k->class_id = PCI_CLASS_SERIAL_USB; k->config_write = usb_ehci_pci_write_config; k->no_hotplug = 1; @@ -128,6 +118,24 @@ static void ehci_class_init(ObjectClass *klass, void *data) dc->props = ehci_pci_properties; } +static const TypeInfo ehci_pci_type_info = { + .name = TYPE_PCI_EHCI, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(EHCIPCIState), + .abstract = true, + .class_init = ehci_class_init, +}; + +static void ehci_data_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + EHCIPCIInfo *i = data; + + k->vendor_id = i->vendor_id; + k->device_id = i->device_id; + k->revision = i->revision; +} + static struct EHCIPCIInfo ehci_pci_info[] = { { .name = "usb-ehci", @@ -150,12 +158,13 @@ static struct EHCIPCIInfo ehci_pci_info[] = { static void ehci_pci_register_types(void) { TypeInfo ehci_type_info = { - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(EHCIPCIState), - .class_init = ehci_class_init, + .parent = TYPE_PCI_EHCI, + .class_init = ehci_data_class_init, }; int i; + type_register_static(&ehci_pci_type_info); + for (i = 0; i < ARRAY_SIZE(ehci_pci_info); i++) { ehci_type_info.name = ehci_pci_info[i].name; ehci_type_info.class_data = ehci_pci_info + i; diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index 803df92f31..b68a66a63b 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -16,12 +16,6 @@ */ #include "hw/usb/hcd-ehci.h" -#include "hw/sysbus.h" - -typedef struct EHCISysBusState { - SysBusDevice busdev; - EHCIState ehci; -} EHCISysBusState; static const VMStateDescription vmstate_ehci_sysbus = { .name = "ehci-sysbus", @@ -40,11 +34,12 @@ static Property ehci_sysbus_properties[] = { static int usb_ehci_sysbus_initfn(SysBusDevice *dev) { - EHCISysBusState *i = FROM_SYSBUS(EHCISysBusState, dev); + EHCISysBusState *i = SYS_BUS_EHCI(dev); + SysBusEHCIClass *sec = SYS_BUS_EHCI_GET_CLASS(dev); EHCIState *s = &i->ehci; - s->capsbase = 0x100; - s->opregbase = 0x140; + s->capsbase = sec->capsbase; + s->opregbase = sec->opregbase; s->dma = &dma_context_memory; usb_ehci_initfn(s, DEVICE(dev)); @@ -63,16 +58,48 @@ static void ehci_sysbus_class_init(ObjectClass *klass, void *data) dc->props = ehci_sysbus_properties; } -TypeInfo ehci_xlnx_type_info = { - .name = "xlnx,ps7-usb", +static const TypeInfo ehci_type_info = { + .name = TYPE_SYS_BUS_EHCI, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(EHCISysBusState), + .abstract = true, .class_init = ehci_sysbus_class_init, + .class_size = sizeof(SysBusEHCIClass), +}; + +static void ehci_xlnx_class_init(ObjectClass *oc, void *data) +{ + SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); + + sec->capsbase = 0x100; + sec->opregbase = 0x140; +} + +static const TypeInfo ehci_xlnx_type_info = { + .name = "xlnx,ps7-usb", + .parent = TYPE_SYS_BUS_EHCI, + .class_init = ehci_xlnx_class_init, +}; + +static void ehci_exynos4210_class_init(ObjectClass *oc, void *data) +{ + SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); + + sec->capsbase = 0x0; + sec->opregbase = 0x10; +} + +static const TypeInfo ehci_exynos4210_type_info = { + .name = TYPE_EXYNOS4210_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .class_init = ehci_exynos4210_class_init, }; static void ehci_sysbus_register_types(void) { + type_register_static(&ehci_type_info); type_register_static(&ehci_xlnx_type_info); + type_register_static(&ehci_exynos4210_type_info); } type_init(ehci_sysbus_register_types) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 7536837fb2..5176251cb4 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -109,12 +109,13 @@ #define FRAME_TIMER_FREQ 1000 #define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ) +#define UFRAME_TIMER_NS (FRAME_TIMER_NS / 8) #define NB_MAXINTRATE 8 // Max rate at which controller issues ints #define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction #define MAX_QH 100 // Max allowable queue heads in a chain -#define MIN_FR_PER_TICK 3 // Min frames to process when catching up -#define PERIODIC_ACTIVE 64 +#define MIN_UFR_PER_TICK 24 /* Min frames to process when catching up */ +#define PERIODIC_ACTIVE 512 /* Micro-frames */ /* Internal periodic / asynchronous schedule state machine states */ @@ -192,6 +193,7 @@ static int ehci_state_executing(EHCIQueue *q); static int ehci_state_writeback(EHCIQueue *q); static int ehci_state_advqueue(EHCIQueue *q); static int ehci_fill_queue(EHCIPacket *p); +static void ehci_free_packet(EHCIPacket *p); static const char *nr2str(const char **n, size_t len, uint32_t nr) { @@ -438,6 +440,136 @@ static inline bool ehci_periodic_enabled(EHCIState *s) return ehci_enabled(s) && (s->usbcmd & USBCMD_PSE); } +/* Get an array of dwords from main memory */ +static inline int get_dwords(EHCIState *ehci, uint32_t addr, + uint32_t *buf, int num) +{ + int i; + + if (!ehci->dma) { + ehci_raise_irq(ehci, USBSTS_HSE); + ehci->usbcmd &= ~USBCMD_RUNSTOP; + trace_usb_ehci_dma_error(); + return -1; + } + + for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { + dma_memory_read(ehci->dma, addr, buf, sizeof(*buf)); + *buf = le32_to_cpu(*buf); + } + + return num; +} + +/* Put an array of dwords in to main memory */ +static inline int put_dwords(EHCIState *ehci, uint32_t addr, + uint32_t *buf, int num) +{ + int i; + + if (!ehci->dma) { + ehci_raise_irq(ehci, USBSTS_HSE); + ehci->usbcmd &= ~USBCMD_RUNSTOP; + trace_usb_ehci_dma_error(); + return -1; + } + + for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { + uint32_t tmp = cpu_to_le32(*buf); + dma_memory_write(ehci->dma, addr, &tmp, sizeof(tmp)); + } + + return num; +} + +static int ehci_get_pid(EHCIqtd *qtd) +{ + switch (get_field(qtd->token, QTD_TOKEN_PID)) { + case 0: + return USB_TOKEN_OUT; + case 1: + return USB_TOKEN_IN; + case 2: + return USB_TOKEN_SETUP; + default: + fprintf(stderr, "bad token\n"); + return 0; + } +} + +static bool ehci_verify_qh(EHCIQueue *q, EHCIqh *qh) +{ + uint32_t devaddr = get_field(qh->epchar, QH_EPCHAR_DEVADDR); + uint32_t endp = get_field(qh->epchar, QH_EPCHAR_EP); + if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) || + (endp != get_field(q->qh.epchar, QH_EPCHAR_EP)) || + (qh->current_qtd != q->qh.current_qtd) || + (q->async && qh->next_qtd != q->qh.next_qtd) || + (memcmp(&qh->altnext_qtd, &q->qh.altnext_qtd, + 7 * sizeof(uint32_t)) != 0) || + (q->dev != NULL && q->dev->addr != devaddr)) { + return false; + } else { + return true; + } +} + +static bool ehci_verify_qtd(EHCIPacket *p, EHCIqtd *qtd) +{ + if (p->qtdaddr != p->queue->qtdaddr || + (p->queue->async && !NLPTR_TBIT(p->qtd.next) && + (p->qtd.next != qtd->next)) || + (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd->altnext)) || + p->qtd.token != qtd->token || + p->qtd.bufptr[0] != qtd->bufptr[0]) { + return false; + } else { + return true; + } +} + +static bool ehci_verify_pid(EHCIQueue *q, EHCIqtd *qtd) +{ + int ep = get_field(q->qh.epchar, QH_EPCHAR_EP); + int pid = ehci_get_pid(qtd); + + /* Note the pid changing is normal for ep 0 (the control ep) */ + if (q->last_pid && ep != 0 && pid != q->last_pid) { + return false; + } else { + return true; + } +} + +/* Finish executing and writeback a packet outside of the regular + fetchqh -> fetchqtd -> execute -> writeback cycle */ +static void ehci_writeback_async_complete_packet(EHCIPacket *p) +{ + EHCIQueue *q = p->queue; + EHCIqtd qtd; + EHCIqh qh; + int state; + + /* Verify the qh + qtd, like we do when going through fetchqh & fetchqtd */ + get_dwords(q->ehci, NLPTR_GET(q->qhaddr), + (uint32_t *) &qh, sizeof(EHCIqh) >> 2); + get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), + (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2); + if (!ehci_verify_qh(q, &qh) || !ehci_verify_qtd(p, &qtd)) { + p->async = EHCI_ASYNC_INITIALIZED; + ehci_free_packet(p); + return; + } + + state = ehci_get_state(q->ehci, q->async); + ehci_state_executing(q); + ehci_state_writeback(q); /* Frees the packet! */ + if (!(q->qh.token & QTD_TOKEN_HALT)) { + ehci_state_advqueue(q); + } + ehci_set_state(q->ehci, q->async, state); +} + /* packet management */ static EHCIPacket *ehci_alloc_packet(EHCIQueue *q) @@ -455,17 +587,7 @@ static EHCIPacket *ehci_alloc_packet(EHCIQueue *q) static void ehci_free_packet(EHCIPacket *p) { if (p->async == EHCI_ASYNC_FINISHED) { - EHCIQueue *q = p->queue; - int state = ehci_get_state(q->ehci, q->async); - /* This is a normal, but rare condition (cancel racing completion) */ - fprintf(stderr, "EHCI: Warning packet completed but not processed\n"); - ehci_state_executing(q); - ehci_state_writeback(q); - if (!(q->qh.token & QTD_TOKEN_HALT)) { - ehci_state_advqueue(q); - } - ehci_set_state(q->ehci, q->async, state); - /* state_writeback recurses into us with async == EHCI_ASYNC_NONE!! */ + ehci_writeback_async_complete_packet(p); return; } trace_usb_ehci_packet_action(p->queue, p, "free"); @@ -500,6 +622,17 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async) return q; } +static void ehci_queue_stopped(EHCIQueue *q) +{ + int endp = get_field(q->qh.epchar, QH_EPCHAR_EP); + + if (!q->last_pid || !q->dev) { + return; + } + + usb_device_ep_stopped(q->dev, usb_ep_get(q->dev, q->last_pid, endp)); +} + static int ehci_cancel_queue(EHCIQueue *q) { EHCIPacket *p; @@ -507,7 +640,7 @@ static int ehci_cancel_queue(EHCIQueue *q) p = QTAILQ_FIRST(&q->packets); if (p == NULL) { - return 0; + goto leave; } trace_usb_ehci_queue_action(q, "cancel"); @@ -515,6 +648,9 @@ static int ehci_cancel_queue(EHCIQueue *q) ehci_free_packet(p); packets++; } while ((p = QTAILQ_FIRST(&q->packets)) != NULL); + +leave: + ehci_queue_stopped(q); return packets; } @@ -526,6 +662,7 @@ static int ehci_reset_queue(EHCIQueue *q) packets = ehci_cancel_queue(q); q->dev = NULL; q->qtdaddr = 0; + q->last_pid = 0; return packets; } @@ -634,7 +771,6 @@ static void ehci_attach(USBPort *port) *portsc |= PORTSC_CSC; ehci_raise_irq(s, USBSTS_PCD); - ehci_commit_irq(s); } static void ehci_detach(USBPort *port) @@ -664,7 +800,6 @@ static void ehci_detach(USBPort *port) *portsc |= PORTSC_CSC; ehci_raise_irq(s, USBSTS_PCD); - ehci_commit_irq(s); } static void ehci_child_detach(USBPort *port, USBDevice *child) @@ -739,7 +874,8 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[], return 0; } -static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep) +static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep, + unsigned int stream) { EHCIState *s = container_of(bus, EHCIState, bus); uint32_t portsc = s->portsc[ep->dev->port->index]; @@ -833,7 +969,15 @@ static uint64_t ehci_opreg_read(void *ptr, hwaddr addr, EHCIState *s = ptr; uint32_t val; - val = s->opreg[addr >> 2]; + switch (addr) { + case FRINDEX: + /* Round down to mult of 8, else it can go backwards on migration */ + val = s->frindex & ~7; + break; + default: + val = s->opreg[addr >> 2]; + } + trace_usb_ehci_opreg_read(addr + s->opregbase, addr2str(addr), val); return val; } @@ -984,7 +1128,8 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, break; case FRINDEX: - val &= 0x00003ff8; /* frindex is 14bits and always a multiple of 8 */ + val &= 0x00003fff; /* frindex is 14bits */ + s->usbsts_frindex = val; break; case CONFIGFLAG: @@ -1017,48 +1162,6 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, *mmio, old); } -/* Get an array of dwords from main memory */ -static inline int get_dwords(EHCIState *ehci, uint32_t addr, - uint32_t *buf, int num) -{ - int i; - - if (!ehci->dma) { - ehci_raise_irq(ehci, USBSTS_HSE); - ehci->usbcmd &= ~USBCMD_RUNSTOP; - trace_usb_ehci_dma_error(); - return -1; - } - - for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - dma_memory_read(ehci->dma, addr, buf, sizeof(*buf)); - *buf = le32_to_cpu(*buf); - } - - return num; -} - -/* Put an array of dwords in to main memory */ -static inline int put_dwords(EHCIState *ehci, uint32_t addr, - uint32_t *buf, int num) -{ - int i; - - if (!ehci->dma) { - ehci_raise_irq(ehci, USBSTS_HSE); - ehci->usbcmd &= ~USBCMD_RUNSTOP; - trace_usb_ehci_dma_error(); - return -1; - } - - for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - uint32_t tmp = cpu_to_le32(*buf); - dma_memory_write(ehci->dma, addr, &tmp, sizeof(tmp)); - } - - return num; -} - /* * Write the qh back to guest physical memory. This step isn't * in the EHCI spec but we need to do it since we don't share @@ -1257,6 +1360,9 @@ static void ehci_execute_complete(EHCIQueue *q) if (tbytes) { /* 4.15.1.2 must raise int on a short input packet */ ehci_raise_irq(q->ehci, USBSTS_INT); + if (q->async) { + q->ehci->int_req_by_async = true; + } } } else { tbytes = 0; @@ -1301,22 +1407,11 @@ static int ehci_execute(EHCIPacket *p, const char *action) return -1; } - p->pid = (p->qtd.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH; - switch (p->pid) { - case 0: - p->pid = USB_TOKEN_OUT; - break; - case 1: - p->pid = USB_TOKEN_IN; - break; - case 2: - p->pid = USB_TOKEN_SETUP; - break; - default: - fprintf(stderr, "bad token\n"); - break; + if (!ehci_verify_pid(p->queue, &p->qtd)) { + ehci_queue_stopped(p->queue); /* Mark the ep in the prev dir stopped */ } - + p->pid = ehci_get_pid(&p->qtd); + p->queue->last_pid = p->pid; endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP); ep = usb_ep_get(p->queue->dev, p->pid, endp); @@ -1326,7 +1421,7 @@ static int ehci_execute(EHCIPacket *p, const char *action) } spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0); - usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr, spd, + usb_packet_setup(&p->packet, p->pid, ep, 0, p->qtdaddr, spd, (p->qtd.token & QTD_TOKEN_IOC) != 0); usb_packet_map(&p->packet, &p->sgl); p->async = EHCI_ASYNC_INITIALIZED; @@ -1399,7 +1494,7 @@ static int ehci_process_itd(EHCIState *ehci, dev = ehci_find_device(ehci, devaddr); ep = usb_ep_get(dev, pid, endp); if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) { - usb_packet_setup(&ehci->ipacket, pid, ep, addr, false, + usb_packet_setup(&ehci->ipacket, pid, ep, 0, addr, false, (itd->transact[i] & ITD_XACT_IOC) != 0); usb_packet_map(&ehci->ipacket, &ehci->isgl); usb_handle_packet(dev, &ehci->ipacket); @@ -1551,8 +1646,7 @@ out: static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) { - EHCIPacket *p; - uint32_t entry, devaddr, endp; + uint32_t entry; EHCIQueue *q; EHCIqh qh; @@ -1561,7 +1655,6 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) if (NULL == q) { q = ehci_alloc_queue(ehci, entry, async); } - p = QTAILQ_FIRST(&q->packets); q->seen++; if (q->seen > 1) { @@ -1582,19 +1675,10 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) * The overlay area of the qh should never be changed by the guest, * except when idle, in which case the reset is a nop. */ - devaddr = get_field(qh.epchar, QH_EPCHAR_DEVADDR); - endp = get_field(qh.epchar, QH_EPCHAR_EP); - if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) || - (endp != get_field(q->qh.epchar, QH_EPCHAR_EP)) || - (qh.current_qtd != q->qh.current_qtd) || - (q->async && qh.next_qtd != q->qh.next_qtd) || - (memcmp(&qh.altnext_qtd, &q->qh.altnext_qtd, - 7 * sizeof(uint32_t)) != 0) || - (q->dev != NULL && q->dev->addr != devaddr)) { + if (!ehci_verify_qh(q, &qh)) { if (ehci_reset_queue(q) > 0) { ehci_trace_guest_bug(ehci, "guest updated active QH"); } - p = NULL; } q->qh = qh; @@ -1604,14 +1688,8 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) } if (q->dev == NULL) { - q->dev = ehci_find_device(q->ehci, devaddr); - } - - if (p && p->async == EHCI_ASYNC_FINISHED) { - /* I/O finished -- continue processing queue */ - trace_usb_ehci_packet_action(p->queue, p, "complete"); - ehci_set_state(ehci, async, EST_EXECUTING); - goto out; + q->dev = ehci_find_device(q->ehci, + get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)); } if (async && (q->qh.epchar & QH_EPCHAR_H)) { @@ -1762,13 +1840,11 @@ static int ehci_state_fetchqtd(EHCIQueue *q) p = QTAILQ_FIRST(&q->packets); if (p != NULL) { - if (p->qtdaddr != q->qtdaddr || - (q->async && !NLPTR_TBIT(p->qtd.next) && - (p->qtd.next != qtd.next)) || - (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd.altnext)) || - p->qtd.bufptr[0] != qtd.bufptr[0]) { + if (!ehci_verify_qtd(p, &qtd)) { ehci_cancel_queue(q); - ehci_trace_guest_bug(q->ehci, "guest updated active QH or qTD"); + if (qtd.token & QTD_TOKEN_ACTIVE) { + ehci_trace_guest_bug(q->ehci, "guest updated active qTD"); + } p = NULL; } else { p->qtd = qtd; @@ -1777,11 +1853,6 @@ static int ehci_state_fetchqtd(EHCIQueue *q) } if (!(qtd.token & QTD_TOKEN_ACTIVE)) { - if (p != NULL) { - /* transfer canceled by guest (clear active) */ - ehci_cancel_queue(q); - p = NULL; - } ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); } else if (p != NULL) { switch (p->async) { @@ -1797,10 +1868,7 @@ static int ehci_state_fetchqtd(EHCIQueue *q) ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); break; case EHCI_ASYNC_FINISHED: - /* - * We get here when advqueue moves to a packet which is already - * finished, which can happen with packets queued up by fill_queue - */ + /* Complete executing of the packet */ ehci_set_state(q->ehci, q->async, EST_EXECUTING); break; } @@ -1859,6 +1927,10 @@ static int ehci_fill_queue(EHCIPacket *p) if (!(qtd.token & QTD_TOKEN_ACTIVE)) { break; } + if (!ehci_verify_pid(q, &qtd)) { + ehci_trace_guest_bug(q->ehci, "guest queued token with wrong pid"); + break; + } p = ehci_alloc_packet(q); p->qtdaddr = qtdaddr; p->qtd = qtd; @@ -2021,18 +2093,22 @@ static void ehci_advance_state(EHCIState *ehci, int async) break; case EST_ADVANCEQUEUE: + assert(q != NULL); again = ehci_state_advqueue(q); break; case EST_FETCHQTD: + assert(q != NULL); again = ehci_state_fetchqtd(q); break; case EST_HORIZONTALQH: + assert(q != NULL); again = ehci_state_horizqh(q); break; case EST_EXECUTE: + assert(q != NULL); again = ehci_state_execute(q); if (async) { ehci->async_stepdown = 0; @@ -2176,16 +2252,16 @@ static void ehci_advance_periodic_state(EHCIState *ehci) } } -static void ehci_update_frindex(EHCIState *ehci, int frames) +static void ehci_update_frindex(EHCIState *ehci, int uframes) { int i; - if (!ehci_enabled(ehci)) { + if (!ehci_enabled(ehci) && ehci->pstate == EST_INACTIVE) { return; } - for (i = 0; i < frames; i++) { - ehci->frindex += 8; + for (i = 0; i < uframes; i++) { + ehci->frindex++; if (ehci->frindex == 0x00002000) { ehci_raise_irq(ehci, USBSTS_FLR); @@ -2209,33 +2285,33 @@ static void ehci_frame_timer(void *opaque) int need_timer = 0; int64_t expire_time, t_now; uint64_t ns_elapsed; - int frames, skipped_frames; + int uframes, skipped_uframes; int i; t_now = qemu_get_clock_ns(vm_clock); ns_elapsed = t_now - ehci->last_run_ns; - frames = ns_elapsed / FRAME_TIMER_NS; + uframes = ns_elapsed / UFRAME_TIMER_NS; if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) { need_timer++; - if (frames > ehci->maxframes) { - skipped_frames = frames - ehci->maxframes; - ehci_update_frindex(ehci, skipped_frames); - ehci->last_run_ns += FRAME_TIMER_NS * skipped_frames; - frames -= skipped_frames; - DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames); + if (uframes > (ehci->maxframes * 8)) { + skipped_uframes = uframes - (ehci->maxframes * 8); + ehci_update_frindex(ehci, skipped_uframes); + ehci->last_run_ns += UFRAME_TIMER_NS * skipped_uframes; + uframes -= skipped_uframes; + DPRINTF("WARNING - EHCI skipped %d uframes\n", skipped_uframes); } - for (i = 0; i < frames; i++) { + for (i = 0; i < uframes; i++) { /* * If we're running behind schedule, we should not catch up * too fast, as that will make some guests unhappy: - * 1) We must process a minimum of MIN_FR_PER_TICK frames, + * 1) We must process a minimum of MIN_UFR_PER_TICK frames, * otherwise we will never catch up * 2) Process frames until the guest has requested an irq (IOC) */ - if (i >= MIN_FR_PER_TICK) { + if (i >= MIN_UFR_PER_TICK) { ehci_commit_irq(ehci); if ((ehci->usbsts & USBINTR_MASK) & ehci->usbintr) { break; @@ -2245,13 +2321,15 @@ static void ehci_frame_timer(void *opaque) ehci->periodic_sched_active--; } ehci_update_frindex(ehci, 1); - ehci_advance_periodic_state(ehci); - ehci->last_run_ns += FRAME_TIMER_NS; + if ((ehci->frindex & 7) == 0) { + ehci_advance_periodic_state(ehci); + } + ehci->last_run_ns += UFRAME_TIMER_NS; } } else { ehci->periodic_sched_active = 0; - ehci_update_frindex(ehci, frames); - ehci->last_run_ns += FRAME_TIMER_NS * frames; + ehci_update_frindex(ehci, uframes); + ehci->last_run_ns += UFRAME_TIMER_NS * uframes; } if (ehci->periodic_sched_active) { @@ -2282,7 +2360,7 @@ static void ehci_frame_timer(void *opaque) /* If we've raised int, we speed up the timer, so that we quickly * notice any new packets queued up in response */ if (ehci->int_req_by_async && (ehci->usbsts & USBSTS_INT)) { - expire_time = t_now + get_ticks_per_sec() / (FRAME_TIMER_FREQ * 2); + expire_time = t_now + get_ticks_per_sec() / (FRAME_TIMER_FREQ * 4); ehci->int_req_by_async = false; } else { expire_time = t_now + (get_ticks_per_sec() @@ -2330,6 +2408,17 @@ static USBBusOps ehci_bus_ops = { .wakeup_endpoint = ehci_wakeup_endpoint, }; +static void usb_ehci_pre_save(void *opaque) +{ + EHCIState *ehci = opaque; + uint32_t new_frindex; + + /* Round down frindex to a multiple of 8 for migration compatibility */ + new_frindex = ehci->frindex & ~7; + ehci->last_run_ns -= (ehci->frindex - new_frindex) * UFRAME_TIMER_NS; + ehci->frindex = new_frindex; +} + static int usb_ehci_post_load(void *opaque, int version_id) { EHCIState *s = opaque; @@ -2380,6 +2469,7 @@ const VMStateDescription vmstate_ehci = { .name = "ehci-core", .version_id = 2, .minimum_version_id = 1, + .pre_save = usb_ehci_pre_save, .post_load = usb_ehci_post_load, .fields = (VMStateField[]) { /* mmio registers */ diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index e35144d386..e95bb7ec46 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -24,6 +24,8 @@ #include "trace.h" #include "sysemu/dma.h" #include "sysemu/sysemu.h" +#include "hw/pci/pci.h" +#include "hw/sysbus.h" #ifndef EHCI_DEBUG #define EHCI_DEBUG 0 @@ -248,6 +250,7 @@ struct EHCIQueue { EHCIqh qh; /* copy of current QH (being worked on) */ uint32_t qhaddr; /* address QH read from */ uint32_t qtdaddr; /* address QTD read from */ + int last_pid; /* pid of last packet executed */ USBDevice *dev; QTAILQ_HEAD(pkts_head, EHCIPacket) packets; }; @@ -321,4 +324,43 @@ extern const VMStateDescription vmstate_ehci; void usb_ehci_initfn(EHCIState *s, DeviceState *dev); +#define TYPE_PCI_EHCI "pci-ehci-usb" +#define PCI_EHCI(obj) OBJECT_CHECK(EHCIPCIState, (obj), TYPE_PCI_EHCI) + +typedef struct EHCIPCIState { + /*< private >*/ + PCIDevice pcidev; + /*< public >*/ + + EHCIState ehci; +} EHCIPCIState; + + +#define TYPE_SYS_BUS_EHCI "sysbus-ehci-usb" +#define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb" + +#define SYS_BUS_EHCI(obj) \ + OBJECT_CHECK(EHCISysBusState, (obj), TYPE_SYS_BUS_EHCI) +#define SYS_BUS_EHCI_CLASS(class) \ + OBJECT_CLASS_CHECK(SysBusEHCIClass, (class), TYPE_SYS_BUS_EHCI) +#define SYS_BUS_EHCI_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SysBusEHCIClass, (obj), TYPE_SYS_BUS_EHCI) + +typedef struct EHCISysBusState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + EHCIState ehci; +} EHCISysBusState; + +typedef struct SysBusEHCIClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + + uint16_t capsbase; + uint16_t opregbase; +} SysBusEHCIClass; + #endif diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c index 64e9e834bf..7968e17c34 100644 --- a/hw/usb/hcd-musb.c +++ b/hw/usb/hcd-musb.c @@ -625,7 +625,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, /* A wild guess on the FADDR semantics... */ dev = usb_find_device(&s->port, ep->faddr[idx]); uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf); - usb_packet_setup(&ep->packey[dir].p, pid, uep, + usb_packet_setup(&ep->packey[dir].p, pid, uep, 0, (dev->addr << 16) | (uep->nr << 8) | pid, false, true); usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); ep->packey[dir].ep = ep; diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 052c4a3037..51241cda78 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -430,6 +430,23 @@ static USBDevice *ohci_find_device(OHCIState *ohci, uint8_t addr) return NULL; } +static void ohci_stop_endpoints(OHCIState *ohci) +{ + USBDevice *dev; + int i, j; + + for (i = 0; i < ohci->num_ports; i++) { + dev = ohci->rhport[i].port.dev; + if (dev && dev->attached) { + usb_device_ep_stopped(dev, &dev->ep_ctl); + for (j = 0; j < USB_MAX_ENDPOINTS; j++) { + usb_device_ep_stopped(dev, &dev->ep_in[j]); + usb_device_ep_stopped(dev, &dev->ep_out[j]); + } + } + } +} + /* Reset the controller */ static void ohci_reset(void *opaque) { @@ -478,6 +495,7 @@ static void ohci_reset(void *opaque) usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; } + ohci_stop_endpoints(ohci); DPRINTF("usb-ohci: Reset %s\n", ohci->name); } @@ -812,7 +830,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, OHCI_BM(iso_td.flags, TD_DI) == 0; dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); - usb_packet_setup(&ohci->usb_packet, pid, ep, addr, false, int_req); + usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, false, int_req); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); usb_handle_packet(dev, &ohci->usb_packet); if (ohci->usb_packet.status == USB_RET_ASYNC) { @@ -1016,7 +1034,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) } dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); - usb_packet_setup(&ohci->usb_packet, pid, ep, addr, !flag_r, + usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, !flag_r, OHCI_BM(td.flags, TD_DI) == 0); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen); usb_handle_packet(dev, &ohci->usb_packet); @@ -1147,6 +1165,8 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) if (ohci->async_td && addr == ohci->async_td) { usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; + usb_device_ep_stopped(ohci->usb_packet.ep->dev, + ohci->usb_packet.ep); } continue; } @@ -1227,10 +1247,12 @@ static void ohci_frame_boundary(void *opaque) } /* Cancel all pending packets if either of the lists has been disabled. */ - if (ohci->async_td && - ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) { - usb_cancel_packet(&ohci->usb_packet); - ohci->async_td = 0; + if (ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) { + if (ohci->async_td) { + usb_cancel_packet(&ohci->usb_packet); + ohci->async_td = 0; + } + ohci_stop_endpoints(ohci); } ohci->old_ctl = ohci->ctl; ohci_process_lists(ohci, 0); @@ -1714,6 +1736,7 @@ static void ohci_mem_write(void *opaque, /* PXA27x specific registers */ case 24: /* HcStatus */ ohci->hstatus &= ~(val & ohci->hmask); + break; case 25: /* HcHReset */ ohci->hreset = val & ~OHCI_HRESET_FSBIR; @@ -1887,7 +1910,7 @@ static void ohci_pci_class_init(ObjectClass *klass, void *data) dc->props = ohci_pci_properties; } -static TypeInfo ohci_pci_info = { +static const TypeInfo ohci_pci_info = { .name = "pci-ohci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(OHCIPCIState), @@ -1910,7 +1933,7 @@ static void ohci_sysbus_class_init(ObjectClass *klass, void *data) dc->props = ohci_sysbus_properties; } -static TypeInfo ohci_sysbus_info = { +static const TypeInfo ohci_sysbus_info = { .name = "sysbus-ohci", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(OHCISysBusState), diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 2af754b5cf..f8c42864d1 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -75,6 +75,11 @@ #define FRAME_MAX_LOOPS 256 +/* Must be large enough to handle 10 frame delay for initial isoc requests */ +#define QH_VALID 32 + +#define MAX_FRAMES_PER_TICK (QH_VALID / 2) + #define NB_PORTS 2 enum { @@ -166,6 +171,7 @@ struct UHCIState { /* Properties */ char *masterbus; uint32_t firstport; + uint32_t maxframes; }; typedef struct UHCI_TD { @@ -206,9 +212,7 @@ static UHCIQueue *uhci_queue_new(UHCIState *s, uint32_t qh_addr, UHCI_TD *td, queue->ep = ep; QTAILQ_INIT(&queue->asyncs); QTAILQ_INSERT_HEAD(&s->queues, queue, next); - /* valid needs to be large enough to handle 10 frame delay - * for initial isochronous requests */ - queue->valid = 32; + queue->valid = QH_VALID; trace_usb_uhci_queue_add(queue->token); return queue; } @@ -222,6 +226,7 @@ static void uhci_queue_free(UHCIQueue *queue, const char *reason) async = QTAILQ_FIRST(&queue->asyncs); uhci_async_cancel(async); } + usb_device_ep_stopped(queue->ep->dev, queue->ep); trace_usb_uhci_queue_del(queue->token, reason); QTAILQ_REMOVE(&s->queues, queue, next); @@ -433,7 +438,7 @@ static int uhci_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_uhci = { .name = "uhci", - .version_id = 2, + .version_id = 3, .minimum_version_id = 1, .minimum_version_id_old = 1, .post_load = uhci_post_load, @@ -451,44 +456,16 @@ static const VMStateDescription vmstate_uhci = { VMSTATE_UINT8(status2, UHCIState), VMSTATE_TIMER(frame_timer, UHCIState), VMSTATE_INT64_V(expire_time, UHCIState, 2), + VMSTATE_UINT32_V(pending_int_mask, UHCIState, 3), VMSTATE_END_OF_LIST() } }; -static void uhci_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) +static void uhci_port_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) { UHCIState *s = opaque; - addr &= 0x1f; - switch(addr) { - case 0x0c: - s->sof_timing = val; - break; - } -} - -static uint32_t uhci_ioport_readb(void *opaque, uint32_t addr) -{ - UHCIState *s = opaque; - uint32_t val; - - addr &= 0x1f; - switch(addr) { - case 0x0c: - val = s->sof_timing; - break; - default: - val = 0xff; - break; - } - return val; -} - -static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) -{ - UHCIState *s = opaque; - - addr &= 0x1f; trace_usb_uhci_mmio_writew(addr, val); switch(addr) { @@ -498,7 +475,7 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) trace_usb_uhci_schedule_start(); s->expire_time = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / FRAME_TIMER_FREQ); - qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock)); + qemu_mod_timer(s->frame_timer, s->expire_time); s->status &= ~UHCI_STS_HCHALTED; } else if (!(val & UHCI_CMD_RS)) { s->status |= UHCI_STS_HCHALTED; @@ -537,6 +514,17 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) if (s->status & UHCI_STS_HCHALTED) s->frnum = val & 0x7ff; break; + case 0x08: + s->fl_base_addr &= 0xffff0000; + s->fl_base_addr |= val & ~0xfff; + break; + case 0x0a: + s->fl_base_addr &= 0x0000ffff; + s->fl_base_addr |= (val << 16); + break; + case 0x0c: + s->sof_timing = val & 0xff; + break; case 0x10 ... 0x1f: { UHCIPort *port; @@ -568,12 +556,11 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) } } -static uint32_t uhci_ioport_readw(void *opaque, uint32_t addr) +static uint64_t uhci_port_read(void *opaque, hwaddr addr, unsigned size) { UHCIState *s = opaque; uint32_t val; - addr &= 0x1f; switch(addr) { case 0x00: val = s->cmd; @@ -587,6 +574,15 @@ static uint32_t uhci_ioport_readw(void *opaque, uint32_t addr) case 0x06: val = s->frnum; break; + case 0x08: + val = s->fl_base_addr & 0xffff; + break; + case 0x0a: + val = (s->fl_base_addr >> 16) & 0xffff; + break; + case 0x0c: + val = s->sof_timing; + break; case 0x10 ... 0x1f: { UHCIPort *port; @@ -609,38 +605,6 @@ static uint32_t uhci_ioport_readw(void *opaque, uint32_t addr) return val; } -static void uhci_ioport_writel(void *opaque, uint32_t addr, uint32_t val) -{ - UHCIState *s = opaque; - - addr &= 0x1f; - trace_usb_uhci_mmio_writel(addr, val); - - switch(addr) { - case 0x08: - s->fl_base_addr = val & ~0xfff; - break; - } -} - -static uint32_t uhci_ioport_readl(void *opaque, uint32_t addr) -{ - UHCIState *s = opaque; - uint32_t val; - - addr &= 0x1f; - switch(addr) { - case 0x08: - val = s->fl_base_addr; - break; - default: - val = 0xffffffff; - break; - } - trace_usb_uhci_mmio_readl(addr, val); - return val; -} - /* signal resume if controller suspended */ static void uhci_resume (void *opaque) { @@ -853,7 +817,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, } if (q) { - q->valid = 32; + q->valid = QH_VALID; } /* Is active ? */ @@ -915,7 +879,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, max_len = ((td->token >> 21) + 1) & 0x7ff; spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0); - usb_packet_setup(&async->packet, pid, q->ep, td_addr, spd, + usb_packet_setup(&async->packet, pid, q->ep, 0, td_addr, spd, (td->ctrl & TD_CTRL_IOC) != 0); qemu_sglist_add(&async->sgl, td->buffer, max_len); usb_packet_map(&async->packet, &async->sgl); @@ -1174,10 +1138,10 @@ static void uhci_bh(void *opaque) static void uhci_frame_timer(void *opaque) { UHCIState *s = opaque; + uint64_t t_now, t_last_run; + int i, frames; + const uint64_t frame_t = get_ticks_per_sec() / FRAME_TIMER_FREQ; - /* prepare the timer for the next frame */ - s->expire_time += (get_ticks_per_sec() / FRAME_TIMER_FREQ); - s->frame_bytes = 0; s->completions_only = false; qemu_bh_cancel(s->bh); @@ -1191,7 +1155,35 @@ static void uhci_frame_timer(void *opaque) return; } - /* Complete the previous frame */ + /* We still store expire_time in our state, for migration */ + t_last_run = s->expire_time - frame_t; + t_now = qemu_get_clock_ns(vm_clock); + + /* Process up to MAX_FRAMES_PER_TICK frames */ + frames = (t_now - t_last_run) / frame_t; + if (frames > s->maxframes) { + int skipped = frames - s->maxframes; + s->expire_time += skipped * frame_t; + s->frnum = (s->frnum + skipped) & 0x7ff; + frames -= skipped; + } + if (frames > MAX_FRAMES_PER_TICK) { + frames = MAX_FRAMES_PER_TICK; + } + + for (i = 0; i < frames; i++) { + s->frame_bytes = 0; + trace_usb_uhci_frame_start(s->frnum); + uhci_async_validate_begin(s); + uhci_process_frame(s); + uhci_async_validate_end(s); + /* The spec says frnum is the frame currently being processed, and + * the guest must look at frnum - 1 on interrupt, so inc frnum now */ + s->frnum = (s->frnum + 1) & 0x7ff; + s->expire_time += frame_t; + } + + /* Complete the previous frame(s) */ if (s->pending_int_mask) { s->status2 |= s->pending_int_mask; s->status |= UHCI_STS_USBINT; @@ -1199,32 +1191,17 @@ static void uhci_frame_timer(void *opaque) } s->pending_int_mask = 0; - /* Start new frame */ - s->frnum = (s->frnum + 1) & 0x7ff; - - trace_usb_uhci_frame_start(s->frnum); - - uhci_async_validate_begin(s); - - uhci_process_frame(s); - - uhci_async_validate_end(s); - - qemu_mod_timer(s->frame_timer, s->expire_time); + qemu_mod_timer(s->frame_timer, t_now + frame_t); } -static const MemoryRegionPortio uhci_portio[] = { - { 0, 32, 2, .write = uhci_ioport_writew, }, - { 0, 32, 2, .read = uhci_ioport_readw, }, - { 0, 32, 4, .write = uhci_ioport_writel, }, - { 0, 32, 4, .read = uhci_ioport_readl, }, - { 0, 32, 1, .write = uhci_ioport_writeb, }, - { 0, 32, 1, .read = uhci_ioport_readb, }, - PORTIO_END_OF_LIST() -}; - static const MemoryRegionOps uhci_ioport_ops = { - .old_portio = uhci_portio, + .read = uhci_port_read, + .write = uhci_port_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 2, + .impl.max_access_size = 2, + .endianness = DEVICE_LITTLE_ENDIAN, }; static USBPortOps uhci_port_ops = { @@ -1311,6 +1288,7 @@ static Property uhci_properties[] = { DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280), + DEFINE_PROP_UINT32("maxframes", UHCIState, maxframes, 128), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index e2de71ef1a..07afdeef5b 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -34,8 +34,8 @@ #else #define DPRINTF(...) do {} while (0) #endif -#define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \ - __func__, __LINE__); abort(); } while (0) +#define FIXME(_msg) do { fprintf(stderr, "FIXME %s:%d %s\n", \ + __func__, __LINE__, _msg); abort(); } while (0) #define MAXPORTS_2 15 #define MAXPORTS_3 15 @@ -301,6 +301,8 @@ typedef enum TRBCCode { #define SLOT_CONTEXT_ENTRIES_SHIFT 27 typedef struct XHCIState XHCIState; +typedef struct XHCIStreamContext XHCIStreamContext; +typedef struct XHCIEPContext XHCIEPContext; #define get_field(data, field) \ (((data) >> field##_SHIFT) & field##_MASK) @@ -351,6 +353,7 @@ typedef struct XHCITransfer { unsigned int iso_pkts; unsigned int slotid; unsigned int epid; + unsigned int streamid; bool in_xfer; bool iso_xfer; @@ -367,7 +370,14 @@ typedef struct XHCITransfer { uint64_t mfindex_kick; } XHCITransfer; -typedef struct XHCIEPContext { +struct XHCIStreamContext { + dma_addr_t pctx; + unsigned int sct; + XHCIRing ring; + XHCIStreamContext *sstreams; +}; + +struct XHCIEPContext { XHCIState *xhci; unsigned int slotid; unsigned int epid; @@ -382,11 +392,17 @@ typedef struct XHCIEPContext { unsigned int max_psize; uint32_t state; + /* streams */ + unsigned int max_pstreams; + bool lsa; + unsigned int nr_pstreams; + XHCIStreamContext *pstreams; + /* iso xfer scheduling */ unsigned int interval; int64_t mfindex_last; QEMUTimer *kick_timer; -} XHCIEPContext; +}; typedef struct XHCISlot { bool enabled; @@ -482,7 +498,7 @@ enum xhci_flags { }; static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid); + unsigned int epid, unsigned int streamid); static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid); static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v); @@ -1068,18 +1084,116 @@ static void xhci_stop(XHCIState *xhci) xhci->crcr_low &= ~CRCR_CRR; } +static XHCIStreamContext *xhci_alloc_stream_contexts(unsigned count, + dma_addr_t base) +{ + XHCIStreamContext *stctx; + unsigned int i; + + stctx = g_new0(XHCIStreamContext, count); + for (i = 0; i < count; i++) { + stctx[i].pctx = base + i * 16; + stctx[i].sct = -1; + } + return stctx; +} + +static void xhci_reset_streams(XHCIEPContext *epctx) +{ + unsigned int i; + + for (i = 0; i < epctx->nr_pstreams; i++) { + epctx->pstreams[i].sct = -1; + g_free(epctx->pstreams[i].sstreams); + } +} + +static void xhci_alloc_streams(XHCIEPContext *epctx, dma_addr_t base) +{ + assert(epctx->pstreams == NULL); + epctx->nr_pstreams = 2 << epctx->max_pstreams; + epctx->pstreams = xhci_alloc_stream_contexts(epctx->nr_pstreams, base); +} + +static void xhci_free_streams(XHCIEPContext *epctx) +{ + int i; + + assert(epctx->pstreams != NULL); + + if (!epctx->lsa) { + for (i = 0; i < epctx->nr_pstreams; i++) { + g_free(epctx->pstreams[i].sstreams); + } + } + g_free(epctx->pstreams); + epctx->pstreams = NULL; + epctx->nr_pstreams = 0; +} + +static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, + unsigned int streamid, + uint32_t *cc_error) +{ + XHCIStreamContext *sctx; + dma_addr_t base; + uint32_t ctx[2], sct; + + assert(streamid != 0); + if (epctx->lsa) { + if (streamid >= epctx->nr_pstreams) { + *cc_error = CC_INVALID_STREAM_ID_ERROR; + return NULL; + } + sctx = epctx->pstreams + streamid; + } else { + FIXME("secondary streams not implemented yet"); + } + + if (sctx->sct == -1) { + xhci_dma_read_u32s(epctx->xhci, sctx->pctx, ctx, sizeof(ctx)); + fprintf(stderr, "%s: init sctx #%d @ " DMA_ADDR_FMT ": %08x %08x\n", + __func__, streamid, sctx->pctx, ctx[0], ctx[1]); + sct = (ctx[0] >> 1) & 0x07; + if (epctx->lsa && sct != 1) { + *cc_error = CC_INVALID_STREAM_TYPE_ERROR; + return NULL; + } + sctx->sct = sct; + base = xhci_addr64(ctx[0] & ~0xf, ctx[1]); + xhci_ring_init(epctx->xhci, &sctx->ring, base); + } + return sctx; +} + static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, - uint32_t state) + XHCIStreamContext *sctx, uint32_t state) { uint32_t ctx[5]; + uint32_t ctx2[2]; + fprintf(stderr, "%s: epid %d, state %d\n", + __func__, epctx->epid, state); xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx)); ctx[0] &= ~EP_STATE_MASK; ctx[0] |= state; - ctx[2] = epctx->ring.dequeue | epctx->ring.ccs; - ctx[3] = (epctx->ring.dequeue >> 16) >> 16; - DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n", - epctx->pctx, state, ctx[3], ctx[2]); + + /* update ring dequeue ptr */ + if (epctx->nr_pstreams) { + if (sctx != NULL) { + xhci_dma_read_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2)); + ctx2[0] &= 0xe; + ctx2[0] |= sctx->ring.dequeue | sctx->ring.ccs; + ctx2[1] = (sctx->ring.dequeue >> 16) >> 16; + xhci_dma_write_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2)); + } + } else { + ctx[2] = epctx->ring.dequeue | epctx->ring.ccs; + ctx[3] = (epctx->ring.dequeue >> 16) >> 16; + DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n", + epctx->pctx, state, ctx[3], ctx[2]); + } + xhci_dma_write_u32s(xhci, epctx->pctx, ctx, sizeof(ctx)); epctx->state = state; } @@ -1087,7 +1201,7 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, static void xhci_ep_kick_timer(void *opaque) { XHCIEPContext *epctx = opaque; - xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid); + xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid, 0); } static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, @@ -1117,16 +1231,22 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, slot->eps[epid-1] = epctx; dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]); - xhci_ring_init(xhci, &epctx->ring, dequeue); - epctx->ring.ccs = ctx[2] & 1; epctx->type = (ctx[1] >> EP_TYPE_SHIFT) & EP_TYPE_MASK; DPRINTF("xhci: endpoint %d.%d type is %d\n", epid/2, epid%2, epctx->type); epctx->pctx = pctx; epctx->max_psize = ctx[1]>>16; epctx->max_psize *= 1+((ctx[1]>>8)&0xff); + epctx->max_pstreams = (ctx[0] >> 10) & 0xf; + epctx->lsa = (ctx[0] >> 15) & 1; DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n", epid/2, epid%2, epctx->max_psize); + if (epctx->max_pstreams) { + xhci_alloc_streams(epctx, dequeue); + } else { + xhci_ring_init(xhci, &epctx->ring, dequeue); + epctx->ring.ccs = ctx[2] & 1; + } for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { usb_packet_init(&epctx->transfers[i].packet); } @@ -1177,6 +1297,7 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, XHCISlot *slot; XHCIEPContext *epctx; int i, xferi, killed = 0; + USBEndpoint *ep = NULL; assert(slotid >= 1 && slotid <= xhci->numslots); assert(epid >= 1 && epid <= 31); @@ -1192,9 +1313,16 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, xferi = epctx->next_xfer; for (i = 0; i < TD_QUEUE; i++) { + if (epctx->transfers[xferi].packet.ep) { + ep = epctx->transfers[xferi].packet.ep; + } killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi]); + epctx->transfers[xferi].packet.ep = NULL; xferi = (xferi + 1) % TD_QUEUE; } + if (ep) { + usb_device_ep_stopped(ep->dev, ep); + } return killed; } @@ -1219,7 +1347,11 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, epctx = slot->eps[epid-1]; - xhci_set_ep_state(xhci, epctx, EP_DISABLED); + if (epctx->nr_pstreams) { + xhci_free_streams(epctx); + } + + xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED); qemu_free_timer(epctx->kick_timer); g_free(epctx); @@ -1256,7 +1388,11 @@ static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid, epctx = slot->eps[epid-1]; - xhci_set_ep_state(xhci, epctx, EP_STOPPED); + xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED); + + if (epctx->nr_pstreams) { + xhci_reset_streams(epctx); + } return CC_SUCCESS; } @@ -1307,16 +1443,22 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, return CC_USB_TRANSACTION_ERROR; } - xhci_set_ep_state(xhci, epctx, EP_STOPPED); + xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED); + + if (epctx->nr_pstreams) { + xhci_reset_streams(epctx); + } return CC_SUCCESS; } static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, - unsigned int epid, uint64_t pdequeue) + unsigned int epid, unsigned int streamid, + uint64_t pdequeue) { XHCISlot *slot; XHCIEPContext *epctx; + XHCIStreamContext *sctx; dma_addr_t dequeue; assert(slotid >= 1 && slotid <= xhci->numslots); @@ -1326,7 +1468,7 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, return CC_TRB_ERROR; } - trace_usb_xhci_ep_set_dequeue(slotid, epid, pdequeue); + trace_usb_xhci_ep_set_dequeue(slotid, epid, streamid, pdequeue); dequeue = xhci_mask64(pdequeue); slot = &xhci->slots[slotid-1]; @@ -1338,16 +1480,26 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, epctx = slot->eps[epid-1]; - if (epctx->state != EP_STOPPED) { fprintf(stderr, "xhci: set EP dequeue pointer while EP %d not stopped\n", epid); return CC_CONTEXT_STATE_ERROR; } - xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF); - epctx->ring.ccs = dequeue & 1; + if (epctx->nr_pstreams) { + uint32_t err; + sctx = xhci_find_stream(epctx, streamid, &err); + if (sctx == NULL) { + return err; + } + xhci_ring_init(xhci, &sctx->ring, dequeue & ~0xf); + sctx->ring.ccs = dequeue & 1; + } else { + sctx = NULL; + xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF); + epctx->ring.ccs = dequeue & 1; + } - xhci_set_ep_state(xhci, epctx, EP_STOPPED); + xhci_set_ep_state(xhci, epctx, sctx, EP_STOPPED); return CC_SUCCESS; } @@ -1476,12 +1628,22 @@ static void xhci_stall_ep(XHCITransfer *xfer) XHCIState *xhci = xfer->xhci; XHCISlot *slot = &xhci->slots[xfer->slotid-1]; XHCIEPContext *epctx = slot->eps[xfer->epid-1]; + uint32_t err; + XHCIStreamContext *sctx; - epctx->ring.dequeue = xfer->trbs[0].addr; - epctx->ring.ccs = xfer->trbs[0].ccs; - xhci_set_ep_state(xhci, epctx, EP_HALTED); - DPRINTF("xhci: stalled slot %d ep %d\n", xfer->slotid, xfer->epid); - DPRINTF("xhci: will continue at "DMA_ADDR_FMT"\n", epctx->ring.dequeue); + if (epctx->nr_pstreams) { + sctx = xhci_find_stream(epctx, xfer->streamid, &err); + if (sctx == NULL) { + return; + } + sctx->ring.dequeue = xfer->trbs[0].addr; + sctx->ring.ccs = xfer->trbs[0].ccs; + xhci_set_ep_state(xhci, epctx, sctx, EP_HALTED); + } else { + epctx->ring.dequeue = xfer->trbs[0].addr; + epctx->ring.ccs = xfer->trbs[0].ccs; + xhci_set_ep_state(xhci, epctx, NULL, EP_HALTED); + } } static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, @@ -1510,8 +1672,8 @@ static int xhci_setup_packet(XHCITransfer *xfer) } xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */ - usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr, false, - xfer->int_req); + usb_packet_setup(&xfer->packet, dir, ep, xfer->streamid, + xfer->trbs[0].addr, false, xfer->int_req); usb_packet_map(&xfer->packet, &xfer->sgl); DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", xfer->packet.pid, dev->addr, ep->nr); @@ -1564,7 +1726,7 @@ static int xhci_complete_packet(XHCITransfer *xfer) default: fprintf(stderr, "%s: FIXME: status = %d\n", __func__, xfer->packet.status); - FIXME(); + FIXME("unhandled USB_RET_*"); } return 0; } @@ -1577,7 +1739,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) trb_setup = &xfer->trbs[0]; trb_status = &xfer->trbs[xfer->trb_count-1]; - trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid); + trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid); /* at most one Event Data TRB allowed after STATUS */ if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) { @@ -1619,7 +1781,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) xhci_complete_packet(xfer); if (!xfer->running_async && !xfer->running_retry) { - xhci_kick_ep(xhci, xfer->slotid, xfer->epid); + xhci_kick_ep(xhci, xfer->slotid, xfer->epid, 0); } return 0; } @@ -1702,26 +1864,29 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx xhci_complete_packet(xfer); if (!xfer->running_async && !xfer->running_retry) { - xhci_kick_ep(xhci, xfer->slotid, xfer->epid); + xhci_kick_ep(xhci, xfer->slotid, xfer->epid, xfer->streamid); } return 0; } static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx) { - trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid); + trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid); return xhci_submit(xhci, xfer, epctx); } -static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid) +static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, + unsigned int epid, unsigned int streamid) { + XHCIStreamContext *stctx; XHCIEPContext *epctx; + XHCIRing *ring; USBEndpoint *ep = NULL; uint64_t mfindex; int length; int i; - trace_usb_xhci_ep_kick(slotid, epid); + trace_usb_xhci_ep_kick(slotid, epid, streamid); assert(slotid >= 1 && slotid <= xhci->numslots); assert(epid >= 1 && epid <= 31); @@ -1774,14 +1939,28 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid return; } - xhci_set_ep_state(xhci, epctx, EP_RUNNING); + + if (epctx->nr_pstreams) { + uint32_t err; + stctx = xhci_find_stream(epctx, streamid, &err); + if (stctx == NULL) { + return; + } + ring = &stctx->ring; + xhci_set_ep_state(xhci, epctx, stctx, EP_RUNNING); + } else { + ring = &epctx->ring; + streamid = 0; + xhci_set_ep_state(xhci, epctx, NULL, EP_RUNNING); + } + assert(ring->base != 0); while (1) { XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer]; if (xfer->running_async || xfer->running_retry) { break; } - length = xhci_ring_chain_length(xhci, &epctx->ring); + length = xhci_ring_chain_length(xhci, ring); if (length < 0) { break; } else if (length == 0) { @@ -1800,11 +1979,12 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid xfer->trb_count = length; for (i = 0; i < length; i++) { - assert(xhci_ring_fetch(xhci, &epctx->ring, &xfer->trbs[i], NULL)); + assert(xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL)); } xfer->xhci = xhci; xfer->epid = epid; xfer->slotid = slotid; + xfer->streamid = streamid; if (epid == 1) { if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) { @@ -1963,13 +2143,18 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, if (bsr) { slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT; } else { + USBPacket p; slot->devaddr = xhci->devaddr++; slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot->devaddr; DPRINTF("xhci: device address is %d\n", slot->devaddr); usb_device_reset(dev); - usb_device_handle_control(dev, NULL, + usb_packet_setup(&p, USB_TOKEN_OUT, + usb_ep_get(dev, USB_TOKEN_OUT, 0), 0, + 0, false, false); + usb_device_handle_control(dev, &p, DeviceOutRequest | USB_REQ_SET_ADDRESS, slot->devaddr, 0, 0, NULL); + assert(p.status != USB_RET_ASYNC); } res = xhci_enable_ep(xhci, slotid, 1, octx+32, ep0_ctx); @@ -2186,6 +2371,28 @@ static unsigned int xhci_get_slot(XHCIState *xhci, XHCIEvent *event, XHCITRB *tr return slotid; } +/* cleanup slot state on usb device detach */ +static void xhci_detach_slot(XHCIState *xhci, USBPort *uport) +{ + int slot, ep; + + for (slot = 0; slot < xhci->numslots; slot++) { + if (xhci->slots[slot].uport == uport) { + break; + } + } + if (slot == xhci->numslots) { + return; + } + + for (ep = 0; ep < 31; ep++) { + if (xhci->slots[slot].eps[ep]) { + xhci_ep_nuke_xfers(xhci, slot+1, ep+1); + } + } + xhci->slots[slot].uport = NULL; +} + static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx) { dma_addr_t ctx; @@ -2322,11 +2529,14 @@ static void xhci_process_commands(XHCIState *xhci) } break; case CR_SET_TR_DEQUEUE: + fprintf(stderr, "%s: CR_SET_TR_DEQUEUE\n", __func__); slotid = xhci_get_slot(xhci, &event, &trb); if (slotid) { unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT) & TRB_CR_EPID_MASK; - event.ccode = xhci_set_ep_dequeue(xhci, slotid, epid, + unsigned int streamid = (trb.status >> 16) & 0xffff; + event.ccode = xhci_set_ep_dequeue(xhci, slotid, + epid, streamid, trb.parameter); } break; @@ -2519,9 +2729,9 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size) break; case 0x10: /* HCCPARAMS */ if (sizeof(dma_addr_t) == 4) { - ret = 0x00081000; + ret = 0x00087000; } else { - ret = 0x00081001; + ret = 0x00087001; } break; case 0x14: /* DBOFF */ @@ -2845,6 +3055,7 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg, uint64_t val, unsigned size) { XHCIState *xhci = ptr; + unsigned int epid, streamid; trace_usb_xhci_doorbell_write(reg, val); @@ -2863,13 +3074,15 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg, (uint32_t)val); } } else { + epid = val & 0xff; + streamid = (val >> 16) & 0xffff; if (reg > xhci->numslots) { fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg); - } else if (val > 31) { + } else if (epid > 31) { fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n", (int)reg, (uint32_t)val); } else { - xhci_kick_ep(xhci, reg, val); + xhci_kick_ep(xhci, reg, epid, streamid); } } } @@ -2928,6 +3141,7 @@ static void xhci_detach(USBPort *usbport) XHCIState *xhci = usbport->opaque; XHCIPort *port = xhci_lookup_port(xhci, usbport); + xhci_detach_slot(xhci, usbport); xhci_port_update(port, 1); } @@ -2952,20 +3166,15 @@ static void xhci_complete(USBPort *port, USBPacket *packet) return; } xhci_complete_packet(xfer); - xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid); + xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid, xfer->streamid); } static void xhci_child_detach(USBPort *uport, USBDevice *child) { USBBus *bus = usb_bus_from_device(child); XHCIState *xhci = container_of(bus, XHCIState, bus); - int i; - for (i = 0; i < xhci->numslots; i++) { - if (xhci->slots[i].uport == uport) { - xhci->slots[i].uport = NULL; - } - } + xhci_detach_slot(xhci, uport); } static USBPortOps xhci_uport_ops = { @@ -3002,7 +3211,8 @@ static int xhci_find_epid(USBEndpoint *ep) } } -static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep) +static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep, + unsigned int stream) { XHCIState *xhci = container_of(bus, XHCIState, bus); int slotid; @@ -3013,7 +3223,7 @@ static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep) DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr); return; } - xhci_kick_ep(xhci, slotid, xhci_find_epid(ep)); + xhci_kick_ep(xhci, slotid, xhci_find_epid(ep), stream); } static USBBusOps xhci_bus_ops = { @@ -3170,7 +3380,7 @@ static void xhci_class_init(ObjectClass *klass, void *data) k->no_hotplug = 1; } -static TypeInfo xhci_info = { +static const TypeInfo xhci_info = { .name = "nec-usb-xhci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(XHCIState), diff --git a/hw/usb/host-bsd.c b/hw/usb/host-bsd.c index 340c21aeb4..39f22810b3 100644 --- a/hw/usb/host-bsd.c +++ b/hw/usb/host-bsd.c @@ -407,7 +407,7 @@ static void usb_host_class_initfn(ObjectClass *klass, void *data) uc->handle_destroy = usb_host_handle_destroy; } -static TypeInfo usb_host_dev_info = { +static const TypeInfo usb_host_dev_info = { .name = "usb-host", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBHostDevice), @@ -633,13 +633,7 @@ static int usb_host_info_device(void *opaque, return 0; } -void usb_host_info(Monitor *mon) +void usb_host_info(Monitor *mon, const QDict *qdict) { usb_host_scan(mon, usb_host_info_device); } - -/* XXX add this */ -int usb_host_device_close(const char *devname) -{ - return 0; -} diff --git a/hw/usb/host-legacy.c b/hw/usb/host-legacy.c new file mode 100644 index 0000000000..3a5f705721 --- /dev/null +++ b/hw/usb/host-legacy.c @@ -0,0 +1,144 @@ +/* + * Linux host USB redirector + * + * Copyright (c) 2005 Fabrice Bellard + * + * Copyright (c) 2008 Max Krasnyansky + * Support for host device auto connect & disconnect + * Major rewrite to support fully async operation + * + * Copyright 2008 TJ + * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition + * to the legacy /proc/bus/usb USB device discovery and handling + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "hw/usb.h" +#include "hw/usb/host.h" + +/* + * Autoconnect filter + * Format: + * auto:bus:dev[:vid:pid] + * auto:bus.dev[:vid:pid] + * + * bus - bus number (dec, * means any) + * dev - device number (dec, * means any) + * vid - vendor id (hex, * means any) + * pid - product id (hex, * means any) + * + * See 'lsusb' output. + */ +static int parse_filter(const char *spec, struct USBAutoFilter *f) +{ + enum { BUS, DEV, VID, PID, DONE }; + const char *p = spec; + int i; + + f->bus_num = 0; + f->addr = 0; + f->vendor_id = 0; + f->product_id = 0; + + for (i = BUS; i < DONE; i++) { + p = strpbrk(p, ":."); + if (!p) { + break; + } + p++; + + if (*p == '*') { + continue; + } + switch (i) { + case BUS: + f->bus_num = strtol(p, NULL, 10); + break; + case DEV: + f->addr = strtol(p, NULL, 10); + break; + case VID: + f->vendor_id = strtol(p, NULL, 16); + break; + case PID: + f->product_id = strtol(p, NULL, 16); + break; + } + } + + if (i < DEV) { + fprintf(stderr, "husb: invalid auto filter spec %s\n", spec); + return -1; + } + + return 0; +} + +USBDevice *usb_host_device_open(USBBus *bus, const char *devname) +{ + struct USBAutoFilter filter; + USBDevice *dev; + char *p; + + dev = usb_create(bus, "usb-host"); + + if (strstr(devname, "auto:")) { + if (parse_filter(devname, &filter) < 0) { + goto fail; + } + } else { + p = strchr(devname, '.'); + if (p) { + filter.bus_num = strtoul(devname, NULL, 0); + filter.addr = strtoul(p + 1, NULL, 0); + filter.vendor_id = 0; + filter.product_id = 0; + } else { + p = strchr(devname, ':'); + if (p) { + filter.bus_num = 0; + filter.addr = 0; + filter.vendor_id = strtoul(devname, NULL, 16); + filter.product_id = strtoul(p + 1, NULL, 16); + } else { + goto fail; + } + } + } + + qdev_prop_set_uint32(&dev->qdev, "hostbus", filter.bus_num); + qdev_prop_set_uint32(&dev->qdev, "hostaddr", filter.addr); + qdev_prop_set_uint32(&dev->qdev, "vendorid", filter.vendor_id); + qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id); + qdev_init_nofail(&dev->qdev); + return dev; + +fail: + qdev_free(&dev->qdev); + return NULL; +} + +static void usb_host_register_types(void) +{ + usb_legacy_register("usb-host", "host", usb_host_device_open); +} + +type_init(usb_host_register_types) diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c index 669fbd245c..b67aeba096 100644 --- a/hw/usb/host-linux.c +++ b/hw/usb/host-linux.c @@ -43,6 +43,7 @@ #include #include "hw/usb.h" #include "hw/usb/desc.h" +#include "hw/usb/host.h" /* We redefine it to avoid version problems */ struct usb_ctrltransfer { @@ -87,14 +88,6 @@ struct endp_data { int inflight; }; -struct USBAutoFilter { - uint32_t bus_num; - uint32_t addr; - char *port; - uint32_t vendor_id; - uint32_t product_id; -}; - enum USBHostDeviceOptions { USB_HOST_OPT_PIPELINE, }; @@ -131,7 +124,6 @@ typedef struct USBHostDevice { static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs); static int usb_host_close(USBHostDevice *dev); -static int parse_filter(const char *spec, struct USBAutoFilter *f); static void usb_host_auto_check(void *unused); static int usb_host_read_file(char *line, size_t line_size, const char *device_file, const char *device_name); @@ -1314,7 +1306,7 @@ static int usb_host_open(USBHostDevice *dev, int bus_num, dev->bus_num = bus_num; dev->addr = addr; - strcpy(dev->port, port); + pstrcpy(dev->port, sizeof(dev->port), port); dev->fd = fd; /* read the device description */ @@ -1534,7 +1526,7 @@ static void usb_host_class_initfn(ObjectClass *klass, void *data) dc->props = usb_host_dev_properties; } -static TypeInfo usb_host_dev_info = { +static const TypeInfo usb_host_dev_info = { .name = "usb-host", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBHostDevice), @@ -1544,75 +1536,10 @@ static TypeInfo usb_host_dev_info = { static void usb_host_register_types(void) { type_register_static(&usb_host_dev_info); - usb_legacy_register("usb-host", "host", usb_host_device_open); } type_init(usb_host_register_types) -USBDevice *usb_host_device_open(USBBus *bus, const char *devname) -{ - struct USBAutoFilter filter; - USBDevice *dev; - char *p; - - dev = usb_create(bus, "usb-host"); - - if (strstr(devname, "auto:")) { - if (parse_filter(devname, &filter) < 0) { - goto fail; - } - } else { - if ((p = strchr(devname, '.'))) { - filter.bus_num = strtoul(devname, NULL, 0); - filter.addr = strtoul(p + 1, NULL, 0); - filter.vendor_id = 0; - filter.product_id = 0; - } else if ((p = strchr(devname, ':'))) { - filter.bus_num = 0; - filter.addr = 0; - filter.vendor_id = strtoul(devname, NULL, 16); - filter.product_id = strtoul(p + 1, NULL, 16); - } else { - goto fail; - } - } - - qdev_prop_set_uint32(&dev->qdev, "hostbus", filter.bus_num); - qdev_prop_set_uint32(&dev->qdev, "hostaddr", filter.addr); - qdev_prop_set_uint32(&dev->qdev, "vendorid", filter.vendor_id); - qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id); - qdev_init_nofail(&dev->qdev); - return dev; - -fail: - qdev_free(&dev->qdev); - return NULL; -} - -int usb_host_device_close(const char *devname) -{ -#if 0 - char product_name[PRODUCT_NAME_SZ]; - int bus_num, addr; - USBHostDevice *s; - - if (strstr(devname, "auto:")) { - return usb_host_auto_del(devname); - } - if (usb_host_find_device(&bus_num, &addr, product_name, - sizeof(product_name), devname) < 0) { - return -1; - } - s = hostdev_find(bus_num, addr); - if (s) { - usb_device_delete_addr(s->bus_num, s->dev.addr); - return 0; - } -#endif - - return -1; -} - /* * Read sys file-system device file * @@ -1760,7 +1687,7 @@ static int usb_host_auto_scan(void *opaque, int bus_num, if (f->addr > 0 && f->addr != addr) { continue; } - if (f->port != NULL && (port == NULL || strcmp(f->port, port) != 0)) { + if (f->port != NULL && strcmp(f->port, port) != 0) { continue; } @@ -1840,56 +1767,6 @@ static void usb_host_auto_check(void *unused) qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000); } -/* - * Autoconnect filter - * Format: - * auto:bus:dev[:vid:pid] - * auto:bus.dev[:vid:pid] - * - * bus - bus number (dec, * means any) - * dev - device number (dec, * means any) - * vid - vendor id (hex, * means any) - * pid - product id (hex, * means any) - * - * See 'lsusb' output. - */ -static int parse_filter(const char *spec, struct USBAutoFilter *f) -{ - enum { BUS, DEV, VID, PID, DONE }; - const char *p = spec; - int i; - - f->bus_num = 0; - f->addr = 0; - f->vendor_id = 0; - f->product_id = 0; - - for (i = BUS; i < DONE; i++) { - p = strpbrk(p, ":."); - if (!p) { - break; - } - p++; - - if (*p == '*') { - continue; - } - switch(i) { - case BUS: f->bus_num = strtol(p, NULL, 10); break; - case DEV: f->addr = strtol(p, NULL, 10); break; - case VID: f->vendor_id = strtol(p, NULL, 16); break; - case PID: f->product_id = strtol(p, NULL, 16); break; - } - } - - if (i < DEV) { - fprintf(stderr, "husb: invalid auto filter spec %s\n", spec); - return -1; - } - - return 0; -} - /**********************/ /* USB host device info */ @@ -1998,7 +1875,7 @@ static void hex2str(int val, char *str, size_t size) } } -void usb_host_info(Monitor *mon) +void usb_host_info(Monitor *mon, const QDict *qdict) { struct USBAutoFilter *f; struct USBHostDevice *s; diff --git a/hw/usb/host-stub.c b/hw/usb/host-stub.c index 58423a0f5c..28d8032caf 100644 --- a/hw/usb/host-stub.c +++ b/hw/usb/host-stub.c @@ -35,7 +35,7 @@ #include "hw/usb.h" #include "monitor/monitor.h" -void usb_host_info(Monitor *mon) +void usb_host_info(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "USB host devices not supported\n"); } @@ -45,8 +45,3 @@ USBDevice *usb_host_device_open(USBBus *bus, const char *devname) { return NULL; } - -int usb_host_device_close(const char *devname) -{ - return 0; -} diff --git a/hw/baum.h b/hw/usb/host.h similarity index 63% rename from hw/baum.h rename to hw/usb/host.h index 763588422a..048ff3b482 100644 --- a/hw/baum.h +++ b/hw/usb/host.h @@ -1,7 +1,15 @@ /* - * QEMU Baum + * Linux host USB redirector * - * Copyright (c) 2008 Samuel Thibault + * Copyright (c) 2005 Fabrice Bellard + * + * Copyright (c) 2008 Max Krasnyansky + * Support for host device auto connect & disconnect + * Major rewrite to support fully async operation + * + * Copyright 2008 TJ + * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition + * to the legacy /proc/bus/usb USB device discovery and handling * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,10 +29,16 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef HW_BAUM_H -#define HW_BAUM_H 1 -/* char device */ -CharDriverState *chr_baum_init(QemuOpts *opts); +#ifndef QEMU_USB_HOST_H +#define QEMU_USB_HOST_H -#endif +struct USBAutoFilter { + uint32_t bus_num; + uint32_t addr; + char *port; + uint32_t vendor_id; + uint32_t product_id; +}; + +#endif /* QEMU_USB_HOST_H */ diff --git a/hw/usb/quirks-ftdi-ids.h b/hw/usb/quirks-ftdi-ids.h new file mode 100644 index 0000000000..57c12ef662 --- /dev/null +++ b/hw/usb/quirks-ftdi-ids.h @@ -0,0 +1,1255 @@ +/* + * vendor/product IDs (VID/PID) of devices using FTDI USB serial converters. + * Please keep numerically sorted within individual areas, thanks! + * + * Philipp Gühring - pg@futureware.at - added the Device ID of the USB relais + * from Rudolf Gugler + * + */ + + +/**********************************/ +/***** devices using FTDI VID *****/ +/**********************************/ + + +#define FTDI_VID 0x0403 /* Vendor Id */ + + +/*** "original" FTDI device PIDs ***/ + +#define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */ +#define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */ +#define FTDI_8U2232C_PID 0x6010 /* Dual channel device */ +#define FTDI_4232H_PID 0x6011 /* Quad channel hi-speed device */ +#define FTDI_232H_PID 0x6014 /* Single channel hi-speed device */ +#define FTDI_FTX_PID 0x6015 /* FT-X series (FT201X, FT230X, FT231X, etc) */ +#define FTDI_SIO_PID 0x8372 /* Product Id SIO application of 8U100AX */ +#define FTDI_232RL_PID 0xFBFA /* Product ID for FT232RL */ + + +/*** third-party PIDs (using FTDI_VID) ***/ + +#define FTDI_LUMEL_PD12_PID 0x6002 + +/* + * Marvell OpenRD Base, Client + * http://www.open-rd.org + * OpenRD Base, Client use VID 0x0403 + */ +#define MARVELL_OPENRD_PID 0x9e90 + +/* www.candapter.com Ewert Energy Systems CANdapter device */ +#define FTDI_CANDAPTER_PID 0x9F80 /* Product Id */ + +/* + * Texas Instruments XDS100v2 JTAG / BeagleBone A3 + * http://processors.wiki.ti.com/index.php/XDS100 + * http://beagleboard.org/bone + */ +#define TI_XDS100V2_PID 0xa6d0 + +#define FTDI_NXTCAM_PID 0xABB8 /* NXTCam for Mindstorms NXT */ + +/* US Interface Navigator (http://www.usinterface.com/) */ +#define FTDI_USINT_CAT_PID 0xb810 /* Navigator CAT and 2nd PTT lines */ +#define FTDI_USINT_WKEY_PID 0xb811 /* Navigator WKEY and FSK lines */ +#define FTDI_USINT_RS232_PID 0xb812 /* Navigator RS232 and CONFIG lines */ + +/* OOCDlink by Joern Kaipf + * (http://www.joernonline.de/) */ +#define FTDI_OOCDLINK_PID 0xbaf8 /* Amontec JTAGkey */ + +/* Luminary Micro Stellaris Boards, VID = FTDI_VID */ +/* FTDI 2332C Dual channel device, side A=245 FIFO (JTAG), Side B=RS232 UART */ +#define LMI_LM3S_DEVEL_BOARD_PID 0xbcd8 +#define LMI_LM3S_EVAL_BOARD_PID 0xbcd9 +#define LMI_LM3S_ICDI_BOARD_PID 0xbcda + +#define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmbH */ + +/* OpenDCC (www.opendcc.de) product id */ +#define FTDI_OPENDCC_PID 0xBFD8 +#define FTDI_OPENDCC_SNIFFER_PID 0xBFD9 +#define FTDI_OPENDCC_THROTTLE_PID 0xBFDA +#define FTDI_OPENDCC_GATEWAY_PID 0xBFDB +#define FTDI_OPENDCC_GBM_PID 0xBFDC + +/* NZR SEM 16+ USB (http://www.nzr.de) */ +#define FTDI_NZR_SEM_USB_PID 0xC1E0 /* NZR SEM-LOG16+ */ + +/* + * RR-CirKits LocoBuffer USB (http://www.rr-cirkits.com) + */ +#define FTDI_RRCIRKITS_LOCOBUFFER_PID 0xc7d0 /* LocoBuffer USB */ + +/* DMX4ALL DMX Interfaces */ +#define FTDI_DMX4ALL 0xC850 + +/* + * ASK.fr devices + */ +#define FTDI_ASK_RDR400_PID 0xC991 /* ASK RDR 400 series card reader */ + +/* www.starting-point-systems.com µChameleon device */ +#define FTDI_MICRO_CHAMELEON_PID 0xCAA0 /* Product Id */ + +/* + * Tactrix OpenPort (ECU) devices. + * OpenPort 1.3M submitted by Donour Sizemore. + * OpenPort 1.3S and 1.3U submitted by Ian Abbott. + */ +#define FTDI_TACTRIX_OPENPORT_13M_PID 0xCC48 /* OpenPort 1.3 Mitsubishi */ +#define FTDI_TACTRIX_OPENPORT_13S_PID 0xCC49 /* OpenPort 1.3 Subaru */ +#define FTDI_TACTRIX_OPENPORT_13U_PID 0xCC4A /* OpenPort 1.3 Universal */ + +#define FTDI_DISTORTEC_JTAG_LOCK_PICK_PID 0xCFF8 + +/* SCS HF Radio Modems PID's (http://www.scs-ptc.com) */ +/* the VID is the standard ftdi vid (FTDI_VID) */ +#define FTDI_SCS_DEVICE_0_PID 0xD010 /* SCS PTC-IIusb */ +#define FTDI_SCS_DEVICE_1_PID 0xD011 /* SCS Tracker / DSP TNC */ +#define FTDI_SCS_DEVICE_2_PID 0xD012 +#define FTDI_SCS_DEVICE_3_PID 0xD013 +#define FTDI_SCS_DEVICE_4_PID 0xD014 +#define FTDI_SCS_DEVICE_5_PID 0xD015 +#define FTDI_SCS_DEVICE_6_PID 0xD016 +#define FTDI_SCS_DEVICE_7_PID 0xD017 + +/* iPlus device */ +#define FTDI_IPLUS_PID 0xD070 /* Product Id */ +#define FTDI_IPLUS2_PID 0xD071 /* Product Id */ + +/* + * Gamma Scout (http://gamma-scout.com/). Submitted by rsc@runtux.com. + */ +#define FTDI_GAMMA_SCOUT_PID 0xD678 /* Gamma Scout online */ + +/* Propox devices */ +#define FTDI_PROPOX_JTAGCABLEII_PID 0xD738 +#define FTDI_PROPOX_ISPCABLEIII_PID 0xD739 + +/* Lenz LI-USB Computer Interface. */ +#define FTDI_LENZ_LIUSB_PID 0xD780 + +/* Vardaan Enterprises Serial Interface VEUSB422R3 */ +#define FTDI_VARDAAN_PID 0xF070 + +/* + * Xsens Technologies BV products (http://www.xsens.com). + */ +#define XSENS_CONVERTER_0_PID 0xD388 +#define XSENS_CONVERTER_1_PID 0xD389 +#define XSENS_CONVERTER_2_PID 0xD38A +#define XSENS_CONVERTER_3_PID 0xD38B +#define XSENS_CONVERTER_4_PID 0xD38C +#define XSENS_CONVERTER_5_PID 0xD38D +#define XSENS_CONVERTER_6_PID 0xD38E +#define XSENS_CONVERTER_7_PID 0xD38F + +/* + * NDI (www.ndigital.com) product ids + */ +#define FTDI_NDI_HUC_PID 0xDA70 /* NDI Host USB Converter */ +#define FTDI_NDI_SPECTRA_SCU_PID 0xDA71 /* NDI Spectra SCU */ +#define FTDI_NDI_FUTURE_2_PID 0xDA72 /* NDI future device #2 */ +#define FTDI_NDI_FUTURE_3_PID 0xDA73 /* NDI future device #3 */ +#define FTDI_NDI_AURORA_SCU_PID 0xDA74 /* NDI Aurora SCU */ + +/* + * ChamSys Limited (www.chamsys.co.uk) USB wing/interface product IDs + */ +#define FTDI_CHAMSYS_24_MASTER_WING_PID 0xDAF8 +#define FTDI_CHAMSYS_PC_WING_PID 0xDAF9 +#define FTDI_CHAMSYS_USB_DMX_PID 0xDAFA +#define FTDI_CHAMSYS_MIDI_TIMECODE_PID 0xDAFB +#define FTDI_CHAMSYS_MINI_WING_PID 0xDAFC +#define FTDI_CHAMSYS_MAXI_WING_PID 0xDAFD +#define FTDI_CHAMSYS_MEDIA_WING_PID 0xDAFE +#define FTDI_CHAMSYS_WING_PID 0xDAFF + +/* + * Westrex International devices submitted by Cory Lee + */ +#define FTDI_WESTREX_MODEL_777_PID 0xDC00 /* Model 777 */ +#define FTDI_WESTREX_MODEL_8900F_PID 0xDC01 /* Model 8900F */ + +/* + * ACG Identification Technologies GmbH products (http://www.acg.de/). + * Submitted by anton -at- goto10 -dot- org. + */ +#define FTDI_ACG_HFDUAL_PID 0xDD20 /* HF Dual ISO Reader (RFID) */ + +/* + * Definitions for Artemis astronomical USB based cameras + * Check it at http://www.artemisccd.co.uk/ + */ +#define FTDI_ARTEMIS_PID 0xDF28 /* All Artemis Cameras */ + +/* + * Definitions for ATIK Instruments astronomical USB based cameras + * Check it at http://www.atik-instruments.com/ + */ +#define FTDI_ATIK_ATK16_PID 0xDF30 /* ATIK ATK-16 Grayscale Camera */ +#define FTDI_ATIK_ATK16C_PID 0xDF32 /* ATIK ATK-16C Colour Camera */ +#define FTDI_ATIK_ATK16HR_PID 0xDF31 /* ATIK ATK-16HR Grayscale Camera */ +#define FTDI_ATIK_ATK16HRC_PID 0xDF33 /* ATIK ATK-16HRC Colour Camera */ +#define FTDI_ATIK_ATK16IC_PID 0xDF35 /* ATIK ATK-16IC Grayscale Camera */ + +/* + * Yost Engineering, Inc. products (www.yostengineering.com). + * PID 0xE050 submitted by Aaron Prose. + */ +#define FTDI_YEI_SERVOCENTER31_PID 0xE050 /* YEI ServoCenter3.1 USB */ + +/* + * ELV USB devices submitted by Christian Abt of ELV (www.elv.de). + * All of these devices use FTDI's vendor ID (0x0403). + * Further IDs taken from ELV Windows .inf file. + * + * The previously included PID for the UO 100 module was incorrect. + * In fact, that PID was for ELV's UR 100 USB-RS232 converter (0xFB58). + * + * Armin Laeuger originally sent the PID for the UM 100 module. + */ +#define FTDI_ELV_USR_PID 0xE000 /* ELV Universal-Sound-Recorder */ +#define FTDI_ELV_MSM1_PID 0xE001 /* ELV Mini-Sound-Modul */ +#define FTDI_ELV_KL100_PID 0xE002 /* ELV Kfz-Leistungsmesser KL 100 */ +#define FTDI_ELV_WS550_PID 0xE004 /* WS 550 */ +#define FTDI_ELV_EC3000_PID 0xE006 /* ENERGY CONTROL 3000 USB */ +#define FTDI_ELV_WS888_PID 0xE008 /* WS 888 */ +#define FTDI_ELV_TWS550_PID 0xE009 /* Technoline WS 550 */ +#define FTDI_ELV_FEM_PID 0xE00A /* Funk Energie Monitor */ +#define FTDI_ELV_FHZ1300PC_PID 0xE0E8 /* FHZ 1300 PC */ +#define FTDI_ELV_WS500_PID 0xE0E9 /* PC-Wetterstation (WS 500) */ +#define FTDI_ELV_HS485_PID 0xE0EA /* USB to RS-485 adapter */ +#define FTDI_ELV_UMS100_PID 0xE0EB /* ELV USB Master-Slave Schaltsteckdose UMS 100 */ +#define FTDI_ELV_TFD128_PID 0xE0EC /* ELV Temperatur-Feuchte-Datenlogger TFD 128 */ +#define FTDI_ELV_FM3RX_PID 0xE0ED /* ELV Messwertuebertragung FM3 RX */ +#define FTDI_ELV_WS777_PID 0xE0EE /* Conrad WS 777 */ +#define FTDI_ELV_EM1010PC_PID 0xE0EF /* Energy monitor EM 1010 PC */ +#define FTDI_ELV_CSI8_PID 0xE0F0 /* Computer-Schalt-Interface (CSI 8) */ +#define FTDI_ELV_EM1000DL_PID 0xE0F1 /* PC-Datenlogger fuer Energiemonitor (EM 1000 DL) */ +#define FTDI_ELV_PCK100_PID 0xE0F2 /* PC-Kabeltester (PCK 100) */ +#define FTDI_ELV_RFP500_PID 0xE0F3 /* HF-Leistungsmesser (RFP 500) */ +#define FTDI_ELV_FS20SIG_PID 0xE0F4 /* Signalgeber (FS 20 SIG) */ +#define FTDI_ELV_UTP8_PID 0xE0F5 /* ELV UTP 8 */ +#define FTDI_ELV_WS300PC_PID 0xE0F6 /* PC-Wetterstation (WS 300 PC) */ +#define FTDI_ELV_WS444PC_PID 0xE0F7 /* Conrad WS 444 PC */ +#define FTDI_PHI_FISCO_PID 0xE40B /* PHI Fisco USB to Serial cable */ +#define FTDI_ELV_UAD8_PID 0xF068 /* USB-AD-Wandler (UAD 8) */ +#define FTDI_ELV_UDA7_PID 0xF069 /* USB-DA-Wandler (UDA 7) */ +#define FTDI_ELV_USI2_PID 0xF06A /* USB-Schrittmotoren-Interface (USI 2) */ +#define FTDI_ELV_T1100_PID 0xF06B /* Thermometer (T 1100) */ +#define FTDI_ELV_PCD200_PID 0xF06C /* PC-Datenlogger (PCD 200) */ +#define FTDI_ELV_ULA200_PID 0xF06D /* USB-LCD-Ansteuerung (ULA 200) */ +#define FTDI_ELV_ALC8500_PID 0xF06E /* ALC 8500 Expert */ +#define FTDI_ELV_FHZ1000PC_PID 0xF06F /* FHZ 1000 PC */ +#define FTDI_ELV_UR100_PID 0xFB58 /* USB-RS232-Umsetzer (UR 100) */ +#define FTDI_ELV_UM100_PID 0xFB5A /* USB-Modul UM 100 */ +#define FTDI_ELV_UO100_PID 0xFB5B /* USB-Modul UO 100 */ +/* Additional ELV PIDs that default to using the FTDI D2XX drivers on + * MS Windows, rather than the FTDI Virtual Com Port drivers. + * Maybe these will be easier to use with the libftdi/libusb user-space + * drivers, or possibly the Comedi drivers in some cases. */ +#define FTDI_ELV_CLI7000_PID 0xFB59 /* Computer-Light-Interface (CLI 7000) */ +#define FTDI_ELV_PPS7330_PID 0xFB5C /* Processor-Power-Supply (PPS 7330) */ +#define FTDI_ELV_TFM100_PID 0xFB5D /* Temperatur-Feuchte-Messgeraet (TFM 100) */ +#define FTDI_ELV_UDF77_PID 0xFB5E /* USB DCF Funkuhr (UDF 77) */ +#define FTDI_ELV_UIO88_PID 0xFB5F /* USB-I/O Interface (UIO 88) */ + +/* + * EVER Eco Pro UPS (http://www.ever.com.pl/) + */ + +#define EVER_ECO_PRO_CDS 0xe520 /* RS-232 converter */ + +/* + * Active Robots product ids. + */ +#define FTDI_ACTIVE_ROBOTS_PID 0xE548 /* USB comms board */ + +/* Pyramid Computer GmbH */ +#define FTDI_PYRAMID_PID 0xE6C8 /* Pyramid Appliance Display */ + +/* www.elsterelectricity.com Elster Unicom III Optical Probe */ +#define FTDI_ELSTER_UNICOM_PID 0xE700 /* Product Id */ + +/* + * Gude Analog- und Digitalsysteme GmbH + */ +#define FTDI_GUDEADS_E808_PID 0xE808 +#define FTDI_GUDEADS_E809_PID 0xE809 +#define FTDI_GUDEADS_E80A_PID 0xE80A +#define FTDI_GUDEADS_E80B_PID 0xE80B +#define FTDI_GUDEADS_E80C_PID 0xE80C +#define FTDI_GUDEADS_E80D_PID 0xE80D +#define FTDI_GUDEADS_E80E_PID 0xE80E +#define FTDI_GUDEADS_E80F_PID 0xE80F +#define FTDI_GUDEADS_E888_PID 0xE888 /* Expert ISDN Control USB */ +#define FTDI_GUDEADS_E889_PID 0xE889 /* USB RS-232 OptoBridge */ +#define FTDI_GUDEADS_E88A_PID 0xE88A +#define FTDI_GUDEADS_E88B_PID 0xE88B +#define FTDI_GUDEADS_E88C_PID 0xE88C +#define FTDI_GUDEADS_E88D_PID 0xE88D +#define FTDI_GUDEADS_E88E_PID 0xE88E +#define FTDI_GUDEADS_E88F_PID 0xE88F + +/* + * Eclo (http://www.eclo.pt/) product IDs. + * PID 0xEA90 submitted by Martin Grill. + */ +#define FTDI_ECLO_COM_1WIRE_PID 0xEA90 /* COM to 1-Wire USB adaptor */ + +/* TNC-X USB-to-packet-radio adapter, versions prior to 3.0 (DLP module) */ +#define FTDI_TNC_X_PID 0xEBE0 + +/* + * Teratronik product ids. + * Submitted by O. Wölfelschneider. + */ +#define FTDI_TERATRONIK_VCP_PID 0xEC88 /* Teratronik device (preferring VCP driver on windows) */ +#define FTDI_TERATRONIK_D2XX_PID 0xEC89 /* Teratronik device (preferring D2XX driver on windows) */ + +/* Rig Expert Ukraine devices */ +#define FTDI_REU_TINY_PID 0xED22 /* RigExpert Tiny */ + +/* + * Hameg HO820 and HO870 interface (using VID 0x0403) + */ +#define HAMEG_HO820_PID 0xed74 +#define HAMEG_HO730_PID 0xed73 +#define HAMEG_HO720_PID 0xed72 +#define HAMEG_HO870_PID 0xed71 + +/* + * MaxStream devices www.maxstream.net + */ +#define FTDI_MAXSTREAM_PID 0xEE18 /* Xbee PKG-U Module */ + +/* + * microHAM product IDs (http://www.microham.com). + * Submitted by Justin Burket (KL1RL) + * and Mike Studer (K6EEP) . + * Ian Abbott added a few more from the driver INF file. + */ +#define FTDI_MHAM_KW_PID 0xEEE8 /* USB-KW interface */ +#define FTDI_MHAM_YS_PID 0xEEE9 /* USB-YS interface */ +#define FTDI_MHAM_Y6_PID 0xEEEA /* USB-Y6 interface */ +#define FTDI_MHAM_Y8_PID 0xEEEB /* USB-Y8 interface */ +#define FTDI_MHAM_IC_PID 0xEEEC /* USB-IC interface */ +#define FTDI_MHAM_DB9_PID 0xEEED /* USB-DB9 interface */ +#define FTDI_MHAM_RS232_PID 0xEEEE /* USB-RS232 interface */ +#define FTDI_MHAM_Y9_PID 0xEEEF /* USB-Y9 interface */ + +/* Domintell products http://www.domintell.com */ +#define FTDI_DOMINTELL_DGQG_PID 0xEF50 /* Master */ +#define FTDI_DOMINTELL_DUSB_PID 0xEF51 /* DUSB01 module */ + +/* + * The following are the values for the Perle Systems + * UltraPort USB serial converters + */ +#define FTDI_PERLE_ULTRAPORT_PID 0xF0C0 /* Perle UltraPort Product Id */ + +/* Sprog II (Andrew Crosland's SprogII DCC interface) */ +#define FTDI_SPROG_II 0xF0C8 + +/* an infrared receiver for user access control with IR tags */ +#define FTDI_PIEGROUP_PID 0xF208 /* Product Id */ + +/* ACT Solutions HomePro ZWave interface + (http://www.act-solutions.com/HomePro-Product-Matrix.html) */ +#define FTDI_ACTZWAVE_PID 0xF2D0 + +/* + * 4N-GALAXY.DE PIDs for CAN-USB, USB-RS232, USB-RS422, USB-RS485, + * USB-TTY aktiv, USB-TTY passiv. Some PIDs are used by several devices + * and I'm not entirely sure which are used by which. + */ +#define FTDI_4N_GALAXY_DE_1_PID 0xF3C0 +#define FTDI_4N_GALAXY_DE_2_PID 0xF3C1 +#define FTDI_4N_GALAXY_DE_3_PID 0xF3C2 + +/* + * Linx Technologies product ids + */ +#define LINX_SDMUSBQSS_PID 0xF448 /* Linx SDM-USB-QS-S */ +#define LINX_MASTERDEVEL2_PID 0xF449 /* Linx Master Development 2.0 */ +#define LINX_FUTURE_0_PID 0xF44A /* Linx future device */ +#define LINX_FUTURE_1_PID 0xF44B /* Linx future device */ +#define LINX_FUTURE_2_PID 0xF44C /* Linx future device */ + +/* + * Oceanic product ids + */ +#define FTDI_OCEANIC_PID 0xF460 /* Oceanic dive instrument */ + +/* + * SUUNTO product ids + */ +#define FTDI_SUUNTO_SPORTS_PID 0xF680 /* Suunto Sports instrument */ + +/* USB-UIRT - An infrared receiver and transmitter using the 8U232AM chip */ +/* http://www.usbuirt.com/ */ +#define FTDI_USB_UIRT_PID 0xF850 /* Product Id */ + +/* CCS Inc. ICDU/ICDU40 product ID - + * the FT232BM is used in an in-circuit-debugger unit for PIC16's/PIC18's */ +#define FTDI_CCSICDU20_0_PID 0xF9D0 +#define FTDI_CCSICDU40_1_PID 0xF9D1 +#define FTDI_CCSMACHX_2_PID 0xF9D2 +#define FTDI_CCSLOAD_N_GO_3_PID 0xF9D3 +#define FTDI_CCSICDU64_4_PID 0xF9D4 +#define FTDI_CCSPRIME8_5_PID 0xF9D5 + +/* + * The following are the values for the Matrix Orbital LCD displays, + * which are the FT232BM ( similar to the 8U232AM ) + */ +#define FTDI_MTXORB_0_PID 0xFA00 /* Matrix Orbital Product Id */ +#define FTDI_MTXORB_1_PID 0xFA01 /* Matrix Orbital Product Id */ +#define FTDI_MTXORB_2_PID 0xFA02 /* Matrix Orbital Product Id */ +#define FTDI_MTXORB_3_PID 0xFA03 /* Matrix Orbital Product Id */ +#define FTDI_MTXORB_4_PID 0xFA04 /* Matrix Orbital Product Id */ +#define FTDI_MTXORB_5_PID 0xFA05 /* Matrix Orbital Product Id */ +#define FTDI_MTXORB_6_PID 0xFA06 /* Matrix Orbital Product Id */ + +/* + * Home Electronics (www.home-electro.com) USB gadgets + */ +#define FTDI_HE_TIRA1_PID 0xFA78 /* Tira-1 IR transceiver */ + +/* Inside Accesso contactless reader (http://www.insidecontactless.com/) */ +#define INSIDE_ACCESSO 0xFAD0 + +/* + * ThorLabs USB motor drivers + */ +#define FTDI_THORLABS_PID 0xfaf0 /* ThorLabs USB motor drivers */ + +/* + * Protego product ids + */ +#define PROTEGO_SPECIAL_1 0xFC70 /* special/unknown device */ +#define PROTEGO_R2X0 0xFC71 /* R200-USB TRNG unit (R210, R220, and R230) */ +#define PROTEGO_SPECIAL_3 0xFC72 /* special/unknown device */ +#define PROTEGO_SPECIAL_4 0xFC73 /* special/unknown device */ + +/* + * Sony Ericsson product ids + */ +#define FTDI_DSS20_PID 0xFC82 /* DSS-20 Sync Station for Sony Ericsson P800 */ +#define FTDI_URBAN_0_PID 0xFC8A /* Sony Ericsson Urban, uart #0 */ +#define FTDI_URBAN_1_PID 0xFC8B /* Sony Ericsson Urban, uart #1 */ + +/* www.irtrans.de device */ +#define FTDI_IRTRANS_PID 0xFC60 /* Product Id */ + +/* + * RM Michaelides CANview USB (http://www.rmcan.com) (FTDI_VID) + * CAN fieldbus interface adapter, added by port GmbH www.port.de) + * Ian Abbott changed the macro names for consistency. + */ +#define FTDI_RM_CANVIEW_PID 0xfd60 /* Product Id */ +/* www.thoughttechnology.com/ TT-USB provide with procomp use ftdi_sio */ +#define FTDI_TTUSB_PID 0xFF20 /* Product Id */ + +#define FTDI_USBX_707_PID 0xF857 /* ADSTech IR Blaster USBX-707 (FTDI_VID) */ + +#define FTDI_RELAIS_PID 0xFA10 /* Relais device from Rudolf Gugler */ + +/* + * PCDJ use ftdi based dj-controllers. The following PID is + * for their DAC-2 device http://www.pcdjhardware.com/DAC2.asp + * (the VID is the standard ftdi vid (FTDI_VID), PID sent by Wouter Paesen) + */ +#define FTDI_PCDJ_DAC2_PID 0xFA88 + +#define FTDI_R2000KU_TRUE_RNG 0xFB80 /* R2000KU TRUE RNG (FTDI_VID) */ + +/* + * DIEBOLD BCS SE923 (FTDI_VID) + */ +#define DIEBOLD_BCS_SE923_PID 0xfb99 + +/* www.crystalfontz.com devices + * - thanx for providing free devices for evaluation ! + * they use the ftdi chipset for the USB interface + * and the vendor id is the same + */ +#define FTDI_XF_632_PID 0xFC08 /* 632: 16x2 Character Display */ +#define FTDI_XF_634_PID 0xFC09 /* 634: 20x4 Character Display */ +#define FTDI_XF_547_PID 0xFC0A /* 547: Two line Display */ +#define FTDI_XF_633_PID 0xFC0B /* 633: 16x2 Character Display with Keys */ +#define FTDI_XF_631_PID 0xFC0C /* 631: 20x2 Character Display */ +#define FTDI_XF_635_PID 0xFC0D /* 635: 20x4 Character Display */ +#define FTDI_XF_640_PID 0xFC0E /* 640: Two line Display */ +#define FTDI_XF_642_PID 0xFC0F /* 642: Two line Display */ + +/* + * Video Networks Limited / Homechoice in the UK use an ftdi-based device + * for their 1Mb broadband internet service. The following PID is exhibited + * by the usb device supplied (the VID is the standard ftdi vid (FTDI_VID) + */ +#define FTDI_VNHCPCUSB_D_PID 0xfe38 /* Product Id */ + +/* AlphaMicro Components AMC-232USB01 device (FTDI_VID) */ +#define FTDI_AMC232_PID 0xFF00 /* Product Id */ + +/* + * IBS elektronik product ids (FTDI_VID) + * Submitted by Thomas Schleusener + */ +#define FTDI_IBS_US485_PID 0xff38 /* IBS US485 (USB<-->RS422/485 interface) */ +#define FTDI_IBS_PICPRO_PID 0xff39 /* IBS PIC-Programmer */ +#define FTDI_IBS_PCMCIA_PID 0xff3a /* IBS Card reader for PCMCIA SRAM-cards */ +#define FTDI_IBS_PK1_PID 0xff3b /* IBS PK1 - Particel counter */ +#define FTDI_IBS_RS232MON_PID 0xff3c /* IBS RS232 - Monitor */ +#define FTDI_IBS_APP70_PID 0xff3d /* APP 70 (dust monitoring system) */ +#define FTDI_IBS_PEDO_PID 0xff3e /* IBS PEDO-Modem (RF modem 868.35 MHz) */ +#define FTDI_IBS_PROD_PID 0xff3f /* future device */ +/* www.canusb.com Lawicel CANUSB device (FTDI_VID) */ +#define FTDI_CANUSB_PID 0xFFA8 /* Product Id */ + +/* + * TavIR AVR product ids (FTDI_VID) + */ +#define FTDI_TAVIR_STK500_PID 0xFA33 /* STK500 AVR programmer */ + +/* + * TIAO product ids (FTDI_VID) + * http://www.tiaowiki.com/w/Main_Page + */ +#define FTDI_TIAO_UMPA_PID 0x8a98 /* TIAO/DIYGADGET USB Multi-Protocol Adapter */ + + +/********************************/ +/** third-party VID/PID combos **/ +/********************************/ + + + +/* + * Atmel STK541 + */ +#define ATMEL_VID 0x03eb /* Vendor ID */ +#define STK541_PID 0x2109 /* Zigbee Controller */ + +/* + * Blackfin gnICE JTAG + * http://docs.blackfin.uclinux.org/doku.php?id=hw:jtag:gnice + */ +#define ADI_VID 0x0456 +#define ADI_GNICE_PID 0xF000 +#define ADI_GNICEPLUS_PID 0xF001 + +/* + * Microchip Technology, Inc. + * + * MICROCHIP_VID (0x04D8) and MICROCHIP_USB_BOARD_PID (0x000A) are + * used by single function CDC ACM class based firmware demo + * applications. The VID/PID has also been used in firmware + * emulating FTDI serial chips by: + * Hornby Elite - Digital Command Control Console + * http://www.hornby.com/hornby-dcc/controllers/ + */ +#define MICROCHIP_VID 0x04D8 +#define MICROCHIP_USB_BOARD_PID 0x000A /* CDC RS-232 Emulation Demo */ + +/* + * RATOC REX-USB60F + */ +#define RATOC_VENDOR_ID 0x0584 +#define RATOC_PRODUCT_ID_USB60F 0xb020 + +/* + * Acton Research Corp. + */ +#define ACTON_VID 0x0647 /* Vendor ID */ +#define ACTON_SPECTRAPRO_PID 0x0100 + +/* + * Contec products (http://www.contec.com) + * Submitted by Daniel Sangorrin + */ +#define CONTEC_VID 0x06CE /* Vendor ID */ +#define CONTEC_COM1USBH_PID 0x8311 /* COM-1(USB)H */ + +/* + * Definitions for B&B Electronics products. + */ +#define BANDB_VID 0x0856 /* B&B Electronics Vendor ID */ +#define BANDB_USOTL4_PID 0xAC01 /* USOTL4 Isolated RS-485 Converter */ +#define BANDB_USTL4_PID 0xAC02 /* USTL4 RS-485 Converter */ +#define BANDB_USO9ML2_PID 0xAC03 /* USO9ML2 Isolated RS-232 Converter */ +#define BANDB_USOPTL4_PID 0xAC11 +#define BANDB_USPTL4_PID 0xAC12 +#define BANDB_USO9ML2DR_2_PID 0xAC16 +#define BANDB_USO9ML2DR_PID 0xAC17 +#define BANDB_USOPTL4DR2_PID 0xAC18 /* USOPTL4R-2 2-port Isolated RS-232 Converter */ +#define BANDB_USOPTL4DR_PID 0xAC19 +#define BANDB_485USB9F_2W_PID 0xAC25 +#define BANDB_485USB9F_4W_PID 0xAC26 +#define BANDB_232USB9M_PID 0xAC27 +#define BANDB_485USBTB_2W_PID 0xAC33 +#define BANDB_485USBTB_4W_PID 0xAC34 +#define BANDB_TTL5USB9M_PID 0xAC49 +#define BANDB_TTL3USB9M_PID 0xAC50 +#define BANDB_ZZ_PROG1_USB_PID 0xBA02 + +/* + * Intrepid Control Systems (http://www.intrepidcs.com/) ValueCAN and NeoVI + */ +#define INTREPID_VID 0x093C +#define INTREPID_VALUECAN_PID 0x0601 +#define INTREPID_NEOVI_PID 0x0701 + +/* + * Definitions for ID TECH (www.idt-net.com) devices + */ +#define IDTECH_VID 0x0ACD /* ID TECH Vendor ID */ +#define IDTECH_IDT1221U_PID 0x0300 /* IDT1221U USB to RS-232 adapter */ + +/* + * Definitions for Omnidirectional Control Technology, Inc. devices + */ +#define OCT_VID 0x0B39 /* OCT vendor ID */ +/* Note: OCT US101 is also rebadged as Dick Smith Electronics (NZ) XH6381 */ +/* Also rebadged as Dick Smith Electronics (Aus) XH6451 */ +/* Also rebadged as SIIG Inc. model US2308 hardware version 1 */ +#define OCT_DK201_PID 0x0103 /* OCT DK201 USB docking station */ +#define OCT_US101_PID 0x0421 /* OCT US101 USB to RS-232 */ + +/* + * Definitions for Icom Inc. devices + */ +#define ICOM_VID 0x0C26 /* Icom vendor ID */ +/* Note: ID-1 is a communications tranceiver for HAM-radio operators */ +#define ICOM_ID_1_PID 0x0004 /* ID-1 USB to RS-232 */ +/* Note: OPC is an Optional cable to connect an Icom Tranceiver */ +#define ICOM_OPC_U_UC_PID 0x0018 /* OPC-478UC, OPC-1122U cloning cable */ +/* Note: ID-RP* devices are Icom Repeater Devices for HAM-radio */ +#define ICOM_ID_RP2C1_PID 0x0009 /* ID-RP2C Asset 1 to RS-232 */ +#define ICOM_ID_RP2C2_PID 0x000A /* ID-RP2C Asset 2 to RS-232 */ +#define ICOM_ID_RP2D_PID 0x000B /* ID-RP2D configuration port*/ +#define ICOM_ID_RP2VT_PID 0x000C /* ID-RP2V Transmit config port */ +#define ICOM_ID_RP2VR_PID 0x000D /* ID-RP2V Receive config port */ +#define ICOM_ID_RP4KVT_PID 0x0010 /* ID-RP4000V Transmit config port */ +#define ICOM_ID_RP4KVR_PID 0x0011 /* ID-RP4000V Receive config port */ +#define ICOM_ID_RP2KVT_PID 0x0012 /* ID-RP2000V Transmit config port */ +#define ICOM_ID_RP2KVR_PID 0x0013 /* ID-RP2000V Receive config port */ + +/* + * GN Otometrics (http://www.otometrics.com) + * Submitted by Ville Sundberg. + */ +#define GN_OTOMETRICS_VID 0x0c33 /* Vendor ID */ +#define AURICAL_USB_PID 0x0010 /* Aurical USB Audiometer */ + +/* + * The following are the values for the Sealevel SeaLINK+ adapters. + * (Original list sent by Tuan Hoang. Ian Abbott renamed the macros and + * removed some PIDs that don't seem to match any existing products.) + */ +#define SEALEVEL_VID 0x0c52 /* Sealevel Vendor ID */ +#define SEALEVEL_2101_PID 0x2101 /* SeaLINK+232 (2101/2105) */ +#define SEALEVEL_2102_PID 0x2102 /* SeaLINK+485 (2102) */ +#define SEALEVEL_2103_PID 0x2103 /* SeaLINK+232I (2103) */ +#define SEALEVEL_2104_PID 0x2104 /* SeaLINK+485I (2104) */ +#define SEALEVEL_2106_PID 0x9020 /* SeaLINK+422 (2106) */ +#define SEALEVEL_2201_1_PID 0x2211 /* SeaPORT+2/232 (2201) Port 1 */ +#define SEALEVEL_2201_2_PID 0x2221 /* SeaPORT+2/232 (2201) Port 2 */ +#define SEALEVEL_2202_1_PID 0x2212 /* SeaPORT+2/485 (2202) Port 1 */ +#define SEALEVEL_2202_2_PID 0x2222 /* SeaPORT+2/485 (2202) Port 2 */ +#define SEALEVEL_2203_1_PID 0x2213 /* SeaPORT+2 (2203) Port 1 */ +#define SEALEVEL_2203_2_PID 0x2223 /* SeaPORT+2 (2203) Port 2 */ +#define SEALEVEL_2401_1_PID 0x2411 /* SeaPORT+4/232 (2401) Port 1 */ +#define SEALEVEL_2401_2_PID 0x2421 /* SeaPORT+4/232 (2401) Port 2 */ +#define SEALEVEL_2401_3_PID 0x2431 /* SeaPORT+4/232 (2401) Port 3 */ +#define SEALEVEL_2401_4_PID 0x2441 /* SeaPORT+4/232 (2401) Port 4 */ +#define SEALEVEL_2402_1_PID 0x2412 /* SeaPORT+4/485 (2402) Port 1 */ +#define SEALEVEL_2402_2_PID 0x2422 /* SeaPORT+4/485 (2402) Port 2 */ +#define SEALEVEL_2402_3_PID 0x2432 /* SeaPORT+4/485 (2402) Port 3 */ +#define SEALEVEL_2402_4_PID 0x2442 /* SeaPORT+4/485 (2402) Port 4 */ +#define SEALEVEL_2403_1_PID 0x2413 /* SeaPORT+4 (2403) Port 1 */ +#define SEALEVEL_2403_2_PID 0x2423 /* SeaPORT+4 (2403) Port 2 */ +#define SEALEVEL_2403_3_PID 0x2433 /* SeaPORT+4 (2403) Port 3 */ +#define SEALEVEL_2403_4_PID 0x2443 /* SeaPORT+4 (2403) Port 4 */ +#define SEALEVEL_2801_1_PID 0X2811 /* SeaLINK+8/232 (2801) Port 1 */ +#define SEALEVEL_2801_2_PID 0X2821 /* SeaLINK+8/232 (2801) Port 2 */ +#define SEALEVEL_2801_3_PID 0X2831 /* SeaLINK+8/232 (2801) Port 3 */ +#define SEALEVEL_2801_4_PID 0X2841 /* SeaLINK+8/232 (2801) Port 4 */ +#define SEALEVEL_2801_5_PID 0X2851 /* SeaLINK+8/232 (2801) Port 5 */ +#define SEALEVEL_2801_6_PID 0X2861 /* SeaLINK+8/232 (2801) Port 6 */ +#define SEALEVEL_2801_7_PID 0X2871 /* SeaLINK+8/232 (2801) Port 7 */ +#define SEALEVEL_2801_8_PID 0X2881 /* SeaLINK+8/232 (2801) Port 8 */ +#define SEALEVEL_2802_1_PID 0X2812 /* SeaLINK+8/485 (2802) Port 1 */ +#define SEALEVEL_2802_2_PID 0X2822 /* SeaLINK+8/485 (2802) Port 2 */ +#define SEALEVEL_2802_3_PID 0X2832 /* SeaLINK+8/485 (2802) Port 3 */ +#define SEALEVEL_2802_4_PID 0X2842 /* SeaLINK+8/485 (2802) Port 4 */ +#define SEALEVEL_2802_5_PID 0X2852 /* SeaLINK+8/485 (2802) Port 5 */ +#define SEALEVEL_2802_6_PID 0X2862 /* SeaLINK+8/485 (2802) Port 6 */ +#define SEALEVEL_2802_7_PID 0X2872 /* SeaLINK+8/485 (2802) Port 7 */ +#define SEALEVEL_2802_8_PID 0X2882 /* SeaLINK+8/485 (2802) Port 8 */ +#define SEALEVEL_2803_1_PID 0X2813 /* SeaLINK+8 (2803) Port 1 */ +#define SEALEVEL_2803_2_PID 0X2823 /* SeaLINK+8 (2803) Port 2 */ +#define SEALEVEL_2803_3_PID 0X2833 /* SeaLINK+8 (2803) Port 3 */ +#define SEALEVEL_2803_4_PID 0X2843 /* SeaLINK+8 (2803) Port 4 */ +#define SEALEVEL_2803_5_PID 0X2853 /* SeaLINK+8 (2803) Port 5 */ +#define SEALEVEL_2803_6_PID 0X2863 /* SeaLINK+8 (2803) Port 6 */ +#define SEALEVEL_2803_7_PID 0X2873 /* SeaLINK+8 (2803) Port 7 */ +#define SEALEVEL_2803_8_PID 0X2883 /* SeaLINK+8 (2803) Port 8 */ +#define SEALEVEL_2803R_1_PID 0Xa02a /* SeaLINK+8 (2803-ROHS) Port 1+2 */ +#define SEALEVEL_2803R_2_PID 0Xa02b /* SeaLINK+8 (2803-ROHS) Port 3+4 */ +#define SEALEVEL_2803R_3_PID 0Xa02c /* SeaLINK+8 (2803-ROHS) Port 5+6 */ +#define SEALEVEL_2803R_4_PID 0Xa02d /* SeaLINK+8 (2803-ROHS) Port 7+8 */ + +/* + * JETI SPECTROMETER SPECBOS 1201 + * http://www.jeti.com/cms/index.php/instruments/other-instruments/specbos-2101 + */ +#define JETI_VID 0x0c6c +#define JETI_SPC1201_PID 0x04b2 + +/* + * FTDI USB UART chips used in construction projects from the + * Elektor Electronics magazine (http://www.elektor.com/) + */ +#define ELEKTOR_VID 0x0C7D +#define ELEKTOR_FT323R_PID 0x0005 /* RFID-Reader, issue 09-2006 */ + +/* + * Posiflex inc retail equipment (http://www.posiflex.com.tw) + */ +#define POSIFLEX_VID 0x0d3a /* Vendor ID */ +#define POSIFLEX_PP7000_PID 0x0300 /* PP-7000II thermal printer */ + +/* + * The following are the values for two KOBIL chipcard terminals. + */ +#define KOBIL_VID 0x0d46 /* KOBIL Vendor ID */ +#define KOBIL_CONV_B1_PID 0x2020 /* KOBIL Konverter for B1 */ +#define KOBIL_CONV_KAAN_PID 0x2021 /* KOBIL_Konverter for KAAN */ + +#define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */ +#define FTDI_NF_RIC_PID 0x0001 /* Product Id */ + +/* + * Falcom Wireless Communications GmbH + */ +#define FALCOM_VID 0x0F94 /* Vendor Id */ +#define FALCOM_TWIST_PID 0x0001 /* Falcom Twist USB GPRS modem */ +#define FALCOM_SAMBA_PID 0x0005 /* Falcom Samba USB GPRS modem */ + +/* Larsen and Brusgaard AltiTrack/USBtrack */ +#define LARSENBRUSGAARD_VID 0x0FD8 +#define LB_ALTITRACK_PID 0x0001 + +/* + * TTi (Thurlby Thandar Instruments) + */ +#define TTI_VID 0x103E /* Vendor Id */ +#define TTI_QL355P_PID 0x03E8 /* TTi QL355P power supply */ + +/* Interbiometrics USB I/O Board */ +/* Developed for Interbiometrics by Rudolf Gugler */ +#define INTERBIOMETRICS_VID 0x1209 +#define INTERBIOMETRICS_IOBOARD_PID 0x1002 +#define INTERBIOMETRICS_MINI_IOBOARD_PID 0x1006 + +/* + * Testo products (http://www.testo.com/) + * Submitted by Colin Leroy + */ +#define TESTO_VID 0x128D +#define TESTO_USB_INTERFACE_PID 0x0001 + +/* + * Mobility Electronics products. + */ +#define MOBILITY_VID 0x1342 +#define MOBILITY_USB_SERIAL_PID 0x0202 /* EasiDock USB 200 serial */ + +/* + * FIC / OpenMoko, Inc. http://wiki.openmoko.org/wiki/Neo1973_Debug_Board_v3 + * Submitted by Harald Welte + */ +#define FIC_VID 0x1457 +#define FIC_NEO1973_DEBUG_PID 0x5118 + +/* Olimex */ +#define OLIMEX_VID 0x15BA +#define OLIMEX_ARM_USB_OCD_PID 0x0003 +#define OLIMEX_ARM_USB_OCD_H_PID 0x002b + +/* + * Telldus Technologies + */ +#define TELLDUS_VID 0x1781 /* Vendor ID */ +#define TELLDUS_TELLSTICK_PID 0x0C30 /* RF control dongle 433 MHz using FT232RL */ + +/* + * RT Systems programming cables for various ham radios + */ +#define RTSYSTEMS_VID 0x2100 /* Vendor ID */ +#define RTSYSTEMS_SERIAL_VX7_PID 0x9e52 /* Serial converter for VX-7 Radios using FT232RL */ +#define RTSYSTEMS_CT29B_PID 0x9e54 /* CT29B Radio Cable */ +#define RTSYSTEMS_RTS01_PID 0x9e57 /* USB-RTS01 Radio Cable */ + + +/* + * Physik Instrumente + * http://www.physikinstrumente.com/en/products/ + */ +/* These two devices use the VID of FTDI */ +#define PI_C865_PID 0xe0a0 /* PI C-865 Piezomotor Controller */ +#define PI_C857_PID 0xe0a1 /* PI Encoder Trigger Box */ + +#define PI_VID 0x1a72 /* Vendor ID */ +#define PI_C866_PID 0x1000 /* PI C-866 Piezomotor Controller */ +#define PI_C663_PID 0x1001 /* PI C-663 Mercury-Step */ +#define PI_C725_PID 0x1002 /* PI C-725 Piezomotor Controller */ +#define PI_E517_PID 0x1005 /* PI E-517 Digital Piezo Controller Operation Module */ +#define PI_C863_PID 0x1007 /* PI C-863 */ +#define PI_E861_PID 0x1008 /* PI E-861 Piezomotor Controller */ +#define PI_C867_PID 0x1009 /* PI C-867 Piezomotor Controller */ +#define PI_E609_PID 0x100D /* PI E-609 Digital Piezo Controller */ +#define PI_E709_PID 0x100E /* PI E-709 Digital Piezo Controller */ +#define PI_100F_PID 0x100F /* PI Digital Piezo Controller */ +#define PI_1011_PID 0x1011 /* PI Digital Piezo Controller */ +#define PI_1012_PID 0x1012 /* PI Motion Controller */ +#define PI_1013_PID 0x1013 /* PI Motion Controller */ +#define PI_1014_PID 0x1014 /* PI Device */ +#define PI_1015_PID 0x1015 /* PI Device */ +#define PI_1016_PID 0x1016 /* PI Digital Servo Module */ + +/* + * Kondo Kagaku Co.Ltd. + * http://www.kondo-robot.com/EN + */ +#define KONDO_VID 0x165c +#define KONDO_USB_SERIAL_PID 0x0002 + +/* + * Bayer Ascensia Contour blood glucose meter USB-converter cable. + * http://winglucofacts.com/cables/ + */ +#define BAYER_VID 0x1A79 +#define BAYER_CONTOUR_CABLE_PID 0x6001 + +/* + * The following are the values for the Matrix Orbital FTDI Range + * Anything in this range will use an FT232RL. + */ +#define MTXORB_VID 0x1B3D +#define MTXORB_FTDI_RANGE_0100_PID 0x0100 +#define MTXORB_FTDI_RANGE_0101_PID 0x0101 +#define MTXORB_FTDI_RANGE_0102_PID 0x0102 +#define MTXORB_FTDI_RANGE_0103_PID 0x0103 +#define MTXORB_FTDI_RANGE_0104_PID 0x0104 +#define MTXORB_FTDI_RANGE_0105_PID 0x0105 +#define MTXORB_FTDI_RANGE_0106_PID 0x0106 +#define MTXORB_FTDI_RANGE_0107_PID 0x0107 +#define MTXORB_FTDI_RANGE_0108_PID 0x0108 +#define MTXORB_FTDI_RANGE_0109_PID 0x0109 +#define MTXORB_FTDI_RANGE_010A_PID 0x010A +#define MTXORB_FTDI_RANGE_010B_PID 0x010B +#define MTXORB_FTDI_RANGE_010C_PID 0x010C +#define MTXORB_FTDI_RANGE_010D_PID 0x010D +#define MTXORB_FTDI_RANGE_010E_PID 0x010E +#define MTXORB_FTDI_RANGE_010F_PID 0x010F +#define MTXORB_FTDI_RANGE_0110_PID 0x0110 +#define MTXORB_FTDI_RANGE_0111_PID 0x0111 +#define MTXORB_FTDI_RANGE_0112_PID 0x0112 +#define MTXORB_FTDI_RANGE_0113_PID 0x0113 +#define MTXORB_FTDI_RANGE_0114_PID 0x0114 +#define MTXORB_FTDI_RANGE_0115_PID 0x0115 +#define MTXORB_FTDI_RANGE_0116_PID 0x0116 +#define MTXORB_FTDI_RANGE_0117_PID 0x0117 +#define MTXORB_FTDI_RANGE_0118_PID 0x0118 +#define MTXORB_FTDI_RANGE_0119_PID 0x0119 +#define MTXORB_FTDI_RANGE_011A_PID 0x011A +#define MTXORB_FTDI_RANGE_011B_PID 0x011B +#define MTXORB_FTDI_RANGE_011C_PID 0x011C +#define MTXORB_FTDI_RANGE_011D_PID 0x011D +#define MTXORB_FTDI_RANGE_011E_PID 0x011E +#define MTXORB_FTDI_RANGE_011F_PID 0x011F +#define MTXORB_FTDI_RANGE_0120_PID 0x0120 +#define MTXORB_FTDI_RANGE_0121_PID 0x0121 +#define MTXORB_FTDI_RANGE_0122_PID 0x0122 +#define MTXORB_FTDI_RANGE_0123_PID 0x0123 +#define MTXORB_FTDI_RANGE_0124_PID 0x0124 +#define MTXORB_FTDI_RANGE_0125_PID 0x0125 +#define MTXORB_FTDI_RANGE_0126_PID 0x0126 +#define MTXORB_FTDI_RANGE_0127_PID 0x0127 +#define MTXORB_FTDI_RANGE_0128_PID 0x0128 +#define MTXORB_FTDI_RANGE_0129_PID 0x0129 +#define MTXORB_FTDI_RANGE_012A_PID 0x012A +#define MTXORB_FTDI_RANGE_012B_PID 0x012B +#define MTXORB_FTDI_RANGE_012C_PID 0x012C +#define MTXORB_FTDI_RANGE_012D_PID 0x012D +#define MTXORB_FTDI_RANGE_012E_PID 0x012E +#define MTXORB_FTDI_RANGE_012F_PID 0x012F +#define MTXORB_FTDI_RANGE_0130_PID 0x0130 +#define MTXORB_FTDI_RANGE_0131_PID 0x0131 +#define MTXORB_FTDI_RANGE_0132_PID 0x0132 +#define MTXORB_FTDI_RANGE_0133_PID 0x0133 +#define MTXORB_FTDI_RANGE_0134_PID 0x0134 +#define MTXORB_FTDI_RANGE_0135_PID 0x0135 +#define MTXORB_FTDI_RANGE_0136_PID 0x0136 +#define MTXORB_FTDI_RANGE_0137_PID 0x0137 +#define MTXORB_FTDI_RANGE_0138_PID 0x0138 +#define MTXORB_FTDI_RANGE_0139_PID 0x0139 +#define MTXORB_FTDI_RANGE_013A_PID 0x013A +#define MTXORB_FTDI_RANGE_013B_PID 0x013B +#define MTXORB_FTDI_RANGE_013C_PID 0x013C +#define MTXORB_FTDI_RANGE_013D_PID 0x013D +#define MTXORB_FTDI_RANGE_013E_PID 0x013E +#define MTXORB_FTDI_RANGE_013F_PID 0x013F +#define MTXORB_FTDI_RANGE_0140_PID 0x0140 +#define MTXORB_FTDI_RANGE_0141_PID 0x0141 +#define MTXORB_FTDI_RANGE_0142_PID 0x0142 +#define MTXORB_FTDI_RANGE_0143_PID 0x0143 +#define MTXORB_FTDI_RANGE_0144_PID 0x0144 +#define MTXORB_FTDI_RANGE_0145_PID 0x0145 +#define MTXORB_FTDI_RANGE_0146_PID 0x0146 +#define MTXORB_FTDI_RANGE_0147_PID 0x0147 +#define MTXORB_FTDI_RANGE_0148_PID 0x0148 +#define MTXORB_FTDI_RANGE_0149_PID 0x0149 +#define MTXORB_FTDI_RANGE_014A_PID 0x014A +#define MTXORB_FTDI_RANGE_014B_PID 0x014B +#define MTXORB_FTDI_RANGE_014C_PID 0x014C +#define MTXORB_FTDI_RANGE_014D_PID 0x014D +#define MTXORB_FTDI_RANGE_014E_PID 0x014E +#define MTXORB_FTDI_RANGE_014F_PID 0x014F +#define MTXORB_FTDI_RANGE_0150_PID 0x0150 +#define MTXORB_FTDI_RANGE_0151_PID 0x0151 +#define MTXORB_FTDI_RANGE_0152_PID 0x0152 +#define MTXORB_FTDI_RANGE_0153_PID 0x0153 +#define MTXORB_FTDI_RANGE_0154_PID 0x0154 +#define MTXORB_FTDI_RANGE_0155_PID 0x0155 +#define MTXORB_FTDI_RANGE_0156_PID 0x0156 +#define MTXORB_FTDI_RANGE_0157_PID 0x0157 +#define MTXORB_FTDI_RANGE_0158_PID 0x0158 +#define MTXORB_FTDI_RANGE_0159_PID 0x0159 +#define MTXORB_FTDI_RANGE_015A_PID 0x015A +#define MTXORB_FTDI_RANGE_015B_PID 0x015B +#define MTXORB_FTDI_RANGE_015C_PID 0x015C +#define MTXORB_FTDI_RANGE_015D_PID 0x015D +#define MTXORB_FTDI_RANGE_015E_PID 0x015E +#define MTXORB_FTDI_RANGE_015F_PID 0x015F +#define MTXORB_FTDI_RANGE_0160_PID 0x0160 +#define MTXORB_FTDI_RANGE_0161_PID 0x0161 +#define MTXORB_FTDI_RANGE_0162_PID 0x0162 +#define MTXORB_FTDI_RANGE_0163_PID 0x0163 +#define MTXORB_FTDI_RANGE_0164_PID 0x0164 +#define MTXORB_FTDI_RANGE_0165_PID 0x0165 +#define MTXORB_FTDI_RANGE_0166_PID 0x0166 +#define MTXORB_FTDI_RANGE_0167_PID 0x0167 +#define MTXORB_FTDI_RANGE_0168_PID 0x0168 +#define MTXORB_FTDI_RANGE_0169_PID 0x0169 +#define MTXORB_FTDI_RANGE_016A_PID 0x016A +#define MTXORB_FTDI_RANGE_016B_PID 0x016B +#define MTXORB_FTDI_RANGE_016C_PID 0x016C +#define MTXORB_FTDI_RANGE_016D_PID 0x016D +#define MTXORB_FTDI_RANGE_016E_PID 0x016E +#define MTXORB_FTDI_RANGE_016F_PID 0x016F +#define MTXORB_FTDI_RANGE_0170_PID 0x0170 +#define MTXORB_FTDI_RANGE_0171_PID 0x0171 +#define MTXORB_FTDI_RANGE_0172_PID 0x0172 +#define MTXORB_FTDI_RANGE_0173_PID 0x0173 +#define MTXORB_FTDI_RANGE_0174_PID 0x0174 +#define MTXORB_FTDI_RANGE_0175_PID 0x0175 +#define MTXORB_FTDI_RANGE_0176_PID 0x0176 +#define MTXORB_FTDI_RANGE_0177_PID 0x0177 +#define MTXORB_FTDI_RANGE_0178_PID 0x0178 +#define MTXORB_FTDI_RANGE_0179_PID 0x0179 +#define MTXORB_FTDI_RANGE_017A_PID 0x017A +#define MTXORB_FTDI_RANGE_017B_PID 0x017B +#define MTXORB_FTDI_RANGE_017C_PID 0x017C +#define MTXORB_FTDI_RANGE_017D_PID 0x017D +#define MTXORB_FTDI_RANGE_017E_PID 0x017E +#define MTXORB_FTDI_RANGE_017F_PID 0x017F +#define MTXORB_FTDI_RANGE_0180_PID 0x0180 +#define MTXORB_FTDI_RANGE_0181_PID 0x0181 +#define MTXORB_FTDI_RANGE_0182_PID 0x0182 +#define MTXORB_FTDI_RANGE_0183_PID 0x0183 +#define MTXORB_FTDI_RANGE_0184_PID 0x0184 +#define MTXORB_FTDI_RANGE_0185_PID 0x0185 +#define MTXORB_FTDI_RANGE_0186_PID 0x0186 +#define MTXORB_FTDI_RANGE_0187_PID 0x0187 +#define MTXORB_FTDI_RANGE_0188_PID 0x0188 +#define MTXORB_FTDI_RANGE_0189_PID 0x0189 +#define MTXORB_FTDI_RANGE_018A_PID 0x018A +#define MTXORB_FTDI_RANGE_018B_PID 0x018B +#define MTXORB_FTDI_RANGE_018C_PID 0x018C +#define MTXORB_FTDI_RANGE_018D_PID 0x018D +#define MTXORB_FTDI_RANGE_018E_PID 0x018E +#define MTXORB_FTDI_RANGE_018F_PID 0x018F +#define MTXORB_FTDI_RANGE_0190_PID 0x0190 +#define MTXORB_FTDI_RANGE_0191_PID 0x0191 +#define MTXORB_FTDI_RANGE_0192_PID 0x0192 +#define MTXORB_FTDI_RANGE_0193_PID 0x0193 +#define MTXORB_FTDI_RANGE_0194_PID 0x0194 +#define MTXORB_FTDI_RANGE_0195_PID 0x0195 +#define MTXORB_FTDI_RANGE_0196_PID 0x0196 +#define MTXORB_FTDI_RANGE_0197_PID 0x0197 +#define MTXORB_FTDI_RANGE_0198_PID 0x0198 +#define MTXORB_FTDI_RANGE_0199_PID 0x0199 +#define MTXORB_FTDI_RANGE_019A_PID 0x019A +#define MTXORB_FTDI_RANGE_019B_PID 0x019B +#define MTXORB_FTDI_RANGE_019C_PID 0x019C +#define MTXORB_FTDI_RANGE_019D_PID 0x019D +#define MTXORB_FTDI_RANGE_019E_PID 0x019E +#define MTXORB_FTDI_RANGE_019F_PID 0x019F +#define MTXORB_FTDI_RANGE_01A0_PID 0x01A0 +#define MTXORB_FTDI_RANGE_01A1_PID 0x01A1 +#define MTXORB_FTDI_RANGE_01A2_PID 0x01A2 +#define MTXORB_FTDI_RANGE_01A3_PID 0x01A3 +#define MTXORB_FTDI_RANGE_01A4_PID 0x01A4 +#define MTXORB_FTDI_RANGE_01A5_PID 0x01A5 +#define MTXORB_FTDI_RANGE_01A6_PID 0x01A6 +#define MTXORB_FTDI_RANGE_01A7_PID 0x01A7 +#define MTXORB_FTDI_RANGE_01A8_PID 0x01A8 +#define MTXORB_FTDI_RANGE_01A9_PID 0x01A9 +#define MTXORB_FTDI_RANGE_01AA_PID 0x01AA +#define MTXORB_FTDI_RANGE_01AB_PID 0x01AB +#define MTXORB_FTDI_RANGE_01AC_PID 0x01AC +#define MTXORB_FTDI_RANGE_01AD_PID 0x01AD +#define MTXORB_FTDI_RANGE_01AE_PID 0x01AE +#define MTXORB_FTDI_RANGE_01AF_PID 0x01AF +#define MTXORB_FTDI_RANGE_01B0_PID 0x01B0 +#define MTXORB_FTDI_RANGE_01B1_PID 0x01B1 +#define MTXORB_FTDI_RANGE_01B2_PID 0x01B2 +#define MTXORB_FTDI_RANGE_01B3_PID 0x01B3 +#define MTXORB_FTDI_RANGE_01B4_PID 0x01B4 +#define MTXORB_FTDI_RANGE_01B5_PID 0x01B5 +#define MTXORB_FTDI_RANGE_01B6_PID 0x01B6 +#define MTXORB_FTDI_RANGE_01B7_PID 0x01B7 +#define MTXORB_FTDI_RANGE_01B8_PID 0x01B8 +#define MTXORB_FTDI_RANGE_01B9_PID 0x01B9 +#define MTXORB_FTDI_RANGE_01BA_PID 0x01BA +#define MTXORB_FTDI_RANGE_01BB_PID 0x01BB +#define MTXORB_FTDI_RANGE_01BC_PID 0x01BC +#define MTXORB_FTDI_RANGE_01BD_PID 0x01BD +#define MTXORB_FTDI_RANGE_01BE_PID 0x01BE +#define MTXORB_FTDI_RANGE_01BF_PID 0x01BF +#define MTXORB_FTDI_RANGE_01C0_PID 0x01C0 +#define MTXORB_FTDI_RANGE_01C1_PID 0x01C1 +#define MTXORB_FTDI_RANGE_01C2_PID 0x01C2 +#define MTXORB_FTDI_RANGE_01C3_PID 0x01C3 +#define MTXORB_FTDI_RANGE_01C4_PID 0x01C4 +#define MTXORB_FTDI_RANGE_01C5_PID 0x01C5 +#define MTXORB_FTDI_RANGE_01C6_PID 0x01C6 +#define MTXORB_FTDI_RANGE_01C7_PID 0x01C7 +#define MTXORB_FTDI_RANGE_01C8_PID 0x01C8 +#define MTXORB_FTDI_RANGE_01C9_PID 0x01C9 +#define MTXORB_FTDI_RANGE_01CA_PID 0x01CA +#define MTXORB_FTDI_RANGE_01CB_PID 0x01CB +#define MTXORB_FTDI_RANGE_01CC_PID 0x01CC +#define MTXORB_FTDI_RANGE_01CD_PID 0x01CD +#define MTXORB_FTDI_RANGE_01CE_PID 0x01CE +#define MTXORB_FTDI_RANGE_01CF_PID 0x01CF +#define MTXORB_FTDI_RANGE_01D0_PID 0x01D0 +#define MTXORB_FTDI_RANGE_01D1_PID 0x01D1 +#define MTXORB_FTDI_RANGE_01D2_PID 0x01D2 +#define MTXORB_FTDI_RANGE_01D3_PID 0x01D3 +#define MTXORB_FTDI_RANGE_01D4_PID 0x01D4 +#define MTXORB_FTDI_RANGE_01D5_PID 0x01D5 +#define MTXORB_FTDI_RANGE_01D6_PID 0x01D6 +#define MTXORB_FTDI_RANGE_01D7_PID 0x01D7 +#define MTXORB_FTDI_RANGE_01D8_PID 0x01D8 +#define MTXORB_FTDI_RANGE_01D9_PID 0x01D9 +#define MTXORB_FTDI_RANGE_01DA_PID 0x01DA +#define MTXORB_FTDI_RANGE_01DB_PID 0x01DB +#define MTXORB_FTDI_RANGE_01DC_PID 0x01DC +#define MTXORB_FTDI_RANGE_01DD_PID 0x01DD +#define MTXORB_FTDI_RANGE_01DE_PID 0x01DE +#define MTXORB_FTDI_RANGE_01DF_PID 0x01DF +#define MTXORB_FTDI_RANGE_01E0_PID 0x01E0 +#define MTXORB_FTDI_RANGE_01E1_PID 0x01E1 +#define MTXORB_FTDI_RANGE_01E2_PID 0x01E2 +#define MTXORB_FTDI_RANGE_01E3_PID 0x01E3 +#define MTXORB_FTDI_RANGE_01E4_PID 0x01E4 +#define MTXORB_FTDI_RANGE_01E5_PID 0x01E5 +#define MTXORB_FTDI_RANGE_01E6_PID 0x01E6 +#define MTXORB_FTDI_RANGE_01E7_PID 0x01E7 +#define MTXORB_FTDI_RANGE_01E8_PID 0x01E8 +#define MTXORB_FTDI_RANGE_01E9_PID 0x01E9 +#define MTXORB_FTDI_RANGE_01EA_PID 0x01EA +#define MTXORB_FTDI_RANGE_01EB_PID 0x01EB +#define MTXORB_FTDI_RANGE_01EC_PID 0x01EC +#define MTXORB_FTDI_RANGE_01ED_PID 0x01ED +#define MTXORB_FTDI_RANGE_01EE_PID 0x01EE +#define MTXORB_FTDI_RANGE_01EF_PID 0x01EF +#define MTXORB_FTDI_RANGE_01F0_PID 0x01F0 +#define MTXORB_FTDI_RANGE_01F1_PID 0x01F1 +#define MTXORB_FTDI_RANGE_01F2_PID 0x01F2 +#define MTXORB_FTDI_RANGE_01F3_PID 0x01F3 +#define MTXORB_FTDI_RANGE_01F4_PID 0x01F4 +#define MTXORB_FTDI_RANGE_01F5_PID 0x01F5 +#define MTXORB_FTDI_RANGE_01F6_PID 0x01F6 +#define MTXORB_FTDI_RANGE_01F7_PID 0x01F7 +#define MTXORB_FTDI_RANGE_01F8_PID 0x01F8 +#define MTXORB_FTDI_RANGE_01F9_PID 0x01F9 +#define MTXORB_FTDI_RANGE_01FA_PID 0x01FA +#define MTXORB_FTDI_RANGE_01FB_PID 0x01FB +#define MTXORB_FTDI_RANGE_01FC_PID 0x01FC +#define MTXORB_FTDI_RANGE_01FD_PID 0x01FD +#define MTXORB_FTDI_RANGE_01FE_PID 0x01FE +#define MTXORB_FTDI_RANGE_01FF_PID 0x01FF + + + +/* + * The Mobility Lab (TML) + * Submitted by Pierre Castella + */ +#define TML_VID 0x1B91 /* Vendor ID */ +#define TML_USB_SERIAL_PID 0x0064 /* USB - Serial Converter */ + +/* Alti-2 products http://www.alti-2.com */ +#define ALTI2_VID 0x1BC9 +#define ALTI2_N3_PID 0x6001 /* Neptune 3 */ + +/* + * Ionics PlugComputer + */ +#define IONICS_VID 0x1c0c +#define IONICS_PLUGCOMPUTER_PID 0x0102 + +/* + * Dresden Elektronik Sensor Terminal Board + */ +#define DE_VID 0x1cf1 /* Vendor ID */ +#define STB_PID 0x0001 /* Sensor Terminal Board */ +#define WHT_PID 0x0004 /* Wireless Handheld Terminal */ + +/* + * STMicroelectonics + */ +#define ST_VID 0x0483 +#define ST_STMCLT1030_PID 0x3747 /* ST Micro Connect Lite STMCLT1030 */ + +/* + * Papouch products (http://www.papouch.com/) + * Submitted by Folkert van Heusden + */ + +#define PAPOUCH_VID 0x5050 /* Vendor ID */ +#define PAPOUCH_SB485_PID 0x0100 /* Papouch SB485 USB-485/422 Converter */ +#define PAPOUCH_AP485_PID 0x0101 /* AP485 USB-RS485 Converter */ +#define PAPOUCH_SB422_PID 0x0102 /* Papouch SB422 USB-RS422 Converter */ +#define PAPOUCH_SB485_2_PID 0x0103 /* Papouch SB485 USB-485/422 Converter */ +#define PAPOUCH_AP485_2_PID 0x0104 /* AP485 USB-RS485 Converter */ +#define PAPOUCH_SB422_2_PID 0x0105 /* Papouch SB422 USB-RS422 Converter */ +#define PAPOUCH_SB485S_PID 0x0106 /* Papouch SB485S USB-485/422 Converter */ +#define PAPOUCH_SB485C_PID 0x0107 /* Papouch SB485C USB-485/422 Converter */ +#define PAPOUCH_LEC_PID 0x0300 /* LEC USB Converter */ +#define PAPOUCH_SB232_PID 0x0301 /* Papouch SB232 USB-RS232 Converter */ +#define PAPOUCH_TMU_PID 0x0400 /* TMU USB Thermometer */ +#define PAPOUCH_IRAMP_PID 0x0500 /* Papouch IRAmp Duplex */ +#define PAPOUCH_DRAK5_PID 0x0700 /* Papouch DRAK5 */ +#define PAPOUCH_QUIDO8x8_PID 0x0800 /* Papouch Quido 8/8 Module */ +#define PAPOUCH_QUIDO4x4_PID 0x0900 /* Papouch Quido 4/4 Module */ +#define PAPOUCH_QUIDO2x2_PID 0x0a00 /* Papouch Quido 2/2 Module */ +#define PAPOUCH_QUIDO10x1_PID 0x0b00 /* Papouch Quido 10/1 Module */ +#define PAPOUCH_QUIDO30x3_PID 0x0c00 /* Papouch Quido 30/3 Module */ +#define PAPOUCH_QUIDO60x3_PID 0x0d00 /* Papouch Quido 60(100)/3 Module */ +#define PAPOUCH_QUIDO2x16_PID 0x0e00 /* Papouch Quido 2/16 Module */ +#define PAPOUCH_QUIDO3x32_PID 0x0f00 /* Papouch Quido 3/32 Module */ +#define PAPOUCH_DRAK6_PID 0x1000 /* Papouch DRAK6 */ +#define PAPOUCH_UPSUSB_PID 0x8000 /* Papouch UPS-USB adapter */ +#define PAPOUCH_MU_PID 0x8001 /* MU controller */ +#define PAPOUCH_SIMUKEY_PID 0x8002 /* Papouch SimuKey */ +#define PAPOUCH_AD4USB_PID 0x8003 /* AD4USB Measurement Module */ +#define PAPOUCH_GMUX_PID 0x8004 /* Papouch GOLIATH MUX */ +#define PAPOUCH_GMSR_PID 0x8005 /* Papouch GOLIATH MSR */ + +/* + * Marvell SheevaPlug + */ +#define MARVELL_VID 0x9e88 +#define MARVELL_SHEEVAPLUG_PID 0x9e8f + +/* + * Evolution Robotics products (http://www.evolution.com/). + * Submitted by Shawn M. Lavelle. + */ +#define EVOLUTION_VID 0xDEEE /* Vendor ID */ +#define EVOLUTION_ER1_PID 0x0300 /* ER1 Control Module */ +#define EVO_8U232AM_PID 0x02FF /* Evolution robotics RCM2 (FT232AM)*/ +#define EVO_HYBRID_PID 0x0302 /* Evolution robotics RCM4 PID (FT232BM)*/ +#define EVO_RCM4_PID 0x0303 /* Evolution robotics RCM4 PID */ + +/* + * MJS Gadgets HD Radio / XM Radio / Sirius Radio interfaces (using VID 0x0403) + */ +#define MJSG_GENERIC_PID 0x9378 +#define MJSG_SR_RADIO_PID 0x9379 +#define MJSG_XM_RADIO_PID 0x937A +#define MJSG_HD_RADIO_PID 0x937C + +/* + * D.O.Tec products (http://www.directout.eu) + */ +#define FTDI_DOTEC_PID 0x9868 + +/* + * Xverve Signalyzer tools (http://www.signalyzer.com/) + */ +#define XVERVE_SIGNALYZER_ST_PID 0xBCA0 +#define XVERVE_SIGNALYZER_SLITE_PID 0xBCA1 +#define XVERVE_SIGNALYZER_SH2_PID 0xBCA2 +#define XVERVE_SIGNALYZER_SH4_PID 0xBCA4 + +/* + * Segway Robotic Mobility Platform USB interface (using VID 0x0403) + * Submitted by John G. Rogers + */ +#define SEGWAY_RMP200_PID 0xe729 + + +/* + * Accesio USB Data Acquisition products (http://www.accesio.com/) + */ +#define ACCESIO_COM4SM_PID 0xD578 + +/* www.sciencescope.co.uk educational dataloggers */ +#define FTDI_SCIENCESCOPE_LOGBOOKML_PID 0xFF18 +#define FTDI_SCIENCESCOPE_LS_LOGBOOK_PID 0xFF1C +#define FTDI_SCIENCESCOPE_HS_LOGBOOK_PID 0xFF1D + +/* + * Milkymist One JTAG/Serial + */ +#define QIHARDWARE_VID 0x20B7 +#define MILKYMISTONE_JTAGSERIAL_PID 0x0713 + +/* + * CTI GmbH RS485 Converter http://www.cti-lean.com/ + */ +/* USB-485-Mini*/ +#define FTDI_CTI_MINI_PID 0xF608 +/* USB-Nano-485*/ +#define FTDI_CTI_NANO_PID 0xF60B + +/* + * ZeitControl cardsystems GmbH rfid-readers http://zeitconrol.de + */ +/* TagTracer MIFARE*/ +#define FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID 0xF7C0 + +/* + * Rainforest Automation + */ +/* ZigBee controller */ +#define FTDI_RF_R106 0x8A28 + +/* + * Product: HCP HIT GPRS modem + * Manufacturer: HCP d.o.o. + * ATI command output: Cinterion MC55i + */ +#define FTDI_CINTERION_MC55I_PID 0xA951 diff --git a/hw/usb/quirks-pl2303-ids.h b/hw/usb/quirks-pl2303-ids.h new file mode 100644 index 0000000000..8dbdb46ffe --- /dev/null +++ b/hw/usb/quirks-pl2303-ids.h @@ -0,0 +1,150 @@ +/* + * Prolific PL2303 USB to serial adaptor driver header file + * + * 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) any later version. + * + */ + +#define BENQ_VENDOR_ID 0x04a5 +#define BENQ_PRODUCT_ID_S81 0x4027 + +#define PL2303_VENDOR_ID 0x067b +#define PL2303_PRODUCT_ID 0x2303 +#define PL2303_PRODUCT_ID_RSAQ2 0x04bb +#define PL2303_PRODUCT_ID_DCU11 0x1234 +#define PL2303_PRODUCT_ID_PHAROS 0xaaa0 +#define PL2303_PRODUCT_ID_RSAQ3 0xaaa2 +#define PL2303_PRODUCT_ID_ALDIGA 0x0611 +#define PL2303_PRODUCT_ID_MMX 0x0612 +#define PL2303_PRODUCT_ID_GPRS 0x0609 +#define PL2303_PRODUCT_ID_HCR331 0x331a +#define PL2303_PRODUCT_ID_MOTOROLA 0x0307 + +#define ATEN_VENDOR_ID 0x0557 +#define ATEN_VENDOR_ID2 0x0547 +#define ATEN_PRODUCT_ID 0x2008 + +#define IODATA_VENDOR_ID 0x04bb +#define IODATA_PRODUCT_ID 0x0a03 +#define IODATA_PRODUCT_ID_RSAQ5 0x0a0e + +#define ELCOM_VENDOR_ID 0x056e +#define ELCOM_PRODUCT_ID 0x5003 +#define ELCOM_PRODUCT_ID_UCSGT 0x5004 + +#define ITEGNO_VENDOR_ID 0x0eba +#define ITEGNO_PRODUCT_ID 0x1080 +#define ITEGNO_PRODUCT_ID_2080 0x2080 + +#define MA620_VENDOR_ID 0x0df7 +#define MA620_PRODUCT_ID 0x0620 + +#define RATOC_VENDOR_ID 0x0584 +#define RATOC_PRODUCT_ID 0xb000 + +#define TRIPP_VENDOR_ID 0x2478 +#define TRIPP_PRODUCT_ID 0x2008 + +#define RADIOSHACK_VENDOR_ID 0x1453 +#define RADIOSHACK_PRODUCT_ID 0x4026 + +#define DCU10_VENDOR_ID 0x0731 +#define DCU10_PRODUCT_ID 0x0528 + +#define SITECOM_VENDOR_ID 0x6189 +#define SITECOM_PRODUCT_ID 0x2068 + +/* Alcatel OT535/735 USB cable */ +#define ALCATEL_VENDOR_ID 0x11f7 +#define ALCATEL_PRODUCT_ID 0x02df + +/* Samsung I330 phone cradle */ +#define SAMSUNG_VENDOR_ID 0x04e8 +#define SAMSUNG_PRODUCT_ID 0x8001 + +#define SIEMENS_VENDOR_ID 0x11f5 +#define SIEMENS_PRODUCT_ID_SX1 0x0001 +#define SIEMENS_PRODUCT_ID_X65 0x0003 +#define SIEMENS_PRODUCT_ID_X75 0x0004 +#define SIEMENS_PRODUCT_ID_EF81 0x0005 + +#define SYNTECH_VENDOR_ID 0x0745 +#define SYNTECH_PRODUCT_ID 0x0001 + +/* Nokia CA-42 Cable */ +#define NOKIA_CA42_VENDOR_ID 0x078b +#define NOKIA_CA42_PRODUCT_ID 0x1234 + +/* CA-42 CLONE Cable www.ca-42.com chipset: Prolific Technology Inc */ +#define CA_42_CA42_VENDOR_ID 0x10b5 +#define CA_42_CA42_PRODUCT_ID 0xac70 + +#define SAGEM_VENDOR_ID 0x079b +#define SAGEM_PRODUCT_ID 0x0027 + +/* Leadtek GPS 9531 (ID 0413:2101) */ +#define LEADTEK_VENDOR_ID 0x0413 +#define LEADTEK_9531_PRODUCT_ID 0x2101 + +/* USB GSM cable from Speed Dragon Multimedia, Ltd */ +#define SPEEDDRAGON_VENDOR_ID 0x0e55 +#define SPEEDDRAGON_PRODUCT_ID 0x110b + +/* DATAPILOT Universal-2 Phone Cable */ +#define DATAPILOT_U2_VENDOR_ID 0x0731 +#define DATAPILOT_U2_PRODUCT_ID 0x2003 + +/* Belkin "F5U257" Serial Adapter */ +#define BELKIN_VENDOR_ID 0x050d +#define BELKIN_PRODUCT_ID 0x0257 + +/* Alcor Micro Corp. USB 2.0 TO RS-232 */ +#define ALCOR_VENDOR_ID 0x058F +#define ALCOR_PRODUCT_ID 0x9720 + +/* Willcom WS002IN Data Driver (by NetIndex Inc.) */ +#define WS002IN_VENDOR_ID 0x11f6 +#define WS002IN_PRODUCT_ID 0x2001 + +/* Corega CG-USBRS232R Serial Adapter */ +#define COREGA_VENDOR_ID 0x07aa +#define COREGA_PRODUCT_ID 0x002a + +/* Y.C. Cable U.S.A., Inc - USB to RS-232 */ +#define YCCABLE_VENDOR_ID 0x05ad +#define YCCABLE_PRODUCT_ID 0x0fba + +/* "Superial" USB - Serial */ +#define SUPERIAL_VENDOR_ID 0x5372 +#define SUPERIAL_PRODUCT_ID 0x2303 + +/* Hewlett-Packard LD220-HP POS Pole Display */ +#define HP_VENDOR_ID 0x03f0 +#define HP_LD220_PRODUCT_ID 0x3524 + +/* Cressi Edy (diving computer) PC interface */ +#define CRESSI_VENDOR_ID 0x04b8 +#define CRESSI_EDY_PRODUCT_ID 0x0521 + +/* Zeagle dive computer interface */ +#define ZEAGLE_VENDOR_ID 0x04b8 +#define ZEAGLE_N2ITION3_PRODUCT_ID 0x0522 + +/* Sony, USB data cable for CMD-Jxx mobile phones */ +#define SONY_VENDOR_ID 0x054c +#define SONY_QN3USB_PRODUCT_ID 0x0437 + +/* Sanwa KB-USB2 multimeter cable (ID: 11ad:0001) */ +#define SANWA_VENDOR_ID 0x11ad +#define SANWA_PRODUCT_ID 0x0001 + +/* ADLINK ND-6530 RS232,RS485 and RS422 adapter */ +#define ADLINK_VENDOR_ID 0x0b63 +#define ADLINK_ND6530_PRODUCT_ID 0x6530 + +/* SMART USB Serial Adapter */ +#define SMART_VENDOR_ID 0x0b8c +#define SMART_PRODUCT_ID 0x2303 diff --git a/hw/usb/quirks.c b/hw/usb/quirks.c new file mode 100644 index 0000000000..a761a96032 --- /dev/null +++ b/hw/usb/quirks.c @@ -0,0 +1,53 @@ +/* + * USB quirk handling + * + * Copyright (c) 2012 Red Hat, Inc. + * + * Red Hat Authors: + * Hans de Goede + * + * 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) any later version. + */ + +#include "quirks.h" +#include "hw/usb.h" + +static bool usb_id_match(const struct usb_device_id *ids, + uint16_t vendor_id, uint16_t product_id, + uint8_t interface_class, uint8_t interface_subclass, + uint8_t interface_protocol) { + int i; + + for (i = 0; ids[i].vendor_id != -1; i++) { + if (ids[i].vendor_id == vendor_id && + ids[i].product_id == product_id && + (ids[i].interface_class == -1 || + (ids[i].interface_class == interface_class && + ids[i].interface_subclass == interface_subclass && + ids[i].interface_protocol == interface_protocol))) { + return true; + } + } + return false; +} + +int usb_get_quirks(uint16_t vendor_id, uint16_t product_id, + uint8_t interface_class, uint8_t interface_subclass, + uint8_t interface_protocol) +{ + int quirks = 0; + + if (usb_id_match(usbredir_raw_serial_ids, vendor_id, product_id, + interface_class, interface_subclass, interface_protocol)) { + quirks |= USB_QUIRK_BUFFER_BULK_IN; + } + if (usb_id_match(usbredir_ftdi_serial_ids, vendor_id, product_id, + interface_class, interface_subclass, interface_protocol)) { + quirks |= USB_QUIRK_BUFFER_BULK_IN | USB_QUIRK_IS_FTDI; + } + + return quirks; +} diff --git a/hw/usb/quirks.h b/hw/usb/quirks.h new file mode 100644 index 0000000000..8dc6065527 --- /dev/null +++ b/hw/usb/quirks.h @@ -0,0 +1,910 @@ +/* + * USB quirk handling + * + * Copyright (c) 2012 Red Hat, Inc. + * + * Red Hat Authors: + * Hans de Goede + * + * 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) any later version. + */ + +/* 1 on 1 copy of linux/drivers/usb/serial/ftdi_sio_ids.h */ +#include "quirks-ftdi-ids.h" +/* 1 on 1 copy of linux/drivers/usb/serial/pl2303.h */ +#include "quirks-pl2303-ids.h" + +struct usb_device_id { + int vendor_id; + int product_id; + int interface_class; + int interface_subclass; + int interface_protocol; +}; + +#define USB_DEVICE(vendor, product) \ + .vendor_id = vendor, .product_id = product, .interface_class = -1, + +#define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, iclass, isubclass, iproto) \ + .vendor_id = vend, .product_id = prod, .interface_class = iclass, \ + .interface_subclass = isubclass, .interface_protocol = iproto + +static const struct usb_device_id usbredir_raw_serial_ids[] = { + /* + * Silicon Laboratories CP210x USB to RS232 serial adapter ids + * copied from linux/drivers/usb/serial/cp210x.c + * + * Copyright (C) 2005 Craig Shelley (craig@microtron.org.uk) + */ + { USB_DEVICE(0x045B, 0x0053) }, /* Renesas RX610 RX-Stick */ + { USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */ + { USB_DEVICE(0x0489, 0xE000) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */ + { USB_DEVICE(0x0489, 0xE003) }, /* Pirelli Broadband S.p.A, DP-L10 SIP/GSM Mobile */ + { USB_DEVICE(0x0745, 0x1000) }, /* CipherLab USB CCD Barcode Scanner 1000 */ + { USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */ + { USB_DEVICE(0x08FD, 0x000A) }, /* Digianswer A/S , ZigBee/802.15.4 MAC Device */ + { USB_DEVICE(0x0BED, 0x1100) }, /* MEI (TM) Cashflow-SC Bill/Voucher Acceptor */ + { USB_DEVICE(0x0BED, 0x1101) }, /* MEI series 2000 Combo Acceptor */ + { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */ + { USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */ + { USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */ + { USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */ + { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */ + { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */ + { USB_DEVICE(0x10C4, 0x0F91) }, /* Vstabi */ + { USB_DEVICE(0x10C4, 0x1101) }, /* Arkham Technology DS101 Bus Monitor */ + { USB_DEVICE(0x10C4, 0x1601) }, /* Arkham Technology DS101 Adapter */ + { USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */ + { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ + { USB_DEVICE(0x10C4, 0x8044) }, /* Cygnal Debug Adapter */ + { USB_DEVICE(0x10C4, 0x804E) }, /* Software Bisque Paramount ME build-in converter */ + { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ + { USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */ + { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */ + { USB_DEVICE(0x10C4, 0x806F) }, /* IMS USB to RS422 Converter Cable */ + { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */ + { USB_DEVICE(0x10C4, 0x80C4) }, /* Cygnal Integrated Products, Inc., Optris infrared thermometer */ + { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */ + { USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */ + { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */ + { USB_DEVICE(0x10C4, 0x8115) }, /* Arygon NFC/Mifare Reader */ + { USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */ + { USB_DEVICE(0x10C4, 0x813F) }, /* Tams Master Easy Control */ + { USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */ + { USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */ + { USB_DEVICE(0x10C4, 0x8156) }, /* B&G H3000 link cable */ + { USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */ + { USB_DEVICE(0x10C4, 0x815F) }, /* Timewave HamLinkUSB */ + { USB_DEVICE(0x10C4, 0x818B) }, /* AVIT Research USB to TTL */ + { USB_DEVICE(0x10C4, 0x819F) }, /* MJS USB Toslink Switcher */ + { USB_DEVICE(0x10C4, 0x81A6) }, /* ThinkOptics WavIt */ + { USB_DEVICE(0x10C4, 0x81A9) }, /* Multiplex RC Interface */ + { USB_DEVICE(0x10C4, 0x81AC) }, /* MSD Dash Hawk */ + { USB_DEVICE(0x10C4, 0x81AD) }, /* INSYS USB Modem */ + { USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */ + { USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */ + { USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */ + { USB_DEVICE(0x10C4, 0x81E8) }, /* Zephyr Bioharness */ + { USB_DEVICE(0x10C4, 0x81F2) }, /* C1007 HF band RFID controller */ + { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ + { USB_DEVICE(0x10C4, 0x822B) }, /* Modem EDGE(GSM) Comander 2 */ + { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */ + { USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */ + { USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */ + { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ + { USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */ + { USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */ + { USB_DEVICE(0x10C4, 0x83D8) }, /* DekTec DTA Plus VHF/UHF Booster/Attenuator */ + { USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */ + { USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */ + { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */ + { USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */ + { USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */ + { USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */ + { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */ + { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ + { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ + { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ + { USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */ + { USB_DEVICE(0x10C4, 0xEA80) }, /* Silicon Labs factory default */ + { USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */ + { USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */ + { USB_DEVICE(0x10C4, 0xF002) }, /* Elan Digital Systems USBwave12 */ + { USB_DEVICE(0x10C4, 0xF003) }, /* Elan Digital Systems USBpulse100 */ + { USB_DEVICE(0x10C4, 0xF004) }, /* Elan Digital Systems USBcount50 */ + { USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */ + { USB_DEVICE(0x10CE, 0xEA6A) }, /* Silicon Labs MobiData GPRS USB Modem 100EU */ + { USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */ + { USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */ + { USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */ + { USB_DEVICE(0x166A, 0x0301) }, /* Clipsal 5800PC C-Bus Wireless PC Interface */ + { USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */ + { USB_DEVICE(0x166A, 0x0304) }, /* Clipsal 5000CT2 C-Bus Black and White Touchscreen */ + { USB_DEVICE(0x166A, 0x0305) }, /* Clipsal C-5000CT2 C-Bus Spectrum Colour Touchscreen */ + { USB_DEVICE(0x166A, 0x0401) }, /* Clipsal L51xx C-Bus Architectural Dimmer */ + { USB_DEVICE(0x166A, 0x0101) }, /* Clipsal 5560884 C-Bus Multi-room Audio Matrix Switcher */ + { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */ + { USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */ + { USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */ + { USB_DEVICE(0x16DC, 0x0012) }, /* W-IE-NE-R Plein & Baus GmbH MPOD Multi Channel Power Supply */ + { USB_DEVICE(0x16DC, 0x0015) }, /* W-IE-NE-R Plein & Baus GmbH CML Control, Monitoring and Data Logger */ + { USB_DEVICE(0x17A8, 0x0001) }, /* Kamstrup Optical Eye/3-wire */ + { USB_DEVICE(0x17A8, 0x0005) }, /* Kamstrup M-Bus Master MultiPort 250D */ + { USB_DEVICE(0x17F4, 0xAAAA) }, /* Wavesense Jazz blood glucose meter */ + { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ + { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ + { USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */ + { USB_DEVICE(0x1E29, 0x0102) }, /* Festo CPX-USB */ + { USB_DEVICE(0x1E29, 0x0501) }, /* Festo CMSP */ + { USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */ + { USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */ + { USB_DEVICE(0x3195, 0xF281) }, /* Link Instruments MSO-28 */ + { USB_DEVICE(0x413C, 0x9500) }, /* DW700 GPS USB interface */ + + /* + * Prolific pl2303 USB to RS232 serial adapter ids + * copied from linux/drivers/usb/serial/pl2303.c + * + * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2003 IBM Corp. + */ + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) }, + { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, + { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, + { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, + { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) }, + { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, + { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) }, + { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) }, + { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) }, + { USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) }, + { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) }, + { USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) }, + { USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) }, + { USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) }, + { USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) }, + { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_ID) }, + { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_ID) }, + { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_SX1) }, + { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X65) }, + { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_X75) }, + { USB_DEVICE(SIEMENS_VENDOR_ID, SIEMENS_PRODUCT_ID_EF81) }, + { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_ID_S81) }, /* Benq/Siemens S81 */ + { USB_DEVICE(SYNTECH_VENDOR_ID, SYNTECH_PRODUCT_ID) }, + { USB_DEVICE(NOKIA_CA42_VENDOR_ID, NOKIA_CA42_PRODUCT_ID) }, + { USB_DEVICE(CA_42_CA42_VENDOR_ID, CA_42_CA42_PRODUCT_ID) }, + { USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_PRODUCT_ID) }, + { USB_DEVICE(LEADTEK_VENDOR_ID, LEADTEK_9531_PRODUCT_ID) }, + { USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) }, + { USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) }, + { USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) }, + { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) }, + { USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) }, + { USB_DEVICE(COREGA_VENDOR_ID, COREGA_PRODUCT_ID) }, + { USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) }, + { USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) }, + { USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) }, + { USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) }, + { USB_DEVICE(ZEAGLE_VENDOR_ID, ZEAGLE_N2ITION3_PRODUCT_ID) }, + { USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) }, + { USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) }, + { USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) }, + { USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) }, + + { USB_DEVICE(-1, -1) } /* Terminating Entry */ +}; + +static const struct usb_device_id usbredir_ftdi_serial_ids[] = { + /* + * FTDI USB to RS232 serial adapter ids + * copied from linux/drivers/usb/serial/ftdi_sio.c + * + * Copyright (C) 2009 - 2010 + * Johan Hovold (jhovold@gmail.com) + * Copyright (C) 1999 - 2001 + * Greg Kroah-Hartman (greg@kroah.com) + * Bill Ryder (bryder@sgi.com) + * Copyright (C) 2002 + * Kuba Ober (kuba@mareimbrium.org) + */ + { USB_DEVICE(FTDI_VID, FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CTI_MINI_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CTI_NANO_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_NXTCAM_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_0_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_1_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_3_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_4_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_5_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_6_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_7_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_USINT_CAT_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_USINT_WKEY_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_USINT_RS232_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ACTZWAVE_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IPLUS_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IPLUS2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_DMX4ALL) }, + { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_232RL_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_4232H_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_232H_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_FTX_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_SNIFFER_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_THROTTLE_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GATEWAY_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_GBM_PID) }, + { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) }, + { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_SPROG_II) }, + { USB_DEVICE(FTDI_VID, FTDI_LENZ_LIUSB_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_XF_632_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_XF_634_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_XF_547_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_XF_633_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_XF_631_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_XF_635_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_XF_640_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_XF_642_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_DSS20_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_URBAN_0_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_URBAN_1_PID) }, + { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_VNHCPCUSB_D_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MTXORB_0_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MTXORB_1_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MTXORB_2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MTXORB_3_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MTXORB_4_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MTXORB_5_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MTXORB_6_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_R2000KU_TRUE_RNG) }, + { USB_DEVICE(FTDI_VID, FTDI_VARDAAN_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0100_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0101_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0102_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0103_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0104_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0105_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0106_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0107_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0108_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0109_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_010F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0110_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0111_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0112_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0113_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0114_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0115_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0116_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0117_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0118_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0119_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_011F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0120_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0121_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0122_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0123_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0124_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0125_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0126_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0127_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0128_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0129_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_012F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0130_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0131_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0132_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0133_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0134_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0135_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0136_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0137_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0138_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0139_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_013F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0140_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0141_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0142_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0143_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0144_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0145_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0146_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0147_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0148_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0149_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_014F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0150_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0151_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0152_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0153_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0154_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0155_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0156_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0157_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0158_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0159_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_015F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0160_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0161_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0162_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0163_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0164_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0165_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0166_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0167_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0168_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0169_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_016F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0170_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0171_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0172_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0173_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0174_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0175_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0176_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0177_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0178_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0179_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_017F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0180_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0181_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0182_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0183_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0184_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0185_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0186_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0187_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0188_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0189_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_018F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0190_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0191_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0192_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0193_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0194_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0195_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0196_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0197_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0198_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0199_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019A_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019B_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019C_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019D_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019E_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_019F_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A0_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A1_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A2_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A3_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A4_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A5_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A6_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A7_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A8_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01A9_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AA_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AB_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AC_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AD_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AE_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01AF_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B0_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B1_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B2_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B3_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B4_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B5_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B6_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B7_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B8_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01B9_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BA_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BB_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BC_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BD_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BE_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01BF_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C0_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C1_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C2_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C3_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C4_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C5_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C6_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C7_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C8_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01C9_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CA_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CB_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CC_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CD_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CE_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01CF_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D0_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D1_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D2_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D3_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D4_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D5_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D6_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D7_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D8_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01D9_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DA_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DB_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DC_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DD_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DE_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01DF_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E0_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E1_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E2_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E3_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E4_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E5_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E6_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E7_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E8_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01E9_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EA_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EB_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EC_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01ED_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EE_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01EF_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F0_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F1_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F2_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F3_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F4_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F5_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F6_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F7_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F8_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01F9_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FA_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FB_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FC_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FD_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FE_PID) }, + { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FF_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_USBX_707_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2104_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2106_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2203_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2203_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_3_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2401_4_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_3_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2402_4_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_3_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2403_4_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_3_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_4_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_5_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_6_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_7_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2801_8_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_3_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_4_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_5_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_6_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_7_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2802_8_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_3_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_4_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_5_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_6_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_7_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_8_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_1_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_2_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_3_PID) }, + { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_4_PID) }, + { USB_DEVICE(IDTECH_VID, IDTECH_IDT1221U_PID) }, + { USB_DEVICE(OCT_VID, OCT_US101_PID) }, + { USB_DEVICE(OCT_VID, OCT_DK201_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_HE_TIRA1_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID) }, + { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_1) }, + { USB_DEVICE(FTDI_VID, PROTEGO_R2X0) }, + { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_3) }, + { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_4) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E808_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E809_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80A_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80B_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80C_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80D_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80E_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E80F_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E888_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E889_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88A_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88B_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88C_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88D_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88E_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GUDEADS_E88F_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_UO100_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_UM100_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_UR100_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_ALC8500_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_PYRAMID_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1000PC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_US485_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_PICPRO_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_PCMCIA_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_PK1_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_RS232MON_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_APP70_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TIAO_UMPA_PID) }, + /* + * ELV devices: + */ + { USB_DEVICE(FTDI_VID, FTDI_ELV_USR_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_MSM1_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_KL100_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_WS550_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_EC3000_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_WS888_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_TWS550_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_FEM_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_CLI7000_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_PPS7330_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_TFM100_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_UDF77_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_UIO88_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_UAD8_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_UDA7_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_USI2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_T1100_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_PCD200_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_ULA200_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_CSI8_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_EM1000DL_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_PCK100_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_RFP500_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_FS20SIG_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_UTP8_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_WS300PC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_WS444PC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1300PC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_EM1010PC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_WS500_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_HS485_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_UMS100_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_TFD128_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_FM3RX_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELV_WS777_PID) }, + { USB_DEVICE(FTDI_VID, LINX_SDMUSBQSS_PID) }, + { USB_DEVICE(FTDI_VID, LINX_MASTERDEVEL2_PID) }, + { USB_DEVICE(FTDI_VID, LINX_FUTURE_0_PID) }, + { USB_DEVICE(FTDI_VID, LINX_FUTURE_1_PID) }, + { USB_DEVICE(FTDI_VID, LINX_FUTURE_2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSICDU20_0_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSICDU40_1_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSMACHX_2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSLOAD_N_GO_3_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSICDU64_4_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSPRIME8_5_PID) }, + { USB_DEVICE(FTDI_VID, INSIDE_ACCESSO) }, + { USB_DEVICE(INTREPID_VID, INTREPID_VALUECAN_PID) }, + { USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) }, + { USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) }, + { USB_DEVICE(FALCOM_VID, FALCOM_SAMBA_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_OCEANIC_PID) }, + { USB_DEVICE(TTI_VID, TTI_QL355P_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_RM_CANVIEW_PID) }, + { USB_DEVICE(ACTON_VID, ACTON_SPECTRAPRO_PID) }, + { USB_DEVICE(CONTEC_VID, CONTEC_COM1USBH_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USOPTL4_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USPTL4_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USO9ML2DR_2_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USO9ML2DR_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USOPTL4DR2_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_USOPTL4DR_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_485USB9F_2W_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_485USB9F_4W_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_232USB9M_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_485USBTB_2W_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_485USBTB_4W_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_TTL5USB9M_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_TTL3USB9M_PID) }, + { USB_DEVICE(BANDB_VID, BANDB_ZZ_PROG1_USB_PID) }, + { USB_DEVICE(FTDI_VID, EVER_ECO_PRO_CDS) }, + { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_1_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_3_PID) }, + { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_0_PID) }, + { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_1_PID) }, + { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_2_PID) }, + { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_3_PID) }, + { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_4_PID) }, + { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_5_PID) }, + { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_6_PID) }, + { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_7_PID) }, + { USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MHAM_KW_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MHAM_YS_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MHAM_Y6_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MHAM_Y8_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MHAM_IC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MHAM_DB9_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MHAM_RS232_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MHAM_Y9_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TERATRONIK_VCP_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TERATRONIK_D2XX_PID) }, + { USB_DEVICE(EVOLUTION_VID, EVOLUTION_ER1_PID) }, + { USB_DEVICE(EVOLUTION_VID, EVO_HYBRID_PID) }, + { USB_DEVICE(EVOLUTION_VID, EVO_RCM4_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ARTEMIS_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16C_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HR_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HRC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16IC_PID) }, + { USB_DEVICE(KOBIL_VID, KOBIL_CONV_B1_PID) }, + { USB_DEVICE(KOBIL_VID, KOBIL_CONV_KAAN_PID) }, + { USB_DEVICE(POSIFLEX_VID, POSIFLEX_PP7000_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TTUSB_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ECLO_COM_1WIRE_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_777_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_8900F_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_RRCIRKITS_LOCOBUFFER_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ASK_RDR400_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_NZR_SEM_USB_PID) }, + { USB_DEVICE(ICOM_VID, ICOM_ID_1_PID) }, + { USB_DEVICE(ICOM_VID, ICOM_OPC_U_UC_PID) }, + { USB_DEVICE(ICOM_VID, ICOM_ID_RP2C1_PID) }, + { USB_DEVICE(ICOM_VID, ICOM_ID_RP2C2_PID) }, + { USB_DEVICE(ICOM_VID, ICOM_ID_RP2D_PID) }, + { USB_DEVICE(ICOM_VID, ICOM_ID_RP2VT_PID) }, + { USB_DEVICE(ICOM_VID, ICOM_ID_RP2VR_PID) }, + { USB_DEVICE(ICOM_VID, ICOM_ID_RP4KVT_PID) }, + { USB_DEVICE(ICOM_VID, ICOM_ID_RP4KVR_PID) }, + { USB_DEVICE(ICOM_VID, ICOM_ID_RP2KVT_PID) }, + { USB_DEVICE(ICOM_VID, ICOM_ID_RP2KVR_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ACG_HFDUAL_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_YEI_SERVOCENTER31_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_THORLABS_PID) }, + { USB_DEVICE(TESTO_VID, TESTO_USB_INTERFACE_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_GAMMA_SCOUT_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13M_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) }, + { USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_SPECTRA_SCU_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_3_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_AURORA_SCU_PID) }, + { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) }, + { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_SERIAL_VX7_PID) }, + { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_CT29B_PID) }, + { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_RTS01_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_MAXSTREAM_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_PHI_FISCO_PID) }, + { USB_DEVICE(TML_VID, TML_USB_SERIAL_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) }, + { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID) }, + { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID) }, + { USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID) }, + { USB_DEVICE(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID) }, + { USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID) }, + { USB_DEVICE(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID) }, + { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, + { USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) }, + + /* Papouch devices based on FTDI chip */ + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_AP485_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB422_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485_2_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_AP485_2_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB422_2_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485S_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB485C_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_LEC_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SB232_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_TMU_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_IRAMP_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_DRAK5_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO8x8_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO4x4_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO2x2_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO10x1_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO30x3_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO60x3_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO2x16_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO3x32_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_DRAK6_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_UPSUSB_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_MU_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_SIMUKEY_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_AD4USB_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_GMUX_PID) }, + { USB_DEVICE(PAPOUCH_VID, PAPOUCH_GMSR_PID) }, + + { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DGQG_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) }, + { USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) }, + { USB_DEVICE(FTDI_VID, DIEBOLD_BCS_SE923_PID) }, + { USB_DEVICE(ATMEL_VID, STK541_PID) }, + { USB_DEVICE(DE_VID, STB_PID) }, + { USB_DEVICE(DE_VID, WHT_PID) }, + { USB_DEVICE(ADI_VID, ADI_GNICE_PID) }, + { USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID) }, + { USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID, + 0xff, 0xff, 0x00) }, + { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) }, + { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID) }, + { USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) }, + { USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) }, + { USB_DEVICE(FTDI_VID, PI_C865_PID) }, + { USB_DEVICE(FTDI_VID, PI_C857_PID) }, + { USB_DEVICE(PI_VID, PI_C866_PID) }, + { USB_DEVICE(PI_VID, PI_C663_PID) }, + { USB_DEVICE(PI_VID, PI_C725_PID) }, + { USB_DEVICE(PI_VID, PI_E517_PID) }, + { USB_DEVICE(PI_VID, PI_C863_PID) }, + { USB_DEVICE(PI_VID, PI_E861_PID) }, + { USB_DEVICE(PI_VID, PI_C867_PID) }, + { USB_DEVICE(PI_VID, PI_E609_PID) }, + { USB_DEVICE(PI_VID, PI_E709_PID) }, + { USB_DEVICE(PI_VID, PI_100F_PID) }, + { USB_DEVICE(PI_VID, PI_1011_PID) }, + { USB_DEVICE(PI_VID, PI_1012_PID) }, + { USB_DEVICE(PI_VID, PI_1013_PID) }, + { USB_DEVICE(PI_VID, PI_1014_PID) }, + { USB_DEVICE(PI_VID, PI_1015_PID) }, + { USB_DEVICE(PI_VID, PI_1016_PID) }, + { USB_DEVICE(KONDO_VID, KONDO_USB_SERIAL_PID) }, + { USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) }, + { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID) }, + { USB_DEVICE(FTDI_VID, TI_XDS100V2_PID) }, + { USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) }, + { USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) }, + { USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) }, + { USB_DEVICE(FTDI_VID, HAMEG_HO870_PID) }, + { USB_DEVICE(FTDI_VID, MJSG_GENERIC_PID) }, + { USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) }, + { USB_DEVICE(FTDI_VID, MJSG_HD_RADIO_PID) }, + { USB_DEVICE(FTDI_VID, MJSG_XM_RADIO_PID) }, + { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_ST_PID) }, + { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SLITE_PID) }, + { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH2_PID) }, + { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH4_PID) }, + { USB_DEVICE(FTDI_VID, SEGWAY_RMP200_PID) }, + { USB_DEVICE(FTDI_VID, ACCESIO_COM4SM_PID) }, + { USB_DEVICE(IONICS_VID, IONICS_PLUGCOMPUTER_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_24_MASTER_WING_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_PC_WING_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_USB_DMX_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MIDI_TIMECODE_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MINI_WING_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MAXI_WING_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MEDIA_WING_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_WING_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LOGBOOKML_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LS_LOGBOOK_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_HS_LOGBOOK_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) }, + { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID) }, + { USB_DEVICE(ST_VID, ST_STMCLT1030_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_RF_R106) }, + { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) }, + + { USB_DEVICE(-1, -1) } /* Terminating Entry */ +}; + +#undef USB_DEVICE +#undef USB_DEVICE_AND_INTERFACE_INFO diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index b65e8682b6..c519b9b92a 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -30,6 +30,7 @@ #include "monitor/monitor.h" #include "sysemu/sysemu.h" #include "qemu/iov.h" +#include "char/char.h" #include #include @@ -43,18 +44,26 @@ #define NO_INTERFACE_INFO 255 /* Valid interface_count always <= 32 */ #define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) #define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) +#define USBEP2I(usb_ep) (((usb_ep)->pid == USB_TOKEN_IN) ? \ + ((usb_ep)->nr | 0x10) : ((usb_ep)->nr)) +#define I2USBEP(d, i) (usb_ep_get(&(d)->dev, \ + ((i) & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT, \ + (i) & 0x0f)) typedef struct USBRedirDevice USBRedirDevice; -/* Struct to hold buffered packets (iso or int input packets) */ +/* Struct to hold buffered packets */ struct buf_packet { uint8_t *data; - int len; - int status; + void *free_on_destroy; + uint16_t len; + uint16_t offset; + uint8_t status; QTAILQ_ENTRY(buf_packet)next; }; struct endp_data { + USBRedirDevice *dev; uint8_t type; uint8_t interval; uint8_t interface; /* bInterfaceNumber this ep belongs to */ @@ -63,11 +72,14 @@ struct endp_data { uint8_t iso_error; /* For reporting iso errors to the HC */ uint8_t interrupt_started; uint8_t interrupt_error; + uint8_t bulk_receiving_enabled; + uint8_t bulk_receiving_started; uint8_t bufpq_prefilled; uint8_t bufpq_dropping_packets; QTAILQ_HEAD(, buf_packet) bufpq; int32_t bufpq_size; int32_t bufpq_target_size; + USBPacket *pending_async_packet; }; struct PacketIdQueueEntry { @@ -101,6 +113,7 @@ struct USBRedirDevice { struct endp_data endpoint[MAX_ENDPOINTS]; struct PacketIdQueue cancelled; struct PacketIdQueue already_in_flight; + void (*buffered_bulk_in_complete)(USBRedirDevice *, USBPacket *, uint8_t); /* Data for device filtering */ struct usb_redir_device_connect_header device_info; struct usb_redir_interface_info_header interface_info; @@ -128,6 +141,8 @@ static void usbredir_interrupt_receiving_status(void *priv, uint64_t id, *interrupt_receiving_status); static void usbredir_bulk_streams_status(void *priv, uint64_t id, struct usb_redir_bulk_streams_status_header *bulk_streams_status); +static void usbredir_bulk_receiving_status(void *priv, uint64_t id, + struct usb_redir_bulk_receiving_status_header *bulk_receiving_status); static void usbredir_control_packet(void *priv, uint64_t id, struct usb_redir_control_packet_header *control_packet, uint8_t *data, int data_len); @@ -140,6 +155,9 @@ static void usbredir_iso_packet(void *priv, uint64_t id, static void usbredir_interrupt_packet(void *priv, uint64_t id, struct usb_redir_interrupt_packet_header *interrupt_header, uint8_t *data, int data_len); +static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, + struct usb_redir_buffered_bulk_packet_header *buffered_bulk_packet, + uint8_t *data, int data_len); static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p, int status); @@ -313,12 +331,19 @@ static void packet_id_queue_empty(struct PacketIdQueue *q) static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) { USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + int i = USBEP2I(p->ep); if (p->combined) { usb_combined_packet_cancel(udev, p); return; } + if (dev->endpoint[i].pending_async_packet) { + assert(dev->endpoint[i].pending_async_packet == p); + dev->endpoint[i].pending_async_packet = NULL; + return; + } + packet_id_queue_add(&dev->cancelled, p->id); usbredirparser_send_cancel_data_packet(dev->parser, p->id); usbredirparser_do_write(dev->parser); @@ -337,6 +362,11 @@ static void usbredir_fill_already_in_flight_from_ep(USBRedirDevice *dev, { static USBPacket *p; + /* async handled packets for bulk receiving eps do not count as inflight */ + if (dev->endpoint[USBEP2I(ep)].bulk_receiving_started) { + return; + } + QTAILQ_FOREACH(p, &ep->queue, queue) { /* Skip combined packets, except for the first */ if (p->combined && p != p->combined->first) { @@ -384,8 +414,8 @@ static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev, return p; } -static void bufp_alloc(USBRedirDevice *dev, - uint8_t *data, int len, int status, uint8_t ep) +static void bufp_alloc(USBRedirDevice *dev, uint8_t *data, uint16_t len, + uint8_t status, uint8_t ep, void *free_on_destroy) { struct buf_packet *bufp; @@ -409,7 +439,9 @@ static void bufp_alloc(USBRedirDevice *dev, bufp = g_malloc(sizeof(struct buf_packet)); bufp->data = data; bufp->len = len; + bufp->offset = 0; bufp->status = status; + bufp->free_on_destroy = free_on_destroy; QTAILQ_INSERT_TAIL(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); dev->endpoint[EP2I(ep)].bufpq_size++; } @@ -419,7 +451,7 @@ static void bufp_free(USBRedirDevice *dev, struct buf_packet *bufp, { QTAILQ_REMOVE(&dev->endpoint[EP2I(ep)].bufpq, bufp, next); dev->endpoint[EP2I(ep)].bufpq_size--; - free(bufp->data); + free(bufp->free_on_destroy); g_free(bufp); } @@ -570,19 +602,162 @@ static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep) usbredir_free_bufpq(dev, ep); } +/* + * The usb-host may poll the endpoint faster then our guest, resulting in lots + * of smaller bulkp-s. The below buffered_bulk_in_complete* functions combine + * data from multiple bulkp-s into a single packet, avoiding bufpq overflows. + */ +static void usbredir_buffered_bulk_add_data_to_packet(USBRedirDevice *dev, + struct buf_packet *bulkp, int count, USBPacket *p, uint8_t ep) +{ + usb_packet_copy(p, bulkp->data + bulkp->offset, count); + bulkp->offset += count; + if (bulkp->offset == bulkp->len) { + /* Store status in the last packet with data from this bulkp */ + usbredir_handle_status(dev, p, bulkp->status); + bufp_free(dev, bulkp, ep); + } +} + +static void usbredir_buffered_bulk_in_complete_raw(USBRedirDevice *dev, + USBPacket *p, uint8_t ep) +{ + struct buf_packet *bulkp; + int count; + + while ((bulkp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq)) && + p->actual_length < p->iov.size && p->status == USB_RET_SUCCESS) { + count = bulkp->len - bulkp->offset; + if (count > (p->iov.size - p->actual_length)) { + count = p->iov.size - p->actual_length; + } + usbredir_buffered_bulk_add_data_to_packet(dev, bulkp, count, p, ep); + } +} + +static void usbredir_buffered_bulk_in_complete_ftdi(USBRedirDevice *dev, + USBPacket *p, uint8_t ep) +{ + const int maxp = dev->endpoint[EP2I(ep)].max_packet_size; + uint8_t header[2] = { 0, 0 }; + struct buf_packet *bulkp; + int count; + + while ((bulkp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq)) && + p->actual_length < p->iov.size && p->status == USB_RET_SUCCESS) { + if (bulkp->len < 2) { + WARNING("malformed ftdi bulk in packet\n"); + bufp_free(dev, bulkp, ep); + continue; + } + + if ((p->actual_length % maxp) == 0) { + usb_packet_copy(p, bulkp->data, 2); + memcpy(header, bulkp->data, 2); + } else { + if (bulkp->data[0] != header[0] || bulkp->data[1] != header[1]) { + break; /* Different header, add to next packet */ + } + } + + if (bulkp->offset == 0) { + bulkp->offset = 2; /* Skip header */ + } + count = bulkp->len - bulkp->offset; + /* Must repeat the header at maxp interval */ + if (count > (maxp - (p->actual_length % maxp))) { + count = maxp - (p->actual_length % maxp); + } + usbredir_buffered_bulk_add_data_to_packet(dev, bulkp, count, p, ep); + } +} + +static void usbredir_buffered_bulk_in_complete(USBRedirDevice *dev, + USBPacket *p, uint8_t ep) +{ + p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */ + dev->buffered_bulk_in_complete(dev, p, ep); + DPRINTF("bulk-token-in ep %02X status %d len %d id %"PRIu64"\n", + ep, p->status, p->actual_length, p->id); +} + +static void usbredir_handle_buffered_bulk_in_data(USBRedirDevice *dev, + USBPacket *p, uint8_t ep) +{ + /* Input bulk endpoint, buffered packet input */ + if (!dev->endpoint[EP2I(ep)].bulk_receiving_started) { + int bpt; + struct usb_redir_start_bulk_receiving_header start = { + .endpoint = ep, + .stream_id = 0, + .no_transfers = 5, + }; + /* Round bytes_per_transfer up to a multiple of max_packet_size */ + bpt = 512 + dev->endpoint[EP2I(ep)].max_packet_size - 1; + bpt /= dev->endpoint[EP2I(ep)].max_packet_size; + bpt *= dev->endpoint[EP2I(ep)].max_packet_size; + start.bytes_per_transfer = bpt; + /* No id, we look at the ep when receiving a status back */ + usbredirparser_send_start_bulk_receiving(dev->parser, 0, &start); + usbredirparser_do_write(dev->parser); + DPRINTF("bulk receiving started bytes/transfer %u count %d ep %02X\n", + start.bytes_per_transfer, start.no_transfers, ep); + dev->endpoint[EP2I(ep)].bulk_receiving_started = 1; + /* We don't really want to drop bulk packets ever, but + having some upper limit to how much we buffer is good. */ + dev->endpoint[EP2I(ep)].bufpq_target_size = 5000; + dev->endpoint[EP2I(ep)].bufpq_dropping_packets = 0; + } + + if (QTAILQ_EMPTY(&dev->endpoint[EP2I(ep)].bufpq)) { + DPRINTF("bulk-token-in ep %02X, no bulkp\n", ep); + assert(dev->endpoint[EP2I(ep)].pending_async_packet == NULL); + dev->endpoint[EP2I(ep)].pending_async_packet = p; + p->status = USB_RET_ASYNC; + return; + } + usbredir_buffered_bulk_in_complete(dev, p, ep); +} + +static void usbredir_stop_bulk_receiving(USBRedirDevice *dev, uint8_t ep) +{ + struct usb_redir_stop_bulk_receiving_header stop_bulk = { + .endpoint = ep, + .stream_id = 0, + }; + if (dev->endpoint[EP2I(ep)].bulk_receiving_started) { + usbredirparser_send_stop_bulk_receiving(dev->parser, 0, &stop_bulk); + DPRINTF("bulk receiving stopped ep %02X\n", ep); + dev->endpoint[EP2I(ep)].bulk_receiving_started = 0; + } + usbredir_free_bufpq(dev, ep); +} + static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, uint8_t ep) { struct usb_redir_bulk_packet_header bulk_packet; - size_t size = (p->combined) ? p->combined->iov.size : p->iov.size; - - DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, size, p->id); + size_t size = usb_packet_size(p); + const int maxp = dev->endpoint[EP2I(ep)].max_packet_size; if (usbredir_already_in_flight(dev, p->id)) { p->status = USB_RET_ASYNC; return; } + if (dev->endpoint[EP2I(ep)].bulk_receiving_enabled) { + if (size != 0 && (size % maxp) == 0) { + usbredir_handle_buffered_bulk_in_data(dev, p, ep); + return; + } + WARNING("bulk recv invalid size %zd ep %02x, disabling\n", size, ep); + assert(dev->endpoint[EP2I(ep)].pending_async_packet == NULL); + usbredir_stop_bulk_receiving(dev, ep); + dev->endpoint[EP2I(ep)].bulk_receiving_enabled = 0; + } + + DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, size, p->id); + bulk_packet.endpoint = ep; bulk_packet.length = size; bulk_packet.stream_id = 0; @@ -596,12 +771,7 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, &bulk_packet, NULL, 0); } else { uint8_t buf[size]; - if (p->combined) { - iov_to_buf(p->combined->iov.iov, p->combined->iov.niov, - 0, buf, size); - } else { - usb_packet_copy(p, buf, size); - } + usb_packet_copy(p, buf, size); usbredir_log_data(dev, "bulk data out:", buf, size); usbredirparser_send_bulk_packet(dev->parser, p->id, &bulk_packet, buf, size); @@ -719,9 +889,6 @@ static void usbredir_handle_data(USBDevice *udev, USBPacket *p) ERROR("handle_data called for control transfer on ep %02X\n", ep); p->status = USB_RET_NAK; break; - case USB_ENDPOINT_XFER_ISOC: - usbredir_handle_iso_data(dev, p, ep); - break; case USB_ENDPOINT_XFER_BULK: if (p->state == USB_PACKET_SETUP && p->pid == USB_TOKEN_IN && p->ep->pipeline) { @@ -730,6 +897,9 @@ static void usbredir_handle_data(USBDevice *udev, USBPacket *p) } usbredir_handle_bulk_data(dev, p, ep); break; + case USB_ENDPOINT_XFER_ISOC: + usbredir_handle_iso_data(dev, p, ep); + break; case USB_ENDPOINT_XFER_INT: if (ep & USB_DIR_IN) { usbredir_handle_interrupt_in_data(dev, p, ep); @@ -751,6 +921,36 @@ static void usbredir_flush_ep_queue(USBDevice *dev, USBEndpoint *ep) } } +static void usbredir_stop_ep(USBRedirDevice *dev, int i) +{ + uint8_t ep = I2EP(i); + + switch (dev->endpoint[i].type) { + case USB_ENDPOINT_XFER_BULK: + if (ep & USB_DIR_IN) { + usbredir_stop_bulk_receiving(dev, ep); + } + break; + case USB_ENDPOINT_XFER_ISOC: + usbredir_stop_iso_stream(dev, ep); + break; + case USB_ENDPOINT_XFER_INT: + if (ep & USB_DIR_IN) { + usbredir_stop_interrupt_receiving(dev, ep); + } + break; + } + usbredir_free_bufpq(dev, ep); +} + +static void usbredir_ep_stopped(USBDevice *udev, USBEndpoint *uep) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + + usbredir_stop_ep(dev, USBEP2I(uep)); + usbredirparser_do_write(dev->parser); +} + static void usbredir_set_config(USBRedirDevice *dev, USBPacket *p, int config) { @@ -760,17 +960,7 @@ static void usbredir_set_config(USBRedirDevice *dev, USBPacket *p, DPRINTF("set config %d id %"PRIu64"\n", config, p->id); for (i = 0; i < MAX_ENDPOINTS; i++) { - switch (dev->endpoint[i].type) { - case USB_ENDPOINT_XFER_ISOC: - usbredir_stop_iso_stream(dev, I2EP(i)); - break; - case USB_ENDPOINT_XFER_INT: - if (i & 0x10) { - usbredir_stop_interrupt_receiving(dev, I2EP(i)); - } - break; - } - usbredir_free_bufpq(dev, I2EP(i)); + usbredir_stop_ep(dev, i); } set_config.configuration = config; @@ -798,17 +988,7 @@ static void usbredir_set_interface(USBRedirDevice *dev, USBPacket *p, for (i = 0; i < MAX_ENDPOINTS; i++) { if (dev->endpoint[i].interface == interface) { - switch (dev->endpoint[i].type) { - case USB_ENDPOINT_XFER_ISOC: - usbredir_stop_iso_stream(dev, I2EP(i)); - break; - case USB_ENDPOINT_XFER_INT: - if (i & 0x10) { - usbredir_stop_interrupt_receiving(dev, I2EP(i)); - } - break; - } - usbredir_free_bufpq(dev, I2EP(i)); + usbredir_stop_ep(dev, i); } } @@ -930,10 +1110,12 @@ static void usbredir_create_parser(USBRedirDevice *dev) dev->parser->interrupt_receiving_status_func = usbredir_interrupt_receiving_status; dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status; + dev->parser->bulk_receiving_status_func = usbredir_bulk_receiving_status; dev->parser->control_packet_func = usbredir_control_packet; dev->parser->bulk_packet_func = usbredir_bulk_packet; dev->parser->iso_packet_func = usbredir_iso_packet; dev->parser->interrupt_packet_func = usbredir_interrupt_packet; + dev->parser->buffered_bulk_packet_func = usbredir_buffered_bulk_packet; dev->read_buf = NULL; dev->read_buf_size = 0; @@ -942,6 +1124,7 @@ static void usbredir_create_parser(USBRedirDevice *dev) usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size); usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length); + usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving); if (runstate_check(RUN_STATE_INMIGRATE)) { flags |= usbredirparser_fl_no_hello; @@ -968,6 +1151,8 @@ static void usbredir_do_attach(void *opaque) if ((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER) && !( usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_ep_info_max_packet_size) && + usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_32bits_bulk_length) && usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_64bits_ids))) { ERROR("usb-redir-host lacks capabilities needed for use with XHCI\n"); @@ -1050,6 +1235,18 @@ static void usbredir_vm_state_change(void *priv, int running, RunState state) } } +static void usbredir_init_endpoints(USBRedirDevice *dev) +{ + int i; + + usb_ep_init(&dev->dev); + memset(dev->endpoint, 0, sizeof(dev->endpoint)); + for (i = 0; i < MAX_ENDPOINTS; i++) { + dev->endpoint[i].dev = dev; + QTAILQ_INIT(&dev->endpoint[i].bufpq); + } +} + static int usbredir_initfn(USBDevice *udev) { USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); @@ -1076,9 +1273,7 @@ static int usbredir_initfn(USBDevice *udev) packet_id_queue_init(&dev->cancelled, dev, "cancelled"); packet_id_queue_init(&dev->already_in_flight, dev, "already-in-flight"); - for (i = 0; i < MAX_ENDPOINTS; i++) { - QTAILQ_INIT(&dev->endpoint[i].bufpq); - } + usbredir_init_endpoints(dev); /* We'll do the attach once we receive the speed from the usb-host */ udev->auto_attach = 0; @@ -1168,6 +1363,52 @@ error: return -1; } +static void usbredir_check_bulk_receiving(USBRedirDevice *dev) +{ + int i, j, quirks; + + if (!usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_bulk_receiving)) { + return; + } + + for (i = EP2I(USB_DIR_IN); i < MAX_ENDPOINTS; i++) { + dev->endpoint[i].bulk_receiving_enabled = 0; + } + for (i = 0; i < dev->interface_info.interface_count; i++) { + quirks = usb_get_quirks(dev->device_info.vendor_id, + dev->device_info.product_id, + dev->interface_info.interface_class[i], + dev->interface_info.interface_subclass[i], + dev->interface_info.interface_protocol[i]); + if (!(quirks & USB_QUIRK_BUFFER_BULK_IN)) { + continue; + } + if (quirks & USB_QUIRK_IS_FTDI) { + dev->buffered_bulk_in_complete = + usbredir_buffered_bulk_in_complete_ftdi; + } else { + dev->buffered_bulk_in_complete = + usbredir_buffered_bulk_in_complete_raw; + } + + for (j = EP2I(USB_DIR_IN); j < MAX_ENDPOINTS; j++) { + if (dev->endpoint[j].interface == + dev->interface_info.interface[i] && + dev->endpoint[j].type == USB_ENDPOINT_XFER_BULK && + dev->endpoint[j].max_packet_size != 0) { + dev->endpoint[j].bulk_receiving_enabled = 1; + /* + * With buffering pipelining is not necessary. Also packet + * combining and bulk in buffering don't play nice together! + */ + I2USBEP(dev, j)->pipeline = false; + break; /* Only buffer for the first ep of each intf */ + } + } + } +} + /* * usbredirparser packet complete callbacks */ @@ -1276,13 +1517,13 @@ static void usbredir_device_connect(void *priv, return; } + usbredir_check_bulk_receiving(dev); qemu_mod_timer(dev->attach_timer, dev->next_attach_time); } static void usbredir_device_disconnect(void *priv) { USBRedirDevice *dev = priv; - int i; /* Stop any pending attaches */ qemu_del_timer(dev->attach_timer); @@ -1299,11 +1540,7 @@ static void usbredir_device_disconnect(void *priv) /* Reset state so that the next dev connected starts with a clean slate */ usbredir_cleanup_device_queues(dev); - memset(dev->endpoint, 0, sizeof(dev->endpoint)); - for (i = 0; i < MAX_ENDPOINTS; i++) { - QTAILQ_INIT(&dev->endpoint[i].bufpq); - } - usb_ep_init(&dev->dev); + usbredir_init_endpoints(dev); dev->interface_info.interface_count = NO_INTERFACE_INFO; dev->dev.addr = 0; dev->dev.speed = 0; @@ -1319,9 +1556,10 @@ static void usbredir_interface_info(void *priv, /* * If we receive interface info after the device has already been - * connected (ie on a set_config), re-check the filter. + * connected (ie on a set_config), re-check interface dependent things. */ if (qemu_timer_pending(dev->attach_timer) || dev->dev.attached) { + usbredir_check_bulk_receiving(dev); if (usbredir_check_filter(dev)) { ERROR("Device no longer matches filter after interface info " "change, disconnecting!\n"); @@ -1353,11 +1591,10 @@ static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep) static void usbredir_setup_usb_eps(USBRedirDevice *dev) { struct USBEndpoint *usb_ep; - int i, pid; + int i; for (i = 0; i < MAX_ENDPOINTS; i++) { - pid = (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT; - usb_ep = usb_ep_get(&dev->dev, pid, i & 0x0f); + usb_ep = I2USBEP(dev, i); usb_ep->type = dev->endpoint[i].type; usb_ep->ifnum = dev->endpoint[i].interface; usb_ep->max_packet_size = dev->endpoint[i].max_packet_size; @@ -1423,6 +1660,7 @@ static void usbredir_ep_info(void *priv, return; } usbredir_setup_usb_eps(dev); + usbredir_check_bulk_receiving(dev); } static void usbredir_configuration_status(void *priv, uint64_t id, @@ -1513,6 +1751,25 @@ static void usbredir_bulk_streams_status(void *priv, uint64_t id, { } +static void usbredir_bulk_receiving_status(void *priv, uint64_t id, + struct usb_redir_bulk_receiving_status_header *bulk_receiving_status) +{ + USBRedirDevice *dev = priv; + uint8_t ep = bulk_receiving_status->endpoint; + + DPRINTF("bulk recv status %d ep %02X id %"PRIu64"\n", + bulk_receiving_status->status, ep, id); + + if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].bulk_receiving_started) { + return; + } + + if (bulk_receiving_status->status == usb_redir_stall) { + DPRINTF("bulk receiving stopped by peer ep %02X\n", ep); + dev->endpoint[EP2I(ep)].bulk_receiving_started = 0; + } +} + static void usbredir_control_packet(void *priv, uint64_t id, struct usb_redir_control_packet_header *control_packet, uint8_t *data, int data_len) @@ -1568,7 +1825,7 @@ static void usbredir_bulk_packet(void *priv, uint64_t id, p = usbredir_find_packet_by_id(dev, ep, id); if (p) { - size_t size = (p->combined) ? p->combined->iov.size : p->iov.size; + size_t size = usb_packet_size(p); usbredir_handle_status(dev, p, bulk_packet->status); if (data_len > 0) { usbredir_log_data(dev, "bulk data in:", data, data_len); @@ -1578,12 +1835,7 @@ static void usbredir_bulk_packet(void *priv, uint64_t id, p->status = USB_RET_BABBLE; data_len = len = size; } - if (p->combined) { - iov_from_buf(p->combined->iov.iov, p->combined->iov.niov, - 0, data, data_len); - } else { - usb_packet_copy(p, data, data_len); - } + usb_packet_copy(p, data, data_len); } p->actual_length = len; if (p->pid == USB_TOKEN_IN && p->ep->pipeline) { @@ -1618,7 +1870,7 @@ static void usbredir_iso_packet(void *priv, uint64_t id, } /* bufp_alloc also adds the packet to the ep queue */ - bufp_alloc(dev, data, data_len, iso_packet->status, ep); + bufp_alloc(dev, data, data_len, iso_packet->status, ep, data); } static void usbredir_interrupt_packet(void *priv, uint64_t id, @@ -1645,11 +1897,11 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id, } if (QTAILQ_EMPTY(&dev->endpoint[EP2I(ep)].bufpq)) { - usb_wakeup(usb_ep_get(&dev->dev, USB_TOKEN_IN, ep & 0x0f)); + usb_wakeup(usb_ep_get(&dev->dev, USB_TOKEN_IN, ep & 0x0f), 0); } /* bufp_alloc also adds the packet to the ep queue */ - bufp_alloc(dev, data, data_len, interrupt_packet->status, ep); + bufp_alloc(dev, data, data_len, interrupt_packet->status, ep, data); } else { /* * We report output interrupt packets as completed directly upon @@ -1662,6 +1914,52 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id, } } +static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, + struct usb_redir_buffered_bulk_packet_header *buffered_bulk_packet, + uint8_t *data, int data_len) +{ + USBRedirDevice *dev = priv; + uint8_t status, ep = buffered_bulk_packet->endpoint; + void *free_on_destroy; + int i, len; + + DPRINTF("buffered-bulk-in status %d ep %02X len %d id %"PRIu64"\n", + buffered_bulk_packet->status, ep, data_len, id); + + if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_BULK) { + ERROR("received buffered-bulk packet for non bulk ep %02X\n", ep); + free(data); + return; + } + + if (dev->endpoint[EP2I(ep)].bulk_receiving_started == 0) { + DPRINTF("received buffered-bulk packet on not started ep %02X\n", ep); + free(data); + return; + } + + /* Data must be in maxp chunks for buffered_bulk_add_*_data_to_packet */ + len = dev->endpoint[EP2I(ep)].max_packet_size; + status = usb_redir_success; + free_on_destroy = NULL; + for (i = 0; i < data_len; i += len) { + if (len >= (data_len - i)) { + len = data_len - i; + status = buffered_bulk_packet->status; + free_on_destroy = data; + } + /* bufp_alloc also adds the packet to the ep queue */ + bufp_alloc(dev, data + i, len, status, ep, free_on_destroy); + } + + if (dev->endpoint[EP2I(ep)].pending_async_packet) { + USBPacket *p = dev->endpoint[EP2I(ep)].pending_async_packet; + dev->endpoint[EP2I(ep)].pending_async_packet = NULL; + usbredir_buffered_bulk_in_complete(dev, p, ep); + usb_packet_complete(&dev->dev, p); + } +} + /* * Migration code */ @@ -1696,6 +1994,7 @@ static int usbredir_post_load(void *priv, int version_id) dev->dev.speedmask = (1 << dev->dev.speed); usbredir_setup_usb_eps(dev); + usbredir_check_bulk_receiving(dev); return 0; } @@ -1767,22 +2066,27 @@ static const VMStateInfo usbredir_parser_vmstate_info = { static void usbredir_put_bufpq(QEMUFile *f, void *priv, size_t unused) { struct endp_data *endp = priv; + USBRedirDevice *dev = endp->dev; struct buf_packet *bufp; - int remain = endp->bufpq_size; + int len, i = 0; qemu_put_be32(f, endp->bufpq_size); QTAILQ_FOREACH(bufp, &endp->bufpq, next) { - qemu_put_be32(f, bufp->len); + len = bufp->len - bufp->offset; + DPRINTF("put_bufpq %d/%d len %d status %d\n", i + 1, endp->bufpq_size, + len, bufp->status); + qemu_put_be32(f, len); qemu_put_be32(f, bufp->status); - qemu_put_buffer(f, bufp->data, bufp->len); - remain--; + qemu_put_buffer(f, bufp->data + bufp->offset, len); + i++; } - assert(remain == 0); + assert(i == endp->bufpq_size); } static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused) { struct endp_data *endp = priv; + USBRedirDevice *dev = endp->dev; struct buf_packet *bufp; int i; @@ -1791,9 +2095,13 @@ static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused) bufp = g_malloc(sizeof(struct buf_packet)); bufp->len = qemu_get_be32(f); bufp->status = qemu_get_be32(f); + bufp->offset = 0; bufp->data = qemu_oom_check(malloc(bufp->len)); /* regular malloc! */ + bufp->free_on_destroy = bufp->data; qemu_get_buffer(f, bufp->data, bufp->len); QTAILQ_INSERT_TAIL(&endp->bufpq, bufp, next); + DPRINTF("get_bufpq %d/%d len %d status %d\n", i + 1, endp->bufpq_size, + bufp->len, bufp->status); } return 0; } @@ -1806,6 +2114,23 @@ static const VMStateInfo usbredir_ep_bufpq_vmstate_info = { /* For endp_data migration */ +static const VMStateDescription usbredir_bulk_receiving_vmstate = { + .name = "usb-redir-ep/bulk-receiving", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(bulk_receiving_started, struct endp_data), + VMSTATE_END_OF_LIST() + } +}; + +static bool usbredir_bulk_receiving_needed(void *priv) +{ + struct endp_data *endp = priv; + + return endp->bulk_receiving_started; +} + static const VMStateDescription usbredir_ep_vmstate = { .name = "usb-redir-ep", .version_id = 1, @@ -1832,6 +2157,14 @@ static const VMStateDescription usbredir_ep_vmstate = { }, VMSTATE_INT32(bufpq_target_size, struct endp_data), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &usbredir_bulk_receiving_vmstate, + .needed = usbredir_bulk_receiving_needed, + }, { + /* empty */ + } } }; @@ -1993,11 +2326,12 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data) uc->handle_data = usbredir_handle_data; uc->handle_control = usbredir_handle_control; uc->flush_ep_queue = usbredir_flush_ep_queue; + uc->ep_stopped = usbredir_ep_stopped; dc->vmsd = &usbredir_vmstate; dc->props = usbredir_properties; } -static TypeInfo usbredir_dev_info = { +static const TypeInfo usbredir_dev_info = { .name = "usb-redir", .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBRedirDevice), diff --git a/hw/versatile_i2c.c b/hw/versatile_i2c.c index ad71e9d92d..d0444aecac 100644 --- a/hw/versatile_i2c.c +++ b/hw/versatile_i2c.c @@ -21,8 +21,8 @@ * */ -#include "sysbus.h" -#include "bitbang_i2c.h" +#include "hw/sysbus.h" +#include "hw/bitbang_i2c.h" typedef struct { SysBusDevice busdev; diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c index 1f4d66934c..0b97a4073d 100644 --- a/hw/versatile_pci.c +++ b/hw/versatile_pci.c @@ -7,9 +7,9 @@ * This code is licensed under the LGPL. */ -#include "sysbus.h" -#include "pci/pci.h" -#include "pci/pci_host.h" +#include "hw/sysbus.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" #include "exec/address-spaces.h" typedef struct { @@ -119,7 +119,7 @@ static void versatile_pci_host_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_PROCESSOR_CO; } -static TypeInfo versatile_pci_host_info = { +static const TypeInfo versatile_pci_host_info = { .name = "versatile_pci_host", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), @@ -133,7 +133,7 @@ static void pci_vpb_class_init(ObjectClass *klass, void *data) sdc->init = pci_vpb_init; } -static TypeInfo pci_vpb_info = { +static const TypeInfo pci_vpb_info = { .name = "versatile_pci", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PCIVPBState), @@ -147,7 +147,7 @@ static void pci_realview_class_init(ObjectClass *klass, void *data) sdc->init = pci_realview_init; } -static TypeInfo pci_realview_info = { +static const TypeInfo pci_realview_info = { .name = "realview_pci", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PCIVPBState), diff --git a/hw/vfio_pci.c b/hw/vfio_pci.c index 41fb7ad1de..288361d0fb 100644 --- a/hw/vfio_pci.c +++ b/hw/vfio_pci.c @@ -31,9 +31,9 @@ #include "exec/address-spaces.h" #include "sysemu/kvm.h" #include "exec/memory.h" -#include "pci/msi.h" -#include "pci/msix.h" -#include "pci/pci.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" +#include "hw/pci/pci.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/queue.h" @@ -289,7 +289,7 @@ static void vfio_enable_intx_kvm(VFIODevice *vdev) /* Get an eventfd for resample/unmask */ if (event_notifier_init(&vdev->intx.unmask, 0)) { - error_report("vfio: Error: event_notifier_init failed eoi\n"); + error_report("vfio: Error: event_notifier_init failed eoi"); goto fail; } @@ -297,7 +297,7 @@ static void vfio_enable_intx_kvm(VFIODevice *vdev) irqfd.resamplefd = event_notifier_get_fd(&vdev->intx.unmask); if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) { - error_report("vfio: Error: Failed to setup resample irqfd: %m\n"); + error_report("vfio: Error: Failed to setup resample irqfd: %m"); goto fail_irqfd; } @@ -316,7 +316,7 @@ static void vfio_enable_intx_kvm(VFIODevice *vdev) ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set); g_free(irq_set); if (ret) { - error_report("vfio: Error: Failed to setup INTx unmask fd: %m\n"); + error_report("vfio: Error: Failed to setup INTx unmask fd: %m"); goto fail_vfio; } @@ -365,7 +365,7 @@ static void vfio_disable_intx_kvm(VFIODevice *vdev) /* Tell KVM to stop listening for an INTx irqfd */ if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) { - error_report("vfio: Error: Failed to disable INTx irqfd: %m\n"); + error_report("vfio: Error: Failed to disable INTx irqfd: %m"); } /* We only need to close the eventfd for VFIO to cleanup the kernel side */ @@ -447,7 +447,7 @@ static int vfio_enable_intx(VFIODevice *vdev) ret = event_notifier_init(&vdev->intx.interrupt, 0); if (ret) { - error_report("vfio: Error: event_notifier_init failed\n"); + error_report("vfio: Error: event_notifier_init failed"); return ret; } @@ -467,7 +467,7 @@ static int vfio_enable_intx(VFIODevice *vdev) ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set); g_free(irq_set); if (ret) { - error_report("vfio: Error: Failed to setup INTx fd: %m\n"); + error_report("vfio: Error: Failed to setup INTx fd: %m"); qemu_set_fd_handler(*pfd, NULL, NULL, vdev); event_notifier_cleanup(&vdev->intx.interrupt); return -errno; @@ -526,7 +526,7 @@ static void vfio_msi_interrupt(void *opaque) } else if (vdev->interrupt == VFIO_INT_MSI) { msi_notify(&vdev->pdev, nr); } else { - error_report("vfio: MSI interrupt receieved, but not enabled?\n"); + error_report("vfio: MSI interrupt receieved, but not enabled?"); } } @@ -562,8 +562,8 @@ static int vfio_enable_vectors(VFIODevice *vdev, bool msix) return ret; } -static int vfio_msix_vector_use(PCIDevice *pdev, - unsigned int nr, MSIMessage msg) +static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, + MSIMessage *msg, IOHandler *handler) { VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); VFIOMSIVector *vector; @@ -580,14 +580,14 @@ static int vfio_msix_vector_use(PCIDevice *pdev, msix_vector_use(pdev, nr); if (event_notifier_init(&vector->interrupt, 0)) { - error_report("vfio: Error: event_notifier_init failed\n"); + error_report("vfio: Error: event_notifier_init failed"); } /* * Attempt to enable route through KVM irqchip, * default to userspace handling if unavailable. */ - vector->virq = kvm_irqchip_add_msi_route(kvm_state, msg); + vector->virq = msg ? kvm_irqchip_add_msi_route(kvm_state, *msg) : -1; if (vector->virq < 0 || kvm_irqchip_add_irqfd_notifier(kvm_state, &vector->interrupt, vector->virq) < 0) { @@ -596,7 +596,7 @@ static int vfio_msix_vector_use(PCIDevice *pdev, vector->virq = -1; } qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), - vfio_msi_interrupt, NULL, vector); + handler, NULL, vector); } /* @@ -609,7 +609,7 @@ static int vfio_msix_vector_use(PCIDevice *pdev, vdev->nr_vectors = nr + 1; ret = vfio_enable_vectors(vdev, true); if (ret) { - error_report("vfio: failed to enable vectors, %d\n", ret); + error_report("vfio: failed to enable vectors, %d", ret); } } else { int argsz; @@ -632,13 +632,19 @@ static int vfio_msix_vector_use(PCIDevice *pdev, ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set); g_free(irq_set); if (ret) { - error_report("vfio: failed to modify vector, %d\n", ret); + error_report("vfio: failed to modify vector, %d", ret); } } return 0; } +static int vfio_msix_vector_use(PCIDevice *pdev, + unsigned int nr, MSIMessage msg) +{ + return vfio_msix_vector_do_use(pdev, nr, &msg, vfio_msi_interrupt); +} + static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) { VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); @@ -697,9 +703,25 @@ static void vfio_enable_msix(VFIODevice *vdev) vdev->interrupt = VFIO_INT_MSIX; + /* + * Some communication channels between VF & PF or PF & fw rely on the + * physical state of the device and expect that enabling MSI-X from the + * guest enables the same on the host. When our guest is Linux, the + * guest driver call to pci_enable_msix() sets the enabling bit in the + * MSI-X capability, but leaves the vector table masked. We therefore + * can't rely on a vector_use callback (from request_irq() in the guest) + * to switch the physical device into MSI-X mode because that may come a + * long time after pci_enable_msix(). This code enables vector 0 with + * triggering to userspace, then immediately release the vector, leaving + * the physical device with no vectors enabled, but MSI-X enabled, just + * like the guest view. + */ + vfio_msix_vector_do_use(&vdev->pdev, 0, NULL, NULL); + vfio_msix_vector_release(&vdev->pdev, 0); + if (msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use, - vfio_msix_vector_release)) { - error_report("vfio: msix_set_vector_notifiers failed\n"); + vfio_msix_vector_release, NULL)) { + error_report("vfio: msix_set_vector_notifiers failed"); } DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain, @@ -724,7 +746,7 @@ retry: vector->use = true; if (event_notifier_init(&vector->interrupt, 0)) { - error_report("vfio: Error: event_notifier_init failed\n"); + error_report("vfio: Error: event_notifier_init failed"); } msg = msi_get_message(&vdev->pdev, i); @@ -745,10 +767,10 @@ retry: ret = vfio_enable_vectors(vdev, false); if (ret) { if (ret < 0) { - error_report("vfio: Error: Failed to setup MSI fds: %m\n"); + error_report("vfio: Error: Failed to setup MSI fds: %m"); } else if (ret != vdev->nr_vectors) { error_report("vfio: Error: Failed to enable %d " - "MSI vectors, retry with %d\n", vdev->nr_vectors, ret); + "MSI vectors, retry with %d", vdev->nr_vectors, ret); } for (i = 0; i < vdev->nr_vectors; i++) { @@ -869,7 +891,7 @@ static void vfio_bar_write(void *opaque, hwaddr addr, } if (pwrite(bar->fd, &buf, size, bar->fd_offset + addr) != size) { - error_report("%s(,0x%"HWADDR_PRIx", 0x%"PRIx64", %d) failed: %m\n", + error_report("%s(,0x%"HWADDR_PRIx", 0x%"PRIx64", %d) failed: %m", __func__, addr, data, size); } @@ -900,7 +922,7 @@ static uint64_t vfio_bar_read(void *opaque, uint64_t data = 0; if (pread(bar->fd, &buf, size, bar->fd_offset + addr) != size) { - error_report("%s(,0x%"HWADDR_PRIx", %d) failed: %m\n", + error_report("%s(,0x%"HWADDR_PRIx", %d) failed: %m", __func__, addr, size); return (uint64_t)-1; } @@ -957,7 +979,7 @@ static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) val = pci_default_read_config(pdev, addr, len); } else { if (pread(vdev->fd, &val, len, vdev->config_offset + addr) != len) { - error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x) failed: %m\n", + error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x) failed: %m", __func__, vdev->host.domain, vdev->host.bus, vdev->host.slot, vdev->host.function, addr, len); return -errno; @@ -999,7 +1021,7 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, /* Write everything to VFIO, let it filter out what we can't write */ if (pwrite(vdev->fd, &val_le, len, vdev->config_offset + addr) != len) { - error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x, 0x%x) failed: %m\n", + error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x, 0x%x) failed: %m", __func__, vdev->host.domain, vdev->host.bus, vdev->host.slot, vdev->host.function, addr, val, len); } @@ -1116,7 +1138,7 @@ static void vfio_listener_region_add(MemoryListener *listener, if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != (section->offset_within_region & ~TARGET_PAGE_MASK))) { - error_report("%s received unaligned region\n", __func__); + error_report("%s received unaligned region", __func__); return; } @@ -1138,7 +1160,7 @@ static void vfio_listener_region_add(MemoryListener *listener, ret = vfio_dma_map(container, iova, end - iova, vaddr, section->readonly); if (ret) { error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx", %p) = %d (%m)\n", + "0x%"HWADDR_PRIx", %p) = %d (%m)", container, iova, end - iova, vaddr, ret); } } @@ -1160,7 +1182,7 @@ static void vfio_listener_region_del(MemoryListener *listener, if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != (section->offset_within_region & ~TARGET_PAGE_MASK))) { - error_report("%s received unaligned region\n", __func__); + error_report("%s received unaligned region", __func__); return; } @@ -1178,7 +1200,7 @@ static void vfio_listener_region_del(MemoryListener *listener, ret = vfio_dma_unmap(container, iova, end - iova); if (ret) { error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%m)\n", + "0x%"HWADDR_PRIx") = %d (%m)", container, iova, end - iova, ret); } } @@ -1235,7 +1257,7 @@ static int vfio_setup_msi(VFIODevice *vdev, int pos) if (ret == -ENOTSUP) { return 0; } - error_report("vfio: msi_init failed\n"); + error_report("vfio: msi_init failed"); return ret; } vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0); @@ -1310,7 +1332,7 @@ static int vfio_setup_msix(VFIODevice *vdev, int pos) if (ret == -ENOTSUP) { return 0; } - error_report("vfio: msix_init failed\n"); + error_report("vfio: msix_init failed"); return ret; } @@ -1426,7 +1448,7 @@ static void vfio_map_bar(VFIODevice *vdev, int nr) ret = pread(vdev->fd, &pci_bar, sizeof(pci_bar), vdev->config_offset + PCI_BASE_ADDRESS_0 + (4 * nr)); if (ret != sizeof(pci_bar)) { - error_report("vfio: Failed to read BAR %d (%m)\n", nr); + error_report("vfio: Failed to read BAR %d (%m)", nr); return; } @@ -1449,7 +1471,7 @@ static void vfio_map_bar(VFIODevice *vdev, int nr) strncat(name, " mmap", sizeof(name) - strlen(name) - 1); if (vfio_mmap_bar(bar, &bar->mem, &bar->mmap_mem, &bar->mmap, size, 0, name)) { - error_report("%s unsupported. Performance may be slow\n", name); + error_report("%s unsupported. Performance may be slow", name); } if (vdev->msix && vdev->msix->table_bar == nr) { @@ -1463,7 +1485,7 @@ static void vfio_map_bar(VFIODevice *vdev, int nr) /* VFIOMSIXInfo contains another MemoryRegion for this mapping */ if (vfio_mmap_bar(bar, &bar->mem, &vdev->msix->mmap_mem, &vdev->msix->mmap, size, start, name)) { - error_report("%s unsupported. Performance may be slow\n", name); + error_report("%s unsupported. Performance may be slow", name); } } } @@ -1550,7 +1572,7 @@ static int vfio_add_std_cap(VFIODevice *vdev, uint8_t pos) if (ret < 0) { error_report("vfio: %04x:%02x:%02x.%x Error adding PCI capability " - "0x%x[0x%x]@0x%x: %d\n", vdev->host.domain, + "0x%x[0x%x]@0x%x: %d", vdev->host.domain, vdev->host.bus, vdev->host.slot, vdev->host.function, cap_id, size, pos, ret); return ret; @@ -1605,7 +1627,7 @@ static int vfio_load_rom(VFIODevice *vdev) if (errno == EINTR || errno == EAGAIN) { continue; } - error_report("vfio: Error reading device ROM: %m\n"); + error_report("vfio: Error reading device ROM: %m"); memory_region_destroy(&vdev->pdev.rom); return -errno; } @@ -1635,14 +1657,14 @@ static int vfio_connect_container(VFIOGroup *group) fd = qemu_open("/dev/vfio/vfio", O_RDWR); if (fd < 0) { - error_report("vfio: failed to open /dev/vfio/vfio: %m\n"); + error_report("vfio: failed to open /dev/vfio/vfio: %m"); return -errno; } ret = ioctl(fd, VFIO_GET_API_VERSION); if (ret != VFIO_API_VERSION) { error_report("vfio: supported vfio version: %d, " - "reported version: %d\n", VFIO_API_VERSION, ret); + "reported version: %d", VFIO_API_VERSION, ret); close(fd); return -EINVAL; } @@ -1653,7 +1675,7 @@ static int vfio_connect_container(VFIOGroup *group) if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU)) { ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd); if (ret) { - error_report("vfio: failed to set group container: %m\n"); + error_report("vfio: failed to set group container: %m"); g_free(container); close(fd); return -errno; @@ -1661,7 +1683,7 @@ static int vfio_connect_container(VFIOGroup *group) ret = ioctl(fd, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU); if (ret) { - error_report("vfio: failed to set iommu for container: %m\n"); + error_report("vfio: failed to set iommu for container: %m"); g_free(container); close(fd); return -errno; @@ -1672,7 +1694,7 @@ static int vfio_connect_container(VFIOGroup *group) memory_listener_register(&container->iommu_data.listener, &address_space_memory); } else { - error_report("vfio: No available IOMMU models\n"); + error_report("vfio: No available IOMMU models"); g_free(container); close(fd); return -EINVAL; @@ -1692,7 +1714,7 @@ static void vfio_disconnect_container(VFIOGroup *group) VFIOContainer *container = group->container; if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER, &container->fd)) { - error_report("vfio: error disconnecting group %d from container\n", + error_report("vfio: error disconnecting group %d from container", group->groupid); } @@ -1727,13 +1749,13 @@ static VFIOGroup *vfio_get_group(int groupid) snprintf(path, sizeof(path), "/dev/vfio/%d", groupid); group->fd = qemu_open(path, O_RDWR); if (group->fd < 0) { - error_report("vfio: error opening %s: %m\n", path); + error_report("vfio: error opening %s: %m", path); g_free(group); return NULL; } if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) { - error_report("vfio: error getting group status: %m\n"); + error_report("vfio: error getting group status: %m"); close(group->fd); g_free(group); return NULL; @@ -1742,7 +1764,7 @@ static VFIOGroup *vfio_get_group(int groupid) if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) { error_report("vfio: error, group %d is not viable, please ensure " "all devices within the iommu_group are bound to their " - "vfio bus driver.\n", groupid); + "vfio bus driver.", groupid); close(group->fd); g_free(group); return NULL; @@ -1752,7 +1774,7 @@ static VFIOGroup *vfio_get_group(int groupid) QLIST_INIT(&group->device_list); if (vfio_connect_container(group)) { - error_report("vfio: failed to setup container for group %d\n", groupid); + error_report("vfio: failed to setup container for group %d", groupid); close(group->fd); g_free(group); return NULL; @@ -1784,9 +1806,9 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) ret = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name); if (ret < 0) { - error_report("vfio: error getting device %s from group %d: %m\n", + error_report("vfio: error getting device %s from group %d: %m", name, group->groupid); - error_report("Verify all devices in group %d are bound to vfio-pci " + error_printf("Verify all devices in group %d are bound to vfio-pci " "or pci-stub and not already in use\n", group->groupid); return ret; } @@ -1798,7 +1820,7 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) /* Sanity check device */ ret = ioctl(vdev->fd, VFIO_DEVICE_GET_INFO, &dev_info); if (ret) { - error_report("vfio: error getting device info: %m\n"); + error_report("vfio: error getting device info: %m"); goto error; } @@ -1806,23 +1828,23 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) dev_info.flags, dev_info.num_regions, dev_info.num_irqs); if (!(dev_info.flags & VFIO_DEVICE_FLAGS_PCI)) { - error_report("vfio: Um, this isn't a PCI device\n"); + error_report("vfio: Um, this isn't a PCI device"); goto error; } vdev->reset_works = !!(dev_info.flags & VFIO_DEVICE_FLAGS_RESET); if (!vdev->reset_works) { - error_report("Warning, device %s does not support reset\n", name); + error_report("Warning, device %s does not support reset", name); } - if (dev_info.num_regions != VFIO_PCI_NUM_REGIONS) { - error_report("vfio: unexpected number of io regions %u\n", + if (dev_info.num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) { + error_report("vfio: unexpected number of io regions %u", dev_info.num_regions); goto error; } - if (dev_info.num_irqs != VFIO_PCI_NUM_IRQS) { - error_report("vfio: unexpected number of irqs %u\n", dev_info.num_irqs); + if (dev_info.num_irqs < VFIO_PCI_MSIX_IRQ_INDEX + 1) { + error_report("vfio: unexpected number of irqs %u", dev_info.num_irqs); goto error; } @@ -1831,7 +1853,7 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); if (ret) { - error_report("vfio: Error getting region %d info: %m\n", i); + error_report("vfio: Error getting region %d info: %m", i); goto error; } @@ -1851,7 +1873,7 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); if (ret) { - error_report("vfio: Error getting ROM info: %m\n"); + error_report("vfio: Error getting ROM info: %m"); goto error; } @@ -1867,7 +1889,7 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); if (ret) { - error_report("vfio: Error getting config info: %m\n"); + error_report("vfio: Error getting config info: %m"); goto error; } @@ -1877,6 +1899,9 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) (unsigned long)reg_info.flags); vdev->config_size = reg_info.size; + if (vdev->config_size == PCI_CONFIG_SPACE_SIZE) { + vdev->pdev.cap_present &= ~QEMU_PCI_CAP_EXPRESS; + } vdev->config_offset = reg_info.offset; error: @@ -1916,7 +1941,7 @@ static int vfio_initfn(PCIDevice *pdev) vdev->host.domain, vdev->host.bus, vdev->host.slot, vdev->host.function); if (stat(path, &st) < 0) { - error_report("vfio: error: no such host device: %s\n", path); + error_report("vfio: error: no such host device: %s", path); return -errno; } @@ -1924,7 +1949,7 @@ static int vfio_initfn(PCIDevice *pdev) len = readlink(path, iommu_group_path, PATH_MAX); if (len <= 0) { - error_report("vfio: error no iommu_group for device\n"); + error_report("vfio: error no iommu_group for device"); return -errno; } @@ -1932,7 +1957,7 @@ static int vfio_initfn(PCIDevice *pdev) group_name = basename(iommu_group_path); if (sscanf(group_name, "%d", &groupid) != 1) { - error_report("vfio: error reading %s: %m\n", path); + error_report("vfio: error reading %s: %m", path); return -errno; } @@ -1941,7 +1966,7 @@ static int vfio_initfn(PCIDevice *pdev) group = vfio_get_group(groupid); if (!group) { - error_report("vfio: failed to get group %d\n", groupid); + error_report("vfio: failed to get group %d", groupid); return -ENOENT; } @@ -1955,7 +1980,7 @@ static int vfio_initfn(PCIDevice *pdev) pvdev->host.slot == vdev->host.slot && pvdev->host.function == vdev->host.function) { - error_report("vfio: error: device %s is already attached\n", path); + error_report("vfio: error: device %s is already attached", path); vfio_put_group(group); return -EBUSY; } @@ -1963,7 +1988,7 @@ static int vfio_initfn(PCIDevice *pdev) ret = vfio_get_device(group, path, vdev); if (ret) { - error_report("vfio: failed to get device %s\n", path); + error_report("vfio: failed to get device %s", path); vfio_put_group(group); return ret; } @@ -1974,7 +1999,7 @@ static int vfio_initfn(PCIDevice *pdev) vdev->config_offset); if (ret < (int)MIN(pci_config_size(&vdev->pdev), vdev->config_size)) { ret = ret < 0 ? -errno : -EFAULT; - error_report("vfio: Failed to read device config space\n"); + error_report("vfio: Failed to read device config space"); goto out_put; } @@ -2061,7 +2086,7 @@ static void vfio_pci_reset(DeviceState *dev) if (vdev->reset_works) { if (ioctl(vdev->fd, VFIO_DEVICE_RESET)) { error_report("vfio: Error unable to reset physical device " - "(%04x:%02x:%02x.%x): %m\n", vdev->host.domain, + "(%04x:%02x:%02x.%x): %m", vdev->host.domain, vdev->host.bus, vdev->host.slot, vdev->host.function); } } @@ -2099,6 +2124,7 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) pdc->exit = vfio_exitfn; pdc->config_read = vfio_pci_read_config; pdc->config_write = vfio_pci_write_config; + pdc->is_express = 1; /* We might be */ } static const TypeInfo vfio_pci_dev_info = { diff --git a/hw/vga-isa-mm.c b/hw/vga-isa-mm.c index 311c966f77..4aa62bf35e 100644 --- a/hw/vga-isa-mm.c +++ b/hw/vga-isa-mm.c @@ -21,10 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "hw/hw.h" #include "ui/console.h" -#include "pc.h" -#include "vga_int.h" +#include "hw/pc.h" +#include "hw/vga_int.h" #include "ui/pixel_ops.h" #include "qemu/timer.h" diff --git a/hw/vga-isa.c b/hw/vga-isa.c index cbe7b05a7e..ffad5226fd 100644 --- a/hw/vga-isa.c +++ b/hw/vga-isa.c @@ -23,13 +23,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "hw/hw.h" #include "ui/console.h" -#include "pc.h" -#include "vga_int.h" +#include "hw/pc.h" +#include "hw/vga_int.h" #include "ui/pixel_ops.h" #include "qemu/timer.h" -#include "loader.h" +#include "hw/loader.h" typedef struct ISAVGAState { ISADevice dev; @@ -86,7 +86,7 @@ static void vga_class_initfn(ObjectClass *klass, void *data) dc->props = vga_isa_properties; } -static TypeInfo vga_info = { +static const TypeInfo vga_info = { .name = "isa-vga", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISAVGAState), diff --git a/hw/vga-pci.c b/hw/vga-pci.c index 87c7c0648d..18018ff1c3 100644 --- a/hw/vga-pci.c +++ b/hw/vga-pci.c @@ -23,13 +23,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "hw/hw.h" #include "ui/console.h" -#include "pci/pci.h" -#include "vga_int.h" +#include "hw/pci/pci.h" +#include "hw/vga_int.h" #include "ui/pixel_ops.h" #include "qemu/timer.h" -#include "loader.h" +#include "hw/loader.h" #define PCI_VGA_IOPORT_OFFSET 0x400 #define PCI_VGA_IOPORT_SIZE (0x3e0 - 0x3c0) @@ -200,7 +200,7 @@ static void vga_class_init(ObjectClass *klass, void *data) dc->props = vga_pci_properties; } -static TypeInfo vga_info = { +static const TypeInfo vga_info = { .name = "VGA", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIVGAState), diff --git a/hw/vga.c b/hw/vga.c index e2ba7f208c..2213bc1a88 100644 --- a/hw/vga.c +++ b/hw/vga.c @@ -21,15 +21,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "vga.h" +#include "hw/hw.h" +#include "hw/vga.h" #include "ui/console.h" -#include "pc.h" -#include "pci/pci.h" -#include "vga_int.h" +#include "hw/pc.h" +#include "hw/pci/pci.h" +#include "hw/vga_int.h" #include "ui/pixel_ops.h" #include "qemu/timer.h" -#include "xen.h" +#include "hw/xen.h" #include "trace.h" //#define DEBUG_VGA @@ -986,28 +986,28 @@ typedef void vga_draw_line_func(VGACommonState *s1, uint8_t *d, const uint8_t *s, int width); #define DEPTH 8 -#include "vga_template.h" +#include "hw/vga_template.h" #define DEPTH 15 -#include "vga_template.h" +#include "hw/vga_template.h" #define BGR_FORMAT #define DEPTH 15 -#include "vga_template.h" +#include "hw/vga_template.h" #define DEPTH 16 -#include "vga_template.h" +#include "hw/vga_template.h" #define BGR_FORMAT #define DEPTH 16 -#include "vga_template.h" +#include "hw/vga_template.h" #define DEPTH 32 -#include "vga_template.h" +#include "hw/vga_template.h" #define BGR_FORMAT #define DEPTH 32 -#include "vga_template.h" +#include "hw/vga_template.h" static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b) { @@ -1643,6 +1643,11 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) uint8_t *d; uint32_t v, addr1, addr; vga_draw_line_func *vga_draw_line; +#if defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) + static const bool byteswap = false; +#else + static const bool byteswap = true; +#endif full_update |= update_basic_params(s); @@ -1685,18 +1690,11 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) disp_width != s->last_width || height != s->last_height || s->last_depth != depth) { -#if defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) - if (depth == 16 || depth == 32) { -#else - if (depth == 32) { -#endif + if (depth == 32 || (depth == 16 && !byteswap)) { qemu_free_displaysurface(s->ds); s->ds->surface = qemu_create_displaysurface_from(disp_width, height, depth, s->line_offset, - s->vram_ptr + (s->start_addr * 4)); -#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) - s->ds->surface->pf = qemu_different_endianness_pixelformat(depth); -#endif + s->vram_ptr + (s->start_addr * 4), byteswap); dpy_gfx_resize(s->ds); } else { qemu_console_resize(s->ds, disp_width, height); @@ -1715,7 +1713,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) s->ds->surface = qemu_create_displaysurface_from(disp_width, height, depth, s->line_offset, - s->vram_ptr + (s->start_addr * 4)); + s->vram_ptr + (s->start_addr * 4), byteswap); dpy_gfx_setdata(s->ds); } diff --git a/hw/vhost.c b/hw/vhost.c index 4e1cb47418..4d6aee3ecd 100644 --- a/hw/vhost.c +++ b/hw/vhost.c @@ -14,7 +14,7 @@ */ #include -#include "vhost.h" +#include "hw/vhost.h" #include "hw/hw.h" #include "qemu/range.h" #include @@ -53,10 +53,14 @@ static void vhost_dev_sync_region(struct vhost_dev *dev, log = __sync_fetch_and_and(from, 0); while ((bit = sizeof(log) > sizeof(int) ? ffsll(log) : ffs(log))) { - ram_addr_t ram_addr; + hwaddr page_addr; + hwaddr section_offset; + hwaddr mr_offset; bit -= 1; - ram_addr = section->offset_within_region + bit * VHOST_LOG_PAGE; - memory_region_set_dirty(section->mr, ram_addr, VHOST_LOG_PAGE); + page_addr = addr + bit * VHOST_LOG_PAGE; + section_offset = page_addr - section->offset_within_address_space; + mr_offset = section_offset + section->offset_within_region; + memory_region_set_dirty(section->mr, mr_offset, VHOST_LOG_PAGE); log &= ~(0x1ull << bit); } addr += VHOST_LOG_CHUNK; @@ -65,14 +69,21 @@ static void vhost_dev_sync_region(struct vhost_dev *dev, static int vhost_sync_dirty_bitmap(struct vhost_dev *dev, MemoryRegionSection *section, - hwaddr start_addr, - hwaddr end_addr) + hwaddr first, + hwaddr last) { int i; + hwaddr start_addr; + hwaddr end_addr; if (!dev->log_enabled || !dev->started) { return 0; } + start_addr = section->offset_within_address_space; + end_addr = range_get_last(start_addr, section->size); + start_addr = MAX(first, start_addr); + end_addr = MIN(last, end_addr); + for (i = 0; i < dev->mem->nregions; ++i) { struct vhost_memory_region *reg = dev->mem->regions + i; vhost_dev_sync_region(dev, section, start_addr, end_addr, @@ -93,10 +104,18 @@ static void vhost_log_sync(MemoryListener *listener, { struct vhost_dev *dev = container_of(listener, struct vhost_dev, memory_listener); - hwaddr start_addr = section->offset_within_address_space; - hwaddr end_addr = start_addr + section->size; + vhost_sync_dirty_bitmap(dev, section, 0x0, ~0x0ULL); +} - vhost_sync_dirty_bitmap(dev, section, start_addr, end_addr); +static void vhost_log_sync_range(struct vhost_dev *dev, + hwaddr first, hwaddr last) +{ + int i; + /* FIXME: this is N^2 in number of sections */ + for (i = 0; i < dev->n_mem_sections; ++i) { + MemoryRegionSection *section = &dev->mem_sections[i]; + vhost_sync_dirty_bitmap(dev, section, first, last); + } } /* Assign/unassign. Keep an unsorted array of non-overlapping @@ -268,19 +287,15 @@ static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size) { vhost_log_chunk_t *log; uint64_t log_base; - int r, i; - if (size) { - log = g_malloc0(size * sizeof *log); - } else { - log = NULL; - } + int r; + + log = g_malloc0(size * sizeof *log); log_base = (uint64_t)(unsigned long)log; r = ioctl(dev->control, VHOST_SET_LOG_BASE, &log_base); assert(r >= 0); - for (i = 0; i < dev->n_mem_sections; ++i) { - /* Sync only the range covered by the old log */ - vhost_sync_dirty_bitmap(dev, &dev->mem_sections[i], 0, - dev->log_size * VHOST_LOG_CHUNK - 1); + /* Sync only the range covered by the old log */ + if (dev->log_size) { + vhost_log_sync_range(dev, 0, dev->log_size * VHOST_LOG_CHUNK - 1); } if (dev->log) { g_free(dev->log); @@ -612,21 +627,24 @@ static void vhost_log_stop(MemoryListener *listener, /* FIXME: implement */ } -static int vhost_virtqueue_init(struct vhost_dev *dev, +static int vhost_virtqueue_start(struct vhost_dev *dev, struct VirtIODevice *vdev, struct vhost_virtqueue *vq, unsigned idx) { hwaddr s, l, a; int r; + int vhost_vq_index = idx - dev->vq_index; struct vhost_vring_file file = { - .index = idx, + .index = vhost_vq_index }; struct vhost_vring_state state = { - .index = idx, + .index = vhost_vq_index }; struct VirtQueue *vvq = virtio_get_queue(vdev, idx); + assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); + vq->num = state.num = virtio_queue_get_num(vdev, idx); r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state); if (r) { @@ -669,11 +687,12 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, goto fail_alloc_ring; } - r = vhost_virtqueue_set_addr(dev, vq, idx, dev->log_enabled); + r = vhost_virtqueue_set_addr(dev, vq, vhost_vq_index, dev->log_enabled); if (r < 0) { r = -errno; goto fail_alloc; } + file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq)); r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file); if (r) { @@ -681,16 +700,11 @@ static int vhost_virtqueue_init(struct vhost_dev *dev, goto fail_kick; } - file.fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vvq)); - r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file); - if (r) { - r = -errno; - goto fail_call; - } + /* Clear and discard previous events if any. */ + event_notifier_test_and_clear(&vq->masked_notifier); return 0; -fail_call: fail_kick: fail_alloc: cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx), @@ -708,15 +722,16 @@ fail_alloc_desc: return r; } -static void vhost_virtqueue_cleanup(struct vhost_dev *dev, +static void vhost_virtqueue_stop(struct vhost_dev *dev, struct VirtIODevice *vdev, struct vhost_virtqueue *vq, unsigned idx) { struct vhost_vring_state state = { - .index = idx, + .index = idx - dev->vq_index }; int r; + assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); r = ioctl(dev->control, VHOST_GET_VRING_BASE, &state); if (r < 0) { fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r); @@ -746,11 +761,39 @@ static void vhost_eventfd_del(MemoryListener *listener, { } +static int vhost_virtqueue_init(struct vhost_dev *dev, + struct vhost_virtqueue *vq, int n) +{ + struct vhost_vring_file file = { + .index = n, + }; + int r = event_notifier_init(&vq->masked_notifier, 0); + if (r < 0) { + return r; + } + + file.fd = event_notifier_get_fd(&vq->masked_notifier); + r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file); + if (r) { + r = -errno; + goto fail_call; + } + return 0; +fail_call: + event_notifier_cleanup(&vq->masked_notifier); + return r; +} + +static void vhost_virtqueue_cleanup(struct vhost_virtqueue *vq) +{ + event_notifier_cleanup(&vq->masked_notifier); +} + int vhost_dev_init(struct vhost_dev *hdev, int devfd, const char *devpath, bool force) { uint64_t features; - int r; + int i, r; if (devfd >= 0) { hdev->control = devfd; } else { @@ -768,6 +811,13 @@ int vhost_dev_init(struct vhost_dev *hdev, int devfd, const char *devpath, if (r < 0) { goto fail; } + + for (i = 0; i < hdev->nvqs; ++i) { + r = vhost_virtqueue_init(hdev, hdev->vqs + i, i); + if (r < 0) { + goto fail_vq; + } + } hdev->features = features; hdev->memory_listener = (MemoryListener) { @@ -795,6 +845,10 @@ int vhost_dev_init(struct vhost_dev *hdev, int devfd, const char *devpath, memory_listener_register(&hdev->memory_listener, &address_space_memory); hdev->force = force; return 0; +fail_vq: + while (--i >= 0) { + vhost_virtqueue_cleanup(hdev->vqs + i); + } fail: r = -errno; close(hdev->control); @@ -803,6 +857,10 @@ fail: void vhost_dev_cleanup(struct vhost_dev *hdev) { + int i; + for (i = 0; i < hdev->nvqs; ++i) { + vhost_virtqueue_cleanup(hdev->vqs + i); + } memory_listener_unregister(&hdev->memory_listener); g_free(hdev->mem); g_free(hdev->mem_sections); @@ -829,7 +887,9 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) } for (i = 0; i < hdev->nvqs; ++i) { - r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, true); + r = vdev->binding->set_host_notifier(vdev->binding_opaque, + hdev->vq_index + i, + true); if (r < 0) { fprintf(stderr, "vhost VQ %d notifier binding failed: %d\n", i, -r); goto fail_vq; @@ -839,7 +899,9 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) return 0; fail_vq: while (--i >= 0) { - r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false); + r = vdev->binding->set_host_notifier(vdev->binding_opaque, + hdev->vq_index + i, + false); if (r < 0) { fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r); fflush(stderr); @@ -860,7 +922,9 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) int i, r; for (i = 0; i < hdev->nvqs; ++i) { - r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false); + r = vdev->binding->set_host_notifier(vdev->binding_opaque, + hdev->vq_index + i, + false); if (r < 0) { fprintf(stderr, "vhost VQ %d notifier cleanup failed: %d\n", i, -r); fflush(stderr); @@ -869,21 +933,45 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) } } +/* Test and clear event pending status. + * Should be called after unmask to avoid losing events. + */ +bool vhost_virtqueue_pending(struct vhost_dev *hdev, int n) +{ + struct vhost_virtqueue *vq = hdev->vqs + n - hdev->vq_index; + assert(hdev->started); + assert(n >= hdev->vq_index && n < hdev->vq_index + hdev->nvqs); + return event_notifier_test_and_clear(&vq->masked_notifier); +} + +/* Mask/unmask events from this vq. */ +void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, + bool mask) +{ + struct VirtQueue *vvq = virtio_get_queue(vdev, n); + int r, index = n - hdev->vq_index; + + assert(hdev->started); + assert(n >= hdev->vq_index && n < hdev->vq_index + hdev->nvqs); + + struct vhost_vring_file file = { + .index = index + }; + if (mask) { + file.fd = event_notifier_get_fd(&hdev->vqs[index].masked_notifier); + } else { + file.fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vvq)); + } + r = ioctl(hdev->control, VHOST_SET_VRING_CALL, &file); + assert(r >= 0); +} + /* Host notifiers must be enabled at this point. */ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) { int i, r; - if (!vdev->binding->set_guest_notifiers) { - fprintf(stderr, "binding does not support guest notifiers\n"); - r = -ENOSYS; - goto fail; - } - r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, true); - if (r < 0) { - fprintf(stderr, "Error binding guest notifier: %d\n", -r); - goto fail_notifiers; - } + hdev->started = true; r = vhost_dev_set_features(hdev, hdev->log_enabled); if (r < 0) { @@ -895,10 +983,10 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) goto fail_mem; } for (i = 0; i < hdev->nvqs; ++i) { - r = vhost_virtqueue_init(hdev, - vdev, - hdev->vqs + i, - i); + r = vhost_virtqueue_start(hdev, + vdev, + hdev->vqs + i, + hdev->vq_index + i); if (r < 0) { goto fail_vq; } @@ -916,49 +1004,39 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) } } - hdev->started = true; - return 0; fail_log: fail_vq: while (--i >= 0) { - vhost_virtqueue_cleanup(hdev, - vdev, - hdev->vqs + i, - i); + vhost_virtqueue_stop(hdev, + vdev, + hdev->vqs + i, + hdev->vq_index + i); } + i = hdev->nvqs; fail_mem: fail_features: - vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); -fail_notifiers: -fail: + + hdev->started = false; return r; } /* Host notifiers must be enabled at this point. */ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) { - int i, r; + int i; for (i = 0; i < hdev->nvqs; ++i) { - vhost_virtqueue_cleanup(hdev, - vdev, - hdev->vqs + i, - i); + vhost_virtqueue_stop(hdev, + vdev, + hdev->vqs + i, + hdev->vq_index + i); } - for (i = 0; i < hdev->n_mem_sections; ++i) { - vhost_sync_dirty_bitmap(hdev, &hdev->mem_sections[i], - 0, (hwaddr)~0x0ull); - } - r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); - if (r < 0) { - fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); - fflush(stderr); - } - assert (r >= 0); + vhost_log_sync_range(hdev, 0, ~0x0ull); hdev->started = false; g_free(hdev->log); hdev->log = NULL; hdev->log_size = 0; } + diff --git a/hw/vhost.h b/hw/vhost.h index 6f6a906f4f..f062d48807 100644 --- a/hw/vhost.h +++ b/hw/vhost.h @@ -18,6 +18,7 @@ struct vhost_virtqueue { void *ring; unsigned long long ring_phys; unsigned ring_size; + EventNotifier masked_notifier; }; typedef unsigned long vhost_log_chunk_t; @@ -34,6 +35,8 @@ struct vhost_dev { MemoryRegionSection *mem_sections; struct vhost_virtqueue *vqs; int nvqs; + /* the first virtuque which would be used by this vhost dev */ + int vq_index; unsigned long long features; unsigned long long acked_features; unsigned long long backend_features; @@ -53,4 +56,13 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev); int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev); void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev); +/* Test and clear masked event pending status. + * Should be called after unmask to avoid losing events. + */ +bool vhost_virtqueue_pending(struct vhost_dev *hdev, int n); + +/* Mask/unmask events from this vq. + */ +void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n, + bool mask); #endif diff --git a/hw/vhost_net.c b/hw/vhost_net.c index ae2785d83f..d3218a07f4 100644 --- a/hw/vhost_net.c +++ b/hw/vhost_net.c @@ -16,8 +16,8 @@ #include "net/net.h" #include "net/tap.h" -#include "virtio-net.h" -#include "vhost_net.h" +#include "hw/virtio-net.h" +#include "hw/vhost_net.h" #include "qemu/error-report.h" #include "config.h" @@ -36,7 +36,7 @@ #include -#include "vhost.h" +#include "hw/vhost.h" struct vhost_net { struct vhost_dev dev; @@ -109,6 +109,9 @@ struct vhost_net *vhost_net_init(NetClientState *backend, int devfd, (1 << VHOST_NET_F_VIRTIO_NET_HDR); net->backend = r; + net->dev.nvqs = 2; + net->dev.vqs = net->vqs; + r = vhost_dev_init(&net->dev, devfd, "/dev/vhost-net", force); if (r < 0) { goto fail; @@ -137,14 +140,20 @@ bool vhost_net_query(VHostNetState *net, VirtIODevice *dev) return vhost_dev_query(&net->dev, dev); } -int vhost_net_start(struct vhost_net *net, - VirtIODevice *dev) +static int vhost_net_start_one(struct vhost_net *net, + VirtIODevice *dev, + int vq_index) { struct vhost_vring_file file = { }; int r; + if (net->dev.started) { + return 0; + } + net->dev.nvqs = 2; net->dev.vqs = net->vqs; + net->dev.vq_index = vq_index; r = vhost_dev_enable_notifiers(&net->dev, dev); if (r < 0) { @@ -181,11 +190,15 @@ fail_notifiers: return r; } -void vhost_net_stop(struct vhost_net *net, - VirtIODevice *dev) +static void vhost_net_stop_one(struct vhost_net *net, + VirtIODevice *dev) { struct vhost_vring_file file = { .fd = -1 }; + if (!net->dev.started) { + return; + } + for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { int r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file); assert(r >= 0); @@ -195,11 +208,77 @@ void vhost_net_stop(struct vhost_net *net, vhost_dev_disable_notifiers(&net->dev, dev); } +int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, + int total_queues) +{ + int r, i = 0; + + if (!dev->binding->set_guest_notifiers) { + error_report("binding does not support guest notifiers"); + r = -ENOSYS; + goto err; + } + + for (i = 0; i < total_queues; i++) { + r = vhost_net_start_one(tap_get_vhost_net(ncs[i].peer), dev, i * 2); + + if (r < 0) { + goto err; + } + } + + r = dev->binding->set_guest_notifiers(dev->binding_opaque, + total_queues * 2, + true); + if (r < 0) { + error_report("Error binding guest notifier: %d", -r); + goto err; + } + + return 0; + +err: + while (--i >= 0) { + vhost_net_stop_one(tap_get_vhost_net(ncs[i].peer), dev); + } + return r; +} + +void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, + int total_queues) +{ + int i, r; + + r = dev->binding->set_guest_notifiers(dev->binding_opaque, + total_queues * 2, + false); + if (r < 0) { + fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); + fflush(stderr); + } + assert(r >= 0); + + for (i = 0; i < total_queues; i++) { + vhost_net_stop_one(tap_get_vhost_net(ncs[i].peer), dev); + } +} + void vhost_net_cleanup(struct vhost_net *net) { vhost_dev_cleanup(&net->dev); g_free(net); } + +bool vhost_net_virtqueue_pending(VHostNetState *net, int idx) +{ + return vhost_virtqueue_pending(&net->dev, idx); +} + +void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, + int idx, bool mask) +{ + vhost_virtqueue_mask(&net->dev, dev, idx, mask); +} #else struct vhost_net *vhost_net_init(NetClientState *backend, int devfd, bool force) @@ -213,13 +292,15 @@ bool vhost_net_query(VHostNetState *net, VirtIODevice *dev) return false; } -int vhost_net_start(struct vhost_net *net, - VirtIODevice *dev) +int vhost_net_start(VirtIODevice *dev, + NetClientState *ncs, + int total_queues) { return -ENOSYS; } -void vhost_net_stop(struct vhost_net *net, - VirtIODevice *dev) +void vhost_net_stop(VirtIODevice *dev, + NetClientState *ncs, + int total_queues) { } @@ -234,4 +315,14 @@ unsigned vhost_net_get_features(struct vhost_net *net, unsigned features) void vhost_net_ack_features(struct vhost_net *net, unsigned features) { } + +bool vhost_net_virtqueue_pending(VHostNetState *net, int idx) +{ + return -ENOSYS; +} + +void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, + int idx, bool mask) +{ +} #endif diff --git a/hw/vhost_net.h b/hw/vhost_net.h index 012aba4148..2d936bb5f5 100644 --- a/hw/vhost_net.h +++ b/hw/vhost_net.h @@ -9,12 +9,15 @@ typedef struct vhost_net VHostNetState; VHostNetState *vhost_net_init(NetClientState *backend, int devfd, bool force); bool vhost_net_query(VHostNetState *net, VirtIODevice *dev); -int vhost_net_start(VHostNetState *net, VirtIODevice *dev); -void vhost_net_stop(VHostNetState *net, VirtIODevice *dev); +int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, int total_queues); +void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, int total_queues); void vhost_net_cleanup(VHostNetState *net); unsigned vhost_net_get_features(VHostNetState *net, unsigned features); void vhost_net_ack_features(VHostNetState *net, unsigned features); +bool vhost_net_virtqueue_pending(VHostNetState *net, int n); +void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, + int idx, bool mask); #endif diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 3040bc63ab..6bfcddc379 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -14,14 +14,16 @@ */ #include "qemu/iov.h" +#include "qemu/timer.h" #include "qemu-common.h" -#include "virtio.h" -#include "pc.h" +#include "hw/virtio.h" +#include "hw/pc.h" #include "cpu.h" #include "sysemu/balloon.h" -#include "virtio-balloon.h" +#include "hw/virtio-balloon.h" #include "sysemu/kvm.h" #include "exec/address-spaces.h" +#include "qapi/visitor.h" #if defined(__linux__) #include @@ -36,6 +38,9 @@ typedef struct VirtIOBalloon uint64_t stats[VIRTIO_BALLOON_S_NR]; VirtQueueElement stats_vq_elem; size_t stats_vq_offset; + QEMUTimer *stats_timer; + int64_t stats_last_update; + int64_t stats_poll_interval; DeviceState *qdev; } VirtIOBalloon; @@ -53,6 +58,16 @@ static void balloon_page(void *addr, int deflate) #endif } +static const char *balloon_stat_names[] = { + [VIRTIO_BALLOON_S_SWAP_IN] = "stat-swap-in", + [VIRTIO_BALLOON_S_SWAP_OUT] = "stat-swap-out", + [VIRTIO_BALLOON_S_MAJFLT] = "stat-major-faults", + [VIRTIO_BALLOON_S_MINFLT] = "stat-minor-faults", + [VIRTIO_BALLOON_S_MEMFREE] = "stat-free-memory", + [VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory", + [VIRTIO_BALLOON_S_NR] = NULL +}; + /* * reset_stats - Mark all items in the stats array as unset * @@ -67,6 +82,118 @@ static inline void reset_stats(VirtIOBalloon *dev) for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1); } +static bool balloon_stats_supported(const VirtIOBalloon *s) +{ + return s->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ); +} + +static bool balloon_stats_enabled(const VirtIOBalloon *s) +{ + return s->stats_poll_interval > 0; +} + +static void balloon_stats_destroy_timer(VirtIOBalloon *s) +{ + if (balloon_stats_enabled(s)) { + qemu_del_timer(s->stats_timer); + qemu_free_timer(s->stats_timer); + s->stats_timer = NULL; + s->stats_poll_interval = 0; + } +} + +static void balloon_stats_change_timer(VirtIOBalloon *s, int secs) +{ + qemu_mod_timer(s->stats_timer, qemu_get_clock_ms(vm_clock) + secs * 1000); +} + +static void balloon_stats_poll_cb(void *opaque) +{ + VirtIOBalloon *s = opaque; + + if (!balloon_stats_supported(s)) { + /* re-schedule */ + balloon_stats_change_timer(s, s->stats_poll_interval); + return; + } + + virtqueue_push(s->svq, &s->stats_vq_elem, s->stats_vq_offset); + virtio_notify(&s->vdev, s->svq); +} + +static void balloon_stats_get_all(Object *obj, struct Visitor *v, + void *opaque, const char *name, Error **errp) +{ + VirtIOBalloon *s = opaque; + int i; + + if (!s->stats_last_update) { + error_setg(errp, "guest hasn't updated any stats yet"); + return; + } + + visit_start_struct(v, NULL, "guest-stats", name, 0, errp); + visit_type_int(v, &s->stats_last_update, "last-update", errp); + + visit_start_struct(v, NULL, NULL, "stats", 0, errp); + for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { + visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i], + errp); + } + visit_end_struct(v, errp); + + visit_end_struct(v, errp); +} + +static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + VirtIOBalloon *s = opaque; + visit_type_int(v, &s->stats_poll_interval, name, errp); +} + +static void balloon_stats_set_poll_interval(Object *obj, struct Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + VirtIOBalloon *s = opaque; + int64_t value; + + visit_type_int(v, &value, name, errp); + if (error_is_set(errp)) { + return; + } + + if (value < 0) { + error_setg(errp, "timer value must be greater than zero"); + return; + } + + if (value == s->stats_poll_interval) { + return; + } + + if (value == 0) { + /* timer=0 disables the timer */ + balloon_stats_destroy_timer(s); + return; + } + + if (balloon_stats_enabled(s)) { + /* timer interval change */ + s->stats_poll_interval = value; + balloon_stats_change_timer(s, value); + return; + } + + /* create a new timer */ + g_assert(s->stats_timer == NULL); + s->stats_timer = qemu_new_timer_ms(vm_clock, balloon_stats_poll_cb, s); + s->stats_poll_interval = value; + balloon_stats_change_timer(s, 0); +} + static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = to_virtio_balloon(vdev); @@ -107,9 +234,10 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) VirtQueueElement *elem = &s->stats_vq_elem; VirtIOBalloonStat stat; size_t offset = 0; + qemu_timeval tv; if (!virtqueue_pop(vq, elem)) { - return; + goto out; } /* Initialize the stats to get rid of any stale values. This is only @@ -128,6 +256,18 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) s->stats[tag] = val; } s->stats_vq_offset = offset; + + if (qemu_gettimeofday(&tv) < 0) { + fprintf(stderr, "warning: %s: failed to get time of day\n", __func__); + goto out; + } + + s->stats_last_update = tv.tv_sec; + +out: + if (balloon_stats_enabled(s)) { + balloon_stats_change_timer(s, s->stats_poll_interval); + } } static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) @@ -164,28 +304,6 @@ static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) static void virtio_balloon_stat(void *opaque, BalloonInfo *info) { VirtIOBalloon *dev = opaque; - -#if 0 - /* Disable guest-provided stats for now. For more details please check: - * https://bugzilla.redhat.com/show_bug.cgi?id=623903 - * - * If you do enable it (which is probably not going to happen as we - * need a new command for it), remember that you also need to fill the - * appropriate members of the BalloonInfo structure so that the stats - * are returned to the client. - */ - if (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ)) { - virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset); - virtio_notify(&dev->vdev, dev->svq); - return; - } -#endif - - /* Stats are not supported. Clear out any stale values that might - * have been set by a more featureful guest kernel. - */ - reset_stats(dev); - info->actual = ram_size - ((uint64_t) dev->actual << VIRTIO_BALLOON_PFN_SHIFT); } @@ -255,12 +373,18 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev) s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); s->svq = virtio_add_queue(&s->vdev, 128, virtio_balloon_receive_stats); - reset_stats(s); - s->qdev = dev; register_savevm(dev, "virtio-balloon", -1, 1, virtio_balloon_save, virtio_balloon_load, s); + object_property_add(OBJECT(dev), "guest-stats", "guest statistics", + balloon_stats_get_all, NULL, NULL, s, NULL); + + object_property_add(OBJECT(dev), "guest-stats-polling-interval", "int", + balloon_stats_get_poll_interval, + balloon_stats_set_poll_interval, + NULL, s, NULL); + return &s->vdev; } @@ -268,6 +392,7 @@ void virtio_balloon_exit(VirtIODevice *vdev) { VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); + balloon_stats_destroy_timer(s); qemu_remove_balloon_handler(s); unregister_savevm(s->qdev, "virtio-balloon", s); virtio_cleanup(vdev); diff --git a/hw/virtio-balloon.h b/hw/virtio-balloon.h index b1828f4a48..f37f31b4d7 100644 --- a/hw/virtio-balloon.h +++ b/hw/virtio-balloon.h @@ -15,8 +15,8 @@ #ifndef _QEMU_VIRTIO_BALLOON_H #define _QEMU_VIRTIO_BALLOON_H -#include "virtio.h" -#include "pci/pci.h" +#include "hw/virtio.h" +#include "hw/pci/pci.h" /* from Linux's linux/virtio_balloon.h */ diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index 90cfa246db..6b69236655 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -16,8 +16,11 @@ #include "trace.h" #include "hw/block-common.h" #include "sysemu/blockdev.h" -#include "virtio-blk.h" -#include "scsi-defs.h" +#include "hw/virtio-blk.h" +#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE +#include "dataplane/virtio-blk.h" +#endif +#include "hw/scsi-defs.h" #ifdef __linux__ # include #endif @@ -33,6 +36,10 @@ typedef struct VirtIOBlock VirtIOBlkConf *blk; unsigned short sector_mask; DeviceState *qdev; + VMChangeStateEntry *change; +#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE + VirtIOBlockDataPlane *dataplane; +#endif } VirtIOBlock; static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev) @@ -392,10 +399,14 @@ static void virtio_blk_handle_request(VirtIOBlockReq *req, qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1], req->elem.out_num - 1); virtio_blk_handle_write(req, mrb); - } else { + } else if (type == VIRTIO_BLK_T_IN || type == VIRTIO_BLK_T_BARRIER) { + /* VIRTIO_BLK_T_IN is 0, so we can't just & it. */ qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0], req->elem.in_num - 1); virtio_blk_handle_read(req); + } else { + virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); + g_free(req); } } @@ -407,6 +418,16 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) .num_writes = 0, }; +#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE + /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start + * dataplane here instead of waiting for .set_status(). + */ + if (s->dataplane) { + virtio_blk_data_plane_start(s->dataplane); + return; + } +#endif + while ((req = virtio_blk_get_request(s))) { virtio_blk_handle_request(req, &mrb); } @@ -446,8 +467,9 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running, { VirtIOBlock *s = opaque; - if (!running) + if (!running) { return; + } if (!s->bh) { s->bh = qemu_bh_new(virtio_blk_dma_restart_bh, s); @@ -457,6 +479,14 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running, static void virtio_blk_reset(VirtIODevice *vdev) { +#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE + VirtIOBlock *s = to_virtio_blk(vdev); + + if (s->dataplane) { + virtio_blk_data_plane_stop(s->dataplane); + } +#endif + /* * This should cancel pending requests, but can't do nicely until there * are per-device request lists. @@ -524,6 +554,9 @@ static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) features |= (1 << VIRTIO_BLK_F_BLK_SIZE); features |= (1 << VIRTIO_BLK_F_SCSI); + if (s->blk->config_wce) { + features |= (1 << VIRTIO_BLK_F_CONFIG_WCE); + } if (bdrv_enable_write_cache(s->bs)) features |= (1 << VIRTIO_BLK_F_WCE); @@ -538,6 +571,13 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) VirtIOBlock *s = to_virtio_blk(vdev); uint32_t features; +#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE + if (s->dataplane && !(status & (VIRTIO_CONFIG_S_DRIVER | + VIRTIO_CONFIG_S_DRIVER_OK))) { + virtio_blk_data_plane_stop(s->dataplane); + } +#endif + if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { return; } @@ -635,8 +675,14 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk) s->sector_mask = (s->conf->logical_block_size / BDRV_SECTOR_SIZE) - 1; s->vq = virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output); +#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE + if (!virtio_blk_data_plane_create(&s->vdev, blk, &s->dataplane)) { + virtio_cleanup(&s->vdev); + return NULL; + } +#endif - qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s); + s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s); s->qdev = dev; register_savevm(dev, "virtio-blk", virtio_blk_id++, 2, virtio_blk_save, virtio_blk_load, s); @@ -652,6 +698,12 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk) void virtio_blk_exit(VirtIODevice *vdev) { VirtIOBlock *s = to_virtio_blk(vdev); + +#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE + virtio_blk_data_plane_destroy(s->dataplane); + s->dataplane = NULL; +#endif + qemu_del_vm_change_state_handler(s->change); unregister_savevm(s->qdev, "virtio-blk", s); blockdev_mark_auto_del(s->bs); virtio_cleanup(vdev); diff --git a/hw/virtio-blk.h b/hw/virtio-blk.h index 651a000b9f..7ef2f35852 100644 --- a/hw/virtio-blk.h +++ b/hw/virtio-blk.h @@ -14,7 +14,7 @@ #ifndef _QEMU_VIRTIO_BLK_H #define _QEMU_VIRTIO_BLK_H -#include "virtio.h" +#include "hw/virtio.h" #include "hw/block-common.h" /* from Linux's linux/virtio_blk.h */ @@ -104,10 +104,11 @@ struct VirtIOBlkConf BlockConf conf; char *serial; uint32_t scsi; + uint32_t config_wce; + uint32_t data_plane; }; #define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \ - DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \ - DEFINE_PROP_BIT("config-wce", _state, _field, VIRTIO_BLK_F_CONFIG_WCE, true) + DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) #endif diff --git a/hw/virtio-bus.c b/hw/virtio-bus.c new file mode 100644 index 0000000000..6c2aab00eb --- /dev/null +++ b/hw/virtio-bus.c @@ -0,0 +1,164 @@ +/* + * VirtioBus + * + * Copyright (C) 2012 : GreenSocs Ltd + * http://www.greensocs.com/ , email: info@greensocs.com + * + * Developed by : + * Frederic Konrad + * + * 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) 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 . + * + */ + +#include "hw/hw.h" +#include "qemu/error-report.h" +#include "hw/qdev.h" +#include "hw/virtio-bus.h" +#include "hw/virtio.h" + +/* #define DEBUG_VIRTIO_BUS */ + +#ifdef DEBUG_VIRTIO_BUS +#define DPRINTF(fmt, ...) \ +do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do { } while (0) +#endif + +/* Plug the VirtIODevice */ +int virtio_bus_plug_device(VirtIODevice *vdev) +{ + DeviceState *qdev = DEVICE(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(qdev)); + VirtioBusState *bus = VIRTIO_BUS(qbus); + VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); + DPRINTF("%s: plug device.\n", qbus->name); + + bus->vdev = vdev; + + /* + * The lines below will disappear when we drop VirtIOBindings, at the end + * of the series. + */ + bus->bindings.notify = klass->notify; + bus->bindings.save_config = klass->save_config; + bus->bindings.save_queue = klass->save_queue; + bus->bindings.load_config = klass->load_config; + bus->bindings.load_queue = klass->load_queue; + bus->bindings.load_done = klass->load_done; + bus->bindings.get_features = klass->get_features; + bus->bindings.query_guest_notifiers = klass->query_guest_notifiers; + bus->bindings.set_guest_notifiers = klass->set_guest_notifiers; + bus->bindings.set_host_notifier = klass->set_host_notifier; + bus->bindings.vmstate_change = klass->vmstate_change; + virtio_bind_device(bus->vdev, &bus->bindings, qbus->parent); + + if (klass->device_plugged != NULL) { + klass->device_plugged(qbus->parent); + } + + return 0; +} + +/* Reset the virtio_bus */ +void virtio_bus_reset(VirtioBusState *bus) +{ + DPRINTF("%s: reset device.\n", qbus->name); + if (bus->vdev != NULL) { + virtio_reset(bus->vdev); + } +} + +/* Destroy the VirtIODevice */ +void virtio_bus_destroy_device(VirtioBusState *bus) +{ + DeviceState *qdev; + BusState *qbus = BUS(bus); + VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); + DPRINTF("%s: remove device.\n", qbus->name); + + if (bus->vdev != NULL) { + if (klass->device_unplug != NULL) { + klass->device_unplug(qbus->parent); + } + qdev = DEVICE(bus->vdev); + qdev_free(qdev); + bus->vdev = NULL; + } +} + +/* Get the device id of the plugged device. */ +uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus) +{ + assert(bus->vdev != NULL); + return bus->vdev->device_id; +} + +/* Get the config_len field of the plugged device. */ +size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus) +{ + assert(bus->vdev != NULL); + return bus->vdev->config_len; +} + +/* Get the features of the plugged device. */ +uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus, + uint32_t requested_features) +{ + VirtioDeviceClass *k; + assert(bus->vdev != NULL); + k = VIRTIO_DEVICE_GET_CLASS(bus->vdev); + assert(k->get_features != NULL); + return k->get_features(bus->vdev, requested_features); +} + +/* Get bad features of the plugged device. */ +uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus) +{ + VirtioDeviceClass *k; + assert(bus->vdev != NULL); + k = VIRTIO_DEVICE_GET_CLASS(bus->vdev); + if (k->bad_features != NULL) { + return k->bad_features(bus->vdev); + } else { + return 0; + } +} + +/* Get config of the plugged device. */ +void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config) +{ + VirtioDeviceClass *k; + assert(bus->vdev != NULL); + k = VIRTIO_DEVICE_GET_CLASS(bus->vdev); + if (k->get_config != NULL) { + k->get_config(bus->vdev, config); + } +} + +static const TypeInfo virtio_bus_info = { + .name = TYPE_VIRTIO_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(VirtioBusState), + .abstract = true, + .class_size = sizeof(VirtioBusClass), +}; + +static void virtio_register_types(void) +{ + type_register_static(&virtio_bus_info); +} + +type_init(virtio_register_types) diff --git a/hw/virtio-bus.h b/hw/virtio-bus.h new file mode 100644 index 0000000000..ae0f7078b4 --- /dev/null +++ b/hw/virtio-bus.h @@ -0,0 +1,94 @@ +/* + * VirtioBus + * + * Copyright (C) 2012 : GreenSocs Ltd + * http://www.greensocs.com/ , email: info@greensocs.com + * + * Developed by : + * Frederic Konrad + * + * 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) 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 . + * + */ + +#ifndef VIRTIO_BUS_H +#define VIRTIO_BUS_H + +#include "hw/qdev.h" +#include "sysemu/sysemu.h" +#include "hw/virtio.h" + +#define TYPE_VIRTIO_BUS "virtio-bus" +#define VIRTIO_BUS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtioBusClass, obj, TYPE_VIRTIO_BUS) +#define VIRTIO_BUS_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtioBusClass, klass, TYPE_VIRTIO_BUS) +#define VIRTIO_BUS(obj) OBJECT_CHECK(VirtioBusState, (obj), TYPE_VIRTIO_BUS) + +typedef struct VirtioBusState VirtioBusState; + +typedef struct VirtioBusClass { + /* This is what a VirtioBus must implement */ + BusClass parent; + void (*notify)(DeviceState *d, uint16_t vector); + void (*save_config)(DeviceState *d, QEMUFile *f); + void (*save_queue)(DeviceState *d, int n, QEMUFile *f); + int (*load_config)(DeviceState *d, QEMUFile *f); + int (*load_queue)(DeviceState *d, int n, QEMUFile *f); + int (*load_done)(DeviceState *d, QEMUFile *f); + unsigned (*get_features)(DeviceState *d); + bool (*query_guest_notifiers)(DeviceState *d); + int (*set_guest_notifiers)(DeviceState *d, int nvqs, bool assign); + int (*set_host_notifier)(DeviceState *d, int n, bool assigned); + void (*vmstate_change)(DeviceState *d, bool running); + /* + * transport independent init function. + * This is called by virtio-bus just after the device is plugged. + */ + void (*device_plugged)(DeviceState *d); + /* + * transport independent exit function. + * This is called by virtio-bus just before the device is unplugged. + */ + void (*device_unplug)(DeviceState *d); +} VirtioBusClass; + +struct VirtioBusState { + BusState parent_obj; + /* + * Only one VirtIODevice can be plugged on the bus. + */ + VirtIODevice *vdev; + /* + * This will be removed at the end of the series. + */ + VirtIOBindings bindings; +}; + +int virtio_bus_plug_device(VirtIODevice *vdev); +void virtio_bus_reset(VirtioBusState *bus); +void virtio_bus_destroy_device(VirtioBusState *bus); +/* Get the device id of the plugged device. */ +uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus); +/* Get the config_len field of the plugged device. */ +size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus); +/* Get the features of the plugged device. */ +uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus, + uint32_t requested_features); +/* Get bad features of the plugged device. */ +uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus); +/* Get config of the plugged device. */ +void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config); + +#endif /* VIRTIO_BUS_H */ diff --git a/hw/virtio-console.c b/hw/virtio-console.c index 002b028b99..e2d1c58d9d 100644 --- a/hw/virtio-console.c +++ b/hw/virtio-console.c @@ -13,13 +13,25 @@ #include "char/char.h" #include "qemu/error-report.h" #include "trace.h" -#include "virtio-serial.h" +#include "hw/virtio-serial.h" typedef struct VirtConsole { VirtIOSerialPort port; CharDriverState *chr; } VirtConsole; +/* + * Callback function that's called from chardevs when backend becomes + * writable. + */ +static gboolean chr_write_unblocked(GIOChannel *chan, GIOCondition cond, + void *opaque) +{ + VirtConsole *vcon = opaque; + + virtio_serial_throttle_port(&vcon->port, false); + return FALSE; +} /* Callback function that's called when the guest sends us data */ static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) @@ -35,19 +47,21 @@ static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) ret = qemu_chr_fe_write(vcon->chr, buf, len); trace_virtio_console_flush_buf(port->id, len, ret); - if (ret < 0) { + if (ret <= 0) { + VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port); + /* * Ideally we'd get a better error code than just -1, but * that's what the chardev interface gives us right now. If * we had a finer-grained message, like -EPIPE, we could close - * this connection. Absent such error messages, the most we - * can do is to return 0 here. - * - * This will prevent stray -1 values to go to - * virtio-serial-bus.c and cause abort()s in - * do_flush_queued_data(). + * this connection. */ ret = 0; + if (!k->is_console) { + virtio_serial_throttle_port(port, true); + qemu_chr_fe_add_watch(vcon->chr, G_IO_OUT, chr_write_unblocked, + vcon); + } } return ret; } @@ -142,7 +156,7 @@ static void virtconsole_class_init(ObjectClass *klass, void *data) dc->props = virtconsole_properties; } -static TypeInfo virtconsole_info = { +static const TypeInfo virtconsole_info = { .name = "virtconsole", .parent = TYPE_VIRTIO_SERIAL_PORT, .instance_size = sizeof(VirtConsole), @@ -166,7 +180,7 @@ static void virtserialport_class_init(ObjectClass *klass, void *data) dc->props = virtserialport_properties; } -static TypeInfo virtserialport_info = { +static const TypeInfo virtserialport_info = { .name = "virtserialport", .parent = TYPE_VIRTIO_SERIAL_PORT, .instance_size = sizeof(VirtConsole), diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 5d03b31c1b..8c9d8713f3 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -12,42 +12,47 @@ */ #include "qemu/iov.h" -#include "virtio.h" +#include "hw/virtio.h" #include "net/net.h" #include "net/checksum.h" #include "net/tap.h" #include "qemu/error-report.h" #include "qemu/timer.h" -#include "virtio-net.h" -#include "vhost_net.h" +#include "hw/virtio-net.h" +#include "hw/vhost_net.h" #define VIRTIO_NET_VM_VERSION 11 #define MAC_TABLE_ENTRIES 64 #define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ +typedef struct VirtIONetQueue { + VirtQueue *rx_vq; + VirtQueue *tx_vq; + QEMUTimer *tx_timer; + QEMUBH *tx_bh; + int tx_waiting; + struct { + VirtQueueElement elem; + ssize_t len; + } async_tx; + struct VirtIONet *n; +} VirtIONetQueue; + typedef struct VirtIONet { VirtIODevice vdev; uint8_t mac[ETH_ALEN]; uint16_t status; - VirtQueue *rx_vq; - VirtQueue *tx_vq; + VirtIONetQueue *vqs; VirtQueue *ctrl_vq; NICState *nic; - QEMUTimer *tx_timer; - QEMUBH *tx_bh; uint32_t tx_timeout; int32_t tx_burst; - int tx_waiting; uint32_t has_vnet_hdr; size_t host_hdr_len; size_t guest_hdr_len; uint8_t has_ufo; - struct { - VirtQueueElement elem; - ssize_t len; - } async_tx; int mergeable_rx_bufs; uint8_t promisc; uint8_t allmulti; @@ -65,8 +70,46 @@ typedef struct VirtIONet } mac_table; uint32_t *vlans; DeviceState *qdev; + int multiqueue; + uint16_t max_queues; + uint16_t curr_queues; + size_t config_size; } VirtIONet; +/* + * Calculate the number of bytes up to and including the given 'field' of + * 'container'. + */ +#define endof(container, field) \ + (offsetof(container, field) + sizeof(((container *)0)->field)) + +typedef struct VirtIOFeature { + uint32_t flags; + size_t end; +} VirtIOFeature; + +static VirtIOFeature feature_sizes[] = { + {.flags = 1 << VIRTIO_NET_F_MAC, + .end = endof(struct virtio_net_config, mac)}, + {.flags = 1 << VIRTIO_NET_F_STATUS, + .end = endof(struct virtio_net_config, status)}, + {.flags = 1 << VIRTIO_NET_F_MQ, + .end = endof(struct virtio_net_config, max_virtqueue_pairs)}, + {} +}; + +static VirtIONetQueue *virtio_net_get_subqueue(NetClientState *nc) +{ + VirtIONet *n = qemu_get_nic_opaque(nc); + + return &n->vqs[nc->queue_index]; +} + +static int vq2q(int queue_index) +{ + return queue_index / 2; +} + /* TODO * - we could suppress RX interrupt if we were so inclined. */ @@ -82,20 +125,22 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) struct virtio_net_config netcfg; stw_p(&netcfg.status, n->status); + stw_p(&netcfg.max_virtqueue_pairs, n->max_queues); memcpy(netcfg.mac, n->mac, ETH_ALEN); - memcpy(config, &netcfg, sizeof(netcfg)); + memcpy(config, &netcfg, n->config_size); } static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) { VirtIONet *n = to_virtio_net(vdev); - struct virtio_net_config netcfg; + struct virtio_net_config netcfg = {}; - memcpy(&netcfg, config, sizeof(netcfg)); + memcpy(&netcfg, config, n->config_size); - if (memcmp(netcfg.mac, n->mac, ETH_ALEN)) { + if (!(n->vdev.guest_features >> VIRTIO_NET_F_CTRL_MAC_ADDR & 1) && + memcmp(netcfg.mac, n->mac, ETH_ALEN)) { memcpy(n->mac, netcfg.mac, ETH_ALEN); - qemu_format_nic_info_str(&n->nic->nc, n->mac); + qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); } } @@ -107,34 +152,38 @@ static bool virtio_net_started(VirtIONet *n, uint8_t status) static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) { - if (!n->nic->nc.peer) { + NetClientState *nc = qemu_get_queue(n->nic); + int queues = n->multiqueue ? n->max_queues : 1; + + if (!nc->peer) { return; } - if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { return; } - if (!tap_get_vhost_net(n->nic->nc.peer)) { + if (!tap_get_vhost_net(nc->peer)) { return; } + if (!!n->vhost_started == virtio_net_started(n, status) && - !n->nic->nc.peer->link_down) { + !nc->peer->link_down) { return; } if (!n->vhost_started) { int r; - if (!vhost_net_query(tap_get_vhost_net(n->nic->nc.peer), &n->vdev)) { + if (!vhost_net_query(tap_get_vhost_net(nc->peer), &n->vdev)) { return; } - r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer), &n->vdev); + n->vhost_started = 1; + r = vhost_net_start(&n->vdev, n->nic->ncs, queues); if (r < 0) { error_report("unable to start vhost net: %d: " "falling back on userspace virtio", -r); - } else { - n->vhost_started = 1; + n->vhost_started = 0; } } else { - vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), &n->vdev); + vhost_net_stop(&n->vdev, n->nic->ncs, queues); n->vhost_started = 0; } } @@ -142,32 +191,45 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) { VirtIONet *n = to_virtio_net(vdev); + VirtIONetQueue *q; + int i; + uint8_t queue_status; virtio_net_vhost_status(n, status); - if (!n->tx_waiting) { - return; - } + for (i = 0; i < n->max_queues; i++) { + q = &n->vqs[i]; - if (virtio_net_started(n, status) && !n->vhost_started) { - if (n->tx_timer) { - qemu_mod_timer(n->tx_timer, - qemu_get_clock_ns(vm_clock) + n->tx_timeout); + if ((!n->multiqueue && i != 0) || i >= n->curr_queues) { + queue_status = 0; } else { - qemu_bh_schedule(n->tx_bh); + queue_status = status; } - } else { - if (n->tx_timer) { - qemu_del_timer(n->tx_timer); + + if (!q->tx_waiting) { + continue; + } + + if (virtio_net_started(n, queue_status) && !n->vhost_started) { + if (q->tx_timer) { + qemu_mod_timer(q->tx_timer, + qemu_get_clock_ns(vm_clock) + n->tx_timeout); + } else { + qemu_bh_schedule(q->tx_bh); + } } else { - qemu_bh_cancel(n->tx_bh); + if (q->tx_timer) { + qemu_del_timer(q->tx_timer); + } else { + qemu_bh_cancel(q->tx_bh); + } } } } static void virtio_net_set_link_status(NetClientState *nc) { - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; + VirtIONet *n = qemu_get_nic_opaque(nc); uint16_t old_status = n->status; if (nc->link_down) @@ -192,6 +254,8 @@ static void virtio_net_reset(VirtIODevice *vdev) n->nomulti = 0; n->nouni = 0; n->nobcast = 0; + /* multiqueue is disabled by default */ + n->curr_queues = 1; /* Flush any MAC and VLAN filter table state */ n->mac_table.in_use = 0; @@ -199,18 +263,22 @@ static void virtio_net_reset(VirtIODevice *vdev) n->mac_table.multi_overflow = 0; n->mac_table.uni_overflow = 0; memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); + memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac)); memset(n->vlans, 0, MAX_VLAN >> 3); } static void peer_test_vnet_hdr(VirtIONet *n) { - if (!n->nic->nc.peer) + NetClientState *nc = qemu_get_queue(n->nic); + if (!nc->peer) { return; + } - if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) + if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { return; + } - n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer); + n->has_vnet_hdr = tap_has_vnet_hdr(nc->peer); } static int peer_has_vnet_hdr(VirtIONet *n) @@ -223,28 +291,81 @@ static int peer_has_ufo(VirtIONet *n) if (!peer_has_vnet_hdr(n)) return 0; - n->has_ufo = tap_has_ufo(n->nic->nc.peer); + n->has_ufo = tap_has_ufo(qemu_get_queue(n->nic)->peer); return n->has_ufo; } static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs) { + int i; + NetClientState *nc; + n->mergeable_rx_bufs = mergeable_rx_bufs; n->guest_hdr_len = n->mergeable_rx_bufs ? sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); - if (peer_has_vnet_hdr(n) && - tap_has_vnet_hdr_len(n->nic->nc.peer, n->guest_hdr_len)) { - tap_set_vnet_hdr_len(n->nic->nc.peer, n->guest_hdr_len); - n->host_hdr_len = n->guest_hdr_len; + for (i = 0; i < n->max_queues; i++) { + nc = qemu_get_subqueue(n->nic, i); + + if (peer_has_vnet_hdr(n) && + tap_has_vnet_hdr_len(nc->peer, n->guest_hdr_len)) { + tap_set_vnet_hdr_len(nc->peer, n->guest_hdr_len); + n->host_hdr_len = n->guest_hdr_len; + } } } +static int peer_attach(VirtIONet *n, int index) +{ + NetClientState *nc = qemu_get_subqueue(n->nic, index); + + if (!nc->peer) { + return 0; + } + + if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + return 0; + } + + return tap_enable(nc->peer); +} + +static int peer_detach(VirtIONet *n, int index) +{ + NetClientState *nc = qemu_get_subqueue(n->nic, index); + + if (!nc->peer) { + return 0; + } + + if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + return 0; + } + + return tap_disable(nc->peer); +} + +static void virtio_net_set_queues(VirtIONet *n) +{ + int i; + + for (i = 0; i < n->max_queues; i++) { + if (i < n->curr_queues) { + assert(!peer_attach(n, i)); + } else { + assert(!peer_detach(n, i)); + } + } +} + +static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue, int ctrl); + static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) { VirtIONet *n = to_virtio_net(vdev); + NetClientState *nc = qemu_get_queue(n->nic); features |= (1 << VIRTIO_NET_F_MAC); @@ -265,14 +386,13 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) features &= ~(0x1 << VIRTIO_NET_F_HOST_UFO); } - if (!n->nic->nc.peer || - n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + if (!nc->peer || nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { return features; } - if (!tap_get_vhost_net(n->nic->nc.peer)) { + if (!tap_get_vhost_net(nc->peer)) { return features; } - return vhost_net_get_features(tap_get_vhost_net(n->nic->nc.peer), features); + return vhost_net_get_features(tap_get_vhost_net(nc->peer), features); } static uint32_t virtio_net_bad_features(VirtIODevice *vdev) @@ -293,66 +413,84 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev) static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) { VirtIONet *n = to_virtio_net(vdev); + int i; + + virtio_net_set_multiqueue(n, !!(features & (1 << VIRTIO_NET_F_MQ)), + !!(features & (1 << VIRTIO_NET_F_CTRL_VQ))); virtio_net_set_mrg_rx_bufs(n, !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF))); if (n->has_vnet_hdr) { - tap_set_offload(n->nic->nc.peer, + tap_set_offload(qemu_get_subqueue(n->nic, 0)->peer, (features >> VIRTIO_NET_F_GUEST_CSUM) & 1, (features >> VIRTIO_NET_F_GUEST_TSO4) & 1, (features >> VIRTIO_NET_F_GUEST_TSO6) & 1, (features >> VIRTIO_NET_F_GUEST_ECN) & 1, (features >> VIRTIO_NET_F_GUEST_UFO) & 1); } - if (!n->nic->nc.peer || - n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { - return; + + for (i = 0; i < n->max_queues; i++) { + NetClientState *nc = qemu_get_subqueue(n->nic, i); + + if (!nc->peer || nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + continue; + } + if (!tap_get_vhost_net(nc->peer)) { + continue; + } + vhost_net_ack_features(tap_get_vhost_net(nc->peer), features); } - if (!tap_get_vhost_net(n->nic->nc.peer)) { - return; - } - vhost_net_ack_features(tap_get_vhost_net(n->nic->nc.peer), features); } static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, - VirtQueueElement *elem) + struct iovec *iov, unsigned int iov_cnt) { uint8_t on; + size_t s; - if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(on)) { - error_report("virtio-net ctrl invalid rx mode command"); - exit(1); + s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on)); + if (s != sizeof(on)) { + return VIRTIO_NET_ERR; } - on = ldub_p(elem->out_sg[1].iov_base); - - if (cmd == VIRTIO_NET_CTRL_RX_MODE_PROMISC) + if (cmd == VIRTIO_NET_CTRL_RX_PROMISC) { n->promisc = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_ALLMULTI) + } else if (cmd == VIRTIO_NET_CTRL_RX_ALLMULTI) { n->allmulti = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_ALLUNI) + } else if (cmd == VIRTIO_NET_CTRL_RX_ALLUNI) { n->alluni = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOMULTI) + } else if (cmd == VIRTIO_NET_CTRL_RX_NOMULTI) { n->nomulti = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOUNI) + } else if (cmd == VIRTIO_NET_CTRL_RX_NOUNI) { n->nouni = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOBCAST) + } else if (cmd == VIRTIO_NET_CTRL_RX_NOBCAST) { n->nobcast = on; - else + } else { return VIRTIO_NET_ERR; + } return VIRTIO_NET_OK; } static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, - VirtQueueElement *elem) + struct iovec *iov, unsigned int iov_cnt) { struct virtio_net_ctrl_mac mac_data; + size_t s; - if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET || elem->out_num != 3 || - elem->out_sg[1].iov_len < sizeof(mac_data) || - elem->out_sg[2].iov_len < sizeof(mac_data)) + if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) { + if (iov_size(iov, iov_cnt) != sizeof(n->mac)) { + return VIRTIO_NET_ERR; + } + s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac)); + assert(s == sizeof(n->mac)); + qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); + return VIRTIO_NET_OK; + } + + if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET) { return VIRTIO_NET_ERR; + } n->mac_table.in_use = 0; n->mac_table.first_multi = 0; @@ -360,54 +498,72 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, n->mac_table.multi_overflow = 0; memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); - mac_data.entries = ldl_p(elem->out_sg[1].iov_base); - - if (sizeof(mac_data.entries) + - (mac_data.entries * ETH_ALEN) > elem->out_sg[1].iov_len) + s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, + sizeof(mac_data.entries)); + mac_data.entries = ldl_p(&mac_data.entries); + if (s != sizeof(mac_data.entries)) { return VIRTIO_NET_ERR; + } + iov_discard_front(&iov, &iov_cnt, s); + + if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) { + return VIRTIO_NET_ERR; + } if (mac_data.entries <= MAC_TABLE_ENTRIES) { - memcpy(n->mac_table.macs, elem->out_sg[1].iov_base + sizeof(mac_data), - mac_data.entries * ETH_ALEN); + s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs, + mac_data.entries * ETH_ALEN); + if (s != mac_data.entries * ETH_ALEN) { + return VIRTIO_NET_ERR; + } n->mac_table.in_use += mac_data.entries; } else { n->mac_table.uni_overflow = 1; } + iov_discard_front(&iov, &iov_cnt, mac_data.entries * ETH_ALEN); + n->mac_table.first_multi = n->mac_table.in_use; - mac_data.entries = ldl_p(elem->out_sg[2].iov_base); - - if (sizeof(mac_data.entries) + - (mac_data.entries * ETH_ALEN) > elem->out_sg[2].iov_len) + s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, + sizeof(mac_data.entries)); + mac_data.entries = ldl_p(&mac_data.entries); + if (s != sizeof(mac_data.entries)) { return VIRTIO_NET_ERR; + } - if (mac_data.entries) { - if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { - memcpy(n->mac_table.macs + (n->mac_table.in_use * ETH_ALEN), - elem->out_sg[2].iov_base + sizeof(mac_data), - mac_data.entries * ETH_ALEN); - n->mac_table.in_use += mac_data.entries; - } else { - n->mac_table.multi_overflow = 1; + iov_discard_front(&iov, &iov_cnt, s); + + if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) { + return VIRTIO_NET_ERR; + } + + if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { + s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs, + mac_data.entries * ETH_ALEN); + if (s != mac_data.entries * ETH_ALEN) { + return VIRTIO_NET_ERR; } + n->mac_table.in_use += mac_data.entries; + } else { + n->mac_table.multi_overflow = 1; } return VIRTIO_NET_OK; } static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, - VirtQueueElement *elem) + struct iovec *iov, unsigned int iov_cnt) { uint16_t vid; + size_t s; - if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(vid)) { - error_report("virtio-net ctrl invalid vlan command"); + s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid)); + vid = lduw_p(&vid); + if (s != sizeof(vid)) { return VIRTIO_NET_ERR; } - vid = lduw_p(elem->out_sg[1].iov_base); - if (vid >= MAX_VLAN) return VIRTIO_NET_ERR; @@ -421,36 +577,73 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_OK; } +static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, + VirtQueueElement *elem) +{ + struct virtio_net_ctrl_mq s; + + if (elem->out_num != 2 || + elem->out_sg[1].iov_len != sizeof(struct virtio_net_ctrl_mq)) { + error_report("virtio-net ctrl invalid steering command"); + return VIRTIO_NET_ERR; + } + + if (cmd != VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) { + return VIRTIO_NET_ERR; + } + + memcpy(&s, elem->out_sg[1].iov_base, sizeof(struct virtio_net_ctrl_mq)); + + if (s.virtqueue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || + s.virtqueue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || + s.virtqueue_pairs > n->max_queues || + !n->multiqueue) { + return VIRTIO_NET_ERR; + } + + n->curr_queues = s.virtqueue_pairs; + /* stop the backend before changing the number of queues to avoid handling a + * disabled queue */ + virtio_net_set_status(&n->vdev, n->vdev.status); + virtio_net_set_queues(n); + + return VIRTIO_NET_OK; +} static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) { VirtIONet *n = to_virtio_net(vdev); struct virtio_net_ctrl_hdr ctrl; virtio_net_ctrl_ack status = VIRTIO_NET_ERR; VirtQueueElement elem; + size_t s; + struct iovec *iov; + unsigned int iov_cnt; while (virtqueue_pop(vq, &elem)) { - if ((elem.in_num < 1) || (elem.out_num < 1)) { + if (iov_size(elem.in_sg, elem.in_num) < sizeof(status) || + iov_size(elem.out_sg, elem.out_num) < sizeof(ctrl)) { error_report("virtio-net ctrl missing headers"); exit(1); } - if (elem.out_sg[0].iov_len < sizeof(ctrl) || - elem.in_sg[elem.in_num - 1].iov_len < sizeof(status)) { - error_report("virtio-net ctrl header not in correct element"); - exit(1); + iov = elem.out_sg; + iov_cnt = elem.out_num; + s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl)); + iov_discard_front(&iov, &iov_cnt, sizeof(ctrl)); + if (s != sizeof(ctrl)) { + status = VIRTIO_NET_ERR; + } else if (ctrl.class == VIRTIO_NET_CTRL_RX) { + status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt); + } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) { + status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt); + } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) { + status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt); + } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) { + status = virtio_net_handle_mq(n, ctrl.cmd, &elem); } - ctrl.class = ldub_p(elem.out_sg[0].iov_base); - ctrl.cmd = ldub_p(elem.out_sg[0].iov_base + sizeof(ctrl.class)); - - if (ctrl.class == VIRTIO_NET_CTRL_RX_MODE) - status = virtio_net_handle_rx_mode(n, ctrl.cmd, &elem); - else if (ctrl.class == VIRTIO_NET_CTRL_MAC) - status = virtio_net_handle_mac(n, ctrl.cmd, &elem); - else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) - status = virtio_net_handle_vlan_table(n, ctrl.cmd, &elem); - - stb_p(elem.in_sg[elem.in_num - 1].iov_base, status); + s = iov_from_buf(elem.in_sg, elem.in_num, 0, &status, sizeof(status)); + assert(s == sizeof(status)); virtqueue_push(vq, &elem, sizeof(status)); virtio_notify(vdev, vq); @@ -462,42 +655,52 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) { VirtIONet *n = to_virtio_net(vdev); + int queue_index = vq2q(virtio_get_queue_index(vq)); - qemu_flush_queued_packets(&n->nic->nc); + qemu_flush_queued_packets(qemu_get_subqueue(n->nic, queue_index)); } static int virtio_net_can_receive(NetClientState *nc) { - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; + VirtIONet *n = qemu_get_nic_opaque(nc); + VirtIONetQueue *q = virtio_net_get_subqueue(nc); + if (!n->vdev.vm_running) { return 0; } - if (!virtio_queue_ready(n->rx_vq) || - !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) + if (nc->queue_index >= n->curr_queues) { return 0; + } + + if (!virtio_queue_ready(q->rx_vq) || + !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { + return 0; + } return 1; } -static int virtio_net_has_buffers(VirtIONet *n, int bufsize) +static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) { - if (virtio_queue_empty(n->rx_vq) || + VirtIONet *n = q->n; + if (virtio_queue_empty(q->rx_vq) || (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) { - virtio_queue_set_notification(n->rx_vq, 1); + !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { + virtio_queue_set_notification(q->rx_vq, 1); /* To avoid a race condition where the guest has made some buffers * available after the above check but before notification was * enabled, check for available buffers again. */ - if (virtio_queue_empty(n->rx_vq) || + if (virtio_queue_empty(q->rx_vq) || (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) + !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { return 0; + } } - virtio_queue_set_notification(n->rx_vq, 0); + virtio_queue_set_notification(q->rx_vq, 0); return 1; } @@ -599,18 +802,21 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; + VirtIONet *n = qemu_get_nic_opaque(nc); + VirtIONetQueue *q = virtio_net_get_subqueue(nc); struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; struct virtio_net_hdr_mrg_rxbuf mhdr; unsigned mhdr_cnt = 0; size_t offset, i, guest_offset; - if (!virtio_net_can_receive(&n->nic->nc)) + if (!virtio_net_can_receive(nc)) { return -1; + } /* hdr_len refers to the header we supply to the guest */ - if (!virtio_net_has_buffers(n, size + n->guest_hdr_len - n->host_hdr_len)) + if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) { return 0; + } if (!receive_filter(n, buf, size)) return size; @@ -624,7 +830,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t total = 0; - if (virtqueue_pop(n->rx_vq, &elem) == 0) { + if (virtqueue_pop(q->rx_vq, &elem) == 0) { if (i == 0) return -1; error_report("virtio-net unexpected empty queue: " @@ -677,7 +883,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t } /* signal other side */ - virtqueue_fill(n->rx_vq, &elem, total, i++); + virtqueue_fill(q->rx_vq, &elem, total, i++); } if (mhdr_cnt) { @@ -687,44 +893,47 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t &mhdr.num_buffers, sizeof mhdr.num_buffers); } - virtqueue_flush(n->rx_vq, i); - virtio_notify(&n->vdev, n->rx_vq); + virtqueue_flush(q->rx_vq, i); + virtio_notify(&n->vdev, q->rx_vq); return size; } -static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq); +static int32_t virtio_net_flush_tx(VirtIONetQueue *q); static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) { - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; + VirtIONet *n = qemu_get_nic_opaque(nc); + VirtIONetQueue *q = virtio_net_get_subqueue(nc); - virtqueue_push(n->tx_vq, &n->async_tx.elem, 0); - virtio_notify(&n->vdev, n->tx_vq); + virtqueue_push(q->tx_vq, &q->async_tx.elem, 0); + virtio_notify(&n->vdev, q->tx_vq); - n->async_tx.elem.out_num = n->async_tx.len = 0; + q->async_tx.elem.out_num = q->async_tx.len = 0; - virtio_queue_set_notification(n->tx_vq, 1); - virtio_net_flush_tx(n, n->tx_vq); + virtio_queue_set_notification(q->tx_vq, 1); + virtio_net_flush_tx(q); } /* TX */ -static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) +static int32_t virtio_net_flush_tx(VirtIONetQueue *q) { + VirtIONet *n = q->n; VirtQueueElement elem; int32_t num_packets = 0; + int queue_index = vq2q(virtio_get_queue_index(q->tx_vq)); if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { return num_packets; } assert(n->vdev.vm_running); - if (n->async_tx.elem.out_num) { - virtio_queue_set_notification(n->tx_vq, 0); + if (q->async_tx.elem.out_num) { + virtio_queue_set_notification(q->tx_vq, 0); return num_packets; } - while (virtqueue_pop(vq, &elem)) { + while (virtqueue_pop(q->tx_vq, &elem)) { ssize_t ret, len; unsigned int out_num = elem.out_num; struct iovec *out_sg = &elem.out_sg[0]; @@ -754,19 +963,19 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) len = n->guest_hdr_len; - ret = qemu_sendv_packet_async(&n->nic->nc, out_sg, out_num, - virtio_net_tx_complete); + ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index), + out_sg, out_num, virtio_net_tx_complete); if (ret == 0) { - virtio_queue_set_notification(n->tx_vq, 0); - n->async_tx.elem = elem; - n->async_tx.len = len; + virtio_queue_set_notification(q->tx_vq, 0); + q->async_tx.elem = elem; + q->async_tx.len = len; return -EBUSY; } len += ret; - virtqueue_push(vq, &elem, 0); - virtio_notify(&n->vdev, vq); + virtqueue_push(q->tx_vq, &elem, 0); + virtio_notify(&n->vdev, q->tx_vq); if (++num_packets >= n->tx_burst) { break; @@ -778,22 +987,23 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) { VirtIONet *n = to_virtio_net(vdev); + VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; /* This happens when device was stopped but VCPU wasn't. */ if (!n->vdev.vm_running) { - n->tx_waiting = 1; + q->tx_waiting = 1; return; } - if (n->tx_waiting) { + if (q->tx_waiting) { virtio_queue_set_notification(vq, 1); - qemu_del_timer(n->tx_timer); - n->tx_waiting = 0; - virtio_net_flush_tx(n, vq); + qemu_del_timer(q->tx_timer); + q->tx_waiting = 0; + virtio_net_flush_tx(q); } else { - qemu_mod_timer(n->tx_timer, + qemu_mod_timer(q->tx_timer, qemu_get_clock_ns(vm_clock) + n->tx_timeout); - n->tx_waiting = 1; + q->tx_waiting = 1; virtio_queue_set_notification(vq, 0); } } @@ -801,48 +1011,51 @@ static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) { VirtIONet *n = to_virtio_net(vdev); + VirtIONetQueue *q = &n->vqs[vq2q(virtio_get_queue_index(vq))]; - if (unlikely(n->tx_waiting)) { + if (unlikely(q->tx_waiting)) { return; } - n->tx_waiting = 1; + q->tx_waiting = 1; /* This happens when device was stopped but VCPU wasn't. */ if (!n->vdev.vm_running) { return; } virtio_queue_set_notification(vq, 0); - qemu_bh_schedule(n->tx_bh); + qemu_bh_schedule(q->tx_bh); } static void virtio_net_tx_timer(void *opaque) { - VirtIONet *n = opaque; + VirtIONetQueue *q = opaque; + VirtIONet *n = q->n; assert(n->vdev.vm_running); - n->tx_waiting = 0; + q->tx_waiting = 0; /* Just in case the driver is not ready on more */ if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) return; - virtio_queue_set_notification(n->tx_vq, 1); - virtio_net_flush_tx(n, n->tx_vq); + virtio_queue_set_notification(q->tx_vq, 1); + virtio_net_flush_tx(q); } static void virtio_net_tx_bh(void *opaque) { - VirtIONet *n = opaque; + VirtIONetQueue *q = opaque; + VirtIONet *n = q->n; int32_t ret; assert(n->vdev.vm_running); - n->tx_waiting = 0; + q->tx_waiting = 0; /* Just in case the driver is not ready on more */ if (unlikely(!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))) return; - ret = virtio_net_flush_tx(n, n->tx_vq); + ret = virtio_net_flush_tx(q); if (ret == -EBUSY) { return; /* Notification re-enable handled by tx_complete */ } @@ -850,24 +1063,61 @@ static void virtio_net_tx_bh(void *opaque) /* If we flush a full burst of packets, assume there are * more coming and immediately reschedule */ if (ret >= n->tx_burst) { - qemu_bh_schedule(n->tx_bh); - n->tx_waiting = 1; + qemu_bh_schedule(q->tx_bh); + q->tx_waiting = 1; return; } /* If less than a full burst, re-enable notification and flush * anything that may have come in while we weren't looking. If * we find something, assume the guest is still active and reschedule */ - virtio_queue_set_notification(n->tx_vq, 1); - if (virtio_net_flush_tx(n, n->tx_vq) > 0) { - virtio_queue_set_notification(n->tx_vq, 0); - qemu_bh_schedule(n->tx_bh); - n->tx_waiting = 1; + virtio_queue_set_notification(q->tx_vq, 1); + if (virtio_net_flush_tx(q) > 0) { + virtio_queue_set_notification(q->tx_vq, 0); + qemu_bh_schedule(q->tx_bh); + q->tx_waiting = 1; } } +static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue, int ctrl) +{ + VirtIODevice *vdev = &n->vdev; + int i, max = multiqueue ? n->max_queues : 1; + + n->multiqueue = multiqueue; + + for (i = 2; i <= n->max_queues * 2 + 1; i++) { + virtio_del_queue(vdev, i); + } + + for (i = 1; i < max; i++) { + n->vqs[i].rx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_rx); + if (n->vqs[i].tx_timer) { + n->vqs[i].tx_vq = + virtio_add_queue(vdev, 256, virtio_net_handle_tx_timer); + n->vqs[i].tx_timer = qemu_new_timer_ns(vm_clock, + virtio_net_tx_timer, + &n->vqs[i]); + } else { + n->vqs[i].tx_vq = + virtio_add_queue(vdev, 256, virtio_net_handle_tx_bh); + n->vqs[i].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[i]); + } + + n->vqs[i].tx_waiting = 0; + n->vqs[i].n = n; + } + + if (ctrl) { + n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl); + } + + virtio_net_set_queues(n); +} + static void virtio_net_save(QEMUFile *f, void *opaque) { + int i; VirtIONet *n = opaque; /* At this point, backend must be stopped, otherwise @@ -876,7 +1126,7 @@ static void virtio_net_save(QEMUFile *f, void *opaque) virtio_save(&n->vdev, f); qemu_put_buffer(f, n->mac, ETH_ALEN); - qemu_put_be32(f, n->tx_waiting); + qemu_put_be32(f, n->vqs[0].tx_waiting); qemu_put_be32(f, n->mergeable_rx_bufs); qemu_put_be16(f, n->status); qemu_put_byte(f, n->promisc); @@ -892,13 +1142,19 @@ static void virtio_net_save(QEMUFile *f, void *opaque) qemu_put_byte(f, n->nouni); qemu_put_byte(f, n->nobcast); qemu_put_byte(f, n->has_ufo); + if (n->max_queues > 1) { + qemu_put_be16(f, n->max_queues); + qemu_put_be16(f, n->curr_queues); + for (i = 1; i < n->curr_queues; i++) { + qemu_put_be32(f, n->vqs[i].tx_waiting); + } + } } static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) { VirtIONet *n = opaque; - int i; - int ret; + int ret, i, link_down; if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION) return -EINVAL; @@ -909,7 +1165,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } qemu_get_buffer(f, n->mac, ETH_ALEN); - n->tx_waiting = qemu_get_be32(f); + n->vqs[0].tx_waiting = qemu_get_be32(f); virtio_net_set_mrg_rx_bufs(n, qemu_get_be32(f)); @@ -951,7 +1207,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } if (n->has_vnet_hdr) { - tap_set_offload(n->nic->nc.peer, + tap_set_offload(qemu_get_queue(n->nic)->peer, (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_CSUM) & 1, (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO4) & 1, (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO6) & 1, @@ -979,6 +1235,20 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } } + if (n->max_queues > 1) { + if (n->max_queues != qemu_get_be16(f)) { + error_report("virtio-net: different max_queues "); + return -1; + } + + n->curr_queues = qemu_get_be16(f); + for (i = 1; i < n->curr_queues; i++) { + n->vqs[i].tx_waiting = qemu_get_be32(f); + } + } + + virtio_net_set_queues(n); + /* Find the first multicast entry in the saved MAC filter */ for (i = 0; i < n->mac_table.in_use; i++) { if (n->mac_table.macs[i * ETH_ALEN] & 1) { @@ -989,14 +1259,17 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) /* nc.link_down can't be migrated, so infer link_down according * to link status bit in n->status */ - n->nic->nc.link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0; + link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0; + for (i = 0; i < n->max_queues; i++) { + qemu_get_subqueue(n->nic, i)->link_down = link_down; + } return 0; } static void virtio_net_cleanup(NetClientState *nc) { - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; + VirtIONet *n = qemu_get_nic_opaque(nc); n->nic = NULL; } @@ -1010,15 +1283,40 @@ static NetClientInfo net_virtio_info = { .link_status_changed = virtio_net_set_link_status, }; +static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx) +{ + VirtIONet *n = to_virtio_net(vdev); + NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); + assert(n->vhost_started); + return vhost_net_virtqueue_pending(tap_get_vhost_net(nc->peer), idx); +} + +static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, + bool mask) +{ + VirtIONet *n = to_virtio_net(vdev); + NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); + assert(n->vhost_started); + vhost_net_virtqueue_mask(tap_get_vhost_net(nc->peer), + vdev, idx, mask); +} + VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, - virtio_net_conf *net) + virtio_net_conf *net, uint32_t host_features) { VirtIONet *n; + int i, config_size = 0; + + for (i = 0; feature_sizes[i].flags != 0; i++) { + if (host_features & feature_sizes[i].flags) { + config_size = MAX(feature_sizes[i].end, config_size); + } + } n = (VirtIONet *)virtio_common_init("virtio-net", VIRTIO_ID_NET, - sizeof(struct virtio_net_config), - sizeof(VirtIONet)); + config_size, sizeof(VirtIONet)); + n->config_size = config_size; n->vdev.get_config = virtio_net_get_config; n->vdev.set_config = virtio_net_set_config; n->vdev.get_features = virtio_net_get_features; @@ -1026,7 +1324,14 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, n->vdev.bad_features = virtio_net_bad_features; n->vdev.reset = virtio_net_reset; n->vdev.set_status = virtio_net_set_status; - n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); + n->vdev.guest_notifier_mask = virtio_net_guest_notifier_mask; + n->vdev.guest_notifier_pending = virtio_net_guest_notifier_pending; + n->max_queues = MAX(conf->queues, 1); + n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues); + n->vqs[0].rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); + n->curr_queues = 1; + n->vqs[0].n = n; + n->tx_timeout = net->txtimer; if (net->tx && strcmp(net->tx, "timer") && strcmp(net->tx, "bh")) { error_report("virtio-net: " @@ -1036,12 +1341,14 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, } if (net->tx && !strcmp(net->tx, "timer")) { - n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_timer); - n->tx_timer = qemu_new_timer_ns(vm_clock, virtio_net_tx_timer, n); - n->tx_timeout = net->txtimer; + n->vqs[0].tx_vq = virtio_add_queue(&n->vdev, 256, + virtio_net_handle_tx_timer); + n->vqs[0].tx_timer = qemu_new_timer_ns(vm_clock, virtio_net_tx_timer, + &n->vqs[0]); } else { - n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_bh); - n->tx_bh = qemu_bh_new(virtio_net_tx_bh, n); + n->vqs[0].tx_vq = virtio_add_queue(&n->vdev, 256, + virtio_net_handle_tx_bh); + n->vqs[0].tx_bh = qemu_bh_new(virtio_net_tx_bh, &n->vqs[0]); } n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl); qemu_macaddr_default_if_unset(&conf->macaddr); @@ -1051,15 +1358,17 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, n->nic = qemu_new_nic(&net_virtio_info, conf, object_get_typename(OBJECT(dev)), dev->id, n); peer_test_vnet_hdr(n); if (peer_has_vnet_hdr(n)) { - tap_using_vnet_hdr(n->nic->nc.peer, 1); + for (i = 0; i < n->max_queues; i++) { + tap_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true); + } n->host_hdr_len = sizeof(struct virtio_net_hdr); } else { n->host_hdr_len = 0; } - qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(n->nic), conf->macaddr.a); - n->tx_waiting = 0; + n->vqs[0].tx_waiting = 0; n->tx_burst = net->txburst; virtio_net_set_mrg_rx_bufs(n, 0); n->promisc = 1; /* for compatibility */ @@ -1080,24 +1389,31 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, void virtio_net_exit(VirtIODevice *vdev) { VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev); + int i; /* This will stop vhost backend if appropriate. */ virtio_net_set_status(vdev, 0); - qemu_purge_queued_packets(&n->nic->nc); - unregister_savevm(n->qdev, "virtio-net", n); g_free(n->mac_table.macs); g_free(n->vlans); - if (n->tx_timer) { - qemu_del_timer(n->tx_timer); - qemu_free_timer(n->tx_timer); - } else { - qemu_bh_delete(n->tx_bh); + for (i = 0; i < n->max_queues; i++) { + VirtIONetQueue *q = &n->vqs[i]; + NetClientState *nc = qemu_get_subqueue(n->nic, i); + + qemu_purge_queued_packets(nc); + + if (q->tx_timer) { + qemu_del_timer(q->tx_timer); + qemu_free_timer(q->tx_timer); + } else { + qemu_bh_delete(q->tx_bh); + } } - qemu_del_net_client(&n->nic->nc); + g_free(n->vqs); + qemu_del_nic(n->nic); virtio_cleanup(&n->vdev); } diff --git a/hw/virtio-net.h b/hw/virtio-net.h index d46fb9840f..0c83ca5cfe 100644 --- a/hw/virtio-net.h +++ b/hw/virtio-net.h @@ -14,8 +14,8 @@ #ifndef _QEMU_VIRTIO_NET_H #define _QEMU_VIRTIO_NET_H -#include "virtio.h" -#include "pci/pci.h" +#include "hw/virtio.h" +#include "hw/pci/pci.h" #define ETH_ALEN 6 @@ -43,6 +43,10 @@ #define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ #define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ #define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ +#define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow + * Steering */ + +#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ @@ -71,6 +75,8 @@ struct virtio_net_config uint8_t mac[ETH_ALEN]; /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ uint16_t status; + /* Max virtqueue pairs supported by the device */ + uint16_t max_virtqueue_pairs; } QEMU_PACKED; /* @@ -97,16 +103,16 @@ typedef uint8_t virtio_net_ctrl_ack; * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature. * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA. */ -#define VIRTIO_NET_CTRL_RX_MODE 0 - #define VIRTIO_NET_CTRL_RX_MODE_PROMISC 0 - #define VIRTIO_NET_CTRL_RX_MODE_ALLMULTI 1 - #define VIRTIO_NET_CTRL_RX_MODE_ALLUNI 2 - #define VIRTIO_NET_CTRL_RX_MODE_NOMULTI 3 - #define VIRTIO_NET_CTRL_RX_MODE_NOUNI 4 - #define VIRTIO_NET_CTRL_RX_MODE_NOBCAST 5 +#define VIRTIO_NET_CTRL_RX 0 + #define VIRTIO_NET_CTRL_RX_PROMISC 0 + #define VIRTIO_NET_CTRL_RX_ALLMULTI 1 + #define VIRTIO_NET_CTRL_RX_ALLUNI 2 + #define VIRTIO_NET_CTRL_RX_NOMULTI 3 + #define VIRTIO_NET_CTRL_RX_NOUNI 4 + #define VIRTIO_NET_CTRL_RX_NOBCAST 5 /* - * Control the MAC filter table. + * Control the MAC * * The MAC filter table is managed by the hypervisor, the guest should * assume the size is infinite. Filtering should be considered @@ -119,6 +125,10 @@ typedef uint8_t virtio_net_ctrl_ack; * first sg list contains unicast addresses, the second is for multicast. * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature * is available. + * + * The ADDR_SET command requests one out scatterlist, it contains a + * 6 bytes MAC address. This functionality is present if the + * VIRTIO_NET_F_CTRL_MAC_ADDR feature is available. */ struct virtio_net_ctrl_mac { uint32_t entries; @@ -126,6 +136,7 @@ struct virtio_net_ctrl_mac { }; #define VIRTIO_NET_CTRL_MAC 1 #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 + #define VIRTIO_NET_CTRL_MAC_ADDR_SET 1 /* * Control VLAN filtering @@ -140,6 +151,26 @@ struct virtio_net_ctrl_mac { #define VIRTIO_NET_CTRL_VLAN_ADD 0 #define VIRTIO_NET_CTRL_VLAN_DEL 1 +/* + * Control Multiqueue + * + * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET + * enables multiqueue, specifying the number of the transmit and + * receive queues that will be used. After the command is consumed and acked by + * the device, the device will not steer new packets on receive virtqueues + * other than specified nor read from transmit virtqueues other than specified. + * Accordingly, driver should not transmit new packets on virtqueues other than + * specified. + */ +struct virtio_net_ctrl_mq { + uint16_t virtqueue_pairs; +}; + +#define VIRTIO_NET_CTRL_MQ 4 + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0 + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1 + #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 + #define DEFINE_VIRTIO_NET_FEATURES(_state, _field) \ DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \ DEFINE_PROP_BIT("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \ @@ -158,5 +189,8 @@ struct virtio_net_ctrl_mac { DEFINE_PROP_BIT("ctrl_vq", _state, _field, VIRTIO_NET_F_CTRL_VQ, true), \ DEFINE_PROP_BIT("ctrl_rx", _state, _field, VIRTIO_NET_F_CTRL_RX, true), \ DEFINE_PROP_BIT("ctrl_vlan", _state, _field, VIRTIO_NET_F_CTRL_VLAN, true), \ - DEFINE_PROP_BIT("ctrl_rx_extra", _state, _field, VIRTIO_NET_F_CTRL_RX_EXTRA, true) + DEFINE_PROP_BIT("ctrl_rx_extra", _state, _field, VIRTIO_NET_F_CTRL_RX_EXTRA, true), \ + DEFINE_PROP_BIT("ctrl_mac_addr", _state, _field, VIRTIO_NET_F_CTRL_MAC_ADDR, true), \ + DEFINE_PROP_BIT("mq", _state, _field, VIRTIO_NET_F_MQ, false) + #endif diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index d2d2454493..39c1966cfc 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -17,20 +17,21 @@ #include -#include "virtio.h" -#include "virtio-blk.h" -#include "virtio-net.h" -#include "virtio-serial.h" -#include "virtio-scsi.h" -#include "pci/pci.h" +#include "hw/virtio.h" +#include "hw/virtio-blk.h" +#include "hw/virtio-net.h" +#include "hw/virtio-serial.h" +#include "hw/virtio-scsi.h" +#include "hw/pci/pci.h" #include "qemu/error-report.h" -#include "pci/msi.h" -#include "pci/msix.h" -#include "loader.h" +#include "hw/pci/msi.h" +#include "hw/pci/msix.h" +#include "hw/loader.h" #include "sysemu/kvm.h" #include "sysemu/blockdev.h" -#include "virtio-pci.h" +#include "hw/virtio-pci.h" #include "qemu/range.h" +#include "hw/virtio-bus.h" /* from Linux's linux/virtio_pci.h */ @@ -96,35 +97,48 @@ bool virtio_is_big_endian(void); /* virtio device */ - -static void virtio_pci_notify(void *opaque, uint16_t vector) +/* DeviceState to VirtIOPCIProxy. For use off data-path. TODO: use QOM. */ +static inline VirtIOPCIProxy *to_virtio_pci_proxy(DeviceState *d) { - VirtIOPCIProxy *proxy = opaque; + return container_of(d, VirtIOPCIProxy, pci_dev.qdev); +} + +/* DeviceState to VirtIOPCIProxy. Note: used on datapath, + * be careful and test performance if you change this. + */ +static inline VirtIOPCIProxy *to_virtio_pci_proxy_fast(DeviceState *d) +{ + return container_of(d, VirtIOPCIProxy, pci_dev.qdev); +} + +static void virtio_pci_notify(DeviceState *d, uint16_t vector) +{ + VirtIOPCIProxy *proxy = to_virtio_pci_proxy_fast(d); if (msix_enabled(&proxy->pci_dev)) msix_notify(&proxy->pci_dev, vector); else qemu_set_irq(proxy->pci_dev.irq[0], proxy->vdev->isr & 1); } -static void virtio_pci_save_config(void * opaque, QEMUFile *f) +static void virtio_pci_save_config(DeviceState *d, QEMUFile *f) { - VirtIOPCIProxy *proxy = opaque; + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); pci_device_save(&proxy->pci_dev, f); msix_save(&proxy->pci_dev, f); if (msix_present(&proxy->pci_dev)) qemu_put_be16(f, proxy->vdev->config_vector); } -static void virtio_pci_save_queue(void * opaque, int n, QEMUFile *f) +static void virtio_pci_save_queue(DeviceState *d, int n, QEMUFile *f) { - VirtIOPCIProxy *proxy = opaque; + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); if (msix_present(&proxy->pci_dev)) qemu_put_be16(f, virtio_queue_vector(proxy->vdev, n)); } -static int virtio_pci_load_config(void * opaque, QEMUFile *f) +static int virtio_pci_load_config(DeviceState *d, QEMUFile *f) { - VirtIOPCIProxy *proxy = opaque; + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); int ret; ret = pci_device_load(&proxy->pci_dev, f); if (ret) { @@ -143,9 +157,9 @@ static int virtio_pci_load_config(void * opaque, QEMUFile *f) return 0; } -static int virtio_pci_load_queue(void * opaque, int n, QEMUFile *f) +static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f) { - VirtIOPCIProxy *proxy = opaque; + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); uint16_t vector; if (msix_present(&proxy->pci_dev)) { qemu_get_be16s(f, &vector); @@ -241,9 +255,9 @@ static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy) proxy->ioeventfd_started = false; } -void virtio_pci_reset(DeviceState *d) +static void virtio_pci_reset(DeviceState *d) { - VirtIOPCIProxy *proxy = container_of(d, VirtIOPCIProxy, pci_dev.qdev); + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); virtio_pci_stop_ioeventfd(proxy); virtio_reset(proxy->vdev); msix_unuse_all_vectors(&proxy->pci_dev); @@ -463,9 +477,9 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, } } -static unsigned virtio_pci_get_features(void *opaque) +static unsigned virtio_pci_get_features(DeviceState *d) { - VirtIOPCIProxy *proxy = opaque; + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); return proxy->host_features; } @@ -474,8 +488,6 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy, unsigned int vector, MSIMessage msg) { - VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no); - EventNotifier *n = virtio_queue_get_guest_notifier(vq); VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; int ret; @@ -487,22 +499,33 @@ static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy, irqfd->virq = ret; } irqfd->users++; - - ret = kvm_irqchip_add_irqfd_notifier(kvm_state, n, irqfd->virq); - if (ret < 0) { - if (--irqfd->users == 0) { - kvm_irqchip_release_virq(kvm_state, irqfd->virq); - } - return ret; - } - - virtio_queue_set_guest_notifier_fd_handler(vq, true, true); return 0; } static void kvm_virtio_pci_vq_vector_release(VirtIOPCIProxy *proxy, - unsigned int queue_no, unsigned int vector) +{ + VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; + if (--irqfd->users == 0) { + kvm_irqchip_release_virq(kvm_state, irqfd->virq); + } +} + +static int kvm_virtio_pci_irqfd_use(VirtIOPCIProxy *proxy, + unsigned int queue_no, + unsigned int vector) +{ + VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; + VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no); + EventNotifier *n = virtio_queue_get_guest_notifier(vq); + int ret; + ret = kvm_irqchip_add_irqfd_notifier(kvm_state, n, irqfd->virq); + return ret; +} + +static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy, + unsigned int queue_no, + unsigned int vector) { VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no); EventNotifier *n = virtio_queue_get_guest_notifier(vq); @@ -511,29 +534,143 @@ static void kvm_virtio_pci_vq_vector_release(VirtIOPCIProxy *proxy, ret = kvm_irqchip_remove_irqfd_notifier(kvm_state, n, irqfd->virq); assert(ret == 0); - - if (--irqfd->users == 0) { - kvm_irqchip_release_virq(kvm_state, irqfd->virq); - } - - virtio_queue_set_guest_notifier_fd_handler(vq, true, false); } -static int kvm_virtio_pci_vector_use(PCIDevice *dev, unsigned vector, +static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs) +{ + PCIDevice *dev = &proxy->pci_dev; + VirtIODevice *vdev = proxy->vdev; + unsigned int vector; + int ret, queue_no; + MSIMessage msg; + + for (queue_no = 0; queue_no < nvqs; queue_no++) { + if (!virtio_queue_get_num(vdev, queue_no)) { + break; + } + vector = virtio_queue_vector(vdev, queue_no); + if (vector >= msix_nr_vectors_allocated(dev)) { + continue; + } + msg = msix_get_message(dev, vector); + ret = kvm_virtio_pci_vq_vector_use(proxy, queue_no, vector, msg); + if (ret < 0) { + goto undo; + } + /* If guest supports masking, set up irqfd now. + * Otherwise, delay until unmasked in the frontend. + */ + if (proxy->vdev->guest_notifier_mask) { + ret = kvm_virtio_pci_irqfd_use(proxy, queue_no, vector); + if (ret < 0) { + kvm_virtio_pci_vq_vector_release(proxy, vector); + goto undo; + } + } + } + return 0; + +undo: + while (--queue_no >= 0) { + vector = virtio_queue_vector(vdev, queue_no); + if (vector >= msix_nr_vectors_allocated(dev)) { + continue; + } + if (proxy->vdev->guest_notifier_mask) { + kvm_virtio_pci_irqfd_release(proxy, queue_no, vector); + } + kvm_virtio_pci_vq_vector_release(proxy, vector); + } + return ret; +} + +static void kvm_virtio_pci_vector_release(VirtIOPCIProxy *proxy, int nvqs) +{ + PCIDevice *dev = &proxy->pci_dev; + VirtIODevice *vdev = proxy->vdev; + unsigned int vector; + int queue_no; + + for (queue_no = 0; queue_no < nvqs; queue_no++) { + if (!virtio_queue_get_num(vdev, queue_no)) { + break; + } + vector = virtio_queue_vector(vdev, queue_no); + if (vector >= msix_nr_vectors_allocated(dev)) { + continue; + } + /* If guest supports masking, clean up irqfd now. + * Otherwise, it was cleaned when masked in the frontend. + */ + if (proxy->vdev->guest_notifier_mask) { + kvm_virtio_pci_irqfd_release(proxy, queue_no, vector); + } + kvm_virtio_pci_vq_vector_release(proxy, vector); + } +} + +static int kvm_virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy, + unsigned int queue_no, + unsigned int vector, + MSIMessage msg) +{ + VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no); + EventNotifier *n = virtio_queue_get_guest_notifier(vq); + VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; + int ret = 0; + + if (irqfd->msg.data != msg.data || irqfd->msg.address != msg.address) { + ret = kvm_irqchip_update_msi_route(kvm_state, irqfd->virq, msg); + if (ret < 0) { + return ret; + } + } + + /* If guest supports masking, irqfd is already setup, unmask it. + * Otherwise, set it up now. + */ + if (proxy->vdev->guest_notifier_mask) { + proxy->vdev->guest_notifier_mask(proxy->vdev, queue_no, false); + /* Test after unmasking to avoid losing events. */ + if (proxy->vdev->guest_notifier_pending && + proxy->vdev->guest_notifier_pending(proxy->vdev, queue_no)) { + event_notifier_set(n); + } + } else { + ret = kvm_virtio_pci_irqfd_use(proxy, queue_no, vector); + } + return ret; +} + +static void kvm_virtio_pci_vq_vector_mask(VirtIOPCIProxy *proxy, + unsigned int queue_no, + unsigned int vector) +{ + /* If guest supports masking, keep irqfd but mask it. + * Otherwise, clean it up now. + */ + if (proxy->vdev->guest_notifier_mask) { + proxy->vdev->guest_notifier_mask(proxy->vdev, queue_no, true); + } else { + kvm_virtio_pci_irqfd_release(proxy, queue_no, vector); + } +} + +static int kvm_virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector, MSIMessage msg) { VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); VirtIODevice *vdev = proxy->vdev; int ret, queue_no; - for (queue_no = 0; queue_no < VIRTIO_PCI_QUEUE_MAX; queue_no++) { + for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) { if (!virtio_queue_get_num(vdev, queue_no)) { break; } if (virtio_queue_vector(vdev, queue_no) != vector) { continue; } - ret = kvm_virtio_pci_vq_vector_use(proxy, queue_no, vector, msg); + ret = kvm_virtio_pci_vq_vector_unmask(proxy, queue_no, vector, msg); if (ret < 0) { goto undo; } @@ -545,31 +682,64 @@ undo: if (virtio_queue_vector(vdev, queue_no) != vector) { continue; } - kvm_virtio_pci_vq_vector_release(proxy, queue_no, vector); + kvm_virtio_pci_vq_vector_mask(proxy, queue_no, vector); } return ret; } -static void kvm_virtio_pci_vector_release(PCIDevice *dev, unsigned vector) +static void kvm_virtio_pci_vector_mask(PCIDevice *dev, unsigned vector) { VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); VirtIODevice *vdev = proxy->vdev; int queue_no; - for (queue_no = 0; queue_no < VIRTIO_PCI_QUEUE_MAX; queue_no++) { + for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) { if (!virtio_queue_get_num(vdev, queue_no)) { break; } if (virtio_queue_vector(vdev, queue_no) != vector) { continue; } - kvm_virtio_pci_vq_vector_release(proxy, queue_no, vector); + kvm_virtio_pci_vq_vector_mask(proxy, queue_no, vector); } } -static int virtio_pci_set_guest_notifier(void *opaque, int n, bool assign) +static void kvm_virtio_pci_vector_poll(PCIDevice *dev, + unsigned int vector_start, + unsigned int vector_end) { - VirtIOPCIProxy *proxy = opaque; + VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); + VirtIODevice *vdev = proxy->vdev; + int queue_no; + unsigned int vector; + EventNotifier *notifier; + VirtQueue *vq; + + for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) { + if (!virtio_queue_get_num(vdev, queue_no)) { + break; + } + vector = virtio_queue_vector(vdev, queue_no); + if (vector < vector_start || vector >= vector_end || + !msix_is_masked(dev, vector)) { + continue; + } + vq = virtio_get_queue(vdev, queue_no); + notifier = virtio_queue_get_guest_notifier(vq); + if (vdev->guest_notifier_pending) { + if (vdev->guest_notifier_pending(vdev, queue_no)) { + msix_set_pending(dev, vector); + } + } else if (event_notifier_test_and_clear(notifier)) { + msix_set_pending(dev, vector); + } + } +} + +static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign, + bool with_irqfd) +{ + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); VirtQueue *vq = virtio_get_queue(proxy->vdev, n); EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); @@ -578,72 +748,94 @@ static int virtio_pci_set_guest_notifier(void *opaque, int n, bool assign) if (r < 0) { return r; } - virtio_queue_set_guest_notifier_fd_handler(vq, true, false); + virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); } else { - virtio_queue_set_guest_notifier_fd_handler(vq, false, false); + virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); event_notifier_cleanup(notifier); } return 0; } -static bool virtio_pci_query_guest_notifiers(void *opaque) +static bool virtio_pci_query_guest_notifiers(DeviceState *d) { - VirtIOPCIProxy *proxy = opaque; + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); return msix_enabled(&proxy->pci_dev); } -static int virtio_pci_set_guest_notifiers(void *opaque, bool assign) +static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign) { - VirtIOPCIProxy *proxy = opaque; + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); VirtIODevice *vdev = proxy->vdev; int r, n; + bool with_irqfd = msix_enabled(&proxy->pci_dev) && + kvm_msi_via_irqfd_enabled(); + + nvqs = MIN(nvqs, VIRTIO_PCI_QUEUE_MAX); + + /* When deassigning, pass a consistent nvqs value + * to avoid leaking notifiers. + */ + assert(assign || nvqs == proxy->nvqs_with_notifiers); + + proxy->nvqs_with_notifiers = nvqs; /* Must unset vector notifier while guest notifier is still assigned */ - if (kvm_msi_via_irqfd_enabled() && !assign) { + if (proxy->vector_irqfd && !assign) { msix_unset_vector_notifiers(&proxy->pci_dev); + kvm_virtio_pci_vector_release(proxy, nvqs); g_free(proxy->vector_irqfd); proxy->vector_irqfd = NULL; } - for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { + for (n = 0; n < nvqs; n++) { if (!virtio_queue_get_num(vdev, n)) { break; } - r = virtio_pci_set_guest_notifier(opaque, n, assign); + r = virtio_pci_set_guest_notifier(d, n, assign, + kvm_msi_via_irqfd_enabled()); if (r < 0) { goto assign_error; } } /* Must set vector notifier after guest notifier has been assigned */ - if (kvm_msi_via_irqfd_enabled() && assign) { + if (with_irqfd && assign) { proxy->vector_irqfd = g_malloc0(sizeof(*proxy->vector_irqfd) * msix_nr_vectors_allocated(&proxy->pci_dev)); - r = msix_set_vector_notifiers(&proxy->pci_dev, - kvm_virtio_pci_vector_use, - kvm_virtio_pci_vector_release); + r = kvm_virtio_pci_vector_use(proxy, nvqs); if (r < 0) { goto assign_error; } + r = msix_set_vector_notifiers(&proxy->pci_dev, + kvm_virtio_pci_vector_unmask, + kvm_virtio_pci_vector_mask, + kvm_virtio_pci_vector_poll); + if (r < 0) { + goto notifiers_error; + } } return 0; +notifiers_error: + assert(assign); + kvm_virtio_pci_vector_release(proxy, nvqs); + assign_error: /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */ assert(assign); while (--n >= 0) { - virtio_pci_set_guest_notifier(opaque, n, !assign); + virtio_pci_set_guest_notifier(d, n, !assign, with_irqfd); } return r; } -static int virtio_pci_set_host_notifier(void *opaque, int n, bool assign) +static int virtio_pci_set_host_notifier(DeviceState *d, int n, bool assign) { - VirtIOPCIProxy *proxy = opaque; + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); /* Stop using ioeventfd for virtqueue kick if the device starts using host * notifiers. This makes it easy to avoid stepping on each others' toes. @@ -659,9 +851,9 @@ static int virtio_pci_set_host_notifier(void *opaque, int n, bool assign) return virtio_pci_set_host_notifier_internal(proxy, n, assign, false); } -static void virtio_pci_vmstate_change(void *opaque, bool running) +static void virtio_pci_vmstate_change(DeviceState *d, bool running) { - VirtIOPCIProxy *proxy = opaque; + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); if (running) { /* Try to find out if the guest has bus master disabled, but is @@ -726,7 +918,7 @@ void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev) proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; } - virtio_bind_device(vdev, &virtio_pci_bindings, proxy); + virtio_bind_device(vdev, &virtio_pci_bindings, DEVICE(proxy)); proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE; proxy->host_features = vdev->get_features(vdev, proxy->host_features); @@ -783,6 +975,9 @@ static int virtio_serial_init_pci(PCIDevice *pci_dev) if (!vdev) { return -1; } + + /* backwards-compatibility with machines that were created with + DEV_NVECTORS_UNSPECIFIED */ vdev->nvectors = proxy->nvectors == DEV_NVECTORS_UNSPECIFIED ? proxy->serial.max_virtserial_ports + 1 : proxy->nvectors; @@ -805,7 +1000,8 @@ static int virtio_net_init_pci(PCIDevice *pci_dev) VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); VirtIODevice *vdev; - vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic, &proxy->net); + vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic, &proxy->net, + proxy->host_features); vdev->nvectors = proxy->nvectors; virtio_init_pci(proxy, vdev); @@ -894,7 +1090,11 @@ static Property virtio_blk_properties[] = { #ifdef __linux__ DEFINE_PROP_BIT("scsi", VirtIOPCIProxy, blk.scsi, 0, true), #endif + DEFINE_PROP_BIT("config-wce", VirtIOPCIProxy, blk.config_wce, 0, true), DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), +#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE + DEFINE_PROP_BIT("x-data-plane", VirtIOPCIProxy, blk.data_plane, 0, false), +#endif DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features), DEFINE_PROP_END_OF_LIST(), @@ -915,7 +1115,7 @@ static void virtio_blk_class_init(ObjectClass *klass, void *data) dc->props = virtio_blk_properties; } -static TypeInfo virtio_blk_info = { +static const TypeInfo virtio_blk_info = { .name = "virtio-blk-pci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VirtIOPCIProxy), @@ -949,7 +1149,7 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) dc->props = virtio_net_properties; } -static TypeInfo virtio_net_info = { +static const TypeInfo virtio_net_info = { .name = "virtio-net-pci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VirtIOPCIProxy), @@ -958,7 +1158,7 @@ static TypeInfo virtio_net_info = { static Property virtio_serial_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), DEFINE_PROP_UINT32("max_ports", VirtIOPCIProxy, serial.max_virtserial_ports, 31), @@ -980,7 +1180,7 @@ static void virtio_serial_class_init(ObjectClass *klass, void *data) dc->props = virtio_serial_properties; } -static TypeInfo virtio_serial_info = { +static const TypeInfo virtio_serial_info = { .name = "virtio-serial-pci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VirtIOPCIProxy), @@ -1008,7 +1208,7 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data) dc->props = virtio_balloon_properties; } -static TypeInfo virtio_balloon_info = { +static const TypeInfo virtio_balloon_info = { .name = "virtio-balloon-pci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VirtIOPCIProxy), @@ -1051,7 +1251,7 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data) dc->props = virtio_rng_properties; } -static TypeInfo virtio_rng_info = { +static const TypeInfo virtio_rng_info = { .name = "virtio-rng-pci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VirtIOPCIProxy), @@ -1109,13 +1309,210 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data) dc->props = virtio_scsi_properties; } -static TypeInfo virtio_scsi_info = { +static const TypeInfo virtio_scsi_info = { .name = "virtio-scsi-pci", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VirtIOPCIProxy), .class_init = virtio_scsi_class_init, }; +#ifdef CONFIG_VIRTFS +static int virtio_9p_init_pci(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + VirtIODevice *vdev; + + vdev = virtio_9p_init(&pci_dev->qdev, &proxy->fsconf); + vdev->nvectors = proxy->nvectors; + virtio_init_pci(proxy, vdev); + /* make the actual value visible */ + proxy->nvectors = vdev->nvectors; + return 0; +} + +static Property virtio_9p_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), + DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), + DEFINE_PROP_STRING("mount_tag", VirtIOPCIProxy, fsconf.tag), + DEFINE_PROP_STRING("fsdev", VirtIOPCIProxy, fsconf.fsdev_id), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_9p_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = virtio_9p_init_pci; + k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + k->device_id = PCI_DEVICE_ID_VIRTIO_9P; + k->revision = VIRTIO_PCI_ABI_VERSION; + k->class_id = 0x2; + dc->props = virtio_9p_properties; + dc->reset = virtio_pci_reset; +} + +static const TypeInfo virtio_9p_info = { + .name = "virtio-9p-pci", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(VirtIOPCIProxy), + .class_init = virtio_9p_class_init, +}; +#endif + +/* + * virtio-pci: This is the PCIDevice which has a virtio-pci-bus. + */ + +/* This is called by virtio-bus just after the device is plugged. */ +static void virtio_pci_device_plugged(DeviceState *d) +{ + VirtIOPCIProxy *proxy = VIRTIO_PCI(d); + VirtioBusState *bus = &proxy->bus; + uint8_t *config; + uint32_t size; + + proxy->vdev = bus->vdev; + + config = proxy->pci_dev.config; + if (proxy->class_code) { + pci_config_set_class(config, proxy->class_code); + } + pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID, + pci_get_word(config + PCI_VENDOR_ID)); + pci_set_word(config + PCI_SUBSYSTEM_ID, virtio_bus_get_vdev_id(bus)); + config[PCI_INTERRUPT_PIN] = 1; + + if (proxy->nvectors && + msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors, 1)) { + proxy->nvectors = 0; + } + + proxy->pci_dev.config_write = virtio_write_config; + + size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev) + + virtio_bus_get_vdev_config_len(bus); + if (size & (size - 1)) { + size = 1 << qemu_fls(size); + } + + memory_region_init_io(&proxy->bar, &virtio_pci_config_ops, proxy, + "virtio-pci", size); + pci_register_bar(&proxy->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, + &proxy->bar); + + if (!kvm_has_many_ioeventfds()) { + proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; + } + + proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; + proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE; + proxy->host_features = virtio_bus_get_vdev_features(bus, + proxy->host_features); +} + +/* This is called by virtio-bus just before the device is unplugged. */ +static void virtio_pci_device_unplug(DeviceState *d) +{ + VirtIOPCIProxy *dev = VIRTIO_PCI(d); + virtio_pci_stop_ioeventfd(dev); +} + +static int virtio_pci_init(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *dev = VIRTIO_PCI(pci_dev); + VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev); + virtio_pci_bus_new(&dev->bus, dev); + if (k->init != NULL) { + return k->init(dev); + } + return 0; +} + +static void virtio_pci_exit(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev); + VirtioBusState *bus = VIRTIO_BUS(&proxy->bus); + BusState *qbus = BUS(&proxy->bus); + virtio_bus_destroy_device(bus); + qbus_free(qbus); + virtio_exit_pci(pci_dev); +} + +/* + * This will be renamed virtio_pci_reset at the end of the series. + * virtio_pci_reset is still in use at this moment. + */ +static void virtio_pci_rst(DeviceState *qdev) +{ + VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev); + VirtioBusState *bus = VIRTIO_BUS(&proxy->bus); + virtio_pci_stop_ioeventfd(proxy); + virtio_bus_reset(bus); + msix_unuse_all_vectors(&proxy->pci_dev); + proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG; +} + +static void virtio_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = virtio_pci_init; + k->exit = virtio_pci_exit; + k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + k->revision = VIRTIO_PCI_ABI_VERSION; + k->class_id = PCI_CLASS_OTHERS; + dc->reset = virtio_pci_rst; +} + +static const TypeInfo virtio_pci_info = { + .name = TYPE_VIRTIO_PCI, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(VirtIOPCIProxy), + .class_init = virtio_pci_class_init, + .class_size = sizeof(VirtioPCIClass), + .abstract = true, +}; + +/* virtio-pci-bus */ + +void virtio_pci_bus_new(VirtioBusState *bus, VirtIOPCIProxy *dev) +{ + DeviceState *qdev = DEVICE(dev); + BusState *qbus; + qbus_create_inplace((BusState *)bus, TYPE_VIRTIO_PCI_BUS, qdev, NULL); + qbus = BUS(bus); + qbus->allow_hotplug = 0; +} + +static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *bus_class = BUS_CLASS(klass); + VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); + bus_class->max_dev = 1; + k->notify = virtio_pci_notify; + k->save_config = virtio_pci_save_config; + k->load_config = virtio_pci_load_config; + k->save_queue = virtio_pci_save_queue; + k->load_queue = virtio_pci_load_queue; + k->get_features = virtio_pci_get_features; + k->query_guest_notifiers = virtio_pci_query_guest_notifiers; + k->set_host_notifier = virtio_pci_set_host_notifier; + k->set_guest_notifiers = virtio_pci_set_guest_notifiers; + k->vmstate_change = virtio_pci_vmstate_change; + k->device_plugged = virtio_pci_device_plugged; + k->device_unplug = virtio_pci_device_unplug; +} + +static const TypeInfo virtio_pci_bus_info = { + .name = TYPE_VIRTIO_PCI_BUS, + .parent = TYPE_VIRTIO_BUS, + .instance_size = sizeof(VirtioPCIBusState), + .class_init = virtio_pci_bus_class_init, +}; + static void virtio_pci_register_types(void) { type_register_static(&virtio_blk_info); @@ -1124,6 +1521,11 @@ static void virtio_pci_register_types(void) type_register_static(&virtio_balloon_info); type_register_static(&virtio_scsi_info); type_register_static(&virtio_rng_info); + type_register_static(&virtio_pci_bus_info); + type_register_static(&virtio_pci_info); +#ifdef CONFIG_VIRTFS + type_register_static(&virtio_9p_info); +#endif } type_init(virtio_pci_register_types) diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h index b58d9a2d19..2ae96f84d6 100644 --- a/hw/virtio-pci.h +++ b/hw/virtio-pci.h @@ -15,11 +15,29 @@ #ifndef QEMU_VIRTIO_PCI_H #define QEMU_VIRTIO_PCI_H -#include "virtio-blk.h" -#include "virtio-net.h" -#include "virtio-rng.h" -#include "virtio-serial.h" -#include "virtio-scsi.h" +#include "hw/pci/msi.h" +#include "hw/virtio-blk.h" +#include "hw/virtio-net.h" +#include "hw/virtio-rng.h" +#include "hw/virtio-serial.h" +#include "hw/virtio-scsi.h" +#include "hw/virtio-bus.h" +#include "hw/9pfs/virtio-9p-device.h" + +typedef struct VirtIOPCIProxy VirtIOPCIProxy; + +/* virtio-pci-bus */ + +typedef struct VirtioBusState VirtioPCIBusState; +typedef struct VirtioBusClass VirtioPCIBusClass; + +#define TYPE_VIRTIO_PCI_BUS "virtio-pci-bus" +#define VIRTIO_PCI_BUS(obj) \ + OBJECT_CHECK(VirtioPCIBusState, (obj), TYPE_VIRTIO_PCI_BUS) +#define VIRTIO_PCI_BUS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtioPCIBusClass, obj, TYPE_VIRTIO_PCI_BUS) +#define VIRTIO_PCI_BUS_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtioPCIBusClass, klass, TYPE_VIRTIO_PCI_BUS) /* Performance improves when virtqueue kick processing is decoupled from the * vcpu thread using ioeventfd for some devices. */ @@ -27,11 +45,28 @@ #define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT) typedef struct { + MSIMessage msg; int virq; unsigned int users; } VirtIOIRQFD; -typedef struct { +/* + * virtio-pci: This is the PCIDevice which has a virtio-pci-bus. + */ +#define TYPE_VIRTIO_PCI "virtio-pci" +#define VIRTIO_PCI_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtioPCIClass, obj, TYPE_VIRTIO_PCI) +#define VIRTIO_PCI_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtioPCIClass, klass, TYPE_VIRTIO_PCI) +#define VIRTIO_PCI(obj) \ + OBJECT_CHECK(VirtIOPCIProxy, (obj), TYPE_VIRTIO_PCI) + +typedef struct VirtioPCIClass { + PCIDeviceClass parent_class; + int (*init)(VirtIOPCIProxy *vpci_dev); +} VirtioPCIClass; + +struct VirtIOPCIProxy { PCIDevice pci_dev; VirtIODevice *vdev; MemoryRegion bar; @@ -41,7 +76,7 @@ typedef struct { VirtIOBlkConf blk; NICConf nic; uint32_t host_features; -#ifdef CONFIG_LINUX +#ifdef CONFIG_VIRTFS V9fsConf fsconf; #endif virtio_serial_conf serial; @@ -51,10 +86,12 @@ typedef struct { bool ioeventfd_disabled; bool ioeventfd_started; VirtIOIRQFD *vector_irqfd; -} VirtIOPCIProxy; + int nvqs_with_notifiers; + VirtioBusState bus; +}; void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev); -void virtio_pci_reset(DeviceState *d); +void virtio_pci_bus_new(VirtioBusState *bus, VirtIOPCIProxy *dev); /* Virtio ABI version, if we increment this, we break the guest driver. */ #define VIRTIO_PCI_ABI_VERSION 0 diff --git a/hw/virtio-rng.c b/hw/virtio-rng.c index e063127df6..54c1421f86 100644 --- a/hw/virtio-rng.c +++ b/hw/virtio-rng.c @@ -10,9 +10,10 @@ */ #include "qemu/iov.h" -#include "qdev.h" -#include "virtio.h" -#include "virtio-rng.h" +#include "hw/qdev.h" +#include "qapi/qmp/qerror.h" +#include "hw/virtio.h" +#include "hw/virtio-rng.h" #include "qemu/rng.h" typedef struct VirtIORNG { diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c index bfe1860505..72cc5198d4 100644 --- a/hw/virtio-scsi.c +++ b/hw/virtio-scsi.c @@ -13,7 +13,8 @@ * */ -#include "virtio-scsi.h" +#include "hw/virtio-scsi.h" +#include "qemu/error-report.h" #include #include @@ -565,6 +566,10 @@ static void virtio_scsi_reset(VirtIODevice *vdev) { VirtIOSCSI *s = (VirtIOSCSI *)vdev; + s->resetting++; + qbus_reset_all(&s->bus.qbus); + s->resetting--; + s->sense_size = VIRTIO_SCSI_SENSE_SIZE; s->cdb_size = VIRTIO_SCSI_CDB_SIZE; s->events_dropped = false; diff --git a/hw/virtio-scsi.h b/hw/virtio-scsi.h index 8d9d15f093..81b3279a57 100644 --- a/hw/virtio-scsi.h +++ b/hw/virtio-scsi.h @@ -14,8 +14,8 @@ #ifndef _QEMU_VIRTIO_SCSI_H #define _QEMU_VIRTIO_SCSI_H -#include "virtio.h" -#include "pci/pci.h" +#include "hw/virtio.h" +#include "hw/pci/pci.h" /* The ID for virtio_scsi */ #define VIRTIO_ID_SCSI 8 diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index 7272bfd5fe..7d0515f551 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -21,9 +21,9 @@ #include "qemu/iov.h" #include "monitor/monitor.h" #include "qemu/queue.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "trace.h" -#include "virtio-serial.h" +#include "hw/virtio-serial.h" /* The virtio-serial bus on top of which the ports will ride as devices */ struct VirtIOSerialBus { @@ -172,24 +172,7 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, port->elem.out_sg[i].iov_base + port->iov_offset, buf_size); - if (ret < 0 && ret != -EAGAIN) { - /* We don't handle any other type of errors here */ - abort(); - } - if (ret == -EAGAIN || (ret >= 0 && ret < buf_size)) { - /* - * this is a temporary check until chardevs can signal to - * frontends that they are writable again. This prevents - * the console from going into throttled mode (forever) - * if virtio-console is connected to a pty without a - * listener. Otherwise the guest spins forever. - * We can revert this if - * 1: chardevs can notify frondends - * 2: the guest driver does not spin in these cases - */ - if (!vsc->is_console) { - virtio_serial_throttle_port(port, true); - } + if (port->throttled) { port->iov_idx = i; if (ret > 0) { port->iov_offset += ret; @@ -1058,7 +1041,7 @@ static void virtio_serial_port_class_init(ObjectClass *klass, void *data) k->props = virtser_props; } -static TypeInfo virtio_serial_port_type_info = { +static const TypeInfo virtio_serial_port_type_info = { .name = TYPE_VIRTIO_SERIAL_PORT, .parent = TYPE_DEVICE, .instance_size = sizeof(VirtIOSerialPort), diff --git a/hw/virtio-serial.h b/hw/virtio-serial.h index 16e39820a2..d2d9fb773e 100644 --- a/hw/virtio-serial.h +++ b/hw/virtio-serial.h @@ -15,8 +15,8 @@ #ifndef _QEMU_VIRTIO_SERIAL_H #define _QEMU_VIRTIO_SERIAL_H -#include "qdev.h" -#include "virtio.h" +#include "hw/qdev.h" +#include "hw/virtio.h" /* == Interface shared between the guest kernel and qemu == */ diff --git a/hw/virtio.c b/hw/virtio.c index 0455a9e8f3..26fbc790ec 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -15,8 +15,9 @@ #include "trace.h" #include "qemu/error-report.h" -#include "virtio.h" +#include "hw/virtio.h" #include "qemu/atomic.h" +#include "hw/virtio-bus.h" /* The alignment to use between consumer and producer parts of vring. * x86 pagesize again. */ @@ -72,6 +73,8 @@ struct VirtQueue /* Notification enabled? */ bool notification; + uint16_t queue_index; + int inuse; uint16_t vector; @@ -700,6 +703,15 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, return &vdev->vq[i]; } +void virtio_del_queue(VirtIODevice *vdev, int n) +{ + if (n < 0 || n >= VIRTIO_PCI_QUEUE_MAX) { + abort(); + } + + vdev->vq[n].vring.num = 0; +} + void virtio_irq(VirtQueue *vq) { trace_virtio_irq(vq); @@ -875,11 +887,16 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f) return 0; } -void virtio_cleanup(VirtIODevice *vdev) +void virtio_common_cleanup(VirtIODevice *vdev) { qemu_del_vm_change_state_handler(vdev->vmstate); g_free(vdev->config); g_free(vdev->vq); +} + +void virtio_cleanup(VirtIODevice *vdev) +{ + virtio_common_cleanup(vdev); g_free(vdev); } @@ -902,14 +919,10 @@ static void virtio_vmstate_change(void *opaque, int running, RunState state) } } -VirtIODevice *virtio_common_init(const char *name, uint16_t device_id, - size_t config_size, size_t struct_size) +void virtio_init(VirtIODevice *vdev, const char *name, + uint16_t device_id, size_t config_size) { - VirtIODevice *vdev; int i; - - vdev = g_malloc0(struct_size); - vdev->device_id = device_id; vdev->status = 0; vdev->isr = 0; @@ -917,25 +930,34 @@ VirtIODevice *virtio_common_init(const char *name, uint16_t device_id, vdev->config_vector = VIRTIO_NO_VECTOR; vdev->vq = g_malloc0(sizeof(VirtQueue) * VIRTIO_PCI_QUEUE_MAX); vdev->vm_running = runstate_is_running(); - for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { + for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { vdev->vq[i].vector = VIRTIO_NO_VECTOR; vdev->vq[i].vdev = vdev; + vdev->vq[i].queue_index = i; } vdev->name = name; vdev->config_len = config_size; - if (vdev->config_len) + if (vdev->config_len) { vdev->config = g_malloc0(config_size); - else + } else { vdev->config = NULL; + } + vdev->vmstate = qemu_add_vm_change_state_handler(virtio_vmstate_change, + vdev); +} - vdev->vmstate = qemu_add_vm_change_state_handler(virtio_vmstate_change, vdev); - +VirtIODevice *virtio_common_init(const char *name, uint16_t device_id, + size_t config_size, size_t struct_size) +{ + VirtIODevice *vdev; + vdev = g_malloc0(struct_size); + virtio_init(vdev, name, device_id, config_size); return vdev; } void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding, - void *opaque) + DeviceState *opaque) { vdev->binding = binding; vdev->binding_opaque = opaque; @@ -999,6 +1021,11 @@ VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n) return vdev->vq + n; } +uint16_t virtio_get_queue_index(VirtQueue *vq) +{ + return vq->queue_index; +} + static void virtio_queue_guest_notifier_read(EventNotifier *n) { VirtQueue *vq = container_of(n, VirtQueue, guest_notifier); @@ -1056,3 +1083,39 @@ EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq) { return &vq->host_notifier; } + +static int virtio_device_init(DeviceState *qdev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(qdev); + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(qdev); + assert(k->init != NULL); + if (k->init(vdev) < 0) { + return -1; + } + virtio_bus_plug_device(vdev); + return 0; +} + +static void virtio_device_class_init(ObjectClass *klass, void *data) +{ + /* Set the default value here. */ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->init = virtio_device_init; + dc->bus_type = TYPE_VIRTIO_BUS; +} + +static const TypeInfo virtio_device_info = { + .name = TYPE_VIRTIO_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(VirtIODevice), + .class_init = virtio_device_class_init, + .abstract = true, + .class_size = sizeof(VirtioDeviceClass), +}; + +static void virtio_register_types(void) +{ + type_register_static(&virtio_device_info); +} + +type_init(virtio_register_types) diff --git a/hw/virtio.h b/hw/virtio.h index 541600484e..ca43fd70cd 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -14,13 +14,13 @@ #ifndef _QEMU_VIRTIO_H #define _QEMU_VIRTIO_H -#include "hw.h" +#include "hw/hw.h" #include "net/net.h" -#include "qdev.h" +#include "hw/qdev.h" #include "sysemu/sysemu.h" #include "qemu/event_notifier.h" -#ifdef CONFIG_LINUX -#include "9p.h" +#ifdef CONFIG_VIRTFS +#include "hw/9pfs/virtio-9p-device.h" #endif /* from Linux's linux/virtio_config.h */ @@ -91,25 +91,34 @@ typedef struct VirtQueueElement } VirtQueueElement; typedef struct { - void (*notify)(void * opaque, uint16_t vector); - void (*save_config)(void * opaque, QEMUFile *f); - void (*save_queue)(void * opaque, int n, QEMUFile *f); - int (*load_config)(void * opaque, QEMUFile *f); - int (*load_queue)(void * opaque, int n, QEMUFile *f); - int (*load_done)(void * opaque, QEMUFile *f); - unsigned (*get_features)(void * opaque); - bool (*query_guest_notifiers)(void * opaque); - int (*set_guest_notifiers)(void * opaque, bool assigned); - int (*set_host_notifier)(void * opaque, int n, bool assigned); - void (*vmstate_change)(void * opaque, bool running); + void (*notify)(DeviceState *d, uint16_t vector); + void (*save_config)(DeviceState *d, QEMUFile *f); + void (*save_queue)(DeviceState *d, int n, QEMUFile *f); + int (*load_config)(DeviceState *d, QEMUFile *f); + int (*load_queue)(DeviceState *d, int n, QEMUFile *f); + int (*load_done)(DeviceState *d, QEMUFile *f); + unsigned (*get_features)(DeviceState *d); + bool (*query_guest_notifiers)(DeviceState *d); + int (*set_guest_notifiers)(DeviceState *d, int nvqs, bool assigned); + int (*set_host_notifier)(DeviceState *d, int n, bool assigned); + void (*vmstate_change)(DeviceState *d, bool running); } VirtIOBindings; #define VIRTIO_PCI_QUEUE_MAX 64 #define VIRTIO_NO_VECTOR 0xffff +#define TYPE_VIRTIO_DEVICE "virtio-device" +#define VIRTIO_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtioDeviceClass, obj, TYPE_VIRTIO_DEVICE) +#define VIRTIO_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtioDeviceClass, klass, TYPE_VIRTIO_DEVICE) +#define VIRTIO_DEVICE(obj) \ + OBJECT_CHECK(VirtIODevice, (obj), TYPE_VIRTIO_DEVICE) + struct VirtIODevice { + DeviceState parent_obj; const char *name; uint8_t status; uint8_t isr; @@ -119,6 +128,10 @@ struct VirtIODevice void *config; uint16_t config_vector; int nvectors; + /* + * Function pointers will be removed at the end of the series as they are in + * VirtioDeviceClass. + */ uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features); uint32_t (*bad_features)(VirtIODevice *vdev); void (*set_features)(VirtIODevice *vdev, uint32_t val); @@ -126,18 +139,50 @@ struct VirtIODevice void (*set_config)(VirtIODevice *vdev, const uint8_t *config); void (*reset)(VirtIODevice *vdev); void (*set_status)(VirtIODevice *vdev, uint8_t val); + /* Test and clear event pending status. + * Should be called after unmask to avoid losing events. + * If backend does not support masking, + * must check in frontend instead. + */ + bool (*guest_notifier_pending)(VirtIODevice *vdev, int n); + /* Mask/unmask events from this vq. Any events reported + * while masked will become pending. + * If backend does not support masking, + * must mask in frontend instead. + */ + void (*guest_notifier_mask)(VirtIODevice *vdev, int n, bool mask); + VirtQueue *vq; const VirtIOBindings *binding; - void *binding_opaque; + DeviceState *binding_opaque; uint16_t device_id; bool vm_running; VMChangeStateEntry *vmstate; }; +typedef struct VirtioDeviceClass { + /* This is what a VirtioDevice must implement */ + DeviceClass parent; + int (*init)(VirtIODevice *vdev); + uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features); + uint32_t (*bad_features)(VirtIODevice *vdev); + void (*set_features)(VirtIODevice *vdev, uint32_t val); + void (*get_config)(VirtIODevice *vdev, uint8_t *config); + void (*set_config)(VirtIODevice *vdev, const uint8_t *config); + void (*reset)(VirtIODevice *vdev); + void (*set_status)(VirtIODevice *vdev, uint8_t val); +} VirtioDeviceClass; + +void virtio_init(VirtIODevice *vdev, const char *name, + uint16_t device_id, size_t config_size); +void virtio_common_cleanup(VirtIODevice *vdev); + VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, void (*handle_output)(VirtIODevice *, VirtQueue *)); +void virtio_del_queue(VirtIODevice *vdev, int n); + void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len); void virtqueue_flush(VirtQueue *vq, unsigned int count); @@ -191,14 +236,15 @@ void virtio_update_irq(VirtIODevice *vdev); int virtio_set_features(VirtIODevice *vdev, uint32_t val); void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding, - void *opaque); + DeviceState *opaque); /* Base devices. */ typedef struct VirtIOBlkConf VirtIOBlkConf; VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk); struct virtio_net_conf; VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, - struct virtio_net_conf *net); + struct virtio_net_conf *net, + uint32_t host_features); typedef struct virtio_serial_conf virtio_serial_conf; VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial); VirtIODevice *virtio_balloon_init(DeviceState *dev); @@ -206,7 +252,7 @@ typedef struct VirtIOSCSIConf VirtIOSCSIConf; VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf); typedef struct VirtIORNGConf VirtIORNGConf; VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf); -#ifdef CONFIG_LINUX +#ifdef CONFIG_VIRTFS VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf); #endif @@ -235,6 +281,7 @@ hwaddr virtio_queue_get_ring_size(VirtIODevice *vdev, int n); uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n); void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx); VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n); +uint16_t virtio_get_queue_index(VirtQueue *vq); int virtio_queue_get_id(VirtQueue *vq); EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq); void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign, diff --git a/hw/vmmouse.c b/hw/vmmouse.c index 004d09851c..a9d227e17d 100644 --- a/hw/vmmouse.c +++ b/hw/vmmouse.c @@ -21,11 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "hw/hw.h" #include "ui/console.h" -#include "ps2.h" -#include "pc.h" -#include "qdev.h" +#include "hw/ps2.h" +#include "hw/pc.h" +#include "hw/qdev.h" /* debug only vmmouse */ //#define DEBUG_VMMOUSE @@ -286,7 +286,7 @@ static void vmmouse_class_initfn(ObjectClass *klass, void *data) dc->props = vmmouse_properties; } -static TypeInfo vmmouse_info = { +static const TypeInfo vmmouse_info = { .name = "vmmouse", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(VMMouseState), diff --git a/hw/vmport.c b/hw/vmport.c index 7d425237ac..cc1466ae96 100644 --- a/hw/vmport.c +++ b/hw/vmport.c @@ -21,11 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "isa.h" -#include "pc.h" +#include "hw/hw.h" +#include "hw/isa.h" +#include "hw/pc.h" #include "sysemu/kvm.h" -#include "qdev.h" +#include "hw/qdev.h" //#define VMPORT_DEBUG @@ -155,7 +155,7 @@ static void vmport_class_initfn(ObjectClass *klass, void *data) dc->no_user = 1; } -static TypeInfo vmport_info = { +static const TypeInfo vmport_info = { .name = "vmport", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(VMPortState), diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c index b0e772f863..db2f187e56 100644 --- a/hw/vmware_vga.c +++ b/hw/vmware_vga.c @@ -21,17 +21,17 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" -#include "loader.h" +#include "hw/hw.h" +#include "hw/loader.h" #include "ui/console.h" -#include "pci/pci.h" +#include "hw/pci/pci.h" #undef VERBOSE #define HW_RECT_ACCEL #define HW_FILL_ACCEL #define HW_MOUSE_ACCEL -#include "vga_int.h" +#include "hw/vga_int.h" /* See http://vmware-svga.sf.net/ for some documentation on VMWare SVGA */ @@ -296,6 +296,15 @@ static inline void vmsvga_update_rect(struct vmsvga_state_s *s, uint8_t *src; uint8_t *dst; + if (x < 0) { + fprintf(stderr, "%s: update x was < 0 (%d)\n", __func__, x); + w += x; + x = 0; + } + if (w < 0) { + fprintf(stderr, "%s: update w was < 0 (%d)\n", __func__, w); + w = 0; + } if (x + w > ds_get_width(s->vga.ds)) { fprintf(stderr, "%s: update width too large x: %d, w: %d\n", __func__, x, w); @@ -303,6 +312,15 @@ static inline void vmsvga_update_rect(struct vmsvga_state_s *s, w = ds_get_width(s->vga.ds) - x; } + if (y < 0) { + fprintf(stderr, "%s: update y was < 0 (%d)\n", __func__, y); + h += y; + y = 0; + } + if (h < 0) { + fprintf(stderr, "%s: update h was < 0 (%d)\n", __func__, h); + h = 0; + } if (y + h > ds_get_height(s->vga.ds)) { fprintf(stderr, "%s: update height too large y: %d, h: %d\n", __func__, y, h); @@ -1056,7 +1074,7 @@ static void vmsvga_screen_dump(void *opaque, const char *filename, bool cswitch, ds_get_height(s->vga.ds), 32, ds_get_linesize(s->vga.ds), - s->vga.vram_ptr); + s->vga.vram_ptr, false); ppm_save(filename, ds, errp); g_free(ds); } @@ -1244,7 +1262,7 @@ static void vmsvga_class_init(ObjectClass *klass, void *data) dc->props = vga_vmware_properties; } -static TypeInfo vmsvga_info = { +static const TypeInfo vmsvga_info = { .name = "vmware-svga", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(struct pci_vmsvga_state_s), diff --git a/hw/vt82c686.c b/hw/vt82c686.c index d3469d49f1..452950826c 100644 --- a/hw/vt82c686.c +++ b/hw/vt82c686.c @@ -10,24 +10,22 @@ * GNU GPL, version 2 or (at your option) any later version. */ -#include "hw.h" -#include "pc.h" -#include "vt82c686.h" -#include "i2c.h" -#include "smbus.h" -#include "pci/pci.h" -#include "isa.h" -#include "sysbus.h" -#include "mips.h" -#include "apm.h" -#include "acpi.h" -#include "pm_smbus.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/vt82c686.h" +#include "hw/i2c.h" +#include "hw/smbus.h" +#include "hw/pci/pci.h" +#include "hw/isa.h" +#include "hw/sysbus.h" +#include "hw/mips.h" +#include "hw/apm.h" +#include "hw/acpi.h" +#include "hw/pm_smbus.h" #include "sysemu/sysemu.h" #include "qemu/timer.h" #include "exec/address-spaces.h" -typedef uint32_t pci_addr_t; -#include "pci/pci_host.h" //#define DEBUG_VT82C686B #ifdef DEBUG_VT82C686B @@ -284,7 +282,7 @@ static void via_ac97_class_init(ObjectClass *klass, void *data) dc->desc = "AC97"; } -static TypeInfo via_ac97_info = { +static const TypeInfo via_ac97_info = { .name = "VT82C686B_AC97", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VT686AC97State), @@ -325,7 +323,7 @@ static void via_mc97_class_init(ObjectClass *klass, void *data) dc->desc = "MC97"; } -static TypeInfo via_mc97_info = { +static const TypeInfo via_mc97_info = { .name = "VT82C686B_MC97", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VT686MC97State), @@ -404,7 +402,7 @@ static void via_pm_class_init(ObjectClass *klass, void *data) dc->props = via_pm_properties; } -static TypeInfo via_pm_info = { +static const TypeInfo via_pm_info = { .name = "VT82C686B_PM", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VT686PMState), @@ -471,7 +469,7 @@ static void via_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_via; } -static TypeInfo via_info = { +static const TypeInfo via_info = { .name = "VT82C686B", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VT82C686BState), diff --git a/hw/wdt_i6300esb.c b/hw/wdt_i6300esb.c index 54f0665135..f13e507fcf 100644 --- a/hw/wdt_i6300esb.c +++ b/hw/wdt_i6300esb.c @@ -23,9 +23,9 @@ #include "qemu-common.h" #include "qemu/timer.h" -#include "watchdog.h" -#include "hw.h" -#include "pci/pci.h" +#include "hw/watchdog.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" /*#define I6300ESB_DEBUG 1*/ @@ -439,7 +439,7 @@ static void i6300esb_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_i6300esb; } -static TypeInfo i6300esb_info = { +static const TypeInfo i6300esb_info = { .name = "i6300esb", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(I6300State), diff --git a/hw/wdt_ib700.c b/hw/wdt_ib700.c index 4475f7b862..6c52808ac0 100644 --- a/hw/wdt_ib700.c +++ b/hw/wdt_ib700.c @@ -21,10 +21,10 @@ #include "qemu-common.h" #include "qemu/timer.h" -#include "watchdog.h" -#include "hw.h" -#include "isa.h" -#include "pc.h" +#include "hw/watchdog.h" +#include "hw/hw.h" +#include "hw/isa.h" +#include "hw/pc.h" /*#define IB700_DEBUG 1*/ @@ -129,7 +129,7 @@ static void wdt_ib700_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_ib700; } -static TypeInfo wdt_ib700_info = { +static const TypeInfo wdt_ib700_info = { .name = "ib700", .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(IB700State), diff --git a/hw/wm8750.c b/hw/wm8750.c index 44f138fd51..0904cf496d 100644 --- a/hw/wm8750.c +++ b/hw/wm8750.c @@ -7,8 +7,8 @@ * This file is licensed under GNU GPL. */ -#include "hw.h" -#include "i2c.h" +#include "hw/hw.h" +#include "hw/i2c.h" #include "audio/audio.h" #define IN_PORT_N 3 @@ -632,7 +632,7 @@ static void wm8750_fini(I2CSlave *i2c) void wm8750_data_req_set(DeviceState *dev, void (*data_req)(void *, int, int), void *opaque) { - WM8750State *s = FROM_I2C_SLAVE(WM8750State, I2C_SLAVE_FROM_QDEV(dev)); + WM8750State *s = FROM_I2C_SLAVE(WM8750State, I2C_SLAVE(dev)); s->data_req = data_req; s->opaque = opaque; } @@ -701,7 +701,7 @@ static void wm8750_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_wm8750; } -static TypeInfo wm8750_info = { +static const TypeInfo wm8750_info = { .name = "wm8750", .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(WM8750State), diff --git a/hw/xen-host-pci-device.c b/hw/xen-host-pci-device.c index 743b37b991..ff2e876b3d 100644 --- a/hw/xen-host-pci-device.c +++ b/hw/xen-host-pci-device.c @@ -7,7 +7,7 @@ */ #include "qemu-common.h" -#include "xen-host-pci-device.h" +#include "hw/xen-host-pci-device.h" #define XEN_HOST_PCI_MAX_EXT_CAP \ ((PCIE_CONFIG_SPACE_SIZE - PCI_CONFIG_SPACE_SIZE) / (PCI_CAP_SIZEOF + 4)) diff --git a/hw/xen-host-pci-device.h b/hw/xen-host-pci-device.h index 942b24dccc..c2486f0c19 100644 --- a/hw/xen-host-pci-device.h +++ b/hw/xen-host-pci-device.h @@ -1,7 +1,7 @@ #ifndef XEN_HOST_PCI_DEVICE_H #define XEN_HOST_PCI_DEVICE_H -#include "pci/pci.h" +#include "hw/pci/pci.h" enum { XEN_HOST_PCI_REGION_TYPE_IO = 1 << 1, diff --git a/hw/xen.h b/hw/xen.h index e3cca7fb92..6235f91fe0 100644 --- a/hw/xen.h +++ b/hw/xen.h @@ -21,9 +21,9 @@ enum xen_mode { extern uint32_t xen_domid; extern enum xen_mode xen_mode; -extern int xen_allowed; +extern bool xen_allowed; -static inline int xen_enabled(void) +static inline bool xen_enabled(void) { #if defined(CONFIG_XEN_BACKEND) && !defined(CONFIG_NO_XEN) return xen_allowed; diff --git a/hw/xen_apic.c b/hw/xen_apic.c index a6632fe798..8f387b6403 100644 --- a/hw/xen_apic.c +++ b/hw/xen_apic.c @@ -11,7 +11,7 @@ */ #include "hw/apic_internal.h" #include "hw/pci/msi.h" -#include "xen.h" +#include "hw/xen.h" static uint64_t xen_apic_mem_read(void *opaque, hwaddr addr, unsigned size) @@ -80,7 +80,7 @@ static void xen_apic_class_init(ObjectClass *klass, void *data) k->external_nmi = xen_apic_external_nmi; } -static TypeInfo xen_apic_info = { +static const TypeInfo xen_apic_info = { .name = "xen-apic", .parent = TYPE_APIC_COMMON, .instance_size = sizeof(APICCommonState), diff --git a/hw/xen_backend.c b/hw/xen_backend.c index 3fa30098ca..24381b55e5 100644 --- a/hw/xen_backend.c +++ b/hw/xen_backend.c @@ -34,10 +34,10 @@ #include #include -#include "hw.h" +#include "hw/hw.h" #include "char/char.h" #include "qemu/log.h" -#include "xen_backend.h" +#include "hw/xen_backend.h" #include diff --git a/hw/xen_backend.h b/hw/xen_backend.h index f37afb1f05..6d5c699c51 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -1,7 +1,7 @@ #ifndef QEMU_HW_XEN_BACKEND_H #define QEMU_HW_XEN_BACKEND_H 1 -#include "xen_common.h" +#include "hw/xen_common.h" #include "sysemu/sysemu.h" #include "net/net.h" diff --git a/hw/xen_common.h b/hw/xen_common.h index 95bc9a7825..c37bde3f7e 100644 --- a/hw/xen_common.h +++ b/hw/xen_common.h @@ -14,8 +14,8 @@ #endif #include -#include "hw.h" -#include "xen.h" +#include "hw/hw.h" +#include "hw/xen.h" #include "qemu/queue.h" /* diff --git a/hw/xen_console.c b/hw/xen_console.c index 44141f8692..a8db6f8d8f 100644 --- a/hw/xen_console.c +++ b/hw/xen_console.c @@ -29,9 +29,9 @@ #include #include -#include "hw.h" +#include "hw/hw.h" #include "char/char.h" -#include "xen_backend.h" +#include "hw/xen_backend.h" #include diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c index e2ba741d54..cdcaf62f26 100644 --- a/hw/xen_devconfig.c +++ b/hw/xen_devconfig.c @@ -1,4 +1,4 @@ -#include "xen_backend.h" +#include "hw/xen_backend.h" #include "sysemu/blockdev.h" /* ------------------------------------------------------------- */ diff --git a/hw/xen_disk.c b/hw/xen_disk.c index a6a64a2455..83329e2e69 100644 --- a/hw/xen_disk.c +++ b/hw/xen_disk.c @@ -35,9 +35,9 @@ #include #include -#include "hw.h" -#include "xen_backend.h" -#include "xen_blkif.h" +#include "hw/hw.h" +#include "hw/xen_backend.h" +#include "hw/xen_blkif.h" #include "sysemu/blockdev.h" /* ------------------------------------------------------------- */ @@ -51,6 +51,13 @@ static int max_requests = 32; #define BLOCK_SIZE 512 #define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) +struct PersistentGrant { + void *page; + struct XenBlkDev *blkdev; +}; + +typedef struct PersistentGrant PersistentGrant; + struct ioreq { blkif_request_t req; int16_t status; @@ -68,6 +75,7 @@ struct ioreq { int prot; void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; void *pages; + int num_unmap; /* aio status */ int aio_inflight; @@ -104,6 +112,12 @@ struct XenBlkDev { int requests_inflight; int requests_finished; + /* Persistent grants extension */ + gboolean feature_persistent; + GTree *persistent_gnts; + unsigned int persistent_gnt_count; + unsigned int max_grants; + /* qemu block driver */ DriveInfo *dinfo; BlockDriverState *bs; @@ -112,6 +126,54 @@ struct XenBlkDev { /* ------------------------------------------------------------- */ +static void ioreq_reset(struct ioreq *ioreq) +{ + memset(&ioreq->req, 0, sizeof(ioreq->req)); + ioreq->status = 0; + ioreq->start = 0; + ioreq->presync = 0; + ioreq->postsync = 0; + ioreq->mapped = 0; + + memset(ioreq->domids, 0, sizeof(ioreq->domids)); + memset(ioreq->refs, 0, sizeof(ioreq->refs)); + ioreq->prot = 0; + memset(ioreq->page, 0, sizeof(ioreq->page)); + ioreq->pages = NULL; + + ioreq->aio_inflight = 0; + ioreq->aio_errors = 0; + + ioreq->blkdev = NULL; + memset(&ioreq->list, 0, sizeof(ioreq->list)); + memset(&ioreq->acct, 0, sizeof(ioreq->acct)); + + qemu_iovec_reset(&ioreq->v); +} + +static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) +{ + uint ua = GPOINTER_TO_UINT(a); + uint ub = GPOINTER_TO_UINT(b); + return (ua > ub) - (ua < ub); +} + +static void destroy_grant(gpointer pgnt) +{ + PersistentGrant *grant = pgnt; + XenGnttab gnt = grant->blkdev->xendev.gnttabdev; + + if (xc_gnttab_munmap(gnt, grant->page, 1) != 0) { + xen_be_printf(&grant->blkdev->xendev, 0, + "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + } + grant->blkdev->persistent_gnt_count--; + xen_be_printf(&grant->blkdev->xendev, 3, + "unmapped grant %p\n", grant->page); + g_free(grant); +} + static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) { struct ioreq *ioreq = NULL; @@ -129,7 +191,6 @@ static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) /* get one from freelist */ ioreq = QLIST_FIRST(&blkdev->freelist); QLIST_REMOVE(ioreq, list); - qemu_iovec_reset(&ioreq->v); } QLIST_INSERT_HEAD(&blkdev->inflight, ioreq, list); blkdev->requests_inflight++; @@ -153,7 +214,7 @@ static void ioreq_release(struct ioreq *ioreq, bool finish) struct XenBlkDev *blkdev = ioreq->blkdev; QLIST_REMOVE(ioreq, list); - memset(ioreq, 0, sizeof(*ioreq)); + ioreq_reset(ioreq); ioreq->blkdev = blkdev; QLIST_INSERT_HEAD(&blkdev->freelist, ioreq, list); if (finish) { @@ -182,12 +243,11 @@ static int ioreq_parse(struct ioreq *ioreq) case BLKIF_OP_READ: ioreq->prot = PROT_WRITE; /* to memory */ break; - case BLKIF_OP_WRITE_BARRIER: + case BLKIF_OP_FLUSH_DISKCACHE: + ioreq->presync = 1; if (!ioreq->req.nr_segments) { - ioreq->presync = 1; return 0; } - ioreq->presync = ioreq->postsync = 1; /* fall through */ case BLKIF_OP_WRITE: ioreq->prot = PROT_READ; /* from memory */ @@ -241,21 +301,21 @@ static void ioreq_unmap(struct ioreq *ioreq) XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev; int i; - if (ioreq->v.niov == 0 || ioreq->mapped == 0) { + if (ioreq->num_unmap == 0 || ioreq->mapped == 0) { return; } if (batch_maps) { if (!ioreq->pages) { return; } - if (xc_gnttab_munmap(gnt, ioreq->pages, ioreq->v.niov) != 0) { + if (xc_gnttab_munmap(gnt, ioreq->pages, ioreq->num_unmap) != 0) { xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", strerror(errno)); } - ioreq->blkdev->cnt_map -= ioreq->v.niov; + ioreq->blkdev->cnt_map -= ioreq->num_unmap; ioreq->pages = NULL; } else { - for (i = 0; i < ioreq->v.niov; i++) { + for (i = 0; i < ioreq->num_unmap; i++) { if (!ioreq->page[i]) { continue; } @@ -273,41 +333,120 @@ static void ioreq_unmap(struct ioreq *ioreq) static int ioreq_map(struct ioreq *ioreq) { XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev; - int i; + uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int i, j, new_maps = 0; + PersistentGrant *grant; + /* domids and refs variables will contain the information necessary + * to map the grants that are needed to fulfill this request. + * + * After mapping the needed grants, the page array will contain the + * memory address of each granted page in the order specified in ioreq + * (disregarding if it's a persistent grant or not). + */ if (ioreq->v.niov == 0 || ioreq->mapped == 1) { return 0; } - if (batch_maps) { + if (ioreq->blkdev->feature_persistent) { + for (i = 0; i < ioreq->v.niov; i++) { + grant = g_tree_lookup(ioreq->blkdev->persistent_gnts, + GUINT_TO_POINTER(ioreq->refs[i])); + + if (grant != NULL) { + page[i] = grant->page; + xen_be_printf(&ioreq->blkdev->xendev, 3, + "using persistent-grant %" PRIu32 "\n", + ioreq->refs[i]); + } else { + /* Add the grant to the list of grants that + * should be mapped + */ + domids[new_maps] = ioreq->domids[i]; + refs[new_maps] = ioreq->refs[i]; + page[i] = NULL; + new_maps++; + } + } + /* Set the protection to RW, since grants may be reused later + * with a different protection than the one needed for this request + */ + ioreq->prot = PROT_WRITE | PROT_READ; + } else { + /* All grants in the request should be mapped */ + memcpy(refs, ioreq->refs, sizeof(refs)); + memcpy(domids, ioreq->domids, sizeof(domids)); + memset(page, 0, sizeof(page)); + new_maps = ioreq->v.niov; + } + + if (batch_maps && new_maps) { ioreq->pages = xc_gnttab_map_grant_refs - (gnt, ioreq->v.niov, ioreq->domids, ioreq->refs, ioreq->prot); + (gnt, new_maps, domids, refs, ioreq->prot); if (ioreq->pages == NULL) { xen_be_printf(&ioreq->blkdev->xendev, 0, "can't map %d grant refs (%s, %d maps)\n", - ioreq->v.niov, strerror(errno), ioreq->blkdev->cnt_map); + new_maps, strerror(errno), ioreq->blkdev->cnt_map); return -1; } - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->v.iov[i].iov_base = ioreq->pages + i * XC_PAGE_SIZE + - (uintptr_t)ioreq->v.iov[i].iov_base; + for (i = 0, j = 0; i < ioreq->v.niov; i++) { + if (page[i] == NULL) { + page[i] = ioreq->pages + (j++) * XC_PAGE_SIZE; + } } - ioreq->blkdev->cnt_map += ioreq->v.niov; - } else { - for (i = 0; i < ioreq->v.niov; i++) { + ioreq->blkdev->cnt_map += new_maps; + } else if (new_maps) { + for (i = 0; i < new_maps; i++) { ioreq->page[i] = xc_gnttab_map_grant_ref - (gnt, ioreq->domids[i], ioreq->refs[i], ioreq->prot); + (gnt, domids[i], refs[i], ioreq->prot); if (ioreq->page[i] == NULL) { xen_be_printf(&ioreq->blkdev->xendev, 0, "can't map grant ref %d (%s, %d maps)\n", - ioreq->refs[i], strerror(errno), ioreq->blkdev->cnt_map); + refs[i], strerror(errno), ioreq->blkdev->cnt_map); ioreq_unmap(ioreq); return -1; } - ioreq->v.iov[i].iov_base = ioreq->page[i] + (uintptr_t)ioreq->v.iov[i].iov_base; ioreq->blkdev->cnt_map++; } + for (i = 0, j = 0; i < ioreq->v.niov; i++) { + if (page[i] == NULL) { + page[i] = ioreq->page[j++]; + } + } + } + if (ioreq->blkdev->feature_persistent) { + while ((ioreq->blkdev->persistent_gnt_count < ioreq->blkdev->max_grants) + && new_maps) { + /* Go through the list of newly mapped grants and add as many + * as possible to the list of persistently mapped grants. + * + * Since we start at the end of ioreq->page(s), we only need + * to decrease new_maps to prevent this granted pages from + * being unmapped in ioreq_unmap. + */ + grant = g_malloc0(sizeof(*grant)); + new_maps--; + if (batch_maps) { + grant->page = ioreq->pages + (new_maps) * XC_PAGE_SIZE; + } else { + grant->page = ioreq->page[new_maps]; + } + grant->blkdev = ioreq->blkdev; + xen_be_printf(&ioreq->blkdev->xendev, 3, + "adding grant %" PRIu32 " page: %p\n", + refs[new_maps], grant->page); + g_tree_insert(ioreq->blkdev->persistent_gnts, + GUINT_TO_POINTER(refs[new_maps]), + grant); + ioreq->blkdev->persistent_gnt_count++; + } + } + for (i = 0; i < ioreq->v.niov; i++) { + ioreq->v.iov[i].iov_base += (uintptr_t)page[i]; } ioreq->mapped = 1; + ioreq->num_unmap = new_maps; return 0; } @@ -369,7 +508,7 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) qemu_aio_complete, ioreq); break; case BLKIF_OP_WRITE: - case BLKIF_OP_WRITE_BARRIER: + case BLKIF_OP_FLUSH_DISKCACHE: if (!ioreq->req.nr_segments) { break; } @@ -624,7 +763,7 @@ static int blk_init(struct XenDevice *xendev) xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); blkdev->bs = bdrv_new(blkdev->dev); if (blkdev->bs) { - if (bdrv_open(blkdev->bs, blkdev->filename, qflags, + if (bdrv_open(blkdev->bs, blkdev->filename, NULL, qflags, bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) { bdrv_delete(blkdev->bs); blkdev->bs = NULL; @@ -654,7 +793,8 @@ static int blk_init(struct XenDevice *xendev) blkdev->file_size, blkdev->file_size >> 20); /* fill info */ - xenstore_write_be_int(&blkdev->xendev, "feature-barrier", 1); + xenstore_write_be_int(&blkdev->xendev, "feature-flush-cache", 1); + xenstore_write_be_int(&blkdev->xendev, "feature-persistent", 1); xenstore_write_be_int(&blkdev->xendev, "info", info); xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); xenstore_write_be_int(&blkdev->xendev, "sectors", @@ -678,6 +818,7 @@ out_error: static int blk_connect(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + int pers; if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1) { return -1; @@ -686,6 +827,11 @@ static int blk_connect(struct XenDevice *xendev) &blkdev->xendev.remote_port) == -1) { return -1; } + if (xenstore_read_fe_int(&blkdev->xendev, "feature-persistent", &pers)) { + blkdev->feature_persistent = FALSE; + } else { + blkdev->feature_persistent = !!pers; + } blkdev->protocol = BLKIF_PROTOCOL_NATIVE; if (blkdev->xendev.protocol) { @@ -729,6 +875,15 @@ static int blk_connect(struct XenDevice *xendev) } } + if (blkdev->feature_persistent) { + /* Init persistent grants */ + blkdev->max_grants = max_requests * BLKIF_MAX_SEGMENTS_PER_REQUEST; + blkdev->persistent_gnts = g_tree_new_full((GCompareDataFunc)int_cmp, + NULL, NULL, + (GDestroyNotify)destroy_grant); + blkdev->persistent_gnt_count = 0; + } + xen_be_bind_evtchn(&blkdev->xendev); xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " @@ -769,6 +924,11 @@ static int blk_free(struct XenDevice *xendev) blk_disconnect(xendev); } + /* Free persistent grants */ + if (blkdev->feature_persistent) { + g_tree_destroy(blkdev->persistent_gnts); + } + while (!QLIST_EMPTY(&blkdev->freelist)) { ioreq = QLIST_FIRST(&blkdev->freelist); QLIST_REMOVE(ioreq, list); diff --git a/hw/xen_domainbuild.h b/hw/xen_domainbuild.h index dea0121868..681cbe5fd8 100644 --- a/hw/xen_domainbuild.h +++ b/hw/xen_domainbuild.h @@ -1,7 +1,7 @@ #ifndef QEMU_HW_XEN_DOMAINBUILD_H #define QEMU_HW_XEN_DOMAINBUILD_H 1 -#include "xen_common.h" +#include "hw/xen_common.h" int xenstore_domain_init1(const char *kernel, const char *ramdisk, const char *cmdline); diff --git a/hw/xen_nic.c b/hw/xen_nic.c index dc12110dba..b6d36793b3 100644 --- a/hw/xen_nic.c +++ b/hw/xen_nic.c @@ -35,11 +35,11 @@ #include #include -#include "hw.h" +#include "hw/hw.h" #include "net/net.h" #include "net/checksum.h" #include "net/util.h" -#include "xen_backend.h" +#include "hw/xen_backend.h" #include @@ -185,9 +185,11 @@ static void net_tx_packets(struct XenNetDev *netdev) } memcpy(tmpbuf, page + txreq.offset, txreq.size); net_checksum_calculate(tmpbuf, txreq.size); - qemu_send_packet(&netdev->nic->nc, tmpbuf, txreq.size); + qemu_send_packet(qemu_get_queue(netdev->nic), tmpbuf, + txreq.size); } else { - qemu_send_packet(&netdev->nic->nc, page + txreq.offset, txreq.size); + qemu_send_packet(qemu_get_queue(netdev->nic), + page + txreq.offset, txreq.size); } xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); @@ -234,7 +236,7 @@ static void net_rx_response(struct XenNetDev *netdev, static int net_rx_ok(NetClientState *nc) { - struct XenNetDev *netdev = DO_UPCAST(NICState, nc, nc)->opaque; + struct XenNetDev *netdev = qemu_get_nic_opaque(nc); RING_IDX rc, rp; if (netdev->xendev.be_state != XenbusStateConnected) { @@ -255,7 +257,7 @@ static int net_rx_ok(NetClientState *nc) static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size) { - struct XenNetDev *netdev = DO_UPCAST(NICState, nc, nc)->opaque; + struct XenNetDev *netdev = qemu_get_nic_opaque(nc); netif_rx_request_t rxreq; RING_IDX rc, rp; void *page; @@ -324,12 +326,11 @@ static int net_init(struct XenDevice *xendev) return -1; } - netdev->conf.peer = NULL; - netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf, "xen", NULL, netdev); - snprintf(netdev->nic->nc.info_str, sizeof(netdev->nic->nc.info_str), + snprintf(qemu_get_queue(netdev->nic)->info_str, + sizeof(qemu_get_queue(netdev->nic)->info_str), "nic: xenbus vif macaddr=%s", netdev->mac); /* fill info */ @@ -405,7 +406,7 @@ static void net_disconnect(struct XenDevice *xendev) netdev->rxs = NULL; } if (netdev->nic) { - qemu_del_net_client(&netdev->nic->nc); + qemu_del_nic(netdev->nic); netdev->nic = NULL; } } @@ -414,7 +415,7 @@ static void net_event(struct XenDevice *xendev) { struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); net_tx_packets(netdev); - qemu_flush_queued_packets(&netdev->nic->nc); + qemu_flush_queued_packets(qemu_get_queue(netdev->nic)); } static int net_free(struct XenDevice *xendev) diff --git a/hw/xen_platform.c b/hw/xen_platform.c index e7611bb353..5e11c950ab 100644 --- a/hw/xen_platform.c +++ b/hw/xen_platform.c @@ -25,12 +25,12 @@ #include -#include "hw.h" -#include "pc.h" -#include "pci/pci.h" -#include "irq.h" -#include "xen_common.h" -#include "xen_backend.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/pci/pci.h" +#include "hw/irq.h" +#include "hw/xen_common.h" +#include "hw/xen_backend.h" #include "trace.h" #include "exec/address-spaces.h" @@ -279,7 +279,8 @@ static void platform_fixed_ioport_init(PCIXenPlatformState* s) /* Xen Platform PCI Device */ -static uint32_t xen_platform_ioport_readb(void *opaque, uint32_t addr) +static uint64_t xen_platform_ioport_readb(void *opaque, hwaddr addr, + unsigned int size) { if (addr == 0) { return platform_fixed_ioport_readb(opaque, 0); @@ -288,30 +289,28 @@ static uint32_t xen_platform_ioport_readb(void *opaque, uint32_t addr) } } -static void xen_platform_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) +static void xen_platform_ioport_writeb(void *opaque, hwaddr addr, + uint64_t val, unsigned int size) { PCIXenPlatformState *s = opaque; switch (addr) { case 0: /* Platform flags */ - platform_fixed_ioport_writeb(opaque, 0, val); + platform_fixed_ioport_writeb(opaque, 0, (uint32_t)val); break; case 8: - log_writeb(s, val); + log_writeb(s, (uint32_t)val); break; default: break; } } -static MemoryRegionPortio xen_pci_portio[] = { - { 0, 0x100, 1, .read = xen_platform_ioport_readb, }, - { 0, 0x100, 1, .write = xen_platform_ioport_writeb, }, - PORTIO_END_OF_LIST() -}; - static const MemoryRegionOps xen_pci_io_ops = { - .old_portio = xen_pci_portio, + .read = xen_platform_ioport_readb, + .write = xen_platform_ioport_writeb, + .impl.min_access_size = 1, + .impl.max_access_size = 1, }; static void platform_ioport_bar_setup(PCIXenPlatformState *d) @@ -420,7 +419,7 @@ static void xen_platform_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_xen_platform; } -static TypeInfo xen_platform_info = { +static const TypeInfo xen_platform_info = { .name = "xen-platform", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIXenPlatformState), diff --git a/hw/xen_pt.c b/hw/xen_pt.c index 6fd8433a2d..ce695d0e64 100644 --- a/hw/xen_pt.c +++ b/hw/xen_pt.c @@ -54,10 +54,10 @@ #include -#include "pci/pci.h" -#include "xen.h" -#include "xen_backend.h" -#include "xen_pt.h" +#include "hw/pci/pci.h" +#include "hw/xen.h" +#include "hw/xen_backend.h" +#include "hw/xen_pt.h" #include "qemu/range.h" #include "exec/address-spaces.h" @@ -829,7 +829,7 @@ static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data) dc->props = xen_pci_passthrough_properties; }; -static TypeInfo xen_pci_passthrough_info = { +static const TypeInfo xen_pci_passthrough_info = { .name = "xen-pci-passthrough", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(XenPCIPassthroughState), diff --git a/hw/xen_pt.h b/hw/xen_pt.h index e3497302cf..1cd9f44704 100644 --- a/hw/xen_pt.h +++ b/hw/xen_pt.h @@ -2,9 +2,9 @@ #define XEN_PT_H #include "qemu-common.h" -#include "xen_common.h" -#include "pci/pci.h" -#include "xen-host-pci-device.h" +#include "hw/xen_common.h" +#include "hw/pci/pci.h" +#include "hw/xen-host-pci-device.h" void xen_pt_log(const PCIDevice *d, const char *f, ...) GCC_FMT_ATTR(2, 3); diff --git a/hw/xen_pt_config_init.c b/hw/xen_pt_config_init.c index 54a179af90..55838216d9 100644 --- a/hw/xen_pt_config_init.c +++ b/hw/xen_pt_config_init.c @@ -13,8 +13,8 @@ */ #include "qemu/timer.h" -#include "xen_backend.h" -#include "xen_pt.h" +#include "hw/xen_backend.h" +#include "hw/xen_pt.h" #define XEN_PT_MERGE_VALUE(value, data, val_mask) \ (((value) & (val_mask)) | ((data) & ~(val_mask))) diff --git a/hw/xen_pt_msi.c b/hw/xen_pt_msi.c index db757cd1f1..a54ee2bfd9 100644 --- a/hw/xen_pt_msi.c +++ b/hw/xen_pt_msi.c @@ -11,9 +11,9 @@ #include -#include "xen_backend.h" -#include "xen_pt.h" -#include "apic-msidef.h" +#include "hw/xen_backend.h" +#include "hw/xen_pt.h" +#include "hw/apic-msidef.h" #define XEN_PT_AUTO_ASSIGN -1 diff --git a/hw/xenfb.c b/hw/xenfb.c index 903efd3073..3462ded619 100644 --- a/hw/xenfb.c +++ b/hw/xenfb.c @@ -35,10 +35,10 @@ #include #include -#include "hw.h" +#include "hw/hw.h" #include "ui/console.h" #include "char/char.h" -#include "xen_backend.h" +#include "hw/xen_backend.h" #include #include @@ -756,7 +756,8 @@ static void xenfb_update(void *opaque) qemu_free_displaysurface(xenfb->c.ds); xenfb->c.ds->surface = qemu_create_displaysurface_from (xenfb->width, xenfb->height, xenfb->depth, - xenfb->row_stride, xenfb->pixels + xenfb->offset); + xenfb->row_stride, xenfb->pixels + xenfb->offset, + false); break; default: /* we must convert stuff */ diff --git a/hw/xgmac.c b/hw/xgmac.c index 9639b6141b..5275f4810d 100644 --- a/hw/xgmac.c +++ b/hw/xgmac.c @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "char/char.h" #include "qemu/log.h" #include "net/net.h" @@ -235,7 +235,7 @@ static void xgmac_enet_send(struct XgmacState *s) frame_size += len; if (bd.ctl_stat & 0x20000000) { /* Last buffer in frame. */ - qemu_send_packet(&s->nic->nc, frame, len); + qemu_send_packet(qemu_get_queue(s->nic), frame, len); ptr = frame; frame_size = 0; s->regs[DMA_STATUS] |= DMA_STATUS_TI | DMA_STATUS_NIS; @@ -310,7 +310,7 @@ static const MemoryRegionOps enet_mem_ops = { static int eth_can_rx(NetClientState *nc) { - struct XgmacState *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct XgmacState *s = qemu_get_nic_opaque(nc); /* RX enabled? */ return s->regs[DMA_CONTROL] & DMA_CONTROL_SR; @@ -318,7 +318,7 @@ static int eth_can_rx(NetClientState *nc) static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) { - struct XgmacState *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct XgmacState *s = qemu_get_nic_opaque(nc); static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; int unicast, broadcast, multicast; @@ -366,7 +366,7 @@ out: static void eth_cleanup(NetClientState *nc) { - struct XgmacState *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct XgmacState *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -391,7 +391,7 @@ static int xgmac_enet_init(SysBusDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) | s->conf.macaddr.a[4]; @@ -418,7 +418,7 @@ static void xgmac_enet_class_init(ObjectClass *klass, void *data) dc->props = xgmac_properties; } -static TypeInfo xgmac_enet_info = { +static const TypeInfo xgmac_enet_info = { .name = "xgmac", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct XgmacState), diff --git a/hw/xilinx.h b/hw/xilinx.h index a12eccbe3c..6c1ee21c54 100644 --- a/hw/xilinx.h +++ b/hw/xilinx.h @@ -2,8 +2,9 @@ #define HW_XILINX_H 1 -#include "stream.h" #include "qemu-common.h" +#include "qapi/qmp/qerror.h" +#include "hw/stream.h" #include "net/net.h" static inline DeviceState * @@ -14,8 +15,8 @@ xilinx_intc_create(hwaddr base, qemu_irq irq, int kind_of_intr) dev = qdev_create(NULL, "xlnx.xps-intc"); qdev_prop_set_uint32(dev, "kind-of-intr", kind_of_intr); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); return dev; } @@ -29,8 +30,8 @@ xilinx_timer_create(hwaddr base, qemu_irq irq, int oto, int freq) qdev_prop_set_uint32(dev, "one-timer-only", oto); qdev_prop_set_uint32(dev, "clock-frequency", freq); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); return dev; } @@ -48,22 +49,17 @@ xilinx_ethlite_create(NICInfo *nd, hwaddr base, qemu_irq irq, qdev_prop_set_uint32(dev, "tx-ping-pong", txpingpong); qdev_prop_set_uint32(dev, "rx-ping-pong", rxpingpong); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); return dev; } -static inline DeviceState * -xilinx_axiethernet_create(NICInfo *nd, StreamSlave *peer, - hwaddr base, qemu_irq irq, - int txmem, int rxmem) +static inline void +xilinx_axiethernet_init(DeviceState *dev, NICInfo *nd, StreamSlave *peer, + hwaddr base, qemu_irq irq, int txmem, int rxmem) { - DeviceState *dev; Error *errp = NULL; - qemu_check_nic_model(nd, "xlnx.axi-ethernet"); - - dev = qdev_create(NULL, "xlnx.axi-ethernet"); qdev_set_nic_properties(dev, nd); qdev_prop_set_uint32(dev, "rxmem", rxmem); qdev_prop_set_uint32(dev, "txmem", txmem); @@ -71,16 +67,13 @@ xilinx_axiethernet_create(NICInfo *nd, StreamSlave *peer, &errp); assert_no_error(errp); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); - - return dev; + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); } static inline void -xilinx_axiethernetdma_init(DeviceState *dev, StreamSlave *peer, - hwaddr base, qemu_irq irq, - qemu_irq irq2, int freqhz) +xilinx_axidma_init(DeviceState *dev, StreamSlave *peer, hwaddr base, + qemu_irq irq, qemu_irq irq2, int freqhz) { Error *errp = NULL; @@ -90,9 +83,9 @@ xilinx_axiethernetdma_init(DeviceState *dev, StreamSlave *peer, assert_no_error(errp); qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); - sysbus_connect_irq(sysbus_from_qdev(dev), 1, irq2); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, irq2); } #endif diff --git a/hw/xilinx_axidma.c b/hw/xilinx_axidma.c index ce02764b3f..8db1a74acf 100644 --- a/hw/xilinx_axidma.c +++ b/hw/xilinx_axidma.c @@ -22,13 +22,13 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/timer.h" -#include "ptimer.h" +#include "hw/ptimer.h" #include "qemu/log.h" -#include "qdev-addr.h" +#include "hw/qdev-addr.h" -#include "stream.h" +#include "hw/stream.h" #define D(x) @@ -444,7 +444,7 @@ static void axidma_write(void *opaque, hwaddr addr, break; default: D(qemu_log("%s: ch=%d addr=" TARGET_FMT_plx " v=%x\n", - __func__, sid, addr * 4, value)); + __func__, sid, addr * 4, (unsigned)value)); s->regs[addr] = value; break; } @@ -503,7 +503,7 @@ static void axidma_class_init(ObjectClass *klass, void *data) ssc->push = axidma_push; } -static TypeInfo axidma_info = { +static const TypeInfo axidma_info = { .name = "xlnx.axi-dma", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct XilinxAXIDMA), diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c index 09e49b0aee..5785290224 100644 --- a/hw/xilinx_axienet.c +++ b/hw/xilinx_axienet.c @@ -22,12 +22,13 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "qemu/log.h" #include "net/net.h" #include "net/checksum.h" +#include "qapi/qmp/qerror.h" -#include "stream.h" +#include "hw/stream.h" #define DPHY(x) @@ -617,7 +618,7 @@ static const MemoryRegionOps enet_ops = { static int eth_can_rx(NetClientState *nc) { - struct XilinxAXIEnet *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc); /* RX enabled? */ return !axienet_rx_resetting(s) && axienet_rx_enabled(s); @@ -640,7 +641,7 @@ static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1) static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) { - struct XilinxAXIEnet *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc); static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static const unsigned char sa_ipmcast[3] = {0x01, 0x00, 0x52}; @@ -785,7 +786,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) static void eth_cleanup(NetClientState *nc) { /* FIXME. */ - struct XilinxAXIEnet *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc); g_free(s->rxmem); g_free(s); } @@ -826,7 +827,7 @@ axienet_stream_push(StreamSlave *obj, uint8_t *buf, size_t size, uint32_t *hdr) buf[write_off + 1] = csum & 0xff; } - qemu_send_packet(&s->nic->nc, buf, size); + qemu_send_packet(qemu_get_queue(s->nic), buf, size); s->stats.tx_bytes += size; s->regs[R_IS] |= IS_TX_COMPLETE; @@ -853,7 +854,7 @@ static int xilinx_enet_init(SysBusDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); tdk_init(&s->TEMAC.phy); mdio_attach(&s->TEMAC.mdio_bus, &s->TEMAC.phy, s->c_phyaddr); @@ -869,9 +870,11 @@ static int xilinx_enet_init(SysBusDevice *dev) static void xilinx_enet_initfn(Object *obj) { struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj)); + Error *errp = NULL; object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE, - (Object **) &s->tx_dev, NULL); + (Object **) &s->tx_dev, &errp); + assert_no_error(errp); } static Property xilinx_enet_properties[] = { @@ -893,7 +896,7 @@ static void xilinx_enet_class_init(ObjectClass *klass, void *data) ssc->push = axienet_stream_push; } -static TypeInfo xilinx_enet_info = { +static const TypeInfo xilinx_enet_info = { .name = "xlnx.axi-ethernet", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct XilinxAXIEnet), diff --git a/hw/xilinx_ethlite.c b/hw/xilinx_ethlite.c index 4de4a53a0b..b2e35237f8 100644 --- a/hw/xilinx_ethlite.c +++ b/hw/xilinx_ethlite.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#include "sysbus.h" -#include "hw.h" +#include "hw/sysbus.h" +#include "hw/hw.h" #include "net/net.h" #define D(x) @@ -89,7 +89,7 @@ eth_read(void *opaque, hwaddr addr, unsigned int size) case R_RX_CTRL1: case R_RX_CTRL0: r = s->regs[addr]; - D(qemu_log("%s %x=%x\n", __func__, addr * 4, r)); + D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr * 4, r)); break; default: @@ -115,9 +115,10 @@ eth_write(void *opaque, hwaddr addr, if (addr == R_TX_CTRL1) base = 0x800 / 4; - D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value)); + D(qemu_log("%s addr=" TARGET_FMT_plx " val=%x\n", + __func__, addr * 4, value)); if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { - qemu_send_packet(&s->nic->nc, + qemu_send_packet(qemu_get_queue(s->nic), (void *) &s->regs[base], s->regs[base + R_TX_LEN0]); D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0])); @@ -135,12 +136,16 @@ eth_write(void *opaque, hwaddr addr, break; /* Keep these native. */ + case R_RX_CTRL0: + case R_RX_CTRL1: + if (!(value & CTRL_S)) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } case R_TX_LEN0: case R_TX_LEN1: case R_TX_GIE0: - case R_RX_CTRL0: - case R_RX_CTRL1: - D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value)); + D(qemu_log("%s addr=" TARGET_FMT_plx " val=%x\n", + __func__, addr * 4, value)); s->regs[addr] = value; break; @@ -162,15 +167,15 @@ static const MemoryRegionOps eth_ops = { static int eth_can_rx(NetClientState *nc) { - struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque; - int r; - r = !(s->regs[R_RX_CTRL0] & CTRL_S); - return r; + struct xlx_ethlite *s = qemu_get_nic_opaque(nc); + unsigned int rxbase = s->rxbuf * (0x800 / 4); + + return !(s->regs[rxbase + R_RX_CTRL0] & CTRL_S); } static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) { - struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct xlx_ethlite *s = qemu_get_nic_opaque(nc); unsigned int rxbase = s->rxbuf * (0x800 / 4); /* DA filter. */ @@ -182,7 +187,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) return -1; } - D(qemu_log("%s %d rxbase=%x\n", __func__, size, rxbase)); + D(qemu_log("%s %zd rxbase=%x\n", __func__, size, rxbase)); memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size); s->regs[rxbase + R_RX_CTRL0] |= CTRL_S; @@ -196,7 +201,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) static void eth_cleanup(NetClientState *nc) { - struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque; + struct xlx_ethlite *s = qemu_get_nic_opaque(nc); s->nic = NULL; } @@ -223,7 +228,7 @@ static int xilinx_ethlite_init(SysBusDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf, object_get_typename(OBJECT(dev)), dev->qdev.id, s); - qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); return 0; } @@ -243,7 +248,7 @@ static void xilinx_ethlite_class_init(ObjectClass *klass, void *data) dc->props = xilinx_ethlite_properties; } -static TypeInfo xilinx_ethlite_info = { +static const TypeInfo xilinx_ethlite_info = { .name = "xlnx.xps-ethernetlite", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct xlx_ethlite), diff --git a/hw/xilinx_intc.c b/hw/xilinx_intc.c index 7765079802..b106e724ab 100644 --- a/hw/xilinx_intc.c +++ b/hw/xilinx_intc.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#include "sysbus.h" -#include "hw.h" +#include "hw/sysbus.h" +#include "hw/hw.h" #define D(x) @@ -175,7 +175,7 @@ static void xilinx_intc_class_init(ObjectClass *klass, void *data) dc->props = xilinx_intc_properties; } -static TypeInfo xilinx_intc_info = { +static const TypeInfo xilinx_intc_info = { .name = "xlnx.xps-intc", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct xlx_pic), diff --git a/hw/xilinx_spi.c b/hw/xilinx_spi.c index 77f9178008..f6bd3bac23 100644 --- a/hw/xilinx_spi.c +++ b/hw/xilinx_spi.c @@ -24,12 +24,12 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "qemu/log.h" -#include "fifo.h" +#include "qemu/fifo8.h" -#include "ssi.h" +#include "hw/ssi.h" #ifdef XILINX_SPI_ERR_DEBUG #define DB_PRINT(...) do { \ @@ -370,7 +370,7 @@ static void xilinx_spi_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_xilinx_spi; } -static TypeInfo xilinx_spi_info = { +static const TypeInfo xilinx_spi_info = { .name = "xlnx.xps-spi", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(XilinxSPI), diff --git a/hw/xilinx_spips.c b/hw/xilinx_spips.c index 42e019dc05..6c21b9668b 100644 --- a/hw/xilinx_spips.c +++ b/hw/xilinx_spips.c @@ -22,12 +22,12 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "sysemu/sysemu.h" -#include "ptimer.h" +#include "hw/ptimer.h" #include "qemu/log.h" -#include "fifo.h" -#include "ssi.h" +#include "qemu/fifo8.h" +#include "hw/ssi.h" #include "qemu/bitops.h" #ifdef XILINX_SPIPS_ERR_DEBUG diff --git a/hw/xilinx_timer.c b/hw/xilinx_timer.c index 69294bb83c..0c39cff089 100644 --- a/hw/xilinx_timer.c +++ b/hw/xilinx_timer.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#include "sysbus.h" -#include "ptimer.h" +#include "hw/sysbus.h" +#include "hw/ptimer.h" #include "qemu/log.h" #define D(x) @@ -240,7 +240,7 @@ static void xilinx_timer_class_init(ObjectClass *klass, void *data) dc->props = xilinx_timer_properties; } -static TypeInfo xilinx_timer_info = { +static const TypeInfo xilinx_timer_info = { .name = "xlnx.xps-timer", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct timerblock), diff --git a/hw/xilinx_uartlite.c b/hw/xilinx_uartlite.c index abd256ae00..079f4d4e1a 100644 --- a/hw/xilinx_uartlite.c +++ b/hw/xilinx_uartlite.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ -#include "sysbus.h" +#include "hw/sysbus.h" #include "char/char.h" #define DUART(x) @@ -216,7 +216,7 @@ static void xilinx_uartlite_class_init(ObjectClass *klass, void *data) sdc->init = xilinx_uartlite_init; } -static TypeInfo xilinx_uartlite_info = { +static const TypeInfo xilinx_uartlite_info = { .name = "xlnx.xps-uartlite", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof (struct xlx_uartlite), diff --git a/hw/xio3130_downstream.c b/hw/xio3130_downstream.c index 2dcd46bff1..4bccd0ddcd 100644 --- a/hw/xio3130_downstream.c +++ b/hw/xio3130_downstream.c @@ -19,10 +19,10 @@ * with this program; if not, see . */ -#include "pci/pci_ids.h" -#include "pci/msi.h" -#include "pci/pcie.h" -#include "xio3130_downstream.h" +#include "hw/pci/pci_ids.h" +#include "hw/pci/msi.h" +#include "hw/pci/pcie.h" +#include "hw/xio3130_downstream.h" #define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */ #define XIO3130_REVISION 0x1 @@ -193,7 +193,7 @@ static void xio3130_downstream_class_init(ObjectClass *klass, void *data) dc->props = xio3130_downstream_properties; } -static TypeInfo xio3130_downstream_info = { +static const TypeInfo xio3130_downstream_info = { .name = "xio3130-downstream", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIESlot), diff --git a/hw/xio3130_downstream.h b/hw/xio3130_downstream.h index 559dff6565..8426d9ffa6 100644 --- a/hw/xio3130_downstream.h +++ b/hw/xio3130_downstream.h @@ -1,7 +1,7 @@ #ifndef QEMU_XIO3130_DOWNSTREAM_H #define QEMU_XIO3130_DOWNSTREAM_H -#include "pci/pcie_port.h" +#include "hw/pci/pcie_port.h" PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction, const char *bus_name, pci_map_irq_fn map_irq, diff --git a/hw/xio3130_upstream.c b/hw/xio3130_upstream.c index 713caf2dda..82556aaadc 100644 --- a/hw/xio3130_upstream.c +++ b/hw/xio3130_upstream.c @@ -19,10 +19,10 @@ * with this program; if not, see . */ -#include "pci/pci_ids.h" -#include "pci/msi.h" -#include "pci/pcie.h" -#include "xio3130_upstream.h" +#include "hw/pci/pci_ids.h" +#include "hw/pci/msi.h" +#include "hw/pci/pcie.h" +#include "hw/xio3130_upstream.h" #define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */ #define XIO3130_REVISION 0x2 @@ -167,7 +167,7 @@ static void xio3130_upstream_class_init(ObjectClass *klass, void *data) dc->props = xio3130_upstream_properties; } -static TypeInfo xio3130_upstream_info = { +static const TypeInfo xio3130_upstream_info = { .name = "x3130-upstream", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIEPort), diff --git a/hw/xio3130_upstream.h b/hw/xio3130_upstream.h index fa09656b35..08c1d5f75b 100644 --- a/hw/xio3130_upstream.h +++ b/hw/xio3130_upstream.h @@ -1,7 +1,7 @@ #ifndef QEMU_XIO3130_UPSTREAM_H #define QEMU_XIO3130_UPSTREAM_H -#include "pci/pcie_port.h" +#include "hw/pci/pcie_port.h" PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction, const char *bus_name, pci_map_irq_fn map_irq, diff --git a/hw/xtensa/Makefile.objs b/hw/xtensa/Makefile.objs index 79698e903d..6ead7820c4 100644 --- a/hw/xtensa/Makefile.objs +++ b/hw/xtensa/Makefile.objs @@ -1,5 +1,3 @@ -obj-y += xtensa_pic.o +obj-y += pic_cpu.o obj-y += xtensa_sim.o obj-y += xtensa_lx60.o - -obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/xtensa_pic.c b/hw/xtensa/pic_cpu.c similarity index 95% rename from hw/xtensa_pic.c rename to hw/xtensa/pic_cpu.c index 97d36be272..7f015ff5ab 100644 --- a/hw/xtensa_pic.c +++ b/hw/xtensa/pic_cpu.c @@ -25,7 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "hw.h" +#include "hw/hw.h" #include "qemu/log.h" #include "qemu/timer.h" @@ -47,6 +47,7 @@ void xtensa_advance_ccount(CPUXtensaState *env, uint32_t d) void check_interrupts(CPUXtensaState *env) { + CPUState *cs = CPU(xtensa_env_get_cpu(env)); int minlevel = xtensa_get_cintlevel(env); uint32_t int_set_enabled = env->sregs[INTSET] & env->sregs[INTENABLE]; int level; @@ -54,7 +55,7 @@ void check_interrupts(CPUXtensaState *env) /* If the CPU is halted advance CCOUNT according to the vm_clock time * elapsed since the moment when it was advanced last time. */ - if (env->halted) { + if (cs->halted) { int64_t now = qemu_get_clock_ns(vm_clock); xtensa_advance_ccount(env, @@ -65,7 +66,7 @@ void check_interrupts(CPUXtensaState *env) for (level = env->config->nlevel; level > minlevel; --level) { if (env->config->level_mask[level] & int_set_enabled) { env->pending_irq_level = level; - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, CPU_INTERRUPT_HARD); qemu_log_mask(CPU_LOG_INT, "%s level = %d, cintlevel = %d, " "pc = %08x, a0 = %08x, ps = %08x, " @@ -79,7 +80,7 @@ void check_interrupts(CPUXtensaState *env) } } env->pending_irq_level = 0; - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } static void xtensa_set_irq(void *opaque, int irq, int active) @@ -127,11 +128,12 @@ static void xtensa_ccompare_cb(void *opaque) { XtensaCPU *cpu = opaque; CPUXtensaState *env = &cpu->env; + CPUState *cs = CPU(cpu); - if (env->halted) { + if (cs->halted) { env->halt_clock = qemu_get_clock_ns(vm_clock); xtensa_advance_ccount(env, env->wake_ccount - env->sregs[CCOUNT]); - if (!cpu_has_work(CPU(cpu))) { + if (!cpu_has_work(cs)) { env->sregs[CCOUNT] = env->wake_ccount + 1; xtensa_rearm_ccompare_timer(env); } diff --git a/hw/xtensa_lx60.c b/hw/xtensa/xtensa_lx60.c similarity index 97% rename from hw/xtensa_lx60.c rename to hw/xtensa/xtensa_lx60.c index 0b9a52851a..f2a63d82da 100644 --- a/hw/xtensa_lx60.c +++ b/hw/xtensa/xtensa_lx60.c @@ -26,18 +26,18 @@ */ #include "sysemu/sysemu.h" -#include "boards.h" -#include "loader.h" +#include "hw/boards.h" +#include "hw/loader.h" #include "elf.h" #include "exec/memory.h" #include "exec/address-spaces.h" -#include "serial.h" +#include "hw/serial.h" #include "net/net.h" -#include "sysbus.h" -#include "flash.h" +#include "hw/sysbus.h" +#include "hw/flash.h" #include "sysemu/blockdev.h" #include "char/char.h" -#include "xtensa_bootparam.h" +#include "hw/xtensa_bootparam.h" typedef struct LxBoardDesc { size_t flash_size; @@ -131,7 +131,7 @@ static void lx60_net_init(MemoryRegion *address_space, qdev_set_nic_properties(dev, nd); qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); + s = SYS_BUS_DEVICE(dev); sysbus_connect_irq(s, 0, irq); memory_region_add_subregion(address_space, base, sysbus_mmio_get_region(s, 0)); @@ -295,6 +295,7 @@ static QEMUMachine xtensa_lx60_machine = { .desc = "lx60 EVB (" XTENSA_DEFAULT_CPU_MODEL ")", .init = xtensa_lx60_init, .max_cpus = 4, + DEFAULT_MACHINE_OPTIONS, }; static QEMUMachine xtensa_lx200_machine = { @@ -302,6 +303,7 @@ static QEMUMachine xtensa_lx200_machine = { .desc = "lx200 EVB (" XTENSA_DEFAULT_CPU_MODEL ")", .init = xtensa_lx200_init, .max_cpus = 4, + DEFAULT_MACHINE_OPTIONS, }; static void xtensa_lx_machines_init(void) diff --git a/hw/xtensa_sim.c b/hw/xtensa/xtensa_sim.c similarity index 98% rename from hw/xtensa_sim.c rename to hw/xtensa/xtensa_sim.c index 14fe85b2fc..5241f8d734 100644 --- a/hw/xtensa_sim.c +++ b/hw/xtensa/xtensa_sim.c @@ -26,8 +26,8 @@ */ #include "sysemu/sysemu.h" -#include "boards.h" -#include "loader.h" +#include "hw/boards.h" +#include "hw/loader.h" #include "elf.h" #include "exec/memory.h" #include "exec/address-spaces.h" @@ -106,6 +106,7 @@ static QEMUMachine xtensa_sim_machine = { .is_default = true, .init = xtensa_sim_init, .max_cpus = 4, + DEFAULT_MACHINE_OPTIONS, }; static void xtensa_sim_machine_init(void) diff --git a/hw/zaurus.c b/hw/zaurus.c index d77b34ecce..7d3258cc66 100644 --- a/hw/zaurus.c +++ b/hw/zaurus.c @@ -15,9 +15,9 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" -#include "sharpsl.h" -#include "sysbus.h" +#include "hw/hw.h" +#include "hw/sharpsl.h" +#include "hw/sysbus.h" #undef REG_FMT #define REG_FMT "0x%02lx" @@ -236,7 +236,7 @@ static void scoop_sysbus_class_init(ObjectClass *klass, void *data) dc->props = scoop_sysbus_properties; } -static TypeInfo scoop_sysbus_info = { +static const TypeInfo scoop_sysbus_info = { .name = "scoop", .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ScoopInfo), diff --git a/hw/zynq_slcr.c b/hw/zynq_slcr.c index 143a7cf436..8418327261 100644 --- a/hw/zynq_slcr.c +++ b/hw/zynq_slcr.c @@ -14,9 +14,9 @@ * with this program; if not, see . */ -#include "hw.h" +#include "hw/hw.h" #include "qemu/timer.h" -#include "sysbus.h" +#include "hw/sysbus.h" #include "sysemu/sysemu.h" #ifdef ZYNQ_ARM_SLCR_ERR_DEBUG @@ -160,7 +160,7 @@ static void zynq_slcr_reset(DeviceState *d) { int i; ZynqSLCRState *s = - FROM_SYSBUS(ZynqSLCRState, sysbus_from_qdev(d)); + FROM_SYSBUS(ZynqSLCRState, SYS_BUS_DEVICE(d)); DB_PRINT("RESET\n"); @@ -521,7 +521,7 @@ static void zynq_slcr_class_init(ObjectClass *klass, void *data) dc->reset = zynq_slcr_reset; } -static TypeInfo zynq_slcr_info = { +static const TypeInfo zynq_slcr_info = { .class_init = zynq_slcr_class_init, .name = "xilinx,zynq_slcr", .parent = TYPE_SYS_BUS_DEVICE, diff --git a/include/block/aio.h b/include/block/aio.h index 0933f05878..183679374f 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -63,6 +63,12 @@ typedef struct AioContext { /* Used for aio_notify. */ EventNotifier notifier; + + /* GPollFDs for aio_poll() */ + GArray *pollfds; + + /* Thread pool for performing work and receiving completion callbacks */ + struct ThreadPool *thread_pool; } AioContext; /* Returns 1 if there are still outstanding AIO requests; 0 otherwise */ @@ -173,16 +179,14 @@ bool aio_pending(AioContext *ctx); * aio as a result of executing I/O completion or bh callbacks. * * If there is no pending AIO operation or completion (bottom half), - * return false. If there are pending bottom halves, return true. + * return false. If there are pending AIO operations of bottom halves, + * return true. * * If there are no pending bottom halves, but there are pending AIO * operations, it may not be possible to make any progress without * blocking. If @blocking is true, this function will wait until one * or more AIO events have completed, to ensure something has moved * before returning. - * - * If @blocking is false, this function will also return false if the - * function cannot make any progress without blocking. */ bool aio_poll(AioContext *ctx, bool blocking); @@ -222,6 +226,9 @@ void aio_set_event_notifier(AioContext *ctx, */ GSource *aio_get_g_source(AioContext *ctx); +/* Return the ThreadPool bound to this AioContext */ +struct ThreadPool *aio_get_thread_pool(AioContext *ctx); + /* Functions to operate on the main QEMU AioContext. */ bool qemu_aio_wait(void); diff --git a/include/block/block.h b/include/block/block.h index b81d200b03..d4f34d6462 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -24,6 +24,7 @@ typedef struct BlockFragInfo { uint64_t allocated_clusters; uint64_t total_clusters; uint64_t fragmented_clusters; + uint64_t compressed_clusters; } BlockFragInfo; typedef struct QEMUSnapshotInfo { @@ -83,6 +84,7 @@ typedef struct BlockDevOps { #define BDRV_O_INCOMING 0x0800 /* consistency hint for incoming migration */ #define BDRV_O_CHECK 0x1000 /* open solely for consistency check */ #define BDRV_O_ALLOW_RDWR 0x2000 /* allow reopen to change from r/o to r/w */ +#define BDRV_O_UNMAP 0x4000 /* execute guest UNMAP/TRIM operations */ #define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH) @@ -132,10 +134,11 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old); void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top); void bdrv_delete(BlockDriverState *bs); int bdrv_parse_cache_flags(const char *mode, int *flags); +int bdrv_parse_discard_flags(const char *mode, int *flags); int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags); int bdrv_open_backing_file(BlockDriverState *bs); -int bdrv_open(BlockDriverState *bs, const char *filename, int flags, - BlockDriver *drv); +int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, + int flags, BlockDriver *drv); BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, int flags); int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); @@ -213,6 +216,7 @@ typedef struct BdrvCheckResult { int check_errors; int corruptions_fixed; int leaks_fixed; + int64_t image_end_offset; BlockFragInfo bfi; } BdrvCheckResult; @@ -278,6 +282,8 @@ int bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors); int bdrv_has_zero_init(BlockDriverState *bs); int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum); +int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, + int64_t sector_num, int nb_sectors, int *pnum); void bdrv_set_on_error(BlockDriverState *bs, BlockdevOnError on_read_error, BlockdevOnError on_write_error); @@ -309,6 +315,10 @@ int bdrv_get_flags(BlockDriverState *bs); int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); +void bdrv_round_to_clusters(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + int64_t *cluster_sector_num, + int *cluster_nb_sectors); const char *bdrv_get_encrypted_filename(BlockDriverState *bs); void bdrv_get_backing_filename(BlockDriverState *bs, @@ -345,18 +355,19 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, void bdrv_img_create(const char *filename, const char *fmt, const char *base_filename, const char *base_fmt, - char *options, uint64_t img_size, int flags, Error **errp); + char *options, uint64_t img_size, int flags, + Error **errp, bool quiet); void bdrv_set_buffer_alignment(BlockDriverState *bs, int align); void *qemu_blockalign(BlockDriverState *bs, size_t size); +bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov); -#define BDRV_SECTORS_PER_DIRTY_CHUNK 2048 - -void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable); +struct HBitmapIter; +void bdrv_set_dirty_tracking(BlockDriverState *bs, int granularity); int bdrv_get_dirty(BlockDriverState *bs, int64_t sector); void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors); void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors); -int64_t bdrv_get_next_dirty(BlockDriverState *bs, int64_t sector); +void bdrv_dirty_iter_init(BlockDriverState *bs, struct HBitmapIter *hbi); int64_t bdrv_get_dirty_count(BlockDriverState *bs); void bdrv_enable_copy_on_read(BlockDriverState *bs); @@ -365,6 +376,15 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs); void bdrv_set_in_use(BlockDriverState *bs, int in_use); int bdrv_in_use(BlockDriverState *bs); +#ifdef CONFIG_LINUX_AIO +int raw_get_aio_fd(BlockDriverState *bs); +#else +static inline int raw_get_aio_fd(BlockDriverState *bs) +{ + return -ENOTSUP; +} +#endif + enum BlockAcctType { BDRV_ACCT_READ, BDRV_ACCT_WRITE, diff --git a/include/block/block_int.h b/include/block/block_int.h index f83ffb8a08..ce0aa26b8e 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -32,6 +32,7 @@ #include "qapi-types.h" #include "qapi/qmp/qerror.h" #include "monitor/monitor.h" +#include "qemu/hbitmap.h" #define BLOCK_FLAG_ENCRYPT 1 #define BLOCK_FLAG_COMPAT6 4 @@ -55,6 +56,7 @@ #define BLOCK_OPT_SUBFMT "subformat" #define BLOCK_OPT_COMPAT_LEVEL "compat" #define BLOCK_OPT_LAZY_REFCOUNTS "lazy_refcounts" +#define BLOCK_OPT_ADAPTER_TYPE "adapter_type" typedef struct BdrvTrackedRequest BdrvTrackedRequest; @@ -80,7 +82,7 @@ struct BlockDriver { void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state); void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state); - int (*bdrv_open)(BlockDriverState *bs, int flags); + int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags); int (*bdrv_file_open)(BlockDriverState *bs, const char *filename, int flags); int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors); @@ -275,8 +277,7 @@ struct BlockDriverState { bool iostatus_enabled; BlockDeviceIoStatus iostatus; char device_name[32]; - unsigned long *dirty_bitmap; - int64_t dirty_count; + HBitmap *dirty_bitmap; int in_use; /* users other than guest access, eg. block migration */ QTAILQ_ENTRY(BlockDriverState) list; @@ -285,6 +286,7 @@ struct BlockDriverState { /* long-running background operation */ BlockJob *job; + QDict *options; }; int get_tmp_filename(char *filename, int size); @@ -292,6 +294,13 @@ int get_tmp_filename(char *filename, int size); void bdrv_set_io_limits(BlockDriverState *bs, BlockIOLimit *io_limits); +/** + * bdrv_get_aio_context: + * + * Returns: the currently bound #AioContext + */ +AioContext *bdrv_get_aio_context(BlockDriverState *bs); + #ifdef _WIN32 int is_windows_drive(const char *filename); #endif @@ -344,6 +353,8 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base, * @bs: Block device to operate on. * @target: Block device to write to. * @speed: The maximum speed, in bytes per second, or 0 for unlimited. + * @granularity: The chosen granularity for the dirty bitmap. + * @buf_size: The amount of data that can be in flight at one time. * @mode: Whether to collapse all images in the chain to the target. * @on_source_error: The action to take upon error reading from the source. * @on_target_error: The action to take upon error writing to the target. @@ -357,8 +368,8 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base, * @bs will be switched to read from @target. */ void mirror_start(BlockDriverState *bs, BlockDriverState *target, - int64_t speed, MirrorSyncMode mode, - BlockdevOnError on_source_error, + int64_t speed, int64_t granularity, int64_t buf_size, + MirrorSyncMode mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, BlockDriverCompletionFunc *cb, void *opaque, Error **errp); diff --git a/include/block/coroutine.h b/include/block/coroutine.h index c31fae3f3c..a978162a3f 100644 --- a/include/block/coroutine.h +++ b/include/block/coroutine.h @@ -104,6 +104,7 @@ bool qemu_in_coroutine(void); */ typedef struct CoQueue { QTAILQ_HEAD(, Coroutine) entries; + AioContext *ctx; } CoQueue; /** diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h index 200703e35f..32afcdd1d6 100644 --- a/include/block/thread-pool.h +++ b/include/block/thread-pool.h @@ -26,9 +26,16 @@ typedef int ThreadPoolFunc(void *opaque); -BlockDriverAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, - BlockDriverCompletionFunc *cb, void *opaque); -int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg); -void thread_pool_submit(ThreadPoolFunc *func, void *arg); +typedef struct ThreadPool ThreadPool; + +ThreadPool *thread_pool_new(struct AioContext *ctx); +void thread_pool_free(ThreadPool *pool); + +BlockDriverAIOCB *thread_pool_submit_aio(ThreadPool *pool, + ThreadPoolFunc *func, void *arg, + BlockDriverCompletionFunc *cb, void *opaque); +int coroutine_fn thread_pool_submit_co(ThreadPool *pool, + ThreadPoolFunc *func, void *arg); +void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg); #endif diff --git a/include/char/char.h b/include/char/char.h index baa5d035fd..d6a03513bf 100644 --- a/include/char/char.h +++ b/include/char/char.h @@ -56,6 +56,7 @@ typedef void IOEventHandler(void *opaque, int event); struct CharDriverState { void (*init)(struct CharDriverState *s); int (*chr_write)(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); int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg); int (*get_msgfd)(struct CharDriverState *s); @@ -70,11 +71,12 @@ struct CharDriverState { void (*chr_guest_open)(struct CharDriverState *chr); void (*chr_guest_close)(struct CharDriverState *chr); void *opaque; - QEMUTimer *open_timer; + int idle_tag; char *label; char *filename; int opened; int avail_connections; + QemuOpts *opts; QTAILQ_ENTRY(CharDriverState) next; }; @@ -89,7 +91,8 @@ struct CharDriverState { * Returns: a new character backend */ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, - void (*init)(struct CharDriverState *s)); + void (*init)(struct CharDriverState *s), + Error **errp); /** * @qemu_chr_new: @@ -150,6 +153,9 @@ void qemu_chr_fe_close(struct CharDriverState *chr); void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, + GIOFunc func, void *user_data); + /** * @qemu_chr_fe_write: * @@ -238,6 +244,10 @@ CharDriverState *qemu_chr_find(const char *name); QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename); +void register_char_driver(const char *name, CharDriverState *(*open)(QemuOpts *)); +void register_char_driver_qapi(const char *name, int kind, + void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp)); + /* add an eventfd to the qemu devices that are polled */ CharDriverState *qemu_chr_open_eventfd(int eventfd); @@ -251,4 +261,10 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr); CharDriverState *qemu_char_get_next_serial(void); +/* msmouse */ +CharDriverState *qemu_chr_open_msmouse(void); + +/* baum.c */ +CharDriverState *chr_baum_init(void); + #endif diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 439e88deb4..e9c3717863 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -354,7 +354,6 @@ int page_check_range(target_ulong start, target_ulong len, int flags); #endif CPUArchState *cpu_copy(CPUArchState *env); -CPUArchState *qemu_get_cpu(int cpu); #define CPU_DUMP_CODE 0x00010000 #define CPU_DUMP_FPU 0x00020000 /* dump FPU register state, not just integer */ @@ -422,21 +421,6 @@ DECLARE_TLS(CPUArchState *,cpu_single_env); | CPU_INTERRUPT_TGT_EXT_3 \ | CPU_INTERRUPT_TGT_EXT_4) -#ifndef CONFIG_USER_ONLY -typedef void (*CPUInterruptHandler)(CPUArchState *, int); - -extern CPUInterruptHandler cpu_interrupt_handler; - -static inline void cpu_interrupt(CPUArchState *s, int mask) -{ - cpu_interrupt_handler(s, mask); -} -#else /* USER_ONLY */ -void cpu_interrupt(CPUArchState *env, int mask); -#endif /* USER_ONLY */ - -void cpu_reset_interrupt(CPUArchState *env, int mask); - void cpu_exit(CPUArchState *s); /* Breakpoint/watchpoint flags */ diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 4d4f8d4e98..2e5f11f47f 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -12,6 +12,18 @@ #include "qemu/bswap.h" #include "qemu/queue.h" +/** + * CPUListState: + * @cpu_fprintf: Print function. + * @file: File to print to using @cpu_fprint. + * + * State commonly used for iterating over CPU models. + */ +typedef struct CPUListState { + fprintf_function cpu_fprintf; + FILE *file; +} CPUListState; + #if !defined(CONFIG_USER_ONLY) enum device_endian { diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index aea0ece051..0ae967ae20 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -26,7 +26,6 @@ #include "config.h" #include #include -#include #include "qemu/osdep.h" #include "qemu/queue.h" #include "exec/hwaddr.h" @@ -134,10 +133,6 @@ typedef struct icount_decr_u16 { } icount_decr_u16; #endif -struct kvm_run; -struct KVMState; -struct qemu_work_item; - typedef struct CPUBreakpoint { target_ulong pc; int flags; /* BP_* */ @@ -153,7 +148,6 @@ typedef struct CPUWatchpoint { #define CPU_TEMP_BUF_NLONGS 128 #define CPU_COMMON \ - struct TranslationBlock *current_tb; /* currently executing TB */ \ /* soft mmu support */ \ /* in order to avoid passing too many arguments to the MMIO \ helpers, we store some rarely used information in the CPU \ @@ -162,9 +156,6 @@ typedef struct CPUWatchpoint { accessed */ \ target_ulong mem_io_vaddr; /* target virtual addr at which the \ memory was accessed */ \ - uint32_t halted; /* Nonzero if the CPU is in suspend state */ \ - uint32_t interrupt_request; \ - volatile sig_atomic_t exit_request; \ CPU_COMMON_TLB \ struct TranslationBlock *tb_jmp_cache[TB_JMP_CACHE_SIZE]; \ /* buffer for temporaries in the code generator */ \ @@ -191,23 +182,13 @@ typedef struct CPUWatchpoint { struct GDBRegisterState *gdb_regs; \ \ /* Core interrupt code */ \ - jmp_buf jmp_env; \ + sigjmp_buf jmp_env; \ int exception_index; \ \ CPUArchState *next_cpu; /* next CPU sharing TB cache */ \ - int cpu_index; /* CPU index (informative) */ \ - uint32_t host_tid; /* host thread ID */ \ - int numa_node; /* NUMA node this cpu is belonging to */ \ - int nr_cores; /* number of cores within this CPU package */ \ - int nr_threads;/* number of threads within this CPU */ \ - int running; /* Nonzero if cpu is currently running(usermode). */ \ /* user data */ \ void *opaque; \ \ - const char *cpu_model_str; \ - struct KVMState *kvm_state; \ - struct kvm_run *kvm_run; \ - int kvm_fd; \ - int kvm_vcpu_dirty; + const char *cpu_model_str; #endif diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 46dca74fda..e856191e40 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -168,6 +168,25 @@ struct TranslationBlock { uint32_t icount; }; +#include "exec/spinlock.h" + +typedef struct TBContext TBContext; + +struct TBContext { + + TranslationBlock *tbs; + TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; + int nb_tbs; + /* any access to the tbs or the page table must use this lock */ + spinlock_t tb_lock; + + /* statistics */ + int tb_flush_count; + int tb_phys_invalidate_count; + + int tb_invalidated_flag; +}; + static inline unsigned int tb_jmp_cache_hash_page(target_ulong pc) { target_ulong tmp; @@ -192,8 +211,6 @@ void tb_free(TranslationBlock *tb); void tb_flush(CPUArchState *env); void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); -extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; - #if defined(USE_DIRECT_JUMP) #if defined(CONFIG_TCG_INTERPRETER) @@ -275,23 +292,11 @@ static inline void tb_add_jump(TranslationBlock *tb, int n, } } -#include "exec/spinlock.h" - -extern spinlock_t tb_lock; - -extern int tb_invalidated_flag; - /* The return address may point to the start of the next instruction. Subtracting one gets us the call instruction itself. */ #if defined(CONFIG_TCG_INTERPRETER) -/* Softmmu, Alpha, MIPS, SH4 and SPARC user mode emulations call GETPC(). - For all others, GETPC remains undefined (which makes TCI a little faster. */ -# if defined(CONFIG_SOFTMMU) || \ - defined(TARGET_ALPHA) || defined(TARGET_MIPS) || \ - defined(TARGET_SH4) || defined(TARGET_SPARC) extern uintptr_t tci_tb_ptr; -# define GETPC() tci_tb_ptr -# endif +# define GETPC() tci_tb_ptr #elif defined(__s390__) && !defined(__s390x__) # define GETPC() \ (((uintptr_t)__builtin_return_address(0) & 0x7fffffffUL) - 1) @@ -399,11 +404,13 @@ extern volatile sig_atomic_t exit_request; instruction of a TB so that interrupts take effect immediately. */ static inline int can_do_io(CPUArchState *env) { + CPUState *cpu = ENV_GET_CPU(env); + if (!use_icount) { return 1; } /* If not executing code then assume we are ok. */ - if (!env->current_tb) { + if (cpu->current_tb == NULL) { return 1; } return env->can_do_io != 0; diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 668de66000..ba20afa091 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -30,12 +30,12 @@ void gdb_register_coprocessor(CPUArchState *env, gdb_reg_cb get_reg, gdb_reg_cb set_reg, int num_regs, const char *xml, int g_pos); -static inline int cpu_index(CPUArchState *env) +static inline int cpu_index(CPUState *cpu) { #if defined(CONFIG_USER_ONLY) && defined(CONFIG_USE_NPTL) - return env->host_tid; + return cpu->host_tid; #else - return env->cpu_index + 1; + return cpu->cpu_index + 1; #endif } diff --git a/include/exec/gen-icount.h b/include/exec/gen-icount.h index 8043b3ba26..4fc7b2981d 100644 --- a/include/exec/gen-icount.h +++ b/include/exec/gen-icount.h @@ -7,10 +7,19 @@ static TCGArg *icount_arg; static int icount_label; +static int exitreq_label; -static inline void gen_icount_start(void) +static inline void gen_tb_start(void) { TCGv_i32 count; + TCGv_i32 flag; + + exitreq_label = gen_new_label(); + flag = tcg_temp_new_i32(); + tcg_gen_ld_i32(flag, cpu_env, + offsetof(CPUState, tcg_exit_req) - ENV_OFFSET); + tcg_gen_brcondi_i32(TCG_COND_NE, flag, 0, exitreq_label); + tcg_temp_free_i32(flag); if (!use_icount) return; @@ -27,12 +36,15 @@ static inline void gen_icount_start(void) tcg_temp_free_i32(count); } -static void gen_icount_end(TranslationBlock *tb, int num_insns) +static void gen_tb_end(TranslationBlock *tb, int num_insns) { + gen_set_label(exitreq_label); + tcg_gen_exit_tb((tcg_target_long)tb + TB_EXIT_REQUESTED); + if (use_icount) { *icount_arg = num_insns; gen_set_label(icount_label); - tcg_gen_exit_tb((tcg_target_long)tb + 2); + tcg_gen_exit_tb((tcg_target_long)tb + TB_EXIT_ICOUNT_EXPIRED); } } diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 0946f0739d..f3927e2419 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -237,6 +237,7 @@ float64 int64_to_float64( int64 STATUS_PARAM ); float64 uint64_to_float64( uint64 STATUS_PARAM ); floatx80 int64_to_floatx80( int64 STATUS_PARAM ); float128 int64_to_float128( int64 STATUS_PARAM ); +float128 uint64_to_float128( uint64 STATUS_PARAM ); /*---------------------------------------------------------------------------- | Software half-precision conversion routines. @@ -630,6 +631,8 @@ INLINE int float128_is_any_nan(float128 a) ((a.low != 0) || ((a.high & 0xffffffffffffLL) != 0)); } +#define float128_zero make_float128(0, 0) + /*---------------------------------------------------------------------------- | The pattern for a default generated quadruple-precision NaN. *----------------------------------------------------------------------------*/ diff --git a/include/libfdt_env.h b/include/libfdt_env.h index 7938d73fae..3667d4cb3a 100644 --- a/include/libfdt_env.h +++ b/include/libfdt_env.h @@ -22,15 +22,15 @@ #include "qemu/bswap.h" #ifdef HOST_WORDS_BIGENDIAN -#define fdt32_to_cpu(x) (x) -#define cpu_to_fdt32(x) (x) -#define fdt64_to_cpu(x) (x) -#define cpu_to_fdt64(x) (x) +#define fdt32_to_cpu(x) (x) +#define cpu_to_fdt32(x) (x) +#define fdt64_to_cpu(x) (x) +#define cpu_to_fdt64(x) (x) #else -#define fdt32_to_cpu(x) (bswap_32((x))) -#define cpu_to_fdt32(x) (bswap_32((x))) -#define fdt64_to_cpu(x) (bswap_64((x))) -#define cpu_to_fdt64(x) (bswap_64((x))) +#define fdt32_to_cpu(x) bswap32(x) +#define cpu_to_fdt32(x) bswap32(x) +#define fdt64_to_cpu(x) bswap64(x) +#define cpu_to_fdt64(x) bswap64(x) #endif #endif /* _LIBFDT_ENV_H */ diff --git a/include/migration/migration.h b/include/migration/migration.h index 2d5b630cce..bb617fdacf 100644 --- a/include/migration/migration.h +++ b/include/migration/migration.h @@ -34,27 +34,19 @@ struct MigrationState int64_t bandwidth_limit; size_t bytes_xfer; size_t xfer_limit; - uint8_t *buffer; - size_t buffer_size; - size_t buffer_capacity; QemuThread thread; - + QEMUBH *cleanup_bh; QEMUFile *file; - int fd; + int state; - int (*get_error)(MigrationState *s); - int (*close)(MigrationState *s); - int (*write)(MigrationState *s, const void *buff, size_t size); - void *opaque; MigrationParams params; int64_t total_time; int64_t downtime; int64_t expected_downtime; int64_t dirty_pages_rate; + int64_t dirty_bytes_rate; bool enabled_capabilities[MIGRATION_CAPABILITY_MAX]; int64_t xbzrle_cache_size; - bool complete; - bool first_time; }; void process_incoming_migration(QEMUFile *f); @@ -87,8 +79,6 @@ void migrate_fd_error(MigrationState *s); void migrate_fd_connect(MigrationState *s); -ssize_t migrate_fd_put_buffer(MigrationState *s, const void *data, - size_t size); int migrate_fd_close(MigrationState *s); void add_migration_state_change_notifier(Notifier *notify); diff --git a/include/migration/page_cache.h b/include/migration/page_cache.h index 3839ac7726..87894fea9f 100644 --- a/include/migration/page_cache.h +++ b/include/migration/page_cache.h @@ -57,7 +57,8 @@ bool cache_is_cached(const PageCache *cache, uint64_t addr); uint8_t *get_cached_data(const PageCache *cache, uint64_t addr); /** - * cache_insert: insert the page into the cache. the previous value will be overwritten + * cache_insert: insert the page into the cache. the page cache + * will dup the data on insert. the previous value will be overwritten * * @cache pointer to the PageCache struct * @addr: page address diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h index 68deefbcfb..df812617f8 100644 --- a/include/migration/qemu-file.h +++ b/include/migration/qemu-file.h @@ -51,36 +51,21 @@ typedef int (QEMUFileCloseFunc)(void *opaque); */ typedef int (QEMUFileGetFD)(void *opaque); -/* Called to determine if the file has exceeded its bandwidth allocation. The - * bandwidth capping is a soft limit, not a hard limit. - */ -typedef int (QEMUFileRateLimit)(void *opaque); - -/* Called to change the current bandwidth allocation. This function must return - * the new actual bandwidth. It should be new_rate if everything goes ok, and - * the old rate otherwise - */ -typedef int64_t (QEMUFileSetRateLimit)(void *opaque, int64_t new_rate); -typedef int64_t (QEMUFileGetRateLimit)(void *opaque); - typedef struct QEMUFileOps { QEMUFilePutBufferFunc *put_buffer; QEMUFileGetBufferFunc *get_buffer; QEMUFileCloseFunc *close; QEMUFileGetFD *get_fd; - QEMUFileRateLimit *rate_limit; - QEMUFileSetRateLimit *set_rate_limit; - QEMUFileGetRateLimit *get_rate_limit; } QEMUFileOps; QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops); QEMUFile *qemu_fopen(const char *filename, const char *mode); QEMUFile *qemu_fdopen(int fd, const char *mode); -QEMUFile *qemu_fopen_socket(int fd); -QEMUFile *qemu_popen(FILE *popen_file, const char *mode); +QEMUFile *qemu_fopen_socket(int fd, const char *mode); QEMUFile *qemu_popen_cmd(const char *command, const char *mode); int qemu_get_fd(QEMUFile *f); int qemu_fclose(QEMUFile *f); +int64_t qemu_ftell(QEMUFile *f); void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); void qemu_put_byte(QEMUFile *f, int v); @@ -109,7 +94,8 @@ unsigned int qemu_get_be32(QEMUFile *f); uint64_t qemu_get_be64(QEMUFile *f); int qemu_file_rate_limit(QEMUFile *f); -int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate); +void qemu_file_reset_rate_limit(QEMUFile *f); +void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate); int64_t qemu_file_get_rate_limit(QEMUFile *f); int qemu_file_get_error(QEMUFile *f); diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index f27276c2d8..6666d27b25 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -26,19 +26,34 @@ #ifndef QEMU_VMSTATE_H #define QEMU_VMSTATE_H 1 +#include + typedef void SaveStateHandler(QEMUFile *f, void *opaque); typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); typedef struct SaveVMHandlers { + /* This runs inside the iothread lock. */ void (*set_params)(const MigrationParams *params, void * opaque); SaveStateHandler *save_state; - int (*save_live_setup)(QEMUFile *f, void *opaque); - int (*save_live_iterate)(QEMUFile *f, void *opaque); - int (*save_live_complete)(QEMUFile *f, void *opaque); - uint64_t (*save_live_pending)(QEMUFile *f, void *opaque, uint64_t max_size); + void (*cancel)(void *opaque); - LoadStateHandler *load_state; + int (*save_live_complete)(QEMUFile *f, void *opaque); + + /* This runs both outside and inside the iothread lock. */ bool (*is_active)(void *opaque); + + /* This runs outside the iothread lock in the migration case, and + * within the lock in the savevm case. The callback had better only + * use data that is local to the migration thread or protected + * by other locks. + */ + int (*save_live_iterate)(QEMUFile *f, void *opaque); + + /* This runs outside the iothread lock! */ + int (*save_live_setup)(QEMUFile *f, void *opaque); + uint64_t (*save_live_pending)(QEMUFile *f, void *opaque, uint64_t max_size); + + LoadStateHandler *load_state; } SaveVMHandlers; int register_savevm(DeviceState *dev, @@ -119,6 +134,10 @@ struct VMStateDescription { const VMStateSubsection *subsections; }; +#ifdef CONFIG_USER_ONLY +extern const VMStateDescription vmstate_dummy; +#endif + extern const VMStateInfo vmstate_info_bool; extern const VMStateInfo vmstate_info_int8; @@ -623,12 +642,20 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id); void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque); -int vmstate_register(DeviceState *dev, int instance_id, - const VMStateDescription *vmsd, void *base); + int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, const VMStateDescription *vmsd, void *base, int alias_id, int required_for_version); + +static inline int vmstate_register(DeviceState *dev, int instance_id, + const VMStateDescription *vmsd, + void *opaque) +{ + return vmstate_register_with_alias_id(dev, instance_id, vmsd, + opaque, -1, 0); +} + void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, void *opaque); diff --git a/hw/qdev-monitor.h b/include/monitor/qdev.h similarity index 63% rename from hw/qdev-monitor.h rename to include/monitor/qdev.h index fae1b1ec84..8d16e119d3 100644 --- a/hw/qdev-monitor.h +++ b/include/monitor/qdev.h @@ -1,15 +1,14 @@ #ifndef QEMU_QDEV_MONITOR_H #define QEMU_QDEV_MONITOR_H -#include "qdev-core.h" +#include "hw/qdev-core.h" #include "monitor/monitor.h" /*** monitor commands ***/ -void do_info_qtree(Monitor *mon); -void do_info_qdm(Monitor *mon); +void do_info_qtree(Monitor *mon, const QDict *qdict); +void do_info_qdm(Monitor *mon, const QDict *qdict); int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data); -int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data); int qdev_device_help(QemuOpts *opts); DeviceState *qdev_device_add(QemuOpts *opts); diff --git a/include/net/net.h b/include/net/net.h index de42dd76da..cb049a16a3 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -9,24 +9,32 @@ #include "migration/vmstate.h" #include "qapi-types.h" +#define MAX_QUEUE_NUM 1024 + struct MACAddr { uint8_t a[6]; }; /* qdev nic properties */ +typedef struct NICPeers { + NetClientState *ncs[MAX_QUEUE_NUM]; +} NICPeers; + typedef struct NICConf { MACAddr macaddr; - NetClientState *peer; + NICPeers peers; int32_t bootindex; + int32_t queues; } NICConf; #define DEFINE_NIC_PROPERTIES(_state, _conf) \ DEFINE_PROP_MACADDR("mac", _state, _conf.macaddr), \ - DEFINE_PROP_VLAN("vlan", _state, _conf.peer), \ - DEFINE_PROP_NETDEV("netdev", _state, _conf.peer), \ + DEFINE_PROP_VLAN("vlan", _state, _conf.peers), \ + DEFINE_PROP_NETDEV("netdev", _state, _conf.peers), \ DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1) + /* Net clients */ typedef void (NetPoll)(NetClientState *, bool enable); @@ -35,6 +43,7 @@ typedef ssize_t (NetReceive)(NetClientState *, const uint8_t *, size_t); typedef ssize_t (NetReceiveIOV)(NetClientState *, const struct iovec *, int); typedef void (NetCleanup) (NetClientState *); typedef void (LinkStatusChanged)(NetClientState *); +typedef void (NetClientDestructor)(NetClientState *); typedef struct NetClientInfo { NetClientOptionsKind type; @@ -58,16 +67,20 @@ struct NetClientState { char *name; char info_str[256]; unsigned receive_disabled : 1; + NetClientDestructor *destructor; + unsigned int queue_index; }; typedef struct NICState { - NetClientState nc; + NetClientState *ncs; NICConf *conf; void *opaque; bool peer_deleted; } NICState; NetClientState *qemu_find_netdev(const char *id); +int qemu_find_net_clients_except(const char *id, NetClientState **ncs, + NetClientOptionsKind type, int max); NetClientState *qemu_new_net_client(NetClientInfo *info, NetClientState *peer, const char *model, @@ -77,6 +90,11 @@ NICState *qemu_new_nic(NetClientInfo *info, const char *model, const char *name, void *opaque); +void qemu_del_nic(NICState *nic); +NetClientState *qemu_get_subqueue(NICState *nic, int queue_index); +NetClientState *qemu_get_queue(NICState *nic); +NICState *qemu_get_nic(NetClientState *nc); +void *qemu_get_nic_opaque(NetClientState *nc); void qemu_del_net_client(NetClientState *nc); NetClientState *qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id, const char *client_str); @@ -112,7 +130,7 @@ ssize_t qemu_deliver_packet_iov(NetClientState *sender, void *opaque); void print_net_client(Monitor *mon, NetClientState *nc); -void do_info_network(Monitor *mon); +void do_info_network(Monitor *mon, const QDict *qdict); /* NIC info */ diff --git a/include/net/slirp.h b/include/net/slirp.h index 54b655c272..0502389c68 100644 --- a/include/net/slirp.h +++ b/include/net/slirp.h @@ -40,7 +40,7 @@ int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret int net_slirp_smb(const char *exported_dir); -void do_info_usernet(Monitor *mon); +void do_info_usernet(Monitor *mon, const QDict *qdict); #endif diff --git a/include/net/tap.h b/include/net/tap.h index bb7efb5439..a994f20447 100644 --- a/include/net/tap.h +++ b/include/net/tap.h @@ -29,12 +29,14 @@ #include "qemu-common.h" #include "qapi-types.h" -int tap_has_ufo(NetClientState *nc); +bool tap_has_ufo(NetClientState *nc); int tap_has_vnet_hdr(NetClientState *nc); int tap_has_vnet_hdr_len(NetClientState *nc, int len); -void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr); +void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr); void tap_set_offload(NetClientState *nc, int csum, int tso4, int tso6, int ecn, int ufo); void tap_set_vnet_hdr_len(NetClientState *nc, int len); +int tap_enable(NetClientState *nc); +int tap_disable(NetClientState *nc); int tap_get_fd(NetClientState *nc); diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index 6d9a4be3a5..685b2e3fcb 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -64,4 +64,6 @@ int64_t qdict_get_try_int(const QDict *qdict, const char *key, int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value); const char *qdict_get_try_str(const QDict *qdict, const char *key); +QDict *qdict_clone_shallow(const QDict *src); + #endif /* QDICT_H */ diff --git a/include/qemu-common.h b/include/qemu-common.h index 6871cab371..5e137084b5 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -68,6 +68,9 @@ #if !defined(ECANCELED) #define ECANCELED 4097 #endif +#if !defined(EMEDIUMTYPE) +#define EMEDIUMTYPE 4098 +#endif #ifndef TIME_MAX #define TIME_MAX LONG_MAX #endif @@ -139,6 +142,18 @@ int qemu_main(int argc, char **argv, char **envp); void qemu_get_timedate(struct tm *tm, int offset); int qemu_timedate_diff(struct tm *tm); +#if !GLIB_CHECK_VERSION(2, 20, 0) +/* + * Glib before 2.20.0 doesn't implement g_poll, so wrap it to compile properly + * on older systems. + */ +static inline gint g_poll(GPollFD *fds, guint nfds, gint timeout) +{ + GMainContext *ctx = g_main_context_default(); + return g_main_context_get_poll_func(ctx)(fds, nfds, timeout); +} +#endif + /** * is_help_option: * @s: string to test @@ -170,6 +185,10 @@ int qemu_fdatasync(int fd); int fcntl_setfl(int fd, int flag); int qemu_parse_fd(const char *param); +int parse_uint(const char *s, unsigned long long *value, char **endptr, + int base); +int parse_uint_full(const char *s, unsigned long long *value, int base); + /* * strtosz() suffixes used to specify the default treatment of an * argument passed to strtosz() without an explicit suffix. @@ -288,7 +307,9 @@ struct qemu_work_item { }; #ifdef CONFIG_USER_ONLY -#define qemu_init_vcpu(env) do { } while (0) +static inline void qemu_init_vcpu(void *env) +{ +} #else void qemu_init_vcpu(void *env); #endif @@ -329,6 +350,9 @@ void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov); void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len); void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t soffset, size_t sbytes); +void qemu_iovec_concat_iov(QEMUIOVector *dst, + struct iovec *src_iov, unsigned int src_cnt, + size_t soffset, size_t sbytes); void qemu_iovec_destroy(QEMUIOVector *qiov); void qemu_iovec_reset(QEMUIOVector *qiov); size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset, diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index 96a194bbee..10becb6101 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -16,6 +16,7 @@ */ #define smp_wmb() barrier() #define smp_rmb() barrier() + /* * We use GCC builtin if it's available, as that can use * mfence on 32 bit as well, e.g. if built with -march=pentium-m. diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index 74e14e5724..affcc969dc 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -13,6 +13,7 @@ #define BITOPS_H #include "qemu-common.h" +#include "host-utils.h" #define BITS_PER_BYTE CHAR_BIT #define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE) @@ -22,93 +23,6 @@ #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) -/** - * bitops_ffs - find first bit in word. - * @word: The word to search - * - * Undefined if no bit exists, so code should check against 0 first. - */ -static unsigned long bitops_ffsl(unsigned long word) -{ - int num = 0; - -#if LONG_MAX > 0x7FFFFFFF - if ((word & 0xffffffff) == 0) { - num += 32; - word >>= 32; - } -#endif - if ((word & 0xffff) == 0) { - num += 16; - word >>= 16; - } - if ((word & 0xff) == 0) { - num += 8; - word >>= 8; - } - if ((word & 0xf) == 0) { - num += 4; - word >>= 4; - } - if ((word & 0x3) == 0) { - num += 2; - word >>= 2; - } - if ((word & 0x1) == 0) { - num += 1; - } - return num; -} - -/** - * bitops_fls - find last (most-significant) set bit in a long word - * @word: the word to search - * - * Undefined if no set bit exists, so code should check against 0 first. - */ -static inline unsigned long bitops_flsl(unsigned long word) -{ - int num = BITS_PER_LONG - 1; - -#if LONG_MAX > 0x7FFFFFFF - if (!(word & (~0ul << 32))) { - num -= 32; - word <<= 32; - } -#endif - if (!(word & (~0ul << (BITS_PER_LONG-16)))) { - num -= 16; - word <<= 16; - } - if (!(word & (~0ul << (BITS_PER_LONG-8)))) { - num -= 8; - word <<= 8; - } - if (!(word & (~0ul << (BITS_PER_LONG-4)))) { - num -= 4; - word <<= 4; - } - if (!(word & (~0ul << (BITS_PER_LONG-2)))) { - num -= 2; - - word <<= 2; - } - if (!(word & (~0ul << (BITS_PER_LONG-1)))) - num -= 1; - return num; -} - -/** - * ffz - find first zero in word. - * @word: The word to search - * - * Undefined if no zero exists, so code should check against ~0UL first. - */ -static inline unsigned long ffz(unsigned long word) -{ - return bitops_ffsl(~word); -} - /** * set_bit - Set a bit in memory * @nr: the bit to set diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index 2006fcd621..d3af35d1d9 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -2,53 +2,16 @@ #define BSWAP_H #include "config-host.h" - #include +#include #include "fpu/softfloat.h" #ifdef CONFIG_MACHINE_BSWAP_H -#include -#include -#include -#else - -#ifdef CONFIG_BYTESWAP_H -#include -#else - -#define bswap_16(x) \ -({ \ - uint16_t __x = (x); \ - ((uint16_t)( \ - (((uint16_t)(__x) & (uint16_t)0x00ffU) << 8) | \ - (((uint16_t)(__x) & (uint16_t)0xff00U) >> 8) )); \ -}) - -#define bswap_32(x) \ -({ \ - uint32_t __x = (x); \ - ((uint32_t)( \ - (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ - (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ - (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ - (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \ -}) - -#define bswap_64(x) \ -({ \ - uint64_t __x = (x); \ - ((uint64_t)( \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \ -}) - -#endif /* !CONFIG_BYTESWAP_H */ +# include +# include +# include +#elif defined(CONFIG_BYTESWAP_H) +# include static inline uint16_t bswap16(uint16_t x) { @@ -64,7 +27,32 @@ static inline uint64_t bswap64(uint64_t x) { return bswap_64(x); } +# else +static inline uint16_t bswap16(uint16_t x) +{ + return (((x & 0x00ff) << 8) | + ((x & 0xff00) >> 8)); +} +static inline uint32_t bswap32(uint32_t x) +{ + return (((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24)); +} + +static inline uint64_t bswap64(uint64_t x) +{ + return (((x & 0x00000000000000ffULL) << 56) | + ((x & 0x000000000000ff00ULL) << 40) | + ((x & 0x0000000000ff0000ULL) << 24) | + ((x & 0x00000000ff000000ULL) << 8) | + ((x & 0x000000ff00000000ULL) >> 8) | + ((x & 0x0000ff0000000000ULL) >> 24) | + ((x & 0x00ff000000000000ULL) >> 40) | + ((x & 0xff00000000000000ULL) >> 56)); +} #endif /* ! CONFIG_MACHINE_BSWAP_H */ static inline void bswap16s(uint16_t *s) @@ -84,45 +72,45 @@ static inline void bswap64s(uint64_t *s) #if defined(HOST_WORDS_BIGENDIAN) #define be_bswap(v, size) (v) -#define le_bswap(v, size) bswap ## size(v) +#define le_bswap(v, size) glue(bswap, size)(v) #define be_bswaps(v, size) -#define le_bswaps(p, size) *p = bswap ## size(*p); +#define le_bswaps(p, size) do { *p = glue(bswap, size)(*p); } while(0) #else #define le_bswap(v, size) (v) -#define be_bswap(v, size) bswap ## size(v) +#define be_bswap(v, size) glue(bswap, size)(v) #define le_bswaps(v, size) -#define be_bswaps(p, size) *p = bswap ## size(*p); +#define be_bswaps(p, size) do { *p = glue(bswap, size)(*p); } while(0) #endif #define CPU_CONVERT(endian, size, type)\ static inline type endian ## size ## _to_cpu(type v)\ {\ - return endian ## _bswap(v, size);\ + return glue(endian, _bswap)(v, size);\ }\ \ static inline type cpu_to_ ## endian ## size(type v)\ {\ - return endian ## _bswap(v, size);\ + return glue(endian, _bswap)(v, size);\ }\ \ static inline void endian ## size ## _to_cpus(type *p)\ {\ - endian ## _bswaps(p, size)\ + glue(endian, _bswaps)(p, size);\ }\ \ static inline void cpu_to_ ## endian ## size ## s(type *p)\ {\ - endian ## _bswaps(p, size)\ + glue(endian, _bswaps)(p, size);\ }\ \ static inline type endian ## size ## _to_cpup(const type *p)\ {\ - return endian ## size ## _to_cpu(*p);\ + return glue(glue(endian, size), _to_cpu)(*p);\ }\ \ static inline void cpu_to_ ## endian ## size ## w(type *p, type v)\ {\ - *p = cpu_to_ ## endian ## size(v);\ + *p = glue(glue(cpu_to_, endian), size)(v);\ } CPU_CONVERT(be, 16, uint16_t) @@ -133,111 +121,14 @@ CPU_CONVERT(le, 16, uint16_t) CPU_CONVERT(le, 32, uint32_t) CPU_CONVERT(le, 64, uint64_t) -/* unaligned versions (optimized for frequent unaligned accesses)*/ - -#if defined(__i386__) || defined(_ARCH_PPC) - -#define cpu_to_le16wu(p, v) cpu_to_le16w(p, v) -#define cpu_to_le32wu(p, v) cpu_to_le32w(p, v) -#define le16_to_cpupu(p) le16_to_cpup(p) -#define le32_to_cpupu(p) le32_to_cpup(p) -#define be32_to_cpupu(p) be32_to_cpup(p) - -#define cpu_to_be16wu(p, v) cpu_to_be16w(p, v) -#define cpu_to_be32wu(p, v) cpu_to_be32w(p, v) -#define cpu_to_be64wu(p, v) cpu_to_be64w(p, v) - -#else - -static inline void cpu_to_le16wu(uint16_t *p, uint16_t v) -{ - uint8_t *p1 = (uint8_t *)p; - - p1[0] = v & 0xff; - p1[1] = v >> 8; -} - -static inline void cpu_to_le32wu(uint32_t *p, uint32_t v) -{ - uint8_t *p1 = (uint8_t *)p; - - p1[0] = v & 0xff; - p1[1] = v >> 8; - p1[2] = v >> 16; - p1[3] = v >> 24; -} - -static inline uint16_t le16_to_cpupu(const uint16_t *p) -{ - const uint8_t *p1 = (const uint8_t *)p; - return p1[0] | (p1[1] << 8); -} - -static inline uint32_t le32_to_cpupu(const uint32_t *p) -{ - const uint8_t *p1 = (const uint8_t *)p; - return p1[0] | (p1[1] << 8) | (p1[2] << 16) | (p1[3] << 24); -} - -static inline uint32_t be32_to_cpupu(const uint32_t *p) -{ - const uint8_t *p1 = (const uint8_t *)p; - return p1[3] | (p1[2] << 8) | (p1[1] << 16) | (p1[0] << 24); -} - -static inline void cpu_to_be16wu(uint16_t *p, uint16_t v) -{ - uint8_t *p1 = (uint8_t *)p; - - p1[0] = v >> 8; - p1[1] = v & 0xff; -} - -static inline void cpu_to_be32wu(uint32_t *p, uint32_t v) -{ - uint8_t *p1 = (uint8_t *)p; - - p1[0] = v >> 24; - p1[1] = v >> 16; - p1[2] = v >> 8; - p1[3] = v & 0xff; -} - -static inline void cpu_to_be64wu(uint64_t *p, uint64_t v) -{ - uint8_t *p1 = (uint8_t *)p; - - p1[0] = v >> 56; - p1[1] = v >> 48; - p1[2] = v >> 40; - p1[3] = v >> 32; - p1[4] = v >> 24; - p1[5] = v >> 16; - p1[6] = v >> 8; - p1[7] = v & 0xff; -} - -#endif - -#ifdef HOST_WORDS_BIGENDIAN -#define cpu_to_32wu cpu_to_be32wu -#define leul_to_cpu(v) glue(glue(le,HOST_LONG_BITS),_to_cpu)(v) -#else -#define cpu_to_32wu cpu_to_le32wu -#define leul_to_cpu(v) (v) -#endif - -#undef le_bswap -#undef be_bswap -#undef le_bswaps -#undef be_bswaps - /* len must be one of 1, 2, 4 */ static inline uint32_t qemu_bswap_len(uint32_t value, int len) { return bswap32(value) >> (32 - 8 * len); } +/* Unions for reinterpreting between floats and integers. */ + typedef union { float32 f; uint32_t l; @@ -321,10 +212,11 @@ typedef union { * q: 64 bits * * endian is: - * (empty): 8 bit access + * (empty): host endian * be : big endian * le : little endian */ + static inline int ldub_p(const void *ptr) { return *(uint8_t *)ptr; @@ -340,115 +232,108 @@ static inline void stb_p(void *ptr, int v) *(uint8_t *)ptr = v; } -/* NOTE: on arm, putting 2 in /proc/sys/debug/alignment so that the - kernel handles unaligned load/stores may give better results, but - it is a system wide setting : bad */ -#if defined(HOST_WORDS_BIGENDIAN) || defined(WORDS_ALIGNED) +/* Any compiler worth its salt will turn these memcpy into native unaligned + operations. Thus we don't need to play games with packed attributes, or + inline byte-by-byte stores. */ + +static inline int lduw_p(const void *ptr) +{ + uint16_t r; + memcpy(&r, ptr, sizeof(r)); + return r; +} + +static inline int ldsw_p(const void *ptr) +{ + int16_t r; + memcpy(&r, ptr, sizeof(r)); + return r; +} + +static inline void stw_p(void *ptr, uint16_t v) +{ + memcpy(ptr, &v, sizeof(v)); +} + +static inline int ldl_p(const void *ptr) +{ + int32_t r; + memcpy(&r, ptr, sizeof(r)); + return r; +} + +static inline void stl_p(void *ptr, uint32_t v) +{ + memcpy(ptr, &v, sizeof(v)); +} + +static inline uint64_t ldq_p(const void *ptr) +{ + uint64_t r; + memcpy(&r, ptr, sizeof(r)); + return r; +} + +static inline void stq_p(void *ptr, uint64_t v) +{ + memcpy(ptr, &v, sizeof(v)); +} -/* conservative code for little endian unaligned accesses */ static inline int lduw_le_p(const void *ptr) { -#ifdef _ARCH_PPC - int val; - __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr)); - return val; -#else - const uint8_t *p = ptr; - return p[0] | (p[1] << 8); -#endif + return (uint16_t)le_bswap(lduw_p(ptr), 16); } static inline int ldsw_le_p(const void *ptr) { -#ifdef _ARCH_PPC - int val; - __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr)); - return (int16_t)val; -#else - const uint8_t *p = ptr; - return (int16_t)(p[0] | (p[1] << 8)); -#endif + return (int16_t)le_bswap(lduw_p(ptr), 16); } static inline int ldl_le_p(const void *ptr) { -#ifdef _ARCH_PPC - int val; - __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (ptr)); - return val; -#else - const uint8_t *p = ptr; - return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); -#endif + return le_bswap(ldl_p(ptr), 32); } static inline uint64_t ldq_le_p(const void *ptr) { - const uint8_t *p = ptr; - uint32_t v1, v2; - v1 = ldl_le_p(p); - v2 = ldl_le_p(p + 4); - return v1 | ((uint64_t)v2 << 32); + return le_bswap(ldq_p(ptr), 64); } static inline void stw_le_p(void *ptr, int v) { -#ifdef _ARCH_PPC - __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(uint16_t *)ptr) : "r" (v), "r" (ptr)); -#else - uint8_t *p = ptr; - p[0] = v; - p[1] = v >> 8; -#endif + stw_p(ptr, le_bswap(v, 16)); } static inline void stl_le_p(void *ptr, int v) { -#ifdef _ARCH_PPC - __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr)); -#else - uint8_t *p = ptr; - p[0] = v; - p[1] = v >> 8; - p[2] = v >> 16; - p[3] = v >> 24; -#endif + stl_p(ptr, le_bswap(v, 32)); } static inline void stq_le_p(void *ptr, uint64_t v) { - uint8_t *p = ptr; - stl_le_p(p, (uint32_t)v); - stl_le_p(p + 4, v >> 32); + stq_p(ptr, le_bswap(v, 64)); } /* float access */ static inline float32 ldfl_le_p(const void *ptr) { - union { - float32 f; - uint32_t i; - } u; - u.i = ldl_le_p(ptr); + CPU_FloatU u; + u.l = ldl_le_p(ptr); return u.f; } static inline void stfl_le_p(void *ptr, float32 v) { - union { - float32 f; - uint32_t i; - } u; + CPU_FloatU u; u.f = v; - stl_le_p(ptr, u.i); + stl_le_p(ptr, u.l); } static inline float64 ldfq_le_p(const void *ptr) { CPU_DoubleU u; - u.l.lower = ldl_le_p(ptr); - u.l.upper = ldl_le_p(ptr + 4); + u.ll = ldq_le_p(ptr); return u.d; } @@ -456,188 +341,64 @@ static inline void stfq_le_p(void *ptr, float64 v) { CPU_DoubleU u; u.d = v; - stl_le_p(ptr, u.l.lower); - stl_le_p(ptr + 4, u.l.upper); + stq_le_p(ptr, u.ll); } -#else - -static inline int lduw_le_p(const void *ptr) -{ - return *(uint16_t *)ptr; -} - -static inline int ldsw_le_p(const void *ptr) -{ - return *(int16_t *)ptr; -} - -static inline int ldl_le_p(const void *ptr) -{ - return *(uint32_t *)ptr; -} - -static inline uint64_t ldq_le_p(const void *ptr) -{ - return *(uint64_t *)ptr; -} - -static inline void stw_le_p(void *ptr, int v) -{ - *(uint16_t *)ptr = v; -} - -static inline void stl_le_p(void *ptr, int v) -{ - *(uint32_t *)ptr = v; -} - -static inline void stq_le_p(void *ptr, uint64_t v) -{ - *(uint64_t *)ptr = v; -} - -/* float access */ - -static inline float32 ldfl_le_p(const void *ptr) -{ - return *(float32 *)ptr; -} - -static inline float64 ldfq_le_p(const void *ptr) -{ - return *(float64 *)ptr; -} - -static inline void stfl_le_p(void *ptr, float32 v) -{ - *(float32 *)ptr = v; -} - -static inline void stfq_le_p(void *ptr, float64 v) -{ - *(float64 *)ptr = v; -} -#endif - -#if !defined(HOST_WORDS_BIGENDIAN) || defined(WORDS_ALIGNED) - static inline int lduw_be_p(const void *ptr) { -#if defined(__i386__) - int val; - asm volatile ("movzwl %1, %0\n" - "xchgb %b0, %h0\n" - : "=q" (val) - : "m" (*(uint16_t *)ptr)); - return val; -#else - const uint8_t *b = ptr; - return ((b[0] << 8) | b[1]); -#endif + return (uint16_t)be_bswap(lduw_p(ptr), 16); } static inline int ldsw_be_p(const void *ptr) { -#if defined(__i386__) - int val; - asm volatile ("movzwl %1, %0\n" - "xchgb %b0, %h0\n" - : "=q" (val) - : "m" (*(uint16_t *)ptr)); - return (int16_t)val; -#else - const uint8_t *b = ptr; - return (int16_t)((b[0] << 8) | b[1]); -#endif + return (int16_t)be_bswap(lduw_p(ptr), 16); } static inline int ldl_be_p(const void *ptr) { -#if defined(__i386__) || defined(__x86_64__) - int val; - asm volatile ("movl %1, %0\n" - "bswap %0\n" - : "=r" (val) - : "m" (*(uint32_t *)ptr)); - return val; -#else - const uint8_t *b = ptr; - return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; -#endif + return be_bswap(ldl_p(ptr), 32); } static inline uint64_t ldq_be_p(const void *ptr) { - uint32_t a,b; - a = ldl_be_p(ptr); - b = ldl_be_p((uint8_t *)ptr + 4); - return (((uint64_t)a<<32)|b); + return be_bswap(ldq_p(ptr), 64); } static inline void stw_be_p(void *ptr, int v) { -#if defined(__i386__) - asm volatile ("xchgb %b0, %h0\n" - "movw %w0, %1\n" - : "=q" (v) - : "m" (*(uint16_t *)ptr), "0" (v)); -#else - uint8_t *d = (uint8_t *) ptr; - d[0] = v >> 8; - d[1] = v; -#endif + stw_p(ptr, be_bswap(v, 16)); } static inline void stl_be_p(void *ptr, int v) { -#if defined(__i386__) || defined(__x86_64__) - asm volatile ("bswap %0\n" - "movl %0, %1\n" - : "=r" (v) - : "m" (*(uint32_t *)ptr), "0" (v)); -#else - uint8_t *d = (uint8_t *) ptr; - d[0] = v >> 24; - d[1] = v >> 16; - d[2] = v >> 8; - d[3] = v; -#endif + stl_p(ptr, be_bswap(v, 32)); } static inline void stq_be_p(void *ptr, uint64_t v) { - stl_be_p(ptr, v >> 32); - stl_be_p((uint8_t *)ptr + 4, v); + stq_p(ptr, be_bswap(v, 64)); } /* float access */ static inline float32 ldfl_be_p(const void *ptr) { - union { - float32 f; - uint32_t i; - } u; - u.i = ldl_be_p(ptr); + CPU_FloatU u; + u.l = ldl_be_p(ptr); return u.f; } static inline void stfl_be_p(void *ptr, float32 v) { - union { - float32 f; - uint32_t i; - } u; + CPU_FloatU u; u.f = v; - stl_be_p(ptr, u.i); + stl_be_p(ptr, u.l); } static inline float64 ldfq_be_p(const void *ptr) { CPU_DoubleU u; - u.l.upper = ldl_be_p(ptr); - u.l.lower = ldl_be_p((uint8_t *)ptr + 4); + u.ll = ldq_be_p(ptr); return u.d; } @@ -645,69 +406,72 @@ static inline void stfq_be_p(void *ptr, float64 v) { CPU_DoubleU u; u.d = v; - stl_be_p(ptr, u.l.upper); - stl_be_p((uint8_t *)ptr + 4, u.l.lower); + stq_be_p(ptr, u.ll); } +/* Legacy unaligned versions. Note that we never had a complete set. */ + +static inline void cpu_to_le16wu(uint16_t *p, uint16_t v) +{ + stw_le_p(p, v); +} + +static inline void cpu_to_le32wu(uint32_t *p, uint32_t v) +{ + stl_le_p(p, v); +} + +static inline uint16_t le16_to_cpupu(const uint16_t *p) +{ + return lduw_le_p(p); +} + +static inline uint32_t le32_to_cpupu(const uint32_t *p) +{ + return ldl_le_p(p); +} + +static inline uint32_t be32_to_cpupu(const uint32_t *p) +{ + return ldl_be_p(p); +} + +static inline void cpu_to_be16wu(uint16_t *p, uint16_t v) +{ + stw_be_p(p, v); +} + +static inline void cpu_to_be32wu(uint32_t *p, uint32_t v) +{ + stl_be_p(p, v); +} + +static inline void cpu_to_be64wu(uint64_t *p, uint64_t v) +{ + stq_be_p(p, v); +} + +static inline void cpu_to_32wu(uint32_t *p, uint32_t v) +{ + stl_p(p, v); +} + +static inline unsigned long leul_to_cpu(unsigned long v) +{ + /* In order to break an include loop between here and + qemu-common.h, don't rely on HOST_LONG_BITS. */ +#if ULONG_MAX == UINT32_MAX + return le_bswap(v, 32); +#elif ULONG_MAX == UINT64_MAX + return le_bswap(v, 64); #else - -static inline int lduw_be_p(const void *ptr) -{ - return *(uint16_t *)ptr; -} - -static inline int ldsw_be_p(const void *ptr) -{ - return *(int16_t *)ptr; -} - -static inline int ldl_be_p(const void *ptr) -{ - return *(uint32_t *)ptr; -} - -static inline uint64_t ldq_be_p(const void *ptr) -{ - return *(uint64_t *)ptr; -} - -static inline void stw_be_p(void *ptr, int v) -{ - *(uint16_t *)ptr = v; -} - -static inline void stl_be_p(void *ptr, int v) -{ - *(uint32_t *)ptr = v; -} - -static inline void stq_be_p(void *ptr, uint64_t v) -{ - *(uint64_t *)ptr = v; -} - -/* float access */ - -static inline float32 ldfl_be_p(const void *ptr) -{ - return *(float32 *)ptr; -} - -static inline float64 ldfq_be_p(const void *ptr) -{ - return *(float64 *)ptr; -} - -static inline void stfl_be_p(void *ptr, float32 v) -{ - *(float32 *)ptr = v; -} - -static inline void stfq_be_p(void *ptr, float64 v) -{ - *(float64 *)ptr = v; -} - +# error Unknown sizeof long #endif +} + +#undef le_bswap +#undef be_bswap +#undef le_bswaps +#undef be_bswaps #endif /* BSWAP_H */ diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h index 486c77cad4..ccfccae2b4 100644 --- a/include/qemu/config-file.h +++ b/include/qemu/config-file.h @@ -6,11 +6,6 @@ #include "qapi/error.h" #include "qemu/option.h" -extern QemuOptsList qemu_fsdev_opts; -extern QemuOptsList qemu_virtfs_opts; -extern QemuOptsList qemu_spice_opts; -extern QemuOptsList qemu_sandbox_opts; - QemuOptsList *qemu_find_opts(const char *group); QemuOptsList *qemu_find_opts_err(const char *group, Error **errp); void qemu_add_opts(QemuOptsList *list); diff --git a/hw/fifo.h b/include/qemu/fifo8.h similarity index 98% rename from hw/fifo.h rename to include/qemu/fifo8.h index f23890abf4..d318f71e11 100644 --- a/hw/fifo.h +++ b/include/qemu/fifo8.h @@ -1,7 +1,7 @@ #ifndef FIFO_H #define FIFO_H -#include "hw.h" +#include "migration/vmstate.h" typedef struct { /* All fields are private */ diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h new file mode 100644 index 0000000000..550d7ce2c3 --- /dev/null +++ b/include/qemu/hbitmap.h @@ -0,0 +1,209 @@ +/* + * Hierarchical Bitmap Data Type + * + * Copyright Red Hat, Inc., 2012 + * + * Author: Paolo Bonzini + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#ifndef HBITMAP_H +#define HBITMAP_H 1 + +#include +#include +#include +#include "bitops.h" +#include "host-utils.h" + +typedef struct HBitmap HBitmap; +typedef struct HBitmapIter HBitmapIter; + +#define BITS_PER_LEVEL (BITS_PER_LONG == 32 ? 5 : 6) + +/* For 32-bit, the largest that fits in a 4 GiB address space. + * For 64-bit, the number of sectors in 1 PiB. Good luck, in + * either case... :) + */ +#define HBITMAP_LOG_MAX_SIZE (BITS_PER_LONG == 32 ? 34 : 41) + +/* We need to place a sentinel in level 0 to speed up iteration. Thus, + * we do this instead of HBITMAP_LOG_MAX_SIZE / BITS_PER_LEVEL. The + * difference is that it allocates an extra level when HBITMAP_LOG_MAX_SIZE + * is an exact multiple of BITS_PER_LEVEL. + */ +#define HBITMAP_LEVELS ((HBITMAP_LOG_MAX_SIZE / BITS_PER_LEVEL) + 1) + +struct HBitmapIter { + const HBitmap *hb; + + /* Copied from hb for access in the inline functions (hb is opaque). */ + int granularity; + + /* Entry offset into the last-level array of longs. */ + size_t pos; + + /* The currently-active path in the tree. Each item of cur[i] stores + * the bits (i.e. the subtrees) yet to be processed under that node. + */ + unsigned long cur[HBITMAP_LEVELS]; +}; + +/** + * hbitmap_alloc: + * @size: Number of bits in the bitmap. + * @granularity: Granularity of the bitmap. Aligned groups of 2^@granularity + * bits will be represented by a single bit. Each operation on a + * range of bits first rounds the bits to determine which group they land + * in, and then affect the entire set; iteration will only visit the first + * bit of each group. + * + * Allocate a new HBitmap. + */ +HBitmap *hbitmap_alloc(uint64_t size, int granularity); + +/** + * hbitmap_empty: + * @hb: HBitmap to operate on. + * + * Return whether the bitmap is empty. + */ +bool hbitmap_empty(const HBitmap *hb); + +/** + * hbitmap_granularity: + * @hb: HBitmap to operate on. + * + * Return the granularity of the HBitmap. + */ +int hbitmap_granularity(const HBitmap *hb); + +/** + * hbitmap_count: + * @hb: HBitmap to operate on. + * + * Return the number of bits set in the HBitmap. + */ +uint64_t hbitmap_count(const HBitmap *hb); + +/** + * hbitmap_set: + * @hb: HBitmap to operate on. + * @start: First bit to set (0-based). + * @count: Number of bits to set. + * + * Set a consecutive range of bits in an HBitmap. + */ +void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count); + +/** + * hbitmap_reset: + * @hb: HBitmap to operate on. + * @start: First bit to reset (0-based). + * @count: Number of bits to reset. + * + * Reset a consecutive range of bits in an HBitmap. + */ +void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count); + +/** + * hbitmap_get: + * @hb: HBitmap to operate on. + * @item: Bit to query (0-based). + * + * Return whether the @item-th bit in an HBitmap is set. + */ +bool hbitmap_get(const HBitmap *hb, uint64_t item); + +/** + * hbitmap_free: + * @hb: HBitmap to operate on. + * + * Free an HBitmap and all of its associated memory. + */ +void hbitmap_free(HBitmap *hb); + +/** + * hbitmap_iter_init: + * @hbi: HBitmapIter to initialize. + * @hb: HBitmap to iterate on. + * @first: First bit to visit (0-based, must be strictly less than the + * size of the bitmap). + * + * Set up @hbi to iterate on the HBitmap @hb. hbitmap_iter_next will return + * the lowest-numbered bit that is set in @hb, starting at @first. + * + * Concurrent setting of bits is acceptable, and will at worst cause the + * iteration to miss some of those bits. Resetting bits before the current + * position of the iterator is also okay. However, concurrent resetting of + * bits can lead to unexpected behavior if the iterator has not yet reached + * those bits. + */ +void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first); + +/* hbitmap_iter_skip_words: + * @hbi: HBitmapIter to operate on. + * + * Internal function used by hbitmap_iter_next and hbitmap_iter_next_word. + */ +unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi); + +/** + * hbitmap_iter_next: + * @hbi: HBitmapIter to operate on. + * + * Return the next bit that is set in @hbi's associated HBitmap, + * or -1 if all remaining bits are zero. + */ +static inline int64_t hbitmap_iter_next(HBitmapIter *hbi) +{ + unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1]; + int64_t item; + + if (cur == 0) { + cur = hbitmap_iter_skip_words(hbi); + if (cur == 0) { + return -1; + } + } + + /* The next call will resume work from the next bit. */ + hbi->cur[HBITMAP_LEVELS - 1] = cur & (cur - 1); + item = ((uint64_t)hbi->pos << BITS_PER_LEVEL) + ctzl(cur); + + return item << hbi->granularity; +} + +/** + * hbitmap_iter_next_word: + * @hbi: HBitmapIter to operate on. + * @p_cur: Location where to store the next non-zero word. + * + * Return the index of the next nonzero word that is set in @hbi's + * associated HBitmap, and set *p_cur to the content of that word + * (bits before the index that was passed to hbitmap_iter_init are + * trimmed on the first call). Return -1, and set *p_cur to zero, + * if all remaining words are zero. + */ +static inline size_t hbitmap_iter_next_word(HBitmapIter *hbi, unsigned long *p_cur) +{ + unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1]; + + if (cur == 0) { + cur = hbitmap_iter_skip_words(hbi); + if (cur == 0) { + *p_cur = 0; + return -1; + } + } + + /* The next call will resume work from the next word. */ + hbi->cur[HBITMAP_LEVELS - 1] = 0; + *p_cur = cur; + return hbi->pos; +} + + +#endif diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 81c9a754ae..0f688c1c00 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -26,39 +26,42 @@ #define HOST_UTILS_H 1 #include "qemu/compiler.h" /* QEMU_GNUC_PREREQ */ +#include -#if defined(__x86_64__) -#define __HAVE_FAST_MULU64__ +#ifdef CONFIG_INT128 static inline void mulu64(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b) { - __asm__ ("mul %0\n\t" - : "=d" (*phigh), "=a" (*plow) - : "a" (a), "0" (b)); + __uint128_t r = (__uint128_t)a * b; + *plow = r; + *phigh = r >> 64; } -#define __HAVE_FAST_MULS64__ + static inline void muls64(uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b) { - __asm__ ("imul %0\n\t" - : "=d" (*phigh), "=a" (*plow) - : "a" (a), "0" (b)); + __int128_t r = (__int128_t)a * b; + *plow = r; + *phigh = r >> 64; } #else void muls64(uint64_t *phigh, uint64_t *plow, int64_t a, int64_t b); void mulu64(uint64_t *phigh, uint64_t *plow, uint64_t a, uint64_t b); #endif -/* Binary search for leading zeros. */ - +/** + * clz32 - count leading zeros in a 32-bit value. + * @val: The value to search + * + * Returns 32 if the value is zero. Note that the GCC builtin is + * undefined if the value is zero. + */ static inline int clz32(uint32_t val) { #if QEMU_GNUC_PREREQ(3, 4) - if (val) - return __builtin_clz(val); - else - return 32; + return val ? __builtin_clz(val) : 32; #else + /* Binary search for the leading one bit. */ int cnt = 0; if (!(val & 0xFFFF0000U)) { @@ -88,18 +91,28 @@ static inline int clz32(uint32_t val) #endif } +/** + * clo32 - count leading ones in a 32-bit value. + * @val: The value to search + * + * Returns 32 if the value is -1. + */ static inline int clo32(uint32_t val) { return clz32(~val); } +/** + * clz64 - count leading zeros in a 64-bit value. + * @val: The value to search + * + * Returns 64 if the value is zero. Note that the GCC builtin is + * undefined if the value is zero. + */ static inline int clz64(uint64_t val) { #if QEMU_GNUC_PREREQ(3, 4) - if (val) - return __builtin_clzll(val); - else - return 64; + return val ? __builtin_clzll(val) : 64; #else int cnt = 0; @@ -113,19 +126,30 @@ static inline int clz64(uint64_t val) #endif } +/** + * clo64 - count leading ones in a 64-bit value. + * @val: The value to search + * + * Returns 64 if the value is -1. + */ static inline int clo64(uint64_t val) { return clz64(~val); } +/** + * ctz32 - count trailing zeros in a 32-bit value. + * @val: The value to search + * + * Returns 32 if the value is zero. Note that the GCC builtin is + * undefined if the value is zero. + */ static inline int ctz32(uint32_t val) { #if QEMU_GNUC_PREREQ(3, 4) - if (val) - return __builtin_ctz(val); - else - return 32; + return val ? __builtin_ctz(val) : 32; #else + /* Binary search for the trailing one bit. */ int cnt; cnt = 0; @@ -157,18 +181,28 @@ static inline int ctz32(uint32_t val) #endif } +/** + * cto32 - count trailing ones in a 32-bit value. + * @val: The value to search + * + * Returns 32 if the value is -1. + */ static inline int cto32(uint32_t val) { return ctz32(~val); } +/** + * ctz64 - count trailing zeros in a 64-bit value. + * @val: The value to search + * + * Returns 64 if the value is zero. Note that the GCC builtin is + * undefined if the value is zero. + */ static inline int ctz64(uint64_t val) { #if QEMU_GNUC_PREREQ(3, 4) - if (val) - return __builtin_ctzll(val); - else - return 64; + return val ? __builtin_ctzll(val) : 64; #else int cnt; @@ -182,30 +216,56 @@ static inline int ctz64(uint64_t val) #endif } +/** + * ctz64 - count trailing ones in a 64-bit value. + * @val: The value to search + * + * Returns 64 if the value is -1. + */ static inline int cto64(uint64_t val) { return ctz64(~val); } +/** + * ctpop8 - count the population of one bits in an 8-bit value. + * @val: The value to search + */ static inline int ctpop8(uint8_t val) { +#if QEMU_GNUC_PREREQ(3, 4) + return __builtin_popcount(val); +#else val = (val & 0x55) + ((val >> 1) & 0x55); val = (val & 0x33) + ((val >> 2) & 0x33); val = (val & 0x0f) + ((val >> 4) & 0x0f); return val; +#endif } +/** + * ctpop16 - count the population of one bits in a 16-bit value. + * @val: The value to search + */ static inline int ctpop16(uint16_t val) { +#if QEMU_GNUC_PREREQ(3, 4) + return __builtin_popcount(val); +#else val = (val & 0x5555) + ((val >> 1) & 0x5555); val = (val & 0x3333) + ((val >> 2) & 0x3333); val = (val & 0x0f0f) + ((val >> 4) & 0x0f0f); val = (val & 0x00ff) + ((val >> 8) & 0x00ff); return val; +#endif } +/** + * ctpop32 - count the population of one bits in a 32-bit value. + * @val: The value to search + */ static inline int ctpop32(uint32_t val) { #if QEMU_GNUC_PREREQ(3, 4) @@ -221,6 +281,10 @@ static inline int ctpop32(uint32_t val) #endif } +/** + * ctpop64 - count the population of one bits in a 64-bit value. + * @val: The value to search + */ static inline int ctpop64(uint64_t val) { #if QEMU_GNUC_PREREQ(3, 4) @@ -237,4 +301,22 @@ static inline int ctpop64(uint64_t val) #endif } +/* Host type specific sizes of these routines. */ + +#if ULONG_MAX == UINT32_MAX +# define clzl clz32 +# define ctzl ctz32 +# define clol clo32 +# define ctol cto32 +# define ctpopl ctpop32 +#elif ULONG_MAX == UINT64_MAX +# define clzl clz64 +# define ctzl ctz64 +# define clol clo64 +# define ctol cto64 +# define ctpopl ctpop64 +#else +# error Unknown sizeof long +#endif + #endif diff --git a/include/qemu/iov.h b/include/qemu/iov.h index d06f8b9ce3..68d25f29b7 100644 --- a/include/qemu/iov.h +++ b/include/qemu/iov.h @@ -99,4 +99,17 @@ unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt, const struct iovec *iov, unsigned int iov_cnt, size_t offset, size_t bytes); +/* + * Remove a given number of bytes from the front or back of a vector. + * This may update iov and/or iov_cnt to exclude iovec elements that are + * no longer required. + * + * The number of bytes actually discarded is returned. This number may be + * smaller than requested if the vector is too small. + */ +size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt, + size_t bytes); +size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt, + size_t bytes); + #endif diff --git a/include/qemu/log.h b/include/qemu/log.h index 58f69cb494..6b0db02efc 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -116,8 +116,12 @@ static inline void qemu_log_flush(void) /* Close the log file */ static inline void qemu_log_close(void) { - fclose(qemu_logfile); - qemu_logfile = NULL; + if (qemu_logfile) { + if (qemu_logfile != stderr) { + fclose(qemu_logfile); + } + qemu_logfile = NULL; + } } /* Set up a new log file */ @@ -126,35 +130,36 @@ static inline void qemu_log_set_file(FILE *f) qemu_logfile = f; } -/* Set up a new log file, only if none is set */ -static inline void qemu_log_try_set_file(FILE *f) -{ - if (!qemu_logfile) { - qemu_logfile = f; - } -} - /* define log items */ -typedef struct CPULogItem { +typedef struct QEMULogItem { int mask; const char *name; const char *help; -} CPULogItem; +} QEMULogItem; -extern const CPULogItem cpu_log_items[]; +extern const QEMULogItem qemu_log_items[]; -void qemu_set_log(int log_flags, bool use_own_buffers); +/* This is the function that actually does the work of + * changing the log level; it should only be accessed via + * the qemu_set_log() wrapper. + */ +void do_qemu_set_log(int log_flags, bool use_own_buffers); -static inline void cpu_set_log(int log_flags) +static inline void qemu_set_log(int log_flags) { #ifdef CONFIG_USER_ONLY - qemu_set_log(log_flags, true); + do_qemu_set_log(log_flags, true); #else - qemu_set_log(log_flags, false); + do_qemu_set_log(log_flags, false); #endif } -void cpu_set_log_filename(const char *filename); -int cpu_str_to_log_mask(const char *str); +void qemu_set_log_filename(const char *filename); +int qemu_str_to_log_mask(const char *str); + +/* Print a usage message listing all the valid logging categories + * to the specified FILE*. + */ +void qemu_print_log_usage(FILE *f); #endif diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index e8059c3d0a..6f0200a7ac 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -81,6 +81,11 @@ int qemu_init_main_loop(void); */ int main_loop_wait(int nonblocking); +/** + * qemu_get_aio_context: Return the main loop's AioContext + */ +AioContext *qemu_get_aio_context(void); + /** * qemu_notify_event: Force processing of pending events. * @@ -297,8 +302,8 @@ void qemu_mutex_unlock_iothread(void); /* internal interfaces */ void qemu_fd_register(int fd); -void qemu_iohandler_fill(int *pnfds, fd_set *readfds, fd_set *writefds, fd_set *xfds); -void qemu_iohandler_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, int rc); +void qemu_iohandler_fill(GArray *pollfds); +void qemu_iohandler_poll(GArray *pollfds, int rc); QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque); void qemu_bh_schedule_idle(QEMUBH *bh); diff --git a/include/qemu/option.h b/include/qemu/option.h index ba197cddcf..bdb6d218b4 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -149,6 +149,7 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params, QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict, Error **errp); QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict); +void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp); typedef int (*qemu_opts_loopfunc)(QemuOpts *opts, void *opaque); int qemu_opts_print(QemuOpts *opts, void *dummy); diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 87d3b9cfa8..df244006c7 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -9,6 +9,13 @@ #include #endif +#ifndef _WIN32 +#include +#else +#define WIFEXITED(x) 1 +#define WEXITSTATUS(x) (x) +#endif + #include #if defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10 diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index 803ae1798c..ae5c21cba3 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -34,9 +34,11 @@ int inet_aton(const char *cp, struct in_addr *ia); int qemu_socket(int domain, int type, int protocol); int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); int socket_set_cork(int fd, int v); +int socket_set_nodelay(int fd); void socket_set_block(int fd); void socket_set_nonblock(int fd); int send_all(int fd, const void *buf, int len1); +int recv_all(int fd, void *buf, int len1, bool single_read); /* callback function for nonblocking connect * valid fd on success, negative error code on failure @@ -69,6 +71,7 @@ SocketAddress *socket_parse(const char *str, Error **errp); int socket_connect(SocketAddress *addr, Error **errp, NonBlockingConnectHandler *callback, void *opaque); int socket_listen(SocketAddress *addr, Error **errp); +int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp); /* Old, ipv4 only bits. Don't use for new code. */ int parse_host_port(struct sockaddr_in *saddr, const char *str); diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h index 380bae209b..0f30dccb53 100644 --- a/include/qemu/thread-posix.h +++ b/include/qemu/thread-posix.h @@ -12,7 +12,7 @@ struct QemuCond { }; struct QemuSemaphore { -#if defined(__OpenBSD__) || defined(__APPLE__) || defined(__NetBSD__) +#if defined(__APPLE__) || defined(__NetBSD__) pthread_mutex_t lock; pthread_cond_t cond; int count; diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 9e9d044bdf..3664a1b631 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -20,7 +20,8 @@ #ifndef QEMU_CPU_H #define QEMU_CPU_H -#include "qom/object.h" +#include +#include "hw/qdev-core.h" #include "qemu/thread.h" /** @@ -40,44 +41,88 @@ typedef struct CPUState CPUState; /** * CPUClass: + * @class_by_name: Callback to map -cpu command line model name to an + * instantiatable CPU type. * @reset: Callback to reset the #CPUState to its initial state. + * @do_interrupt: Callback for interrupt handling. + * @vmsd: State description for migration. * * Represents a CPU family or model. */ typedef struct CPUClass { /*< private >*/ - ObjectClass parent_class; + DeviceClass parent_class; /*< public >*/ + ObjectClass *(*class_by_name)(const char *cpu_model); + void (*reset)(CPUState *cpu); + void (*do_interrupt)(CPUState *cpu); + + const struct VMStateDescription *vmsd; } CPUClass; +struct KVMState; +struct kvm_run; + /** * CPUState: + * @cpu_index: CPU index (informative). + * @nr_cores: Number of cores within this CPU package. + * @nr_threads: Number of threads within this CPU. + * @numa_node: NUMA node this CPU is belonging to. + * @host_tid: Host thread ID. + * @running: #true if CPU is currently running (usermode). * @created: Indicates whether the CPU thread has been successfully created. + * @interrupt_request: Indicates a pending interrupt request. + * @halted: Nonzero if the CPU is in suspended state. * @stop: Indicates a pending stop request. * @stopped: Indicates the CPU has been artificially stopped. + * @tcg_exit_req: Set to force TCG to stop executing linked TBs for this + * CPU and return to its top level loop. + * @env_ptr: Pointer to subclass-specific CPUArchState field. + * @current_tb: Currently executing TB. + * @kvm_fd: vCPU file descriptor for KVM. * * State of one CPU core or thread. */ struct CPUState { /*< private >*/ - Object parent_obj; + DeviceState parent_obj; /*< public >*/ + int nr_cores; + int nr_threads; + int numa_node; + struct QemuThread *thread; #ifdef _WIN32 HANDLE hThread; #endif int thread_id; + uint32_t host_tid; + bool running; struct QemuCond *halt_cond; struct qemu_work_item *queued_work_first, *queued_work_last; bool thread_kicked; bool created; bool stop; bool stopped; + volatile sig_atomic_t exit_request; + volatile sig_atomic_t tcg_exit_req; + uint32_t interrupt_request; + + void *env_ptr; /* CPUArchState */ + struct TranslationBlock *current_tb; + + int kvm_fd; + bool kvm_vcpu_dirty; + struct KVMState *kvm_state; + struct kvm_run *kvm_run; /* TODO Move common fields from CPUArchState here. */ + int cpu_index; /* used by alpha TCG */ + uint32_t halted; /* used by alpha, cris, ppc TCG */ }; @@ -87,6 +132,38 @@ struct CPUState { */ void cpu_reset(CPUState *cpu); +/** + * cpu_class_by_name: + * @typename: The CPU base type. + * @cpu_model: The model string without any parameters. + * + * Looks up a CPU #ObjectClass matching name @cpu_model. + * + * Returns: A #CPUClass or %NULL if not matching class is found. + */ +ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model); + +/** + * cpu_class_set_vmsd: + * @cc: CPU class + * @value: Value to set. Unused for %CONFIG_USER_ONLY. + * + * Sets #VMStateDescription for @cc. + * + * The @value argument is intentionally discarded for the non-softmmu targets + * to avoid linker errors or excessive preprocessor usage. If this behavior + * is undesired, you should assign #CPUState.vmsd directly instead. + */ +#ifndef CONFIG_USER_ONLY +static inline void cpu_class_set_vmsd(CPUClass *cc, + const struct VMStateDescription *value) +{ + cc->vmsd = value; +} +#else +#define cpu_class_set_vmsd(cc, value) ((cc)->vmsd = NULL) +#endif + /** * qemu_cpu_has_work: * @cpu: The vCPU to check. @@ -136,5 +213,48 @@ bool cpu_is_stopped(CPUState *cpu); */ void run_on_cpu(CPUState *cpu, void (*func)(void *data), void *data); +/** + * qemu_get_cpu: + * @index: The CPUState@cpu_index value of the CPU to obtain. + * + * Gets a CPU matching @index. + * + * Returns: The CPU or %NULL if there is no matching CPU. + */ +CPUState *qemu_get_cpu(int index); + +#ifndef CONFIG_USER_ONLY + +typedef void (*CPUInterruptHandler)(CPUState *, int); + +extern CPUInterruptHandler cpu_interrupt_handler; + +/** + * cpu_interrupt: + * @cpu: The CPU to set an interrupt on. + * @mask: The interupts to set. + * + * Invokes the interrupt handler. + */ +static inline void cpu_interrupt(CPUState *cpu, int mask) +{ + cpu_interrupt_handler(cpu, mask); +} + +#else /* USER_ONLY */ + +void cpu_interrupt(CPUState *cpu, int mask); + +#endif /* USER_ONLY */ + +/** + * cpu_reset_interrupt: + * @cpu: The CPU to clear the interrupt on. + * @mask: The interrupt mask to clear. + * + * Resets interrupts on the vCPU @cpu. + */ +void cpu_reset_interrupt(CPUState *cpu, int mask); + #endif diff --git a/include/qom/object.h b/include/qom/object.h index abe9691cb7..cf094e7142 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -65,7 +65,7 @@ typedef struct InterfaceInfo InterfaceInfo; * int reg0, reg1, reg2; * } MyDevice; * - * static TypeInfo my_device_info = { + * static const TypeInfo my_device_info = { * .name = TYPE_MY_DEVICE, * .parent = TYPE_DEVICE, * .instance_size = sizeof(MyDevice), @@ -138,7 +138,7 @@ typedef struct InterfaceInfo InterfaceInfo; * dc->reset = my_device_reset; * } * - * static TypeInfo my_device_info = { + * static const TypeInfo my_device_info = { * .name = TYPE_MY_DEVICE, * .parent = TYPE_DEVICE, * .instance_size = sizeof(MyDevice), @@ -147,9 +147,9 @@ typedef struct InterfaceInfo InterfaceInfo; * * * - * Introducing new virtual functions requires a class to define its own - * struct and to add a .class_size member to the TypeInfo. Each function - * will also have a wrapper to call it easily: + * Introducing new virtual methods requires a class to define its own + * struct and to add a .class_size member to the #TypeInfo. Each method + * will also have a wrapper function to call it easily: * * * Defining an abstract class @@ -163,7 +163,7 @@ typedef struct InterfaceInfo InterfaceInfo; * void (*frobnicate) (MyDevice *obj); * } MyDeviceClass; * - * static TypeInfo my_device_info = { + * static const TypeInfo my_device_info = { * .name = TYPE_MY_DEVICE, * .parent = TYPE_DEVICE, * .instance_size = sizeof(MyDevice), @@ -186,6 +186,104 @@ typedef struct InterfaceInfo InterfaceInfo; * similar to normal types except for the fact that are only defined by * their classes and never carry any state. You can dynamically cast an object * to one of its #Interface types and vice versa. + * + * # Methods # + * + * A method is a function within the namespace scope of + * a class. It usually operates on the object instance by passing it as a + * strongly-typed first argument. + * If it does not operate on an object instance, it is dubbed + * class method. + * + * Methods cannot be overloaded. That is, the #ObjectClass and method name + * uniquely identity the function to be called; the signature does not vary + * except for trailing varargs. + * + * Methods are always virtual. Overriding a method in + * #TypeInfo.class_init of a subclass leads to any user of the class obtained + * via OBJECT_GET_CLASS() accessing the overridden function. + * The original function is not automatically invoked. It is the responsability + * of the overriding class to determine whether and when to invoke the method + * being overridden. + * + * To invoke the method being overridden, the preferred solution is to store + * the original value in the overriding class before overriding the method. + * This corresponds to |[ {super,base}.method(...) ]| in Java and C# + * respectively; this frees the overriding class from hardcoding its parent + * class, which someone might choose to change at some point. + * + * + * Overriding a virtual method + * + * typedef struct MyState MyState; + * + * typedef void (*MyDoSomething)(MyState *obj); + * + * typedef struct MyClass { + * ObjectClass parent_class; + * + * MyDoSomething do_something; + * } MyClass; + * + * static void my_do_something(MyState *obj) + * { + * // do something + * } + * + * static void my_class_init(ObjectClass *oc, void *data) + * { + * MyClass *mc = MY_CLASS(oc); + * + * mc->do_something = my_do_something; + * } + * + * static const TypeInfo my_type_info = { + * .name = TYPE_MY, + * .parent = TYPE_OBJECT, + * .instance_size = sizeof(MyState), + * .class_size = sizeof(MyClass), + * .class_init = my_class_init, + * }; + * + * typedef struct DerivedClass { + * MyClass parent_class; + * + * MyDoSomething parent_do_something; + * } MyClass; + * + * static void derived_do_something(MyState *obj) + * { + * DerivedClass *dc = DERIVED_GET_CLASS(obj); + * + * // do something here + * dc->parent_do_something(obj); + * // do something else here + * } + * + * static void derived_class_init(ObjectClass *oc, void *data) + * { + * MyClass *mc = MY_CLASS(oc); + * DerivedClass *dc = DERIVED_CLASS(oc); + * + * dc->parent_do_something = mc->do_something; + * mc->do_something = derived_do_something; + * } + * + * static const TypeInfo derived_type_info = { + * .name = TYPE_DERIVED, + * .parent = TYPE_MY, + * .class_size = sizeof(DerivedClass), + * .class_init = my_class_init, + * }; + * + * + * + * Alternatively, object_class_by_name() can be used to obtain the class and + * its non-overridden methods for a specific type. This would correspond to + * |[ MyClass::method(...) ]| in C++. + * + * The first example of such a QOM method was #CPUClass.reset, + * another example is #DeviceClass.realize. */ @@ -455,9 +553,9 @@ struct InterfaceClass * object_new: * @typename: The name of the type of the object to instantiate. * - * This function will initialize a new object using heap allocated memory. This - * function should be paired with object_delete() to free the resources - * associated with the object. + * This function will initialize a new object using heap allocated memory. + * The returned object has a reference count of 1, and will be freed when + * the last reference is dropped. * * Returns: The newly allocated and instantiated object. */ @@ -467,30 +565,22 @@ Object *object_new(const char *typename); * object_new_with_type: * @type: The type of the object to instantiate. * - * This function will initialize a new object using heap allocated memory. This - * function should be paired with object_delete() to free the resources - * associated with the object. + * This function will initialize a new object using heap allocated memory. + * The returned object has a reference count of 1, and will be freed when + * the last reference is dropped. * * Returns: The newly allocated and instantiated object. */ Object *object_new_with_type(Type type); -/** - * object_delete: - * @obj: The object to free. - * - * Finalize an object and then free the memory associated with it. This should - * be paired with object_new() to free the resources associated with an object. - */ -void object_delete(Object *obj); - /** * object_initialize_with_type: * @obj: A pointer to the memory to be used for the object. * @type: The type of the object to instantiate. * * This function will initialize an object. The memory for the object should - * have already been allocated. + * have already been allocated. The returned object has a reference count of 1, + * and will be finalized when the last reference is dropped. */ void object_initialize_with_type(void *data, Type type); @@ -500,7 +590,8 @@ void object_initialize_with_type(void *data, Type type); * @typename: The name of the type of the object to instantiate. * * This function will initialize an object. The memory for the object should - * have already been allocated. + * have already been allocated. The returned object has a reference count of 1, + * and will be finalized when the last reference is dropped. */ void object_initialize(void *obj, const char *typename); @@ -592,6 +683,14 @@ ObjectClass *object_class_get_parent(ObjectClass *klass); */ const char *object_class_get_name(ObjectClass *klass); +/** + * object_class_is_abstract: + * @klass: The class to obtain the abstractness for. + * + * Returns: %true if @klass is abstract, %false otherwise. + */ +bool object_class_is_abstract(ObjectClass *klass); + /** * object_class_by_name: * @typename: The QOM typename to obtain the class for. @@ -900,7 +999,7 @@ Object *object_resolve_path_type(const char *path, const char *typename, * * Returns: The resolved object or NULL on path lookup failure. */ -Object *object_resolve_path_component(Object *parent, gchar *part); +Object *object_resolve_path_component(Object *parent, const gchar *part); /** * object_property_add_child: @@ -935,6 +1034,11 @@ void object_property_add_child(Object *obj, const char *name, * between objects. * * Links form the graph in the object model. + * + * Ownership of the pointer that @child points to is transferred to the + * link property. The reference count for *@child is + * managed by the property from after the function returns till the + * property is deleted with object_property_del(). */ void object_property_add_link(Object *obj, const char *name, const char *type, Object **child, diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index aa86dd7ce8..cac1a414d0 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -41,7 +41,7 @@ struct DriveInfo { int cyls, heads, secs, trans; bool locked; QemuOpts *opts; - const char *serial; + char *serial; QTAILQ_ENTRY(DriveInfo) next; int refcount; }; diff --git a/include/sysemu/cpus.h b/include/sysemu/cpus.h index 81bd81773f..6502488a05 100644 --- a/include/sysemu/cpus.h +++ b/include/sysemu/cpus.h @@ -13,12 +13,17 @@ void cpu_synchronize_all_post_init(void); void qtest_clock_warp(int64_t dest); +#ifndef CONFIG_USER_ONLY /* vl.c */ extern int smp_cores; extern int smp_threads; +#else +/* *-user doesn't have configurable SMP topology */ +#define smp_cores 1 +#define smp_threads 1 +#endif + void set_numa_modes(void); -void set_cpu_log(const char *optarg); -void set_cpu_log_filename(const char *optarg); void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg); #endif diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 131d2bdc15..f2d97b580d 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -17,13 +17,29 @@ #include #include "config-host.h" #include "qemu/queue.h" +#include "qom/cpu.h" #ifdef CONFIG_KVM #include #include +#else +/* These constants must never be used at runtime if kvm_enabled() is false. + * They exist so we don't need #ifdefs around KVM-specific code that already + * checks kvm_enabled() properly. + */ +#define KVM_CPUID_SIGNATURE 0 +#define KVM_CPUID_FEATURES 0 +#define KVM_FEATURE_CLOCKSOURCE 0 +#define KVM_FEATURE_NOP_IO_DELAY 0 +#define KVM_FEATURE_MMU_OP 0 +#define KVM_FEATURE_CLOCKSOURCE2 0 +#define KVM_FEATURE_ASYNC_PF 0 +#define KVM_FEATURE_STEAL_TIME 0 +#define KVM_FEATURE_PV_EOI 0 +#define KVM_FEATURE_CLOCKSOURCE_STABLE_BIT 0 #endif -extern int kvm_allowed; +extern bool kvm_allowed; extern bool kvm_kernel_irqchip; extern bool kvm_async_interrupts_allowed; extern bool kvm_irqfds_allowed; @@ -120,9 +136,9 @@ int kvm_has_many_ioeventfds(void); int kvm_has_gsi_routing(void); int kvm_has_intx_set_mask(void); -#ifdef NEED_CPU_H -int kvm_init_vcpu(CPUArchState *env); +int kvm_init_vcpu(CPUState *cpu); +#ifdef NEED_CPU_H int kvm_cpu_exec(CPUArchState *env); #if !defined(CONFIG_USER_ONLY) @@ -143,7 +159,7 @@ int kvm_update_guest_debug(CPUArchState *env, unsigned long reinject_trap); int kvm_set_signal_mask(CPUArchState *env, const sigset_t *sigset); #endif -int kvm_on_sigbus_vcpu(CPUArchState *env, int code, void *addr); +int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); int kvm_on_sigbus(int code, void *addr); /* internal API */ @@ -152,20 +168,20 @@ int kvm_ioctl(KVMState *s, int type, ...); int kvm_vm_ioctl(KVMState *s, int type, ...); -int kvm_vcpu_ioctl(CPUArchState *env, int type, ...); +int kvm_vcpu_ioctl(CPUState *cpu, int type, ...); /* Arch specific hooks */ extern const KVMCapabilityInfo kvm_arch_required_capabilities[]; -void kvm_arch_pre_run(CPUArchState *env, struct kvm_run *run); -void kvm_arch_post_run(CPUArchState *env, struct kvm_run *run); +void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run); +void kvm_arch_post_run(CPUState *cpu, struct kvm_run *run); -int kvm_arch_handle_exit(CPUArchState *env, struct kvm_run *run); +int kvm_arch_handle_exit(CPUState *cpu, struct kvm_run *run); -int kvm_arch_process_async_events(CPUArchState *env); +int kvm_arch_process_async_events(CPUState *cpu); -int kvm_arch_get_registers(CPUArchState *env); +int kvm_arch_get_registers(CPUState *cpu); /* state subset only touched by the VCPU itself during runtime */ #define KVM_PUT_RUNTIME_STATE 1 @@ -174,15 +190,18 @@ int kvm_arch_get_registers(CPUArchState *env); /* full state set, modified during initialization or on vmload */ #define KVM_PUT_FULL_STATE 3 -int kvm_arch_put_registers(CPUArchState *env, int level); +int kvm_arch_put_registers(CPUState *cpu, int level); int kvm_arch_init(KVMState *s); -int kvm_arch_init_vcpu(CPUArchState *env); +int kvm_arch_init_vcpu(CPUState *cpu); -void kvm_arch_reset_vcpu(CPUArchState *env); +/* Returns VCPU ID to be used on KVM_CREATE_VCPU ioctl() */ +unsigned long kvm_arch_vcpu_id(CPUState *cpu); -int kvm_arch_on_sigbus_vcpu(CPUArchState *env, int code, void *addr); +void kvm_arch_reset_vcpu(CPUState *cpu); + +int kvm_arch_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); int kvm_arch_on_sigbus(int code, void *addr); void kvm_arch_init_irq_routing(KVMState *s); @@ -207,14 +226,14 @@ struct kvm_sw_breakpoint { QTAILQ_HEAD(kvm_sw_breakpoint_head, kvm_sw_breakpoint); -struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUArchState *env, +struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu, target_ulong pc); -int kvm_sw_breakpoints_active(CPUArchState *env); +int kvm_sw_breakpoints_active(CPUState *cpu); -int kvm_arch_insert_sw_breakpoint(CPUArchState *current_env, +int kvm_arch_insert_sw_breakpoint(CPUState *current_cpu, struct kvm_sw_breakpoint *bp); -int kvm_arch_remove_sw_breakpoint(CPUArchState *current_env, +int kvm_arch_remove_sw_breakpoint(CPUState *current_cpu, struct kvm_sw_breakpoint *bp); int kvm_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len, int type); @@ -222,9 +241,9 @@ int kvm_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len, int type); void kvm_arch_remove_all_hw_breakpoints(void); -void kvm_arch_update_guest_debug(CPUArchState *env, struct kvm_guest_debug *dbg); +void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg); -bool kvm_arch_stop_on_emulation_error(CPUArchState *env); +bool kvm_arch_stop_on_emulation_error(CPUState *cpu); int kvm_check_extension(KVMState *s, unsigned int extension); diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h index d0e9234d24..71f5fa0a91 100644 --- a/include/sysemu/os-win32.h +++ b/include/sysemu/os-win32.h @@ -63,6 +63,14 @@ # undef setjmp # define setjmp(env) _setjmp(env, NULL) #endif +/* QEMU uses sigsetjmp()/siglongjmp() as the portable way to specify + * "longjmp and don't touch the signal masks". Since we know that the + * savemask parameter will always be zero we can safely define these + * in terms of setjmp/longjmp on Win32. + */ +#define sigjmp_buf jmp_buf +#define sigsetjmp(env, savemask) setjmp(env) +#define siglongjmp(env, val) longjmp(env, val) /* Declaration of ffs() is missing in MinGW's strings.h. */ int ffs(int i); @@ -73,6 +81,8 @@ struct tm *gmtime_r(const time_t *timep, struct tm *result); #undef localtime_r struct tm *localtime_r(const time_t *timep, struct tm *result); +char *strtok_r(char *str, const char *delim, char **saveptr); + static inline void os_setup_signal_handling(void) {} static inline void os_daemonize(void) {} static inline void os_setup_post(void) {} diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h index 723a4f9536..9a0c6b31c8 100644 --- a/include/sysemu/qtest.h +++ b/include/sysemu/qtest.h @@ -17,7 +17,7 @@ #include "qemu-common.h" #if !defined(CONFIG_USER_ONLY) -extern int qtest_allowed; +extern bool qtest_allowed; extern const char *qtest_chrdev; extern const char *qtest_log; diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 28a783e2be..6578782fc3 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -68,16 +68,16 @@ void qemu_add_machine_init_done_notifier(Notifier *notify); void do_savevm(Monitor *mon, const QDict *qdict); int load_vmstate(const char *name); void do_delvm(Monitor *mon, const QDict *qdict); -void do_info_snapshots(Monitor *mon); +void do_info_snapshots(Monitor *mon, const QDict *qdict); void qemu_announce_self(void); bool qemu_savevm_state_blocked(Error **errp); -int qemu_savevm_state_begin(QEMUFile *f, - const MigrationParams *params); +void qemu_savevm_state_begin(QEMUFile *f, + const MigrationParams *params); int qemu_savevm_state_iterate(QEMUFile *f); -int qemu_savevm_state_complete(QEMUFile *f); -void qemu_savevm_state_cancel(QEMUFile *f); +void qemu_savevm_state_complete(QEMUFile *f); +void qemu_savevm_state_cancel(void); uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size); int qemu_loadvm_state(QEMUFile *f); @@ -89,12 +89,12 @@ typedef enum DisplayType DT_DEFAULT, DT_CURSES, DT_SDL, + DT_GTK, DT_NOGRAPHIC, DT_NONE, } DisplayType; extern int autostart; -extern int bios_size; typedef enum { VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL, @@ -122,7 +122,7 @@ extern int semihosting_enabled; extern int old_param; extern int boot_menu; extern uint8_t *boot_splash_filedata; -extern int boot_splash_filedata_size; +extern size_t boot_splash_filedata_size; extern uint8_t qemu_extra_params_fw[2]; extern QEMUClock *rtc_clock; @@ -171,7 +171,7 @@ extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; void do_usb_add(Monitor *mon, const QDict *qdict); void do_usb_del(Monitor *mon, const QDict *qdict); -void usb_info(Monitor *mon); +void usb_info(Monitor *mon, const QDict *qdict); void rtc_change_mon_event(struct tm *tm); @@ -179,8 +179,16 @@ void register_devices(void); void add_boot_device_path(int32_t bootindex, DeviceState *dev, const char *suffix); -char *get_boot_devices_list(uint32_t *size); +char *get_boot_devices_list(size_t *size); bool usb_enabled(bool default_usb); +extern QemuOptsList qemu_drive_opts; +extern QemuOptsList qemu_chardev_opts; +extern QemuOptsList qemu_device_opts; +extern QemuOptsList qemu_netdev_opts; +extern QemuOptsList qemu_net_opts; +extern QemuOptsList qemu_global_opts; +extern QemuOptsList qemu_mon_opts; + #endif diff --git a/include/tpm/tpm.h b/include/tpm/tpm.h new file mode 100644 index 0000000000..cc8f20e69e --- /dev/null +++ b/include/tpm/tpm.h @@ -0,0 +1,21 @@ +/* + * Public TPM functions + * + * Copyright (C) 2011-2013 IBM Corporation + * + * Authors: + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef QEMU_TPM_H +#define QEMU_TPM_H + +#include "qemu/option.h" + +int tpm_config_parse(QemuOptsList *opts_list, const char *optarg); +int tpm_init(void); +void tpm_cleanup(void); + +#endif /* QEMU_TPM_H */ diff --git a/include/trace.h b/include/trace.h new file mode 100644 index 0000000000..c15f498128 --- /dev/null +++ b/include/trace.h @@ -0,0 +1,6 @@ +#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 fc23baa06b..a37cf65602 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -184,7 +184,8 @@ struct DisplayState { void register_displaystate(DisplayState *ds); DisplayState *get_displaystate(void); DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp, - int linesize, uint8_t *data); + int linesize, uint8_t *data, + bool byteswap); PixelFormat qemu_different_endianness_pixelformat(int bpp); PixelFormat qemu_default_pixelformat(int bpp); @@ -442,7 +443,6 @@ void vga_hw_text_update(console_ch_t *chardata); int is_graphic_console(void); int is_fixedsize_console(void); -CharDriverState *text_console_init(QemuOpts *opts); void text_consoles_set_display(DisplayState *ds); void console_select(unsigned int index); void console_color_init(DisplayState *ds); @@ -450,6 +450,11 @@ void qemu_console_resize(DisplayState *ds, int width, int height); void qemu_console_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h); +typedef CharDriverState *(VcHandler)(ChardevVC *vc); + +CharDriverState *vc_init(ChardevVC *vc); +void register_vc_handler(VcHandler *handler); + /* sdl.c */ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame); @@ -482,4 +487,8 @@ void curses_display_init(DisplayState *ds, int full_screen); int index_from_key(const char *key); int index_from_keycode(int code); +/* gtk.c */ +void early_gtk_display_init(void); +void gtk_display_init(DisplayState *ds); + #endif diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index 3c05c83a7c..b032f529aa 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -6,9 +6,16 @@ #ifndef QEMU_PIXMAN_H #define QEMU_PIXMAN_H +/* pixman-0.16.0 headers have a redundant declaration */ +#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE +#pragma GCC diagnostic ignored "-Wredundant-decls" +#endif #include +#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE +#pragma GCC diagnostic error "-Wredundant-decls" +#endif -#include "console.h" +#include "qemu/typedefs.h" /* * pixman image formats are defined to be native endian, diff --git a/include/ui/qemu-spice.h b/include/ui/qemu-spice.h index 5a78fd764d..eba6d77d1d 100644 --- a/include/ui/qemu-spice.h +++ b/include/ui/qemu-spice.h @@ -44,10 +44,13 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, void do_info_spice_print(Monitor *mon, const QObject *data); void do_info_spice(Monitor *mon, QObject **ret_data); -CharDriverState *qemu_chr_open_spice(QemuOpts *opts); +CharDriverState *qemu_chr_open_spice_vmc(const char *type); #if SPICE_SERVER_VERSION >= 0x000c02 -CharDriverState *qemu_chr_open_spice_port(QemuOpts *opts); +CharDriverState *qemu_chr_open_spice_port(const char *name); void qemu_spice_register_ports(void); +#else +static inline CharDriverState *qemu_chr_open_spice_port(const char *name) +{ return NULL; } #endif #else /* CONFIG_SPICE */ diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index 8b192e9613..46f9530fe3 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -21,6 +21,7 @@ #include "qemu/thread.h" #include "ui/qemu-pixman.h" +#include "ui/console.h" #include "sysemu/sysemu.h" #define NUM_MEMSLOTS 8 diff --git a/iohandler.c b/iohandler.c index 2523adc11d..ae2ef8f966 100644 --- a/iohandler.c +++ b/iohandler.c @@ -39,6 +39,7 @@ typedef struct IOHandlerRecord { void *opaque; QLIST_ENTRY(IOHandlerRecord) next; int fd; + int pollfds_idx; bool deleted; } IOHandlerRecord; @@ -78,6 +79,7 @@ int qemu_set_fd_handler2(int fd, ioh->fd_read = fd_read; ioh->fd_write = fd_write; ioh->opaque = opaque; + ioh->pollfds_idx = -1; ioh->deleted = 0; qemu_notify_event(); } @@ -92,38 +94,56 @@ int qemu_set_fd_handler(int fd, return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque); } -void qemu_iohandler_fill(int *pnfds, fd_set *readfds, fd_set *writefds, fd_set *xfds) +void qemu_iohandler_fill(GArray *pollfds) { IOHandlerRecord *ioh; QLIST_FOREACH(ioh, &io_handlers, next) { + int events = 0; + if (ioh->deleted) continue; if (ioh->fd_read && (!ioh->fd_read_poll || ioh->fd_read_poll(ioh->opaque) != 0)) { - FD_SET(ioh->fd, readfds); - if (ioh->fd > *pnfds) - *pnfds = ioh->fd; + events |= G_IO_IN | G_IO_HUP | G_IO_ERR; } if (ioh->fd_write) { - FD_SET(ioh->fd, writefds); - if (ioh->fd > *pnfds) - *pnfds = ioh->fd; + events |= G_IO_OUT | G_IO_ERR; + } + if (events) { + GPollFD pfd = { + .fd = ioh->fd, + .events = events, + }; + ioh->pollfds_idx = pollfds->len; + g_array_append_val(pollfds, pfd); + } else { + ioh->pollfds_idx = -1; } } } -void qemu_iohandler_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, int ret) +void qemu_iohandler_poll(GArray *pollfds, int ret) { if (ret > 0) { IOHandlerRecord *pioh, *ioh; QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { - if (!ioh->deleted && ioh->fd_read && FD_ISSET(ioh->fd, readfds)) { + int revents = 0; + + if (!ioh->deleted && ioh->pollfds_idx != -1) { + GPollFD *pfd = &g_array_index(pollfds, GPollFD, + ioh->pollfds_idx); + revents = pfd->revents; + } + + if (!ioh->deleted && ioh->fd_read && + (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) { ioh->fd_read(ioh->opaque); } - if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, writefds)) { + if (!ioh->deleted && ioh->fd_write && + (revents & (G_IO_OUT | G_IO_ERR))) { ioh->fd_write(ioh->opaque); } diff --git a/kvm-all.c b/kvm-all.c index 5aa65c4c15..9b433d3163 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -209,12 +209,12 @@ static int kvm_set_user_memory_region(KVMState *s, KVMSlot *slot) static void kvm_reset_vcpu(void *opaque) { - CPUArchState *env = opaque; + CPUState *cpu = opaque; - kvm_arch_reset_vcpu(env); + kvm_arch_reset_vcpu(cpu); } -int kvm_init_vcpu(CPUArchState *env) +int kvm_init_vcpu(CPUState *cpu) { KVMState *s = kvm_state; long mmap_size; @@ -222,15 +222,15 @@ int kvm_init_vcpu(CPUArchState *env) DPRINTF("kvm_init_vcpu\n"); - ret = kvm_vm_ioctl(s, KVM_CREATE_VCPU, env->cpu_index); + ret = kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)kvm_arch_vcpu_id(cpu)); if (ret < 0) { DPRINTF("kvm_create_vcpu failed\n"); goto err; } - env->kvm_fd = ret; - env->kvm_state = s; - env->kvm_vcpu_dirty = 1; + cpu->kvm_fd = ret; + cpu->kvm_state = s; + cpu->kvm_vcpu_dirty = true; mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); if (mmap_size < 0) { @@ -239,9 +239,9 @@ int kvm_init_vcpu(CPUArchState *env) goto err; } - env->kvm_run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, - env->kvm_fd, 0); - if (env->kvm_run == MAP_FAILED) { + cpu->kvm_run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, + cpu->kvm_fd, 0); + if (cpu->kvm_run == MAP_FAILED) { ret = -errno; DPRINTF("mmap'ing vcpu state failed\n"); goto err; @@ -249,13 +249,13 @@ int kvm_init_vcpu(CPUArchState *env) if (s->coalesced_mmio && !s->coalesced_mmio_ring) { s->coalesced_mmio_ring = - (void *)env->kvm_run + s->coalesced_mmio * PAGE_SIZE; + (void *)cpu->kvm_run + s->coalesced_mmio * PAGE_SIZE; } - ret = kvm_arch_init_vcpu(env); + ret = kvm_arch_init_vcpu(cpu); if (ret == 0) { - qemu_register_reset(kvm_reset_vcpu, env); - kvm_arch_reset_vcpu(env); + qemu_register_reset(kvm_reset_vcpu, cpu); + kvm_arch_reset_vcpu(cpu); } err: return ret; @@ -826,11 +826,9 @@ static MemoryListener kvm_io_listener = { .priority = 10, }; -static void kvm_handle_interrupt(CPUArchState *env, int mask) +static void kvm_handle_interrupt(CPUState *cpu, int mask) { - CPUState *cpu = ENV_GET_CPU(env); - - env->interrupt_request |= mask; + cpu->interrupt_request |= mask; if (!qemu_cpu_is_self(cpu)) { qemu_cpu_kick(cpu); @@ -1181,6 +1179,11 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int virq, bool assign) { abort(); } + +int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg) +{ + return -ENOSYS; +} #endif /* !KVM_CAP_IRQ_ROUTING */ int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n, int virq) @@ -1435,6 +1438,8 @@ static void kvm_handle_io(uint16_t port, void *data, int direction, int size, static int kvm_handle_internal_error(CPUArchState *env, struct kvm_run *run) { + CPUState *cpu = ENV_GET_CPU(env); + fprintf(stderr, "KVM internal error."); if (kvm_check_extension(kvm_state, KVM_CAP_INTERNAL_ERROR_DATA)) { int i; @@ -1449,7 +1454,7 @@ static int kvm_handle_internal_error(CPUArchState *env, struct kvm_run *run) } if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) { fprintf(stderr, "emulation failure\n"); - if (!kvm_arch_stop_on_emulation_error(env)) { + if (!kvm_arch_stop_on_emulation_error(cpu)) { cpu_dump_state(env, stderr, fprintf, CPU_DUMP_CODE); return EXCP_INTERRUPT; } @@ -1486,13 +1491,13 @@ void kvm_flush_coalesced_mmio_buffer(void) s->coalesced_flush_in_progress = false; } -static void do_kvm_cpu_synchronize_state(void *_env) +static void do_kvm_cpu_synchronize_state(void *arg) { - CPUArchState *env = _env; + CPUState *cpu = arg; - if (!env->kvm_vcpu_dirty) { - kvm_arch_get_registers(env); - env->kvm_vcpu_dirty = 1; + if (!cpu->kvm_vcpu_dirty) { + kvm_arch_get_registers(cpu); + cpu->kvm_vcpu_dirty = true; } } @@ -1500,43 +1505,48 @@ void kvm_cpu_synchronize_state(CPUArchState *env) { CPUState *cpu = ENV_GET_CPU(env); - if (!env->kvm_vcpu_dirty) { - run_on_cpu(cpu, do_kvm_cpu_synchronize_state, env); + if (!cpu->kvm_vcpu_dirty) { + run_on_cpu(cpu, do_kvm_cpu_synchronize_state, cpu); } } void kvm_cpu_synchronize_post_reset(CPUArchState *env) { - kvm_arch_put_registers(env, KVM_PUT_RESET_STATE); - env->kvm_vcpu_dirty = 0; + CPUState *cpu = ENV_GET_CPU(env); + + kvm_arch_put_registers(cpu, KVM_PUT_RESET_STATE); + cpu->kvm_vcpu_dirty = false; } void kvm_cpu_synchronize_post_init(CPUArchState *env) { - kvm_arch_put_registers(env, KVM_PUT_FULL_STATE); - env->kvm_vcpu_dirty = 0; + CPUState *cpu = ENV_GET_CPU(env); + + kvm_arch_put_registers(cpu, KVM_PUT_FULL_STATE); + cpu->kvm_vcpu_dirty = false; } int kvm_cpu_exec(CPUArchState *env) { - struct kvm_run *run = env->kvm_run; + CPUState *cpu = ENV_GET_CPU(env); + struct kvm_run *run = cpu->kvm_run; int ret, run_ret; DPRINTF("kvm_cpu_exec()\n"); - if (kvm_arch_process_async_events(env)) { - env->exit_request = 0; + if (kvm_arch_process_async_events(cpu)) { + cpu->exit_request = 0; return EXCP_HLT; } do { - if (env->kvm_vcpu_dirty) { - kvm_arch_put_registers(env, KVM_PUT_RUNTIME_STATE); - env->kvm_vcpu_dirty = 0; + if (cpu->kvm_vcpu_dirty) { + kvm_arch_put_registers(cpu, KVM_PUT_RUNTIME_STATE); + cpu->kvm_vcpu_dirty = false; } - kvm_arch_pre_run(env, run); - if (env->exit_request) { + kvm_arch_pre_run(cpu, run); + if (cpu->exit_request) { DPRINTF("interrupt exit requested\n"); /* * KVM requires us to reenter the kernel after IO exits to complete @@ -1547,10 +1557,10 @@ int kvm_cpu_exec(CPUArchState *env) } qemu_mutex_unlock_iothread(); - run_ret = kvm_vcpu_ioctl(env, KVM_RUN, 0); + run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0); qemu_mutex_lock_iothread(); - kvm_arch_post_run(env, run); + kvm_arch_post_run(cpu, run); if (run_ret < 0) { if (run_ret == -EINTR || run_ret == -EAGAIN) { @@ -1600,7 +1610,7 @@ int kvm_cpu_exec(CPUArchState *env) break; default: DPRINTF("kvm_arch_handle_exit\n"); - ret = kvm_arch_handle_exit(env, run); + ret = kvm_arch_handle_exit(cpu, run); break; } } while (ret == 0); @@ -1610,7 +1620,7 @@ int kvm_cpu_exec(CPUArchState *env) vm_stop(RUN_STATE_INTERNAL_ERROR); } - env->exit_request = 0; + cpu->exit_request = 0; return ret; } @@ -1648,7 +1658,7 @@ int kvm_vm_ioctl(KVMState *s, int type, ...) return ret; } -int kvm_vcpu_ioctl(CPUArchState *env, int type, ...) +int kvm_vcpu_ioctl(CPUState *cpu, int type, ...) { int ret; void *arg; @@ -1658,7 +1668,7 @@ int kvm_vcpu_ioctl(CPUArchState *env, int type, ...) arg = va_arg(ap, void *); va_end(ap); - ret = ioctl(env->kvm_fd, type, arg); + ret = ioctl(cpu->kvm_fd, type, arg); if (ret == -1) { ret = -errno; } @@ -1753,12 +1763,12 @@ void kvm_setup_guest_memory(void *start, size_t size) } #ifdef KVM_CAP_SET_GUEST_DEBUG -struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUArchState *env, +struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu, target_ulong pc) { struct kvm_sw_breakpoint *bp; - QTAILQ_FOREACH(bp, &env->kvm_state->kvm_sw_breakpoints, entry) { + QTAILQ_FOREACH(bp, &cpu->kvm_state->kvm_sw_breakpoints, entry) { if (bp->pc == pc) { return bp; } @@ -1766,23 +1776,23 @@ struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUArchState *env, return NULL; } -int kvm_sw_breakpoints_active(CPUArchState *env) +int kvm_sw_breakpoints_active(CPUState *cpu) { - return !QTAILQ_EMPTY(&env->kvm_state->kvm_sw_breakpoints); + return !QTAILQ_EMPTY(&cpu->kvm_state->kvm_sw_breakpoints); } struct kvm_set_guest_debug_data { struct kvm_guest_debug dbg; - CPUArchState *env; + CPUState *cpu; int err; }; static void kvm_invoke_set_guest_debug(void *data) { struct kvm_set_guest_debug_data *dbg_data = data; - CPUArchState *env = dbg_data->env; - dbg_data->err = kvm_vcpu_ioctl(env, KVM_SET_GUEST_DEBUG, &dbg_data->dbg); + dbg_data->err = kvm_vcpu_ioctl(dbg_data->cpu, KVM_SET_GUEST_DEBUG, + &dbg_data->dbg); } int kvm_update_guest_debug(CPUArchState *env, unsigned long reinject_trap) @@ -1795,8 +1805,8 @@ int kvm_update_guest_debug(CPUArchState *env, unsigned long reinject_trap) if (env->singlestep_enabled) { data.dbg.control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP; } - kvm_arch_update_guest_debug(env, &data.dbg); - data.env = env; + kvm_arch_update_guest_debug(cpu, &data.dbg); + data.cpu = cpu; run_on_cpu(cpu, kvm_invoke_set_guest_debug, &data); return data.err; @@ -1805,12 +1815,13 @@ int kvm_update_guest_debug(CPUArchState *env, unsigned long reinject_trap) int kvm_insert_breakpoint(CPUArchState *current_env, target_ulong addr, target_ulong len, int type) { + CPUState *current_cpu = ENV_GET_CPU(current_env); struct kvm_sw_breakpoint *bp; CPUArchState *env; int err; if (type == GDB_BREAKPOINT_SW) { - bp = kvm_find_sw_breakpoint(current_env, addr); + bp = kvm_find_sw_breakpoint(current_cpu, addr); if (bp) { bp->use_count++; return 0; @@ -1823,13 +1834,13 @@ int kvm_insert_breakpoint(CPUArchState *current_env, target_ulong addr, bp->pc = addr; bp->use_count = 1; - err = kvm_arch_insert_sw_breakpoint(current_env, bp); + err = kvm_arch_insert_sw_breakpoint(current_cpu, bp); if (err) { g_free(bp); return err; } - QTAILQ_INSERT_HEAD(¤t_env->kvm_state->kvm_sw_breakpoints, + QTAILQ_INSERT_HEAD(¤t_cpu->kvm_state->kvm_sw_breakpoints, bp, entry); } else { err = kvm_arch_insert_hw_breakpoint(addr, len, type); @@ -1850,12 +1861,13 @@ int kvm_insert_breakpoint(CPUArchState *current_env, target_ulong addr, int kvm_remove_breakpoint(CPUArchState *current_env, target_ulong addr, target_ulong len, int type) { + CPUState *current_cpu = ENV_GET_CPU(current_env); struct kvm_sw_breakpoint *bp; CPUArchState *env; int err; if (type == GDB_BREAKPOINT_SW) { - bp = kvm_find_sw_breakpoint(current_env, addr); + bp = kvm_find_sw_breakpoint(current_cpu, addr); if (!bp) { return -ENOENT; } @@ -1865,12 +1877,12 @@ int kvm_remove_breakpoint(CPUArchState *current_env, target_ulong addr, return 0; } - err = kvm_arch_remove_sw_breakpoint(current_env, bp); + err = kvm_arch_remove_sw_breakpoint(current_cpu, bp); if (err) { return err; } - QTAILQ_REMOVE(¤t_env->kvm_state->kvm_sw_breakpoints, bp, entry); + QTAILQ_REMOVE(¤t_cpu->kvm_state->kvm_sw_breakpoints, bp, entry); g_free(bp); } else { err = kvm_arch_remove_hw_breakpoint(addr, len, type); @@ -1890,15 +1902,18 @@ int kvm_remove_breakpoint(CPUArchState *current_env, target_ulong addr, void kvm_remove_all_breakpoints(CPUArchState *current_env) { + CPUState *current_cpu = ENV_GET_CPU(current_env); struct kvm_sw_breakpoint *bp, *next; - KVMState *s = current_env->kvm_state; + KVMState *s = current_cpu->kvm_state; CPUArchState *env; + CPUState *cpu; QTAILQ_FOREACH_SAFE(bp, &s->kvm_sw_breakpoints, entry, next) { - if (kvm_arch_remove_sw_breakpoint(current_env, bp) != 0) { + if (kvm_arch_remove_sw_breakpoint(current_cpu, bp) != 0) { /* Try harder to find a CPU that currently sees the breakpoint. */ for (env = first_cpu; env != NULL; env = env->next_cpu) { - if (kvm_arch_remove_sw_breakpoint(env, bp) == 0) { + cpu = ENV_GET_CPU(env); + if (kvm_arch_remove_sw_breakpoint(cpu, bp) == 0) { break; } } @@ -1939,18 +1954,19 @@ void kvm_remove_all_breakpoints(CPUArchState *current_env) int kvm_set_signal_mask(CPUArchState *env, const sigset_t *sigset) { + CPUState *cpu = ENV_GET_CPU(env); struct kvm_signal_mask *sigmask; int r; if (!sigset) { - return kvm_vcpu_ioctl(env, KVM_SET_SIGNAL_MASK, NULL); + return kvm_vcpu_ioctl(cpu, KVM_SET_SIGNAL_MASK, NULL); } sigmask = g_malloc(sizeof(*sigmask) + sizeof(*sigset)); sigmask->len = 8; memcpy(sigmask->sigset, sigset, sizeof(*sigset)); - r = kvm_vcpu_ioctl(env, KVM_SET_SIGNAL_MASK, sigmask); + r = kvm_vcpu_ioctl(cpu, KVM_SET_SIGNAL_MASK, sigmask); g_free(sigmask); return r; @@ -2008,9 +2024,9 @@ int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign) return 0; } -int kvm_on_sigbus_vcpu(CPUArchState *env, int code, void *addr) +int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) { - return kvm_arch_on_sigbus_vcpu(env, code, addr); + return kvm_arch_on_sigbus_vcpu(cpu, code, addr); } int kvm_on_sigbus(int code, void *addr) diff --git a/kvm-stub.c b/kvm-stub.c index 5b971521cd..760aadc874 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -24,7 +24,7 @@ bool kvm_irqfds_allowed; bool kvm_msi_via_irqfd_allowed; bool kvm_gsi_routing_allowed; -int kvm_init_vcpu(CPUArchState *env) +int kvm_init_vcpu(CPUState *cpu) { return -ENOSYS; } @@ -112,7 +112,7 @@ int kvm_set_ioeventfd_mmio(int fd, uint32_t adr, uint32_t val, bool assign, uint return -ENOSYS; } -int kvm_on_sigbus_vcpu(CPUArchState *env, int code, void *addr) +int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) { return 1; } @@ -131,6 +131,11 @@ void kvm_irqchip_release_virq(KVMState *s, int virq) { } +int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg) +{ + return -ENOSYS; +} + int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n, int virq) { return -ENOSYS; diff --git a/libcacard/Makefile b/libcacard/Makefile index c26aac65c3..47827a0eb8 100644 --- a/libcacard/Makefile +++ b/libcacard/Makefile @@ -1,63 +1,49 @@ --include ../config-host.mak --include $(SRC_PATH)/rules.mak --include $(SRC_PATH)/Makefile.objs - libcacard_includedir=$(includedir)/cacard -$(call set-vpath, $(SRC_PATH)) +TOOLS += vscclient$(EXESUF) # objects linked into a shared library, built with libtool with -fPIC if required -QEMU_OBJS=$(oslib-obj-y) qemu-timer-common.o $(trace-obj-y) $(stub-obj-y) -QEMU_OBJS_LIB=$(patsubst %.o,%.lo,$(QEMU_OBJS)) +libcacard-obj-y = $(stub-obj-y) $(libcacard-y) +libcacard-obj-y += util/osdep.o util/cutils.o util/qemu-timer-common.o util/error.o +libcacard-obj-$(CONFIG_WIN32) += util/oslib-win32.o util/qemu-thread-win32.o +libcacard-obj-$(CONFIG_POSIX) += util/oslib-posix.o util/qemu-thread-posix.o +libcacard-obj-y += $(filter trace/%, $(util-obj-y)) -QEMU_CFLAGS+=-I../ +libcacard-lobj-y=$(patsubst %.o,%.lo,$(libcacard-obj-y)) -libcacard.lib-y=$(patsubst %.o,%.lo,$(libcacard-y)) - -vscclient: $(libcacard-y) $(QEMU_OBJS) vscclient.o cutils.o - $(call quiet-command,$(CC) -o $@ $^ $(libcacard_libs) $(LIBS)," LINK $@") - -clean: - rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~ vscclient *.lo */*.lo .libs/* */.libs/* *.la */*.la *.pc - rm -Rf .libs */.libs +# libtool will build the .o files, too +$(libcacard-obj-y): | $(libcacard-lobj-y) all: libcacard.la libcacard.pc -# Dummy command so that make thinks it has done something - @true + +vscclient$(EXESUF): libcacard/vscclient.o libcacard.la + $(call LINK,$^) ######################################################################### # Rules for building libcacard standalone library -ifeq ($(LIBTOOL),) -libcacard.la: - @echo "libtool is missing, please install and rerun configure"; exit 1 +libcacard.la: LDFLAGS += -rpath $(libdir) -no-undefined \ + -export-syms $(SRC_PATH)/libcacard/libcacard.syms +libcacard.la: LIBS += $(libcacard_libs) +libcacard.la: $(libcacard-lobj-y) + $(call LINK,$^) -install-libcacard: - @echo "libtool is missing, please install and rerun configure"; exit 1 -else -libcacard.la: $(libcacard.lib-y) $(QEMU_OBJS_LIB) - $(call quiet-command,$(LIBTOOL) --mode=link --quiet --tag=CC $(CC) -rpath $(libdir) -o $@ $^ $(libcacard_libs)," lt LINK $@") - -libcacard_srcpath=$(SRC_PATH)/libcacard -libcacard.pc: $(libcacard_srcpath)/libcacard.pc.in +libcacard.pc: $(SRC_PATH)/libcacard/libcacard.pc.in $(call quiet-command,sed -e 's|@LIBDIR@|$(libdir)|' \ -e 's|@INCLUDEDIR@|$(libcacard_includedir)|' \ -e 's|@VERSION@|$(shell cat $(SRC_PATH)/VERSION)|' \ - -e 's|@PREFIX@|$(prefix)|' \ - < $(libcacard_srcpath)/libcacard.pc.in > libcacard.pc,\ + -e 's|@PREFIX@|$(prefix)|' $< > libcacard.pc,\ " GEN $@") .PHONY: install-libcacard -install-libcacard: libcacard.pc libcacard.la vscclient +install: install-libcacard +install-libcacard: libcacard.pc libcacard.la $(INSTALL_DIR) "$(DESTDIR)$(libdir)" $(INSTALL_DIR) "$(DESTDIR)$(libdir)/pkgconfig" $(INSTALL_DIR) "$(DESTDIR)$(libcacard_includedir)" - $(INSTALL_DIR) "$(DESTDIR)$(bindir)" - $(LIBTOOL) --mode=install $(INSTALL_PROG) vscclient "$(DESTDIR)$(bindir)" - $(LIBTOOL) --mode=install $(INSTALL_DATA) libcacard.la "$(DESTDIR)$(libdir)" - $(LIBTOOL) --mode=install $(INSTALL_DATA) libcacard.pc "$(DESTDIR)$(libdir)/pkgconfig" - for inc in *.h; do \ - $(LIBTOOL) --mode=install $(INSTALL_DATA) $(libcacard_srcpath)/$$inc "$(DESTDIR)$(libcacard_includedir)"; \ + $(INSTALL_LIB) libcacard.la "$(DESTDIR)$(libdir)" + $(INSTALL_DATA) libcacard.pc "$(DESTDIR)$(libdir)/pkgconfig" + for inc in $(SRC_PATH)/libcacard/*.h; do \ + $(INSTALL_DATA) $$inc "$(DESTDIR)$(libcacard_includedir)"; \ done -endif diff --git a/libcacard/libcacard.syms b/libcacard/libcacard.syms new file mode 100644 index 0000000000..1697515a7f --- /dev/null +++ b/libcacard/libcacard.syms @@ -0,0 +1,77 @@ +cac_card_init +cac_is_cac_card +vcard_add_applet +vcard_apdu_delete +vcard_apdu_new +vcard_applet_get_aid +vcard_buffer_response_delete +vcard_buffer_response_new +vcard_delete_applet +vcard_emul_delete_key +vcard_emul_force_card_insert +vcard_emul_force_card_remove +vcard_emul_get_atr +vcard_emul_get_login_count +vcard_emul_init +vcard_emul_login +vcard_emul_options +vcard_emul_replay_insertion_events +vcard_emul_reset +vcard_emul_rsa_op +vcard_emul_type_from_string +vcard_emul_type_select +vcard_emul_usage +vcard_find_applet +vcard_free +vcard_get_atr +vcard_get_buffer_response +vcard_get_current_applet_private +vcard_get_private +vcard_get_type +vcard_init +vcard_make_response +vcard_new +vcard_new_applet +vcard_process_apdu +vcard_process_applet_apdu +vcard_reference +vcard_reset +vcard_response_delete +vcard_response_new +vcard_response_new_bytes +vcard_response_new_data +vcard_response_new_status_bytes +vcard_select_applet +vcard_set_applet_private +vcard_set_atr_func +vcard_set_buffer_response +vcard_set_type +vevent_delete +vevent_get_next_vevent +vevent_new +vevent_queue_init +vevent_queue_vevent +vevent_wait_next_vevent +vreader_add_reader +vreader_card_is_present +vreader_free +vreader_get_id +vreader_get_name +vreader_get_private +vreader_get_reader_by_id +vreader_get_reader_by_name +vreader_get_reader_list +vreader_init +vreader_insert_card +vreader_list_delete +vreader_list_get_first +vreader_list_get_next +vreader_list_get_reader +vreader_new +vreader_power_off +vreader_power_on +vreader_queue_card_event +vreader_reference +vreader_remove_reader +vreader_set_id +vreader_xfr_bytes diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c index 5f565e0b4a..df79476db8 100644 --- a/libcacard/vcard_emul_nss.c +++ b/libcacard/vcard_emul_nss.c @@ -454,7 +454,7 @@ vreader_emul_new(PK11SlotInfo *slot, VCardEmulType type, const char *params) new_reader_emul->slot = PK11_ReferenceSlot(slot); new_reader_emul->default_type = type; - new_reader_emul->type_params = strdup(params); + new_reader_emul->type_params = g_strdup(params); new_reader_emul->present = PR_FALSE; new_reader_emul->series = 0; new_reader_emul->saved_vcard = NULL; @@ -997,7 +997,7 @@ vcard_emul_init(const VCardEmulOptions *options) /* We should control this with options. For now we mirror out any * removable hardware slot */ default_card_type = options->hw_card_type; - default_type_params = strdup(options->hw_type_params); + default_type_params = g_strdup(options->hw_type_params); SECMOD_GetReadLock(module_lock); for (mlp = module_list; mlp; mlp = mlp->next) { diff --git a/libcacard/vreader.c b/libcacard/vreader.c index 313349b656..f3efc270a2 100644 --- a/libcacard/vreader.c +++ b/libcacard/vreader.c @@ -49,7 +49,7 @@ vreader_new(const char *name, VReaderEmul *private, reader = (VReader *)g_malloc(sizeof(VReader)); qemu_mutex_init(&reader->lock); reader->reference_count = 1; - reader->name = name ? strdup(name) : NULL; + reader->name = g_strdup(name); reader->card = NULL; reader->id = (vreader_id_t)-1; reader->reader_private = private; diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c index 2fce52bed5..9b744f249c 100644 --- a/libcacard/vscclient.c +++ b/libcacard/vscclient.c @@ -503,8 +503,8 @@ main( command_line_options = vcard_emul_options(emul_args); } - qemu_host = strdup(argv[argc - 2]); - qemu_port = strdup(argv[argc - 1]); + qemu_host = g_strdup(argv[argc - 2]); + qemu_port = g_strdup(argv[argc - 1]); sock = connect_to_qemu(qemu_host, qemu_port); if (sock == -1) { fprintf(stderr, "error opening socket, exiting.\n"); diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h new file mode 100644 index 0000000000..023bfeb367 --- /dev/null +++ b/linux-headers/asm-arm/kvm.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARM_KVM_H__ +#define __ARM_KVM_H__ + +#include +#include + +#define __KVM_HAVE_GUEST_DEBUG +#define __KVM_HAVE_IRQ_LINE + +#define KVM_REG_SIZE(id) \ + (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) + +/* Valid for svc_regs, abt_regs, und_regs, irq_regs in struct kvm_regs */ +#define KVM_ARM_SVC_sp svc_regs[0] +#define KVM_ARM_SVC_lr svc_regs[1] +#define KVM_ARM_SVC_spsr svc_regs[2] +#define KVM_ARM_ABT_sp abt_regs[0] +#define KVM_ARM_ABT_lr abt_regs[1] +#define KVM_ARM_ABT_spsr abt_regs[2] +#define KVM_ARM_UND_sp und_regs[0] +#define KVM_ARM_UND_lr und_regs[1] +#define KVM_ARM_UND_spsr und_regs[2] +#define KVM_ARM_IRQ_sp irq_regs[0] +#define KVM_ARM_IRQ_lr irq_regs[1] +#define KVM_ARM_IRQ_spsr irq_regs[2] + +/* Valid only for fiq_regs in struct kvm_regs */ +#define KVM_ARM_FIQ_r8 fiq_regs[0] +#define KVM_ARM_FIQ_r9 fiq_regs[1] +#define KVM_ARM_FIQ_r10 fiq_regs[2] +#define KVM_ARM_FIQ_fp fiq_regs[3] +#define KVM_ARM_FIQ_ip fiq_regs[4] +#define KVM_ARM_FIQ_sp fiq_regs[5] +#define KVM_ARM_FIQ_lr fiq_regs[6] +#define KVM_ARM_FIQ_spsr fiq_regs[7] + +struct kvm_regs { + struct pt_regs usr_regs;/* R0_usr - R14_usr, PC, CPSR */ + __u32 svc_regs[3]; /* SP_svc, LR_svc, SPSR_svc */ + __u32 abt_regs[3]; /* SP_abt, LR_abt, SPSR_abt */ + __u32 und_regs[3]; /* SP_und, LR_und, SPSR_und */ + __u32 irq_regs[3]; /* SP_irq, LR_irq, SPSR_irq */ + __u32 fiq_regs[8]; /* R8_fiq - R14_fiq, SPSR_fiq */ +}; + +/* Supported Processor Types */ +#define KVM_ARM_TARGET_CORTEX_A15 0 +#define KVM_ARM_NUM_TARGETS 1 + +/* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */ +#define KVM_ARM_DEVICE_TYPE_SHIFT 0 +#define KVM_ARM_DEVICE_TYPE_MASK (0xffff << KVM_ARM_DEVICE_TYPE_SHIFT) +#define KVM_ARM_DEVICE_ID_SHIFT 16 +#define KVM_ARM_DEVICE_ID_MASK (0xffff << KVM_ARM_DEVICE_ID_SHIFT) + +/* Supported device IDs */ +#define KVM_ARM_DEVICE_VGIC_V2 0 + +/* Supported VGIC address types */ +#define KVM_VGIC_V2_ADDR_TYPE_DIST 0 +#define KVM_VGIC_V2_ADDR_TYPE_CPU 1 + +#define KVM_VGIC_V2_DIST_SIZE 0x1000 +#define KVM_VGIC_V2_CPU_SIZE 0x2000 + +#define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */ + +struct kvm_vcpu_init { + __u32 target; + __u32 features[7]; +}; + +struct kvm_sregs { +}; + +struct kvm_fpu { +}; + +struct kvm_guest_debug_arch { +}; + +struct kvm_debug_exit_arch { +}; + +struct kvm_sync_regs { +}; + +struct kvm_arch_memory_slot { +}; + +/* If you need to interpret the index values, here is the key: */ +#define KVM_REG_ARM_COPROC_MASK 0x000000000FFF0000 +#define KVM_REG_ARM_COPROC_SHIFT 16 +#define KVM_REG_ARM_32_OPC2_MASK 0x0000000000000007 +#define KVM_REG_ARM_32_OPC2_SHIFT 0 +#define KVM_REG_ARM_OPC1_MASK 0x0000000000000078 +#define KVM_REG_ARM_OPC1_SHIFT 3 +#define KVM_REG_ARM_CRM_MASK 0x0000000000000780 +#define KVM_REG_ARM_CRM_SHIFT 7 +#define KVM_REG_ARM_32_CRN_MASK 0x0000000000007800 +#define KVM_REG_ARM_32_CRN_SHIFT 11 + +/* Normal registers are mapped as coprocessor 16. */ +#define KVM_REG_ARM_CORE (0x0010 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_CORE_REG(name) (offsetof(struct kvm_regs, name) / 4) + +/* Some registers need more space to represent values. */ +#define KVM_REG_ARM_DEMUX (0x0011 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_DEMUX_ID_MASK 0x000000000000FF00 +#define KVM_REG_ARM_DEMUX_ID_SHIFT 8 +#define KVM_REG_ARM_DEMUX_ID_CCSIDR (0x00 << KVM_REG_ARM_DEMUX_ID_SHIFT) +#define KVM_REG_ARM_DEMUX_VAL_MASK 0x00000000000000FF +#define KVM_REG_ARM_DEMUX_VAL_SHIFT 0 + +/* VFP registers: we could overload CP10 like ARM does, but that's ugly. */ +#define KVM_REG_ARM_VFP (0x0012 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_VFP_MASK 0x000000000000FFFF +#define KVM_REG_ARM_VFP_BASE_REG 0x0 +#define KVM_REG_ARM_VFP_FPSID 0x1000 +#define KVM_REG_ARM_VFP_FPSCR 0x1001 +#define KVM_REG_ARM_VFP_MVFR1 0x1006 +#define KVM_REG_ARM_VFP_MVFR0 0x1007 +#define KVM_REG_ARM_VFP_FPEXC 0x1008 +#define KVM_REG_ARM_VFP_FPINST 0x1009 +#define KVM_REG_ARM_VFP_FPINST2 0x100A + + +/* KVM_IRQ_LINE irq field index values */ +#define KVM_ARM_IRQ_TYPE_SHIFT 24 +#define KVM_ARM_IRQ_TYPE_MASK 0xff +#define KVM_ARM_IRQ_VCPU_SHIFT 16 +#define KVM_ARM_IRQ_VCPU_MASK 0xff +#define KVM_ARM_IRQ_NUM_SHIFT 0 +#define KVM_ARM_IRQ_NUM_MASK 0xffff + +/* irq_type field */ +#define KVM_ARM_IRQ_TYPE_CPU 0 +#define KVM_ARM_IRQ_TYPE_SPI 1 +#define KVM_ARM_IRQ_TYPE_PPI 2 + +/* out-of-kernel GIC cpu interrupt injection irq_number field */ +#define KVM_ARM_IRQ_CPU_IRQ 0 +#define KVM_ARM_IRQ_CPU_FIQ 1 + +/* Highest supported SPI, from VGIC_NR_IRQS */ +#define KVM_ARM_IRQ_GIC_MAX 127 + +/* PSCI interface */ +#define KVM_PSCI_FN_BASE 0x95c1ba5e +#define KVM_PSCI_FN(n) (KVM_PSCI_FN_BASE + (n)) + +#define KVM_PSCI_FN_CPU_SUSPEND KVM_PSCI_FN(0) +#define KVM_PSCI_FN_CPU_OFF KVM_PSCI_FN(1) +#define KVM_PSCI_FN_CPU_ON KVM_PSCI_FN(2) +#define KVM_PSCI_FN_MIGRATE KVM_PSCI_FN(3) + +#define KVM_PSCI_RET_SUCCESS 0 +#define KVM_PSCI_RET_NI ((unsigned long)-1) +#define KVM_PSCI_RET_INVAL ((unsigned long)-2) +#define KVM_PSCI_RET_DENIED ((unsigned long)-3) + +#endif /* __ARM_KVM_H__ */ diff --git a/linux-headers/asm-arm/kvm_para.h b/linux-headers/asm-arm/kvm_para.h new file mode 100644 index 0000000000..14fab8f0b9 --- /dev/null +++ b/linux-headers/asm-arm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/linux-headers/asm-generic/kvm_para.h b/linux-headers/asm-generic/kvm_para.h new file mode 100644 index 0000000000..486f0af73c --- /dev/null +++ b/linux-headers/asm-generic/kvm_para.h @@ -0,0 +1,4 @@ +/* + * There isn't anything here, but the file must not be empty or patch + * will delete it. + */ diff --git a/linux-headers/asm-powerpc/epapr_hcalls.h b/linux-headers/asm-powerpc/epapr_hcalls.h new file mode 100644 index 0000000000..06f724786a --- /dev/null +++ b/linux-headers/asm-powerpc/epapr_hcalls.h @@ -0,0 +1,98 @@ +/* + * ePAPR hcall interface + * + * Copyright 2008-2011 Freescale Semiconductor, Inc. + * + * Author: Timur Tabi + * + * This file is provided under a dual BSD/GPL license. When using or + * redistributing this file, you may do so under either license. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ASM_POWERPC_EPAPR_HCALLS_H +#define _ASM_POWERPC_EPAPR_HCALLS_H + +#define EV_BYTE_CHANNEL_SEND 1 +#define EV_BYTE_CHANNEL_RECEIVE 2 +#define EV_BYTE_CHANNEL_POLL 3 +#define EV_INT_SET_CONFIG 4 +#define EV_INT_GET_CONFIG 5 +#define EV_INT_SET_MASK 6 +#define EV_INT_GET_MASK 7 +#define EV_INT_IACK 9 +#define EV_INT_EOI 10 +#define EV_INT_SEND_IPI 11 +#define EV_INT_SET_TASK_PRIORITY 12 +#define EV_INT_GET_TASK_PRIORITY 13 +#define EV_DOORBELL_SEND 14 +#define EV_MSGSND 15 +#define EV_IDLE 16 + +/* vendor ID: epapr */ +#define EV_LOCAL_VENDOR_ID 0 /* for private use */ +#define EV_EPAPR_VENDOR_ID 1 +#define EV_FSL_VENDOR_ID 2 /* Freescale Semiconductor */ +#define EV_IBM_VENDOR_ID 3 /* IBM */ +#define EV_GHS_VENDOR_ID 4 /* Green Hills Software */ +#define EV_ENEA_VENDOR_ID 5 /* Enea */ +#define EV_WR_VENDOR_ID 6 /* Wind River Systems */ +#define EV_AMCC_VENDOR_ID 7 /* Applied Micro Circuits */ +#define EV_KVM_VENDOR_ID 42 /* KVM */ + +/* The max number of bytes that a byte channel can send or receive per call */ +#define EV_BYTE_CHANNEL_MAX_BYTES 16 + + +#define _EV_HCALL_TOKEN(id, num) (((id) << 16) | (num)) +#define EV_HCALL_TOKEN(hcall_num) _EV_HCALL_TOKEN(EV_EPAPR_VENDOR_ID, hcall_num) + +/* epapr return codes */ +#define EV_SUCCESS 0 +#define EV_EPERM 1 /* Operation not permitted */ +#define EV_ENOENT 2 /* Entry Not Found */ +#define EV_EIO 3 /* I/O error occured */ +#define EV_EAGAIN 4 /* The operation had insufficient + * resources to complete and should be + * retried + */ +#define EV_ENOMEM 5 /* There was insufficient memory to + * complete the operation */ +#define EV_EFAULT 6 /* Bad guest address */ +#define EV_ENODEV 7 /* No such device */ +#define EV_EINVAL 8 /* An argument supplied to the hcall + was out of range or invalid */ +#define EV_INTERNAL 9 /* An internal error occured */ +#define EV_CONFIG 10 /* A configuration error was detected */ +#define EV_INVALID_STATE 11 /* The object is in an invalid state */ +#define EV_UNIMPLEMENTED 12 /* Unimplemented hypercall */ +#define EV_BUFFER_OVERFLOW 13 /* Caller-supplied buffer too small */ + +#endif /* _ASM_POWERPC_EPAPR_HCALLS_H */ diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 1bea4d8ea6..16064d00ad 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -114,7 +114,10 @@ struct kvm_regs { /* Embedded Floating Point (SPE) -- IVOR32-34 if KVM_SREGS_E_IVOR */ #define KVM_SREGS_E_SPE (1 << 9) -/* External Proxy (EXP) -- EPR */ +/* + * DEPRECATED! USE ONE_REG FOR THIS ONE! + * External Proxy (EXP) -- EPR + */ #define KVM_SREGS_EXP (1 << 10) /* External PID (E.PD) -- EPSC/EPLC */ @@ -221,6 +224,12 @@ struct kvm_sregs { __u32 dbsr; /* KVM_SREGS_E_UPDATE_DBSR */ __u32 dbcr[3]; + /* + * iac/dac registers are 64bit wide, while this API + * interface provides only lower 32 bits on 64 bit + * processors. ONE_REG interface is added for 64bit + * iac/dac registers. + */ __u32 iac[4]; __u32 dac[2]; __u32 dvc[2]; @@ -325,6 +334,87 @@ struct kvm_book3e_206_tlb_params { __u32 reserved[8]; }; +/* For KVM_PPC_GET_HTAB_FD */ +struct kvm_get_htab_fd { + __u64 flags; + __u64 start_index; + __u64 reserved[2]; +}; + +/* Values for kvm_get_htab_fd.flags */ +#define KVM_GET_HTAB_BOLTED_ONLY ((__u64)0x1) +#define KVM_GET_HTAB_WRITE ((__u64)0x2) + +/* + * Data read on the file descriptor is formatted as a series of + * records, each consisting of a header followed by a series of + * `n_valid' HPTEs (16 bytes each), which are all valid. Following + * those valid HPTEs there are `n_invalid' invalid HPTEs, which + * are not represented explicitly in the stream. The same format + * is used for writing. + */ +struct kvm_get_htab_header { + __u32 index; + __u16 n_valid; + __u16 n_invalid; +}; + #define KVM_REG_PPC_HIOR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x1) +#define KVM_REG_PPC_IAC1 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x2) +#define KVM_REG_PPC_IAC2 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x3) +#define KVM_REG_PPC_IAC3 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x4) +#define KVM_REG_PPC_IAC4 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x5) +#define KVM_REG_PPC_DAC1 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x6) +#define KVM_REG_PPC_DAC2 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x7) +#define KVM_REG_PPC_DABR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x8) +#define KVM_REG_PPC_DSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x9) +#define KVM_REG_PPC_PURR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xa) +#define KVM_REG_PPC_SPURR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xb) +#define KVM_REG_PPC_DAR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc) +#define KVM_REG_PPC_DSISR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xd) +#define KVM_REG_PPC_AMR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xe) +#define KVM_REG_PPC_UAMOR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xf) + +#define KVM_REG_PPC_MMCR0 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x10) +#define KVM_REG_PPC_MMCR1 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x11) +#define KVM_REG_PPC_MMCRA (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x12) + +#define KVM_REG_PPC_PMC1 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x18) +#define KVM_REG_PPC_PMC2 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x19) +#define KVM_REG_PPC_PMC3 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1a) +#define KVM_REG_PPC_PMC4 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1b) +#define KVM_REG_PPC_PMC5 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1c) +#define KVM_REG_PPC_PMC6 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1d) +#define KVM_REG_PPC_PMC7 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1e) +#define KVM_REG_PPC_PMC8 (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x1f) + +/* 32 floating-point registers */ +#define KVM_REG_PPC_FPR0 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x20) +#define KVM_REG_PPC_FPR(n) (KVM_REG_PPC_FPR0 + (n)) +#define KVM_REG_PPC_FPR31 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x3f) + +/* 32 VMX/Altivec vector registers */ +#define KVM_REG_PPC_VR0 (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x40) +#define KVM_REG_PPC_VR(n) (KVM_REG_PPC_VR0 + (n)) +#define KVM_REG_PPC_VR31 (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x5f) + +/* 32 double-width FP registers for VSX */ +/* High-order halves overlap with FP regs */ +#define KVM_REG_PPC_VSR0 (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x60) +#define KVM_REG_PPC_VSR(n) (KVM_REG_PPC_VSR0 + (n)) +#define KVM_REG_PPC_VSR31 (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x7f) + +/* FP and vector status/control registers */ +#define KVM_REG_PPC_FPSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x80) +#define KVM_REG_PPC_VSCR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x81) + +/* Virtual processor areas */ +/* For SLB & DTL, address in high (first) half, length in low half */ +#define KVM_REG_PPC_VPA_ADDR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0x82) +#define KVM_REG_PPC_VPA_SLB (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x83) +#define KVM_REG_PPC_VPA_DTL (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x84) + +#define KVM_REG_PPC_EPCR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x85) +#define KVM_REG_PPC_EPR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x86) #endif /* __LINUX_KVM_POWERPC_H */ diff --git a/linux-headers/asm-powerpc/kvm_para.h b/linux-headers/asm-powerpc/kvm_para.h index 5e04383a1d..7e64f575f6 100644 --- a/linux-headers/asm-powerpc/kvm_para.h +++ b/linux-headers/asm-powerpc/kvm_para.h @@ -17,8 +17,8 @@ * Authors: Hollis Blanchard */ -#ifndef _UAPI__POWERPC_KVM_PARA_H__ -#define _UAPI__POWERPC_KVM_PARA_H__ +#ifndef __POWERPC_KVM_PARA_H__ +#define __POWERPC_KVM_PARA_H__ #include @@ -75,9 +75,10 @@ struct kvm_vcpu_arch_shared { }; #define KVM_SC_MAGIC_R0 0x4b564d21 /* "KVM!" */ -#define HC_VENDOR_KVM (42 << 16) -#define HC_EV_SUCCESS 0 -#define HC_EV_UNIMPLEMENTED 12 + +#define KVM_HCALL_TOKEN(num) _EV_HCALL_TOKEN(EV_KVM_VENDOR_ID, num) + +#include #define KVM_FEATURE_MAGIC_PAGE 1 @@ -87,4 +88,4 @@ struct kvm_vcpu_arch_shared { #define KVM_MAGIC_FEAT_MAS0_TO_SPRG7 (1 << 1) -#endif /* _UAPI__POWERPC_KVM_PARA_H__ */ +#endif /* __POWERPC_KVM_PARA_H__ */ diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 81d2feb7ab..caca979c49 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -115,6 +115,7 @@ struct kvm_irq_level { * ACPI gsi notion of irq. * For IA-64 (APIC model) IOAPIC0: irq 0-23; IOAPIC1: irq 24-47.. * For X86 (standard AT mode) PIC0/1: irq 0-15. IOAPIC0: 0-23.. + * For ARM: See Documentation/virtual/kvm/api.txt */ union { __u32 irq; @@ -167,10 +168,17 @@ struct kvm_pit_config { #define KVM_EXIT_OSI 18 #define KVM_EXIT_PAPR_HCALL 19 #define KVM_EXIT_S390_UCONTROL 20 +#define KVM_EXIT_WATCHDOG 21 +#define KVM_EXIT_S390_TSCH 22 +#define KVM_EXIT_EPR 23 /* For KVM_EXIT_INTERNAL_ERROR */ -#define KVM_INTERNAL_ERROR_EMULATION 1 -#define KVM_INTERNAL_ERROR_SIMUL_EX 2 +/* Emulate instruction failed. */ +#define KVM_INTERNAL_ERROR_EMULATION 1 +/* Encounter unexpected simultaneous exceptions. */ +#define KVM_INTERNAL_ERROR_SIMUL_EX 2 +/* Encounter unexpected vm-exit due to delivery event. */ +#define KVM_INTERNAL_ERROR_DELIVERY_EV 3 /* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */ struct kvm_run { @@ -280,6 +288,19 @@ struct kvm_run { __u64 ret; __u64 args[9]; } papr_hcall; + /* KVM_EXIT_S390_TSCH */ + struct { + __u16 subchannel_id; + __u16 subchannel_nr; + __u32 io_int_parm; + __u32 io_int_word; + __u32 ipb; + __u8 dequeued; + } s390_tsch; + /* KVM_EXIT_EPR */ + struct { + __u32 epr; + } epr; /* Fix the size of the union. */ char padding[256]; }; @@ -392,10 +413,20 @@ struct kvm_s390_psw { #define KVM_S390_PROGRAM_INT 0xfffe0001u #define KVM_S390_SIGP_SET_PREFIX 0xfffe0002u #define KVM_S390_RESTART 0xfffe0003u +#define KVM_S390_MCHK 0xfffe1000u #define KVM_S390_INT_VIRTIO 0xffff2603u #define KVM_S390_INT_SERVICE 0xffff2401u #define KVM_S390_INT_EMERGENCY 0xffff1201u #define KVM_S390_INT_EXTERNAL_CALL 0xffff1202u +/* Anything below 0xfffe0000u is taken by INT_IO */ +#define KVM_S390_INT_IO(ai,cssid,ssid,schid) \ + (((schid)) | \ + ((ssid) << 16) | \ + ((cssid) << 18) | \ + ((ai) << 26)) +#define KVM_S390_INT_IO_MIN 0x00000000u +#define KVM_S390_INT_IO_MAX 0xfffdffffu + struct kvm_s390_interrupt { __u32 type; @@ -477,6 +508,8 @@ struct kvm_ppc_smmu_info { struct kvm_ppc_one_seg_page_size sps[KVM_PPC_PAGE_SIZES_MAX_SZ]; }; +#define KVM_PPC_PVINFO_FLAGS_EV_IDLE (1<<0) + #define KVMIO 0xAE /* machine type bits, to be used as argument to KVM_CREATE_VM */ @@ -626,6 +659,12 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_READONLY_MEM 81 #endif #define KVM_CAP_IRQFD_RESAMPLE 82 +#define KVM_CAP_PPC_BOOKE_WATCHDOG 83 +#define KVM_CAP_PPC_HTAB_FD 84 +#define KVM_CAP_S390_CSS_SUPPORT 85 +#define KVM_CAP_PPC_EPR 86 +#define KVM_CAP_ARM_PSCI 87 +#define KVM_CAP_ARM_SET_DEVICE_ADDR 88 #ifdef KVM_CAP_IRQ_ROUTING @@ -755,6 +794,11 @@ struct kvm_dirty_tlb { #define KVM_REG_SIZE_U512 0x0060000000000000ULL #define KVM_REG_SIZE_U1024 0x0070000000000000ULL +struct kvm_reg_list { + __u64 n; /* number of regs */ + __u64 reg[0]; +}; + struct kvm_one_reg { __u64 id; __u64 addr; @@ -768,6 +812,11 @@ struct kvm_msi { __u8 pad[16]; }; +struct kvm_arm_device_addr { + __u64 id; + __u64 addr; +}; + /* * ioctls for VM fds */ @@ -848,6 +897,13 @@ struct kvm_s390_ucas_mapping { #define KVM_PPC_GET_SMMU_INFO _IOR(KVMIO, 0xa6, struct kvm_ppc_smmu_info) /* Available with KVM_CAP_PPC_ALLOC_HTAB */ #define KVM_PPC_ALLOCATE_HTAB _IOWR(KVMIO, 0xa7, __u32) +#define KVM_CREATE_SPAPR_TCE _IOW(KVMIO, 0xa8, struct kvm_create_spapr_tce) +/* Available with KVM_CAP_RMA */ +#define KVM_ALLOCATE_RMA _IOR(KVMIO, 0xa9, struct kvm_allocate_rma) +/* Available with KVM_CAP_PPC_HTAB_FD */ +#define KVM_PPC_GET_HTAB_FD _IOW(KVMIO, 0xaa, struct kvm_get_htab_fd) +/* Available with KVM_CAP_ARM_SET_DEVICE_ADDR */ +#define KVM_ARM_SET_DEVICE_ADDR _IOW(KVMIO, 0xab, struct kvm_arm_device_addr) /* * ioctls for vcpu fds @@ -911,9 +967,6 @@ struct kvm_s390_ucas_mapping { /* Available with KVM_CAP_XCRS */ #define KVM_GET_XCRS _IOR(KVMIO, 0xa6, struct kvm_xcrs) #define KVM_SET_XCRS _IOW(KVMIO, 0xa7, struct kvm_xcrs) -#define KVM_CREATE_SPAPR_TCE _IOW(KVMIO, 0xa8, struct kvm_create_spapr_tce) -/* Available with KVM_CAP_RMA */ -#define KVM_ALLOCATE_RMA _IOR(KVMIO, 0xa9, struct kvm_allocate_rma) /* Available with KVM_CAP_SW_TLB */ #define KVM_DIRTY_TLB _IOW(KVMIO, 0xaa, struct kvm_dirty_tlb) /* Available with KVM_CAP_ONE_REG */ @@ -921,6 +974,8 @@ struct kvm_s390_ucas_mapping { #define KVM_SET_ONE_REG _IOW(KVMIO, 0xac, struct kvm_one_reg) /* VM is being stopped by host */ #define KVM_KVMCLOCK_CTRL _IO(KVMIO, 0xad) +#define KVM_ARM_VCPU_INIT _IOW(KVMIO, 0xae, struct kvm_vcpu_init) +#define KVM_GET_REG_LIST _IOWR(KVMIO, 0xb0, struct kvm_reg_list) #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) diff --git a/linux-headers/linux/kvm_para.h b/linux-headers/linux/kvm_para.h index cea2c5c72d..7bdcf93c1d 100644 --- a/linux-headers/linux/kvm_para.h +++ b/linux-headers/linux/kvm_para.h @@ -1,5 +1,5 @@ -#ifndef _UAPI__LINUX_KVM_PARA_H -#define _UAPI__LINUX_KVM_PARA_H +#ifndef __LINUX_KVM_PARA_H +#define __LINUX_KVM_PARA_H /* * This header file provides a method for making a hypercall to the host @@ -25,4 +25,4 @@ */ #include -#endif /* _UAPI__LINUX_KVM_PARA_H */ +#endif /* __LINUX_KVM_PARA_H */ diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index 4758d1bfcf..f787b727a9 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -8,8 +8,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#ifndef _UAPIVFIO_H -#define _UAPIVFIO_H +#ifndef VFIO_H +#define VFIO_H #include #include @@ -365,4 +365,4 @@ struct vfio_iommu_type1_dma_unmap { #define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14) -#endif /* _UAPIVFIO_H */ +#endif /* VFIO_H */ diff --git a/linux-headers/linux/virtio_config.h b/linux-headers/linux/virtio_config.h index b7cda390fd..4f51d8f3af 100644 --- a/linux-headers/linux/virtio_config.h +++ b/linux-headers/linux/virtio_config.h @@ -1,5 +1,5 @@ -#ifndef _UAPI_LINUX_VIRTIO_CONFIG_H -#define _UAPI_LINUX_VIRTIO_CONFIG_H +#ifndef _LINUX_VIRTIO_CONFIG_H +#define _LINUX_VIRTIO_CONFIG_H /* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so * anyone can use the definitions to implement compatible drivers/servers. * @@ -51,4 +51,4 @@ * suppressed them? */ #define VIRTIO_F_NOTIFY_ON_EMPTY 24 -#endif /* _UAPI_LINUX_VIRTIO_CONFIG_H */ +#endif /* _LINUX_VIRTIO_CONFIG_H */ diff --git a/linux-headers/linux/virtio_ring.h b/linux-headers/linux/virtio_ring.h index 921694a084..1b333e2536 100644 --- a/linux-headers/linux/virtio_ring.h +++ b/linux-headers/linux/virtio_ring.h @@ -1,5 +1,5 @@ -#ifndef _UAPI_LINUX_VIRTIO_RING_H -#define _UAPI_LINUX_VIRTIO_RING_H +#ifndef _LINUX_VIRTIO_RING_H +#define _LINUX_VIRTIO_RING_H /* An interface for efficient virtio implementation, currently for use by KVM * and lguest, but hopefully others soon. Do NOT change this since it will * break existing servers and clients. @@ -160,4 +160,4 @@ static __inline__ int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old); } -#endif /* _UAPI_LINUX_VIRTIO_RING_H */ +#endif /* _LINUX_VIRTIO_RING_H */ diff --git a/linux-user/main.c b/linux-user/main.c index f6c4c8d7a3..4e92a0b4c5 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -35,8 +35,6 @@ #include "qemu/envlist.h" #include "elf.h" -#define DEBUG_LOGFILE "/tmp/qemu.log" - char *exec_path; int singlestep; @@ -57,7 +55,12 @@ int have_guest_base; * This way we will never overlap with our own libraries or binaries or stack * or anything else that QEMU maps. */ +# ifdef TARGET_MIPS +/* MIPS only supports 31 bits of virtual address space for user space */ +unsigned long reserved_va = 0x77000000; +# else unsigned long reserved_va = 0xf7000000; +# endif #else unsigned long reserved_va; #endif @@ -106,7 +109,7 @@ static int pending_cpus; /* Make sure everything is in a consistent state for calling fork(). */ void fork_start(void) { - pthread_mutex_lock(&tb_lock); + pthread_mutex_lock(&tcg_ctx.tb_ctx.tb_lock); pthread_mutex_lock(&exclusive_lock); mmap_fork_start(); } @@ -124,11 +127,11 @@ void fork_end(int child) pthread_mutex_init(&cpu_list_mutex, NULL); pthread_cond_init(&exclusive_cond, NULL); pthread_cond_init(&exclusive_resume, NULL); - pthread_mutex_init(&tb_lock, NULL); + pthread_mutex_init(&tcg_ctx.tb_ctx.tb_lock, NULL); gdbserver_fork(thread_env); } else { pthread_mutex_unlock(&exclusive_lock); - pthread_mutex_unlock(&tb_lock); + pthread_mutex_unlock(&tcg_ctx.tb_ctx.tb_lock); } } @@ -146,13 +149,16 @@ static inline void exclusive_idle(void) static inline void start_exclusive(void) { CPUArchState *other; + CPUState *other_cpu; + pthread_mutex_lock(&exclusive_lock); exclusive_idle(); pending_cpus = 1; /* Make all other cpus stop executing. */ for (other = first_cpu; other; other = other->next_cpu) { - if (other->running) { + other_cpu = ENV_GET_CPU(other); + if (other_cpu->running) { pending_cpus++; cpu_exit(other); } @@ -171,19 +177,19 @@ static inline void end_exclusive(void) } /* Wait for exclusive ops to finish, and begin cpu execution. */ -static inline void cpu_exec_start(CPUArchState *env) +static inline void cpu_exec_start(CPUState *cpu) { pthread_mutex_lock(&exclusive_lock); exclusive_idle(); - env->running = 1; + cpu->running = true; pthread_mutex_unlock(&exclusive_lock); } /* Mark cpu as not executing, and release pending exclusive ops. */ -static inline void cpu_exec_end(CPUArchState *env) +static inline void cpu_exec_end(CPUState *cpu) { pthread_mutex_lock(&exclusive_lock); - env->running = 0; + cpu->running = false; if (pending_cpus > 1) { pending_cpus--; if (pending_cpus == 1) { @@ -205,11 +211,11 @@ void cpu_list_unlock(void) } #else /* if !CONFIG_USE_NPTL */ /* These are no-ops because we are not threadsafe. */ -static inline void cpu_exec_start(CPUArchState *env) +static inline void cpu_exec_start(CPUState *cpu) { } -static inline void cpu_exec_end(CPUArchState *env) +static inline void cpu_exec_end(CPUState *cpu) { } @@ -692,15 +698,16 @@ done: void cpu_loop(CPUARMState *env) { + CPUState *cs = CPU(arm_env_get_cpu(env)); int trapnr; unsigned int n, insn; target_siginfo_t info; uint32_t addr; for(;;) { - cpu_exec_start(env); + cpu_exec_start(cs); trapnr = cpu_arm_exec(env); - cpu_exec_end(env); + cpu_exec_end(cs); switch(trapnr) { case EXCP_UDEF: { @@ -907,14 +914,15 @@ void cpu_loop(CPUARMState *env) void cpu_loop(CPUUniCore32State *env) { + CPUState *cs = CPU(uc32_env_get_cpu(env)); int trapnr; unsigned int n, insn; target_siginfo_t info; for (;;) { - cpu_exec_start(env); + cpu_exec_start(cs); trapnr = uc32_cpu_exec(env); - cpu_exec_end(env); + cpu_exec_end(cs); switch (trapnr) { case UC32_EXCP_PRIV: { @@ -1362,14 +1370,15 @@ static int do_store_exclusive(CPUPPCState *env) void cpu_loop(CPUPPCState *env) { + CPUState *cs = CPU(ppc_env_get_cpu(env)); target_siginfo_t info; int trapnr; target_ulong ret; for(;;) { - cpu_exec_start(env); + cpu_exec_start(cs); trapnr = cpu_ppc_exec(env); - cpu_exec_end(env); + cpu_exec_end(cs); switch(trapnr) { case POWERPC_EXCP_NONE: /* Just go on */ @@ -1778,8 +1787,8 @@ void cpu_loop(CPUPPCState *env) #ifdef TARGET_MIPS -#define MIPS_SYS(name, args) args, - +# ifdef TARGET_ABI_MIPSO32 +# define MIPS_SYS(name, args) args, static const uint8_t mips_syscall_args[] = { MIPS_SYS(sys_syscall , 8) /* 4000 */ MIPS_SYS(sys_exit , 1) @@ -2125,8 +2134,8 @@ static const uint8_t mips_syscall_args[] = { MIPS_SYS(sys_clock_adjtime, 2) MIPS_SYS(sys_syncfs, 1) }; - -#undef MIPS_SYS +# undef MIPS_SYS +# endif /* O32 */ static int do_store_exclusive(CPUMIPSState *env) { @@ -2177,20 +2186,52 @@ static int do_store_exclusive(CPUMIPSState *env) return segv; } +/* Break codes */ +enum { + BRK_OVERFLOW = 6, + BRK_DIVZERO = 7 +}; + +static int do_break(CPUMIPSState *env, target_siginfo_t *info, + unsigned int code) +{ + int ret = -1; + + switch (code) { + case BRK_OVERFLOW: + case BRK_DIVZERO: + info->si_signo = TARGET_SIGFPE; + info->si_errno = 0; + info->si_code = (code == BRK_OVERFLOW) ? FPE_INTOVF : FPE_INTDIV; + queue_signal(env, info->si_signo, &*info); + ret = 0; + break; + default: + break; + } + + return ret; +} + void cpu_loop(CPUMIPSState *env) { + CPUState *cs = CPU(mips_env_get_cpu(env)); target_siginfo_t info; - int trapnr, ret; + int trapnr; + abi_long ret; +# ifdef TARGET_ABI_MIPSO32 unsigned int syscall_num; +# endif for(;;) { - cpu_exec_start(env); + cpu_exec_start(cs); trapnr = cpu_mips_exec(env); - cpu_exec_end(env); + cpu_exec_end(cs); switch(trapnr) { case EXCP_SYSCALL: - syscall_num = env->active_tc.gpr[2] - 4000; env->active_tc.PC += 4; +# ifdef TARGET_ABI_MIPSO32 + syscall_num = env->active_tc.gpr[2] - 4000; if (syscall_num >= sizeof(mips_syscall_args)) { ret = -TARGET_ENOSYS; } else { @@ -2229,12 +2270,19 @@ void cpu_loop(CPUMIPSState *env) arg5, arg6, arg7, arg8); } done_syscall: +# else + ret = do_syscall(env, env->active_tc.gpr[2], + env->active_tc.gpr[4], env->active_tc.gpr[5], + env->active_tc.gpr[6], env->active_tc.gpr[7], + env->active_tc.gpr[8], env->active_tc.gpr[9], + env->active_tc.gpr[10], env->active_tc.gpr[11]); +# endif /* O32 */ if (ret == -TARGET_QEMU_ESIGRETURN) { /* Returning from a successful sigreturn syscall. Avoid clobbering register state. */ break; } - if ((unsigned int)ret >= (unsigned int)(-1133)) { + if ((abi_ulong)ret >= (abi_ulong)-1133) { env->active_tc.gpr[7] = 1; /* error flag */ ret = -ret; } else { @@ -2292,8 +2340,55 @@ done_syscall: info.si_code = TARGET_ILL_ILLOPC; queue_signal(env, info.si_signo, &info); break; + /* The code below was inspired by the MIPS Linux kernel trap + * handling code in arch/mips/kernel/traps.c. + */ + case EXCP_BREAK: + { + abi_ulong trap_instr; + unsigned int code; + + ret = get_user_ual(trap_instr, env->active_tc.PC); + if (ret != 0) { + goto error; + } + + /* As described in the original Linux kernel code, the + * below checks on 'code' are to work around an old + * assembly bug. + */ + code = ((trap_instr >> 6) & ((1 << 20) - 1)); + if (code >= (1 << 10)) { + code >>= 10; + } + + if (do_break(env, &info, code) != 0) { + goto error; + } + } + break; + case EXCP_TRAP: + { + abi_ulong trap_instr; + unsigned int code = 0; + + ret = get_user_ual(trap_instr, env->active_tc.PC); + if (ret != 0) { + goto error; + } + + /* The immediate versions don't provide a code. */ + if (!(trap_instr & 0xFC000000)) { + code = ((trap_instr >> 6) & ((1 << 10) - 1)); + } + + if (do_break(env, &info, code) != 0) { + goto error; + } + } + break; default: - // error: +error: fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); cpu_dump_state(env, stderr, fprintf, 0); @@ -2933,71 +3028,115 @@ void cpu_loop(CPUAlphaState *env) #ifdef TARGET_S390X void cpu_loop(CPUS390XState *env) { - int trapnr; + int trapnr, n, sig; target_siginfo_t info; + target_ulong addr; while (1) { - trapnr = cpu_s390x_exec (env); - + trapnr = cpu_s390x_exec(env); switch (trapnr) { case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ + /* Just indicate that signals should be handled asap. */ break; - case EXCP_DEBUG: - { - int sig; - sig = gdb_handlesig (env, TARGET_SIGTRAP); - if (sig) { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); - } - } - break; case EXCP_SVC: - { - int n = env->int_svc_code; - if (!n) { - /* syscalls > 255 */ - n = env->regs[1]; - } - env->psw.addr += env->int_svc_ilc; - env->regs[2] = do_syscall(env, n, - env->regs[2], - env->regs[3], - env->regs[4], - env->regs[5], - env->regs[6], - env->regs[7], - 0, 0); + n = env->int_svc_code; + if (!n) { + /* syscalls > 255 */ + n = env->regs[1]; + } + env->psw.addr += env->int_svc_ilen; + env->regs[2] = do_syscall(env, n, env->regs[2], env->regs[3], + env->regs[4], env->regs[5], + env->regs[6], env->regs[7], 0, 0); + break; + + case EXCP_DEBUG: + sig = gdb_handlesig(env, TARGET_SIGTRAP); + if (sig) { + n = TARGET_TRAP_BRKPT; + goto do_signal_pc; } break; - case EXCP_ADDR: - { - info.si_signo = SIGSEGV; - info.si_errno = 0; + case EXCP_PGM: + n = env->int_pgm_code; + switch (n) { + case PGM_OPERATION: + case PGM_PRIVILEGED: + sig = SIGILL; + n = TARGET_ILL_ILLOPC; + goto do_signal_pc; + case PGM_PROTECTION: + case PGM_ADDRESSING: + sig = SIGSEGV; /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->__excp_addr; - queue_signal(env, info.si_signo, &info); + n = TARGET_SEGV_MAPERR; + addr = env->__excp_addr; + goto do_signal; + case PGM_EXECUTE: + case PGM_SPECIFICATION: + case PGM_SPECIAL_OP: + case PGM_OPERAND: + do_sigill_opn: + sig = SIGILL; + n = TARGET_ILL_ILLOPN; + goto do_signal_pc; + + case PGM_FIXPT_OVERFLOW: + sig = SIGFPE; + n = TARGET_FPE_INTOVF; + goto do_signal_pc; + case PGM_FIXPT_DIVIDE: + sig = SIGFPE; + n = TARGET_FPE_INTDIV; + goto do_signal_pc; + + case PGM_DATA: + n = (env->fpc >> 8) & 0xff; + if (n == 0xff) { + /* compare-and-trap */ + goto do_sigill_opn; + } else { + /* An IEEE exception, simulated or otherwise. */ + if (n & 0x80) { + n = TARGET_FPE_FLTINV; + } else if (n & 0x40) { + n = TARGET_FPE_FLTDIV; + } else if (n & 0x20) { + n = TARGET_FPE_FLTOVF; + } else if (n & 0x10) { + n = TARGET_FPE_FLTUND; + } else if (n & 0x08) { + n = TARGET_FPE_FLTRES; + } else { + /* ??? Quantum exception; BFP, DFP error. */ + goto do_sigill_opn; + } + sig = SIGFPE; + goto do_signal_pc; + } + + default: + fprintf(stderr, "Unhandled program exception: %#x\n", n); + cpu_dump_state(env, stderr, fprintf, 0); + exit(1); } break; - case EXCP_SPEC: - { - fprintf(stderr,"specification exception insn 0x%08x%04x\n", ldl(env->psw.addr), lduw(env->psw.addr + 4)); - info.si_signo = SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPC; - info._sifields._sigfault._addr = env->__excp_addr; - queue_signal(env, info.si_signo, &info); - } + + do_signal_pc: + addr = env->psw.addr; + do_signal: + info.si_signo = sig; + info.si_errno = 0; + info.si_code = n; + info._sifields._sigfault._addr = addr; + queue_signal(env, info.si_signo, &info); break; + default: - printf ("Unhandled trap: 0x%x\n", trapnr); + fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(env, stderr, fprintf, 0); - exit (1); + exit(1); } process_pending_signals (env); } @@ -3049,22 +3188,18 @@ static void handle_arg_help(const char *arg) static void handle_arg_log(const char *arg) { int mask; - const CPULogItem *item; - mask = cpu_str_to_log_mask(arg); + mask = qemu_str_to_log_mask(arg); if (!mask) { - printf("Log items (comma separated):\n"); - for (item = cpu_log_items; item->mask != 0; item++) { - printf("%-10s %s\n", item->name, item->help); - } + qemu_print_log_usage(stdout); exit(1); } - cpu_set_log(mask); + qemu_set_log(mask); } static void handle_arg_log_filename(const char *arg) { - cpu_set_log_filename(arg); + qemu_set_log_filename(arg); } static void handle_arg_set_env(const char *arg) @@ -3244,9 +3379,10 @@ static const struct qemu_argument arg_table[] = { "size", "reserve 'size' bytes for guest virtual address space"}, #endif {"d", "QEMU_LOG", true, handle_arg_log, - "options", "activate log"}, + "item[,...]", "enable logging of specified items " + "(use '-d help' for a list of items)"}, {"D", "QEMU_LOG_FILENAME", true, handle_arg_log_filename, - "logfile", "override default logfile location"}, + "logfile", "write logs to 'logfile' (default stderr)"}, {"p", "QEMU_PAGESIZE", true, handle_arg_pagesize, "pagesize", "set the host page size to 'pagesize'"}, {"singlestep", "QEMU_SINGLESTEP", false, handle_arg_singlestep, @@ -3270,27 +3406,35 @@ static void usage(void) "Options and associated environment variables:\n" "\n"); - maxarglen = maxenvlen = 0; + /* Calculate column widths. We must always have at least enough space + * for the column header. + */ + maxarglen = strlen("Argument"); + maxenvlen = strlen("Env-variable"); for (arginfo = arg_table; arginfo->handle_opt != NULL; arginfo++) { + int arglen = strlen(arginfo->argv); + if (arginfo->has_arg) { + arglen += strlen(arginfo->example) + 1; + } if (strlen(arginfo->env) > maxenvlen) { maxenvlen = strlen(arginfo->env); } - if (strlen(arginfo->argv) > maxarglen) { - maxarglen = strlen(arginfo->argv); + if (arglen > maxarglen) { + maxarglen = arglen; } } - printf("%-*s%-*sDescription\n", maxarglen+3, "Argument", - maxenvlen+1, "Env-variable"); + printf("%-*s %-*s Description\n", maxarglen+1, "Argument", + maxenvlen, "Env-variable"); for (arginfo = arg_table; arginfo->handle_opt != NULL; arginfo++) { if (arginfo->has_arg) { printf("-%s %-*s %-*s %s\n", arginfo->argv, - (int)(maxarglen-strlen(arginfo->argv)), arginfo->example, - maxenvlen, arginfo->env, arginfo->help); + (int)(maxarglen - strlen(arginfo->argv) - 1), + arginfo->example, maxenvlen, arginfo->env, arginfo->help); } else { - printf("-%-*s %-*s %s\n", maxarglen+1, arginfo->argv, + printf("-%-*s %-*s %s\n", maxarglen, arginfo->argv, maxenvlen, arginfo->env, arginfo->help); } @@ -3299,11 +3443,9 @@ static void usage(void) printf("\n" "Defaults:\n" "QEMU_LD_PREFIX = %s\n" - "QEMU_STACK_SIZE = %ld byte\n" - "QEMU_LOG = %s\n", + "QEMU_STACK_SIZE = %ld byte\n", interp_prefix, - guest_stack_size, - DEBUG_LOGFILE); + guest_stack_size); printf("\n" "You can use -E and -U options or the QEMU_SET_ENV and\n" @@ -3387,7 +3529,6 @@ static int parse_args(int argc, char **argv) int main(int argc, char **argv, char **envp) { - const char *log_file = DEBUG_LOGFILE; struct target_pt_regs regs1, *regs = ®s1; struct image_info info1, *info = &info1; struct linux_binprm bprm; @@ -3430,8 +3571,6 @@ int main(int argc, char **argv, char **envp) cpudef_setup(); /* parse cpu definitions in target config file (TBD) */ #endif - /* init debug */ - cpu_set_log_filename(log_file); optind = parse_args(argc, argv); /* Zero out regs */ @@ -3491,7 +3630,7 @@ int main(int argc, char **argv, char **envp) fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } -#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC) +#if defined(TARGET_SPARC) || defined(TARGET_PPC) cpu_reset(ENV_GET_CPU(env)); #endif diff --git a/linux-user/mips64/syscall.h b/linux-user/mips64/syscall.h index e436ea57dc..cd707df32f 100644 --- a/linux-user/mips64/syscall.h +++ b/linux-user/mips64/syscall.h @@ -3,16 +3,16 @@ stack during a system call. */ struct target_pt_regs { - /* Saved main processor registers. */ - abi_ulong regs[32]; + /* Saved main processor registers. */ + target_ulong regs[32]; - /* Saved special registers. */ - abi_ulong cp0_status; - abi_ulong lo; - abi_ulong hi; - abi_ulong cp0_badvaddr; - abi_ulong cp0_cause; - abi_ulong cp0_epc; + /* Saved special registers. */ + target_ulong cp0_status; + target_ulong lo; + target_ulong hi; + target_ulong cp0_badvaddr; + target_ulong cp0_cause; + target_ulong cp0_epc; }; /* Target errno definitions taken from asm-mips/errno.h */ diff --git a/linux-user/mips64/syscall_nr.h b/linux-user/mips64/syscall_nr.h index 36d27b5159..0f4a6b107b 100644 --- a/linux-user/mips64/syscall_nr.h +++ b/linux-user/mips64/syscall_nr.h @@ -1,306 +1,620 @@ +#ifdef TARGET_ABI32 +/* + * Linux N32 syscalls are in the range from 6000 to 6999. + */ +#define TARGET_NR_Linux 6000 +#define TARGET_NR_read (TARGET_NR_Linux + 0) +#define TARGET_NR_write (TARGET_NR_Linux + 1) +#define TARGET_NR_open (TARGET_NR_Linux + 2) +#define TARGET_NR_close (TARGET_NR_Linux + 3) +#define TARGET_NR_stat (TARGET_NR_Linux + 4) +#define TARGET_NR_fstat (TARGET_NR_Linux + 5) +#define TARGET_NR_lstat (TARGET_NR_Linux + 6) +#define TARGET_NR_poll (TARGET_NR_Linux + 7) +#define TARGET_NR_lseek (TARGET_NR_Linux + 8) +#define TARGET_NR_mmap (TARGET_NR_Linux + 9) +#define TARGET_NR_mprotect (TARGET_NR_Linux + 10) +#define TARGET_NR_munmap (TARGET_NR_Linux + 11) +#define TARGET_NR_brk (TARGET_NR_Linux + 12) +#define TARGET_NR_rt_sigaction (TARGET_NR_Linux + 13) +#define TARGET_NR_rt_sigprocmask (TARGET_NR_Linux + 14) +#define TARGET_NR_ioctl (TARGET_NR_Linux + 15) +#define TARGET_NR_pread64 (TARGET_NR_Linux + 16) +#define TARGET_NR_pwrite64 (TARGET_NR_Linux + 17) +#define TARGET_NR_readv (TARGET_NR_Linux + 18) +#define TARGET_NR_writev (TARGET_NR_Linux + 19) +#define TARGET_NR_access (TARGET_NR_Linux + 20) +#define TARGET_NR_pipe (TARGET_NR_Linux + 21) +#define TARGET_NR__newselect (TARGET_NR_Linux + 22) +#define TARGET_NR_sched_yield (TARGET_NR_Linux + 23) +#define TARGET_NR_mremap (TARGET_NR_Linux + 24) +#define TARGET_NR_msync (TARGET_NR_Linux + 25) +#define TARGET_NR_mincore (TARGET_NR_Linux + 26) +#define TARGET_NR_madvise (TARGET_NR_Linux + 27) +#define TARGET_NR_shmget (TARGET_NR_Linux + 28) +#define TARGET_NR_shmat (TARGET_NR_Linux + 29) +#define TARGET_NR_shmctl (TARGET_NR_Linux + 30) +#define TARGET_NR_dup (TARGET_NR_Linux + 31) +#define TARGET_NR_dup2 (TARGET_NR_Linux + 32) +#define TARGET_NR_pause (TARGET_NR_Linux + 33) +#define TARGET_NR_nanosleep (TARGET_NR_Linux + 34) +#define TARGET_NR_getitimer (TARGET_NR_Linux + 35) +#define TARGET_NR_setitimer (TARGET_NR_Linux + 36) +#define TARGET_NR_alarm (TARGET_NR_Linux + 37) +#define TARGET_NR_getpid (TARGET_NR_Linux + 38) +#define TARGET_NR_sendfile (TARGET_NR_Linux + 39) +#define TARGET_NR_socket (TARGET_NR_Linux + 40) +#define TARGET_NR_connect (TARGET_NR_Linux + 41) +#define TARGET_NR_accept (TARGET_NR_Linux + 42) +#define TARGET_NR_sendto (TARGET_NR_Linux + 43) +#define TARGET_NR_recvfrom (TARGET_NR_Linux + 44) +#define TARGET_NR_sendmsg (TARGET_NR_Linux + 45) +#define TARGET_NR_recvmsg (TARGET_NR_Linux + 46) +#define TARGET_NR_shutdown (TARGET_NR_Linux + 47) +#define TARGET_NR_bind (TARGET_NR_Linux + 48) +#define TARGET_NR_listen (TARGET_NR_Linux + 49) +#define TARGET_NR_getsockname (TARGET_NR_Linux + 50) +#define TARGET_NR_getpeername (TARGET_NR_Linux + 51) +#define TARGET_NR_socketpair (TARGET_NR_Linux + 52) +#define TARGET_NR_setsockopt (TARGET_NR_Linux + 53) +#define TARGET_NR_getsockopt (TARGET_NR_Linux + 54) +#define TARGET_NR_clone (TARGET_NR_Linux + 55) +#define TARGET_NR_fork (TARGET_NR_Linux + 56) +#define TARGET_NR_execve (TARGET_NR_Linux + 57) +#define TARGET_NR_exit (TARGET_NR_Linux + 58) +#define TARGET_NR_wait4 (TARGET_NR_Linux + 59) +#define TARGET_NR_kill (TARGET_NR_Linux + 60) +#define TARGET_NR_uname (TARGET_NR_Linux + 61) +#define TARGET_NR_semget (TARGET_NR_Linux + 62) +#define TARGET_NR_semop (TARGET_NR_Linux + 63) +#define TARGET_NR_semctl (TARGET_NR_Linux + 64) +#define TARGET_NR_shmdt (TARGET_NR_Linux + 65) +#define TARGET_NR_msgget (TARGET_NR_Linux + 66) +#define TARGET_NR_msgsnd (TARGET_NR_Linux + 67) +#define TARGET_NR_msgrcv (TARGET_NR_Linux + 68) +#define TARGET_NR_msgctl (TARGET_NR_Linux + 69) +#define TARGET_NR_fcntl (TARGET_NR_Linux + 70) +#define TARGET_NR_flock (TARGET_NR_Linux + 71) +#define TARGET_NR_fsync (TARGET_NR_Linux + 72) +#define TARGET_NR_fdatasync (TARGET_NR_Linux + 73) +#define TARGET_NR_truncate (TARGET_NR_Linux + 74) +#define TARGET_NR_ftruncate (TARGET_NR_Linux + 75) +#define TARGET_NR_getdents (TARGET_NR_Linux + 76) +#define TARGET_NR_getcwd (TARGET_NR_Linux + 77) +#define TARGET_NR_chdir (TARGET_NR_Linux + 78) +#define TARGET_NR_fchdir (TARGET_NR_Linux + 79) +#define TARGET_NR_rename (TARGET_NR_Linux + 80) +#define TARGET_NR_mkdir (TARGET_NR_Linux + 81) +#define TARGET_NR_rmdir (TARGET_NR_Linux + 82) +#define TARGET_NR_creat (TARGET_NR_Linux + 83) +#define TARGET_NR_link (TARGET_NR_Linux + 84) +#define TARGET_NR_unlink (TARGET_NR_Linux + 85) +#define TARGET_NR_symlink (TARGET_NR_Linux + 86) +#define TARGET_NR_readlink (TARGET_NR_Linux + 87) +#define TARGET_NR_chmod (TARGET_NR_Linux + 88) +#define TARGET_NR_fchmod (TARGET_NR_Linux + 89) +#define TARGET_NR_chown (TARGET_NR_Linux + 90) +#define TARGET_NR_fchown (TARGET_NR_Linux + 91) +#define TARGET_NR_lchown (TARGET_NR_Linux + 92) +#define TARGET_NR_umask (TARGET_NR_Linux + 93) +#define TARGET_NR_gettimeofday (TARGET_NR_Linux + 94) +#define TARGET_NR_getrlimit (TARGET_NR_Linux + 95) +#define TARGET_NR_getrusage (TARGET_NR_Linux + 96) +#define TARGET_NR_sysinfo (TARGET_NR_Linux + 97) +#define TARGET_NR_times (TARGET_NR_Linux + 98) +#define TARGET_NR_ptrace (TARGET_NR_Linux + 99) +#define TARGET_NR_getuid (TARGET_NR_Linux + 100) +#define TARGET_NR_syslog (TARGET_NR_Linux + 101) +#define TARGET_NR_getgid (TARGET_NR_Linux + 102) +#define TARGET_NR_setuid (TARGET_NR_Linux + 103) +#define TARGET_NR_setgid (TARGET_NR_Linux + 104) +#define TARGET_NR_geteuid (TARGET_NR_Linux + 105) +#define TARGET_NR_getegid (TARGET_NR_Linux + 106) +#define TARGET_NR_setpgid (TARGET_NR_Linux + 107) +#define TARGET_NR_getppid (TARGET_NR_Linux + 108) +#define TARGET_NR_getpgrp (TARGET_NR_Linux + 109) +#define TARGET_NR_setsid (TARGET_NR_Linux + 110) +#define TARGET_NR_setreuid (TARGET_NR_Linux + 111) +#define TARGET_NR_setregid (TARGET_NR_Linux + 112) +#define TARGET_NR_getgroups (TARGET_NR_Linux + 113) +#define TARGET_NR_setgroups (TARGET_NR_Linux + 114) +#define TARGET_NR_setresuid (TARGET_NR_Linux + 115) +#define TARGET_NR_getresuid (TARGET_NR_Linux + 116) +#define TARGET_NR_setresgid (TARGET_NR_Linux + 117) +#define TARGET_NR_getresgid (TARGET_NR_Linux + 118) +#define TARGET_NR_getpgid (TARGET_NR_Linux + 119) +#define TARGET_NR_setfsuid (TARGET_NR_Linux + 120) +#define TARGET_NR_setfsgid (TARGET_NR_Linux + 121) +#define TARGET_NR_getsid (TARGET_NR_Linux + 122) +#define TARGET_NR_capget (TARGET_NR_Linux + 123) +#define TARGET_NR_capset (TARGET_NR_Linux + 124) +#define TARGET_NR_rt_sigpending (TARGET_NR_Linux + 125) +#define TARGET_NR_rt_sigtimedwait (TARGET_NR_Linux + 126) +#define TARGET_NR_rt_sigqueueinfo (TARGET_NR_Linux + 127) +#define TARGET_NR_rt_sigsuspend (TARGET_NR_Linux + 128) +#define TARGET_NR_sigaltstack (TARGET_NR_Linux + 129) +#define TARGET_NR_utime (TARGET_NR_Linux + 130) +#define TARGET_NR_mknod (TARGET_NR_Linux + 131) +#define TARGET_NR_personality (TARGET_NR_Linux + 132) +#define TARGET_NR_ustat (TARGET_NR_Linux + 133) +#define TARGET_NR_statfs (TARGET_NR_Linux + 134) +#define TARGET_NR_fstatfs (TARGET_NR_Linux + 135) +#define TARGET_NR_sysfs (TARGET_NR_Linux + 136) +#define TARGET_NR_getpriority (TARGET_NR_Linux + 137) +#define TARGET_NR_setpriority (TARGET_NR_Linux + 138) +#define TARGET_NR_sched_setparam (TARGET_NR_Linux + 139) +#define TARGET_NR_sched_getparam (TARGET_NR_Linux + 140) +#define TARGET_NR_sched_setscheduler (TARGET_NR_Linux + 141) +#define TARGET_NR_sched_getscheduler (TARGET_NR_Linux + 142) +#define TARGET_NR_sched_get_priority_max (TARGET_NR_Linux + 143) +#define TARGET_NR_sched_get_priority_min (TARGET_NR_Linux + 144) +#define TARGET_NR_sched_rr_get_interval (TARGET_NR_Linux + 145) +#define TARGET_NR_mlock (TARGET_NR_Linux + 146) +#define TARGET_NR_munlock (TARGET_NR_Linux + 147) +#define TARGET_NR_mlockall (TARGET_NR_Linux + 148) +#define TARGET_NR_munlockall (TARGET_NR_Linux + 149) +#define TARGET_NR_vhangup (TARGET_NR_Linux + 150) +#define TARGET_NR_pivot_root (TARGET_NR_Linux + 151) +#define TARGET_NR__sysctl (TARGET_NR_Linux + 152) +#define TARGET_NR_prctl (TARGET_NR_Linux + 153) +#define TARGET_NR_adjtimex (TARGET_NR_Linux + 154) +#define TARGET_NR_setrlimit (TARGET_NR_Linux + 155) +#define TARGET_NR_chroot (TARGET_NR_Linux + 156) +#define TARGET_NR_sync (TARGET_NR_Linux + 157) +#define TARGET_NR_acct (TARGET_NR_Linux + 158) +#define TARGET_NR_settimeofday (TARGET_NR_Linux + 159) +#define TARGET_NR_mount (TARGET_NR_Linux + 160) +#define TARGET_NR_umount2 (TARGET_NR_Linux + 161) +#define TARGET_NR_swapon (TARGET_NR_Linux + 162) +#define TARGET_NR_swapoff (TARGET_NR_Linux + 163) +#define TARGET_NR_reboot (TARGET_NR_Linux + 164) +#define TARGET_NR_sethostname (TARGET_NR_Linux + 165) +#define TARGET_NR_setdomainname (TARGET_NR_Linux + 166) +#define TARGET_NR_create_module (TARGET_NR_Linux + 167) +#define TARGET_NR_init_module (TARGET_NR_Linux + 168) +#define TARGET_NR_delete_module (TARGET_NR_Linux + 169) +#define TARGET_NR_get_kernel_syms (TARGET_NR_Linux + 170) +#define TARGET_NR_query_module (TARGET_NR_Linux + 171) +#define TARGET_NR_quotactl (TARGET_NR_Linux + 172) +#define TARGET_NR_nfsservctl (TARGET_NR_Linux + 173) +#define TARGET_NR_getpmsg (TARGET_NR_Linux + 174) +#define TARGET_NR_putpmsg (TARGET_NR_Linux + 175) +#define TARGET_NR_afs_syscall (TARGET_NR_Linux + 176) +#define TARGET_NR_reserved177 (TARGET_NR_Linux + 177) +#define TARGET_NR_gettid (TARGET_NR_Linux + 178) +#define TARGET_NR_readahead (TARGET_NR_Linux + 179) +#define TARGET_NR_setxattr (TARGET_NR_Linux + 180) +#define TARGET_NR_lsetxattr (TARGET_NR_Linux + 181) +#define TARGET_NR_fsetxattr (TARGET_NR_Linux + 182) +#define TARGET_NR_getxattr (TARGET_NR_Linux + 183) +#define TARGET_NR_lgetxattr (TARGET_NR_Linux + 184) +#define TARGET_NR_fgetxattr (TARGET_NR_Linux + 185) +#define TARGET_NR_listxattr (TARGET_NR_Linux + 186) +#define TARGET_NR_llistxattr (TARGET_NR_Linux + 187) +#define TARGET_NR_flistxattr (TARGET_NR_Linux + 188) +#define TARGET_NR_removexattr (TARGET_NR_Linux + 189) +#define TARGET_NR_lremovexattr (TARGET_NR_Linux + 190) +#define TARGET_NR_fremovexattr (TARGET_NR_Linux + 191) +#define TARGET_NR_tkill (TARGET_NR_Linux + 192) +#define TARGET_NR_reserved193 (TARGET_NR_Linux + 193) +#define TARGET_NR_futex (TARGET_NR_Linux + 194) +#define TARGET_NR_sched_setaffinity (TARGET_NR_Linux + 195) +#define TARGET_NR_sched_getaffinity (TARGET_NR_Linux + 196) +#define TARGET_NR_cacheflush (TARGET_NR_Linux + 197) +#define TARGET_NR_cachectl (TARGET_NR_Linux + 198) +#define TARGET_NR_sysmips (TARGET_NR_Linux + 199) +#define TARGET_NR_io_setup (TARGET_NR_Linux + 200) +#define TARGET_NR_io_destroy (TARGET_NR_Linux + 201) +#define TARGET_NR_io_getevents (TARGET_NR_Linux + 202) +#define TARGET_NR_io_submit (TARGET_NR_Linux + 203) +#define TARGET_NR_io_cancel (TARGET_NR_Linux + 204) +#define TARGET_NR_exit_group (TARGET_NR_Linux + 205) +#define TARGET_NR_lookup_dcookie (TARGET_NR_Linux + 206) +#define TARGET_NR_epoll_create (TARGET_NR_Linux + 207) +#define TARGET_NR_epoll_ctl (TARGET_NR_Linux + 208) +#define TARGET_NR_epoll_wait (TARGET_NR_Linux + 209) +#define TARGET_NR_remap_file_pages (TARGET_NR_Linux + 210) +#define TARGET_NR_rt_sigreturn (TARGET_NR_Linux + 211) +#define TARGET_NR_fcntl64 (TARGET_NR_Linux + 212) +#define TARGET_NR_set_tid_address (TARGET_NR_Linux + 213) +#define TARGET_NR_restart_syscall (TARGET_NR_Linux + 214) +#define TARGET_NR_semtimedop (TARGET_NR_Linux + 215) +#define TARGET_NR_fadvise64 (TARGET_NR_Linux + 216) +#define TARGET_NR_statfs64 (TARGET_NR_Linux + 217) +#define TARGET_NR_fstatfs64 (TARGET_NR_Linux + 218) +#define TARGET_NR_sendfile64 (TARGET_NR_Linux + 219) +#define TARGET_NR_timer_create (TARGET_NR_Linux + 220) +#define TARGET_NR_timer_settime (TARGET_NR_Linux + 221) +#define TARGET_NR_timer_gettime (TARGET_NR_Linux + 222) +#define TARGET_NR_timer_getoverrun (TARGET_NR_Linux + 223) +#define TARGET_NR_timer_delete (TARGET_NR_Linux + 224) +#define TARGET_NR_clock_settime (TARGET_NR_Linux + 225) +#define TARGET_NR_clock_gettime (TARGET_NR_Linux + 226) +#define TARGET_NR_clock_getres (TARGET_NR_Linux + 227) +#define TARGET_NR_clock_nanosleep (TARGET_NR_Linux + 228) +#define TARGET_NR_tgkill (TARGET_NR_Linux + 229) +#define TARGET_NR_utimes (TARGET_NR_Linux + 230) +#define TARGET_NR_mbind (TARGET_NR_Linux + 231) +#define TARGET_NR_get_mempolicy (TARGET_NR_Linux + 232) +#define TARGET_NR_set_mempolicy (TARGET_NR_Linux + 233) +#define TARGET_NR_mq_open (TARGET_NR_Linux + 234) +#define TARGET_NR_mq_unlink (TARGET_NR_Linux + 235) +#define TARGET_NR_mq_timedsend (TARGET_NR_Linux + 236) +#define TARGET_NR_mq_timedreceive (TARGET_NR_Linux + 237) +#define TARGET_NR_mq_notify (TARGET_NR_Linux + 238) +#define TARGET_NR_mq_getsetattr (TARGET_NR_Linux + 239) +#define TARGET_NR_vserver (TARGET_NR_Linux + 240) +#define TARGET_NR_waitid (TARGET_NR_Linux + 241) +/* #define TARGET_NR_sys_setaltroot (TARGET_NR_Linux + 242) */ +#define TARGET_NR_add_key (TARGET_NR_Linux + 243) +#define TARGET_NR_request_key (TARGET_NR_Linux + 244) +#define TARGET_NR_keyctl (TARGET_NR_Linux + 245) +#define TARGET_NR_set_thread_area (TARGET_NR_Linux + 246) +#define TARGET_NR_inotify_init (TARGET_NR_Linux + 247) +#define TARGET_NR_inotify_add_watch (TARGET_NR_Linux + 248) +#define TARGET_NR_inotify_rm_watch (TARGET_NR_Linux + 249) +#define TARGET_NR_migrate_pages (TARGET_NR_Linux + 250) +#define TARGET_NR_openat (TARGET_NR_Linux + 251) +#define TARGET_NR_mkdirat (TARGET_NR_Linux + 252) +#define TARGET_NR_mknodat (TARGET_NR_Linux + 253) +#define TARGET_NR_fchownat (TARGET_NR_Linux + 254) +#define TARGET_NR_futimesat (TARGET_NR_Linux + 255) +#define TARGET_NR_newfstatat (TARGET_NR_Linux + 256) +#define TARGET_NR_unlinkat (TARGET_NR_Linux + 257) +#define TARGET_NR_renameat (TARGET_NR_Linux + 258) +#define TARGET_NR_linkat (TARGET_NR_Linux + 259) +#define TARGET_NR_symlinkat (TARGET_NR_Linux + 260) +#define TARGET_NR_readlinkat (TARGET_NR_Linux + 261) +#define TARGET_NR_fchmodat (TARGET_NR_Linux + 262) +#define TARGET_NR_faccessat (TARGET_NR_Linux + 263) +#define TARGET_NR_pselect6 (TARGET_NR_Linux + 264) +#define TARGET_NR_ppoll (TARGET_NR_Linux + 265) +#define TARGET_NR_unshare (TARGET_NR_Linux + 266) +#define TARGET_NR_splice (TARGET_NR_Linux + 267) +#define TARGET_NR_sync_file_range (TARGET_NR_Linux + 268) +#define TARGET_NR_tee (TARGET_NR_Linux + 269) +#define TARGET_NR_vmsplice (TARGET_NR_Linux + 270) +#define TARGET_NR_move_pages (TARGET_NR_Linux + 271) +#define TARGET_NR_set_robust_list (TARGET_NR_Linux + 272) +#define TARGET_NR_get_robust_list (TARGET_NR_Linux + 273) +#define TARGET_NR_kexec_load (TARGET_NR_Linux + 274) +#define TARGET_NR_getcpu (TARGET_NR_Linux + 275) +#define TARGET_NR_epoll_pwait (TARGET_NR_Linux + 276) +#define TARGET_NR_ioprio_set (TARGET_NR_Linux + 277) +#define TARGET_NR_ioprio_get (TARGET_NR_Linux + 278) +#define TARGET_NR_utimensat (TARGET_NR_Linux + 279) +#define TARGET_NR_signalfd (TARGET_NR_Linux + 280) +#define TARGET_NR_timerfd (TARGET_NR_Linux + 281) +#define TARGET_NR_eventfd (TARGET_NR_Linux + 282) +#define TARGET_NR_fallocate (TARGET_NR_Linux + 283) +#define TARGET_NR_timerfd_create (TARGET_NR_Linux + 284) +#define TARGET_NR_timerfd_gettime (TARGET_NR_Linux + 285) +#define TARGET_NR_timerfd_settime (TARGET_NR_Linux + 286) +#define TARGET_NR_signalfd4 (TARGET_NR_Linux + 287) +#define TARGET_NR_eventfd2 (TARGET_NR_Linux + 288) +#define TARGET_NR_epoll_create1 (TARGET_NR_Linux + 289) +#define TARGET_NR_dup3 (TARGET_NR_Linux + 290) +#define TARGET_NR_pipe2 (TARGET_NR_Linux + 291) +#define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 292) +#define TARGET_NR_preadv (TARGET_NR_Linux + 293) +#define TARGET_NR_pwritev (TARGET_NR_Linux + 294) +#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 295) +#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 296) +#define TARGET_NR_accept4 (TARGET_NR_Linux + 297) +#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 298) +#define TARGET_NR_getdents64 (TARGET_NR_Linux + 299) +#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 300) +#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 301) +#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 302) +#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 303) +#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 304) +#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 305) +#define TARGET_NR_syncfs (TARGET_NR_Linux + 306) +#else /* * Linux 64-bit syscalls are in the range from 5000 to 5999. */ -#define TARGET_NR_Linux 5000 -#define TARGET_NR_read (TARGET_NR_Linux + 0) -#define TARGET_NR_write (TARGET_NR_Linux + 1) -#define TARGET_NR_open (TARGET_NR_Linux + 2) -#define TARGET_NR_close (TARGET_NR_Linux + 3) -#define TARGET_NR_stat (TARGET_NR_Linux + 4) -#define TARGET_NR_fstat (TARGET_NR_Linux + 5) -#define TARGET_NR_lstat (TARGET_NR_Linux + 6) -#define TARGET_NR_poll (TARGET_NR_Linux + 7) -#define TARGET_NR_lseek (TARGET_NR_Linux + 8) -#define TARGET_NR_mmap (TARGET_NR_Linux + 9) -#define TARGET_NR_mprotect (TARGET_NR_Linux + 10) -#define TARGET_NR_munmap (TARGET_NR_Linux + 11) -#define TARGET_NR_brk (TARGET_NR_Linux + 12) -#define TARGET_NR_rt_sigaction (TARGET_NR_Linux + 13) -#define TARGET_NR_rt_sigprocmask (TARGET_NR_Linux + 14) -#define TARGET_NR_ioctl (TARGET_NR_Linux + 15) -#define TARGET_NR_pread64 (TARGET_NR_Linux + 16) -#define TARGET_NR_pwrite64 (TARGET_NR_Linux + 17) -#define TARGET_NR_readv (TARGET_NR_Linux + 18) -#define TARGET_NR_writev (TARGET_NR_Linux + 19) -#define TARGET_NR_access (TARGET_NR_Linux + 20) -#define TARGET_NR_pipe (TARGET_NR_Linux + 21) -#define TARGET_NR__newselect (TARGET_NR_Linux + 22) -#define TARGET_NR_sched_yield (TARGET_NR_Linux + 23) -#define TARGET_NR_mremap (TARGET_NR_Linux + 24) -#define TARGET_NR_msync (TARGET_NR_Linux + 25) -#define TARGET_NR_mincore (TARGET_NR_Linux + 26) -#define TARGET_NR_madvise (TARGET_NR_Linux + 27) -#define TARGET_NR_shmget (TARGET_NR_Linux + 28) -#define TARGET_NR_shmat (TARGET_NR_Linux + 29) -#define TARGET_NR_shmctl (TARGET_NR_Linux + 30) -#define TARGET_NR_dup (TARGET_NR_Linux + 31) -#define TARGET_NR_dup2 (TARGET_NR_Linux + 32) -#define TARGET_NR_pause (TARGET_NR_Linux + 33) -#define TARGET_NR_nanosleep (TARGET_NR_Linux + 34) -#define TARGET_NR_getitimer (TARGET_NR_Linux + 35) -#define TARGET_NR_setitimer (TARGET_NR_Linux + 36) -#define TARGET_NR_alarm (TARGET_NR_Linux + 37) -#define TARGET_NR_getpid (TARGET_NR_Linux + 38) -#define TARGET_NR_sendfile (TARGET_NR_Linux + 39) -#define TARGET_NR_socket (TARGET_NR_Linux + 40) -#define TARGET_NR_connect (TARGET_NR_Linux + 41) -#define TARGET_NR_accept (TARGET_NR_Linux + 42) -#define TARGET_NR_sendto (TARGET_NR_Linux + 43) -#define TARGET_NR_recvfrom (TARGET_NR_Linux + 44) -#define TARGET_NR_sendmsg (TARGET_NR_Linux + 45) -#define TARGET_NR_recvmsg (TARGET_NR_Linux + 46) -#define TARGET_NR_shutdown (TARGET_NR_Linux + 47) -#define TARGET_NR_bind (TARGET_NR_Linux + 48) -#define TARGET_NR_listen (TARGET_NR_Linux + 49) -#define TARGET_NR_getsockname (TARGET_NR_Linux + 50) -#define TARGET_NR_getpeername (TARGET_NR_Linux + 51) -#define TARGET_NR_socketpair (TARGET_NR_Linux + 52) -#define TARGET_NR_setsockopt (TARGET_NR_Linux + 53) -#define TARGET_NR_getsockopt (TARGET_NR_Linux + 54) -#define TARGET_NR_clone (TARGET_NR_Linux + 55) -#define TARGET_NR_fork (TARGET_NR_Linux + 56) -#define TARGET_NR_execve (TARGET_NR_Linux + 57) -#define TARGET_NR_exit (TARGET_NR_Linux + 58) -#define TARGET_NR_wait4 (TARGET_NR_Linux + 59) -#define TARGET_NR_kill (TARGET_NR_Linux + 60) -#define TARGET_NR_uname (TARGET_NR_Linux + 61) -#define TARGET_NR_semget (TARGET_NR_Linux + 62) -#define TARGET_NR_semop (TARGET_NR_Linux + 63) -#define TARGET_NR_semctl (TARGET_NR_Linux + 64) -#define TARGET_NR_shmdt (TARGET_NR_Linux + 65) -#define TARGET_NR_msgget (TARGET_NR_Linux + 66) -#define TARGET_NR_msgsnd (TARGET_NR_Linux + 67) -#define TARGET_NR_msgrcv (TARGET_NR_Linux + 68) -#define TARGET_NR_msgctl (TARGET_NR_Linux + 69) -#define TARGET_NR_fcntl (TARGET_NR_Linux + 70) -#define TARGET_NR_flock (TARGET_NR_Linux + 71) -#define TARGET_NR_fsync (TARGET_NR_Linux + 72) -#define TARGET_NR_fdatasync (TARGET_NR_Linux + 73) -#define TARGET_NR_truncate (TARGET_NR_Linux + 74) -#define TARGET_NR_ftruncate (TARGET_NR_Linux + 75) -#define TARGET_NR_getdents (TARGET_NR_Linux + 76) -#define TARGET_NR_getcwd (TARGET_NR_Linux + 77) -#define TARGET_NR_chdir (TARGET_NR_Linux + 78) -#define TARGET_NR_fchdir (TARGET_NR_Linux + 79) -#define TARGET_NR_rename (TARGET_NR_Linux + 80) -#define TARGET_NR_mkdir (TARGET_NR_Linux + 81) -#define TARGET_NR_rmdir (TARGET_NR_Linux + 82) -#define TARGET_NR_creat (TARGET_NR_Linux + 83) -#define TARGET_NR_link (TARGET_NR_Linux + 84) -#define TARGET_NR_unlink (TARGET_NR_Linux + 85) -#define TARGET_NR_symlink (TARGET_NR_Linux + 86) -#define TARGET_NR_readlink (TARGET_NR_Linux + 87) -#define TARGET_NR_chmod (TARGET_NR_Linux + 88) -#define TARGET_NR_fchmod (TARGET_NR_Linux + 89) -#define TARGET_NR_chown (TARGET_NR_Linux + 90) -#define TARGET_NR_fchown (TARGET_NR_Linux + 91) -#define TARGET_NR_lchown (TARGET_NR_Linux + 92) -#define TARGET_NR_umask (TARGET_NR_Linux + 93) -#define TARGET_NR_gettimeofday (TARGET_NR_Linux + 94) -#define TARGET_NR_getrlimit (TARGET_NR_Linux + 95) -#define TARGET_NR_getrusage (TARGET_NR_Linux + 96) -#define TARGET_NR_sysinfo (TARGET_NR_Linux + 97) -#define TARGET_NR_times (TARGET_NR_Linux + 98) -#define TARGET_NR_ptrace (TARGET_NR_Linux + 99) -#define TARGET_NR_getuid (TARGET_NR_Linux + 100) -#define TARGET_NR_syslog (TARGET_NR_Linux + 101) -#define TARGET_NR_getgid (TARGET_NR_Linux + 102) -#define TARGET_NR_setuid (TARGET_NR_Linux + 103) -#define TARGET_NR_setgid (TARGET_NR_Linux + 104) -#define TARGET_NR_geteuid (TARGET_NR_Linux + 105) -#define TARGET_NR_getegid (TARGET_NR_Linux + 106) -#define TARGET_NR_setpgid (TARGET_NR_Linux + 107) -#define TARGET_NR_getppid (TARGET_NR_Linux + 108) -#define TARGET_NR_getpgrp (TARGET_NR_Linux + 109) -#define TARGET_NR_setsid (TARGET_NR_Linux + 110) -#define TARGET_NR_setreuid (TARGET_NR_Linux + 111) -#define TARGET_NR_setregid (TARGET_NR_Linux + 112) -#define TARGET_NR_getgroups (TARGET_NR_Linux + 113) -#define TARGET_NR_setgroups (TARGET_NR_Linux + 114) -#define TARGET_NR_setresuid (TARGET_NR_Linux + 115) -#define TARGET_NR_getresuid (TARGET_NR_Linux + 116) -#define TARGET_NR_setresgid (TARGET_NR_Linux + 117) -#define TARGET_NR_getresgid (TARGET_NR_Linux + 118) -#define TARGET_NR_getpgid (TARGET_NR_Linux + 119) -#define TARGET_NR_setfsuid (TARGET_NR_Linux + 120) -#define TARGET_NR_setfsgid (TARGET_NR_Linux + 121) -#define TARGET_NR_getsid (TARGET_NR_Linux + 122) -#define TARGET_NR_capget (TARGET_NR_Linux + 123) -#define TARGET_NR_capset (TARGET_NR_Linux + 124) -#define TARGET_NR_rt_sigpending (TARGET_NR_Linux + 125) -#define TARGET_NR_rt_sigtimedwait (TARGET_NR_Linux + 126) -#define TARGET_NR_rt_sigqueueinfo (TARGET_NR_Linux + 127) -#define TARGET_NR_rt_sigsuspend (TARGET_NR_Linux + 128) -#define TARGET_NR_sigaltstack (TARGET_NR_Linux + 129) -#define TARGET_NR_utime (TARGET_NR_Linux + 130) -#define TARGET_NR_mknod (TARGET_NR_Linux + 131) -#define TARGET_NR_personality (TARGET_NR_Linux + 132) -#define TARGET_NR_ustat (TARGET_NR_Linux + 133) -#define TARGET_NR_statfs (TARGET_NR_Linux + 134) -#define TARGET_NR_fstatfs (TARGET_NR_Linux + 135) -#define TARGET_NR_sysfs (TARGET_NR_Linux + 136) -#define TARGET_NR_getpriority (TARGET_NR_Linux + 137) -#define TARGET_NR_setpriority (TARGET_NR_Linux + 138) -#define TARGET_NR_sched_setparam (TARGET_NR_Linux + 139) -#define TARGET_NR_sched_getparam (TARGET_NR_Linux + 140) -#define TARGET_NR_sched_setscheduler (TARGET_NR_Linux + 141) -#define TARGET_NR_sched_getscheduler (TARGET_NR_Linux + 142) -#define TARGET_NR_sched_get_priority_max (TARGET_NR_Linux + 143) -#define TARGET_NR_sched_get_priority_min (TARGET_NR_Linux + 144) -#define TARGET_NR_sched_rr_get_interval (TARGET_NR_Linux + 145) -#define TARGET_NR_mlock (TARGET_NR_Linux + 146) -#define TARGET_NR_munlock (TARGET_NR_Linux + 147) -#define TARGET_NR_mlockall (TARGET_NR_Linux + 148) -#define TARGET_NR_munlockall (TARGET_NR_Linux + 149) -#define TARGET_NR_vhangup (TARGET_NR_Linux + 150) -#define TARGET_NR_pivot_root (TARGET_NR_Linux + 151) -#define TARGET_NR__sysctl (TARGET_NR_Linux + 152) -#define TARGET_NR_prctl (TARGET_NR_Linux + 153) -#define TARGET_NR_adjtimex (TARGET_NR_Linux + 154) -#define TARGET_NR_setrlimit (TARGET_NR_Linux + 155) -#define TARGET_NR_chroot (TARGET_NR_Linux + 156) -#define TARGET_NR_sync (TARGET_NR_Linux + 157) -#define TARGET_NR_acct (TARGET_NR_Linux + 158) -#define TARGET_NR_settimeofday (TARGET_NR_Linux + 159) -#define TARGET_NR_mount (TARGET_NR_Linux + 160) -#define TARGET_NR_umount2 (TARGET_NR_Linux + 161) -#define TARGET_NR_swapon (TARGET_NR_Linux + 162) -#define TARGET_NR_swapoff (TARGET_NR_Linux + 163) -#define TARGET_NR_reboot (TARGET_NR_Linux + 164) -#define TARGET_NR_sethostname (TARGET_NR_Linux + 165) -#define TARGET_NR_setdomainname (TARGET_NR_Linux + 166) -#define TARGET_NR_create_module (TARGET_NR_Linux + 167) -#define TARGET_NR_init_module (TARGET_NR_Linux + 168) -#define TARGET_NR_delete_module (TARGET_NR_Linux + 169) -#define TARGET_NR_get_kernel_syms (TARGET_NR_Linux + 170) -#define TARGET_NR_query_module (TARGET_NR_Linux + 171) -#define TARGET_NR_quotactl (TARGET_NR_Linux + 172) -#define TARGET_NR_nfsservctl (TARGET_NR_Linux + 173) -#define TARGET_NR_getpmsg (TARGET_NR_Linux + 174) -#define TARGET_NR_putpmsg (TARGET_NR_Linux + 175) -#define TARGET_NR_afs_syscall (TARGET_NR_Linux + 176) -#define TARGET_NR_reserved177 (TARGET_NR_Linux + 177) -#define TARGET_NR_gettid (TARGET_NR_Linux + 178) -#define TARGET_NR_readahead (TARGET_NR_Linux + 179) -#define TARGET_NR_setxattr (TARGET_NR_Linux + 180) -#define TARGET_NR_lsetxattr (TARGET_NR_Linux + 181) -#define TARGET_NR_fsetxattr (TARGET_NR_Linux + 182) -#define TARGET_NR_getxattr (TARGET_NR_Linux + 183) -#define TARGET_NR_lgetxattr (TARGET_NR_Linux + 184) -#define TARGET_NR_fgetxattr (TARGET_NR_Linux + 185) -#define TARGET_NR_listxattr (TARGET_NR_Linux + 186) -#define TARGET_NR_llistxattr (TARGET_NR_Linux + 187) -#define TARGET_NR_flistxattr (TARGET_NR_Linux + 188) -#define TARGET_NR_removexattr (TARGET_NR_Linux + 189) -#define TARGET_NR_lremovexattr (TARGET_NR_Linux + 190) -#define TARGET_NR_fremovexattr (TARGET_NR_Linux + 191) -#define TARGET_NR_tkill (TARGET_NR_Linux + 192) -#define TARGET_NR_reserved193 (TARGET_NR_Linux + 193) -#define TARGET_NR_futex (TARGET_NR_Linux + 194) -#define TARGET_NR_sched_setaffinity (TARGET_NR_Linux + 195) -#define TARGET_NR_sched_getaffinity (TARGET_NR_Linux + 196) -#define TARGET_NR_cacheflush (TARGET_NR_Linux + 197) -#define TARGET_NR_cachectl (TARGET_NR_Linux + 198) -#define TARGET_NR_sysmips (TARGET_NR_Linux + 199) -#define TARGET_NR_io_setup (TARGET_NR_Linux + 200) -#define TARGET_NR_io_destroy (TARGET_NR_Linux + 201) -#define TARGET_NR_io_getevents (TARGET_NR_Linux + 202) -#define TARGET_NR_io_submit (TARGET_NR_Linux + 203) -#define TARGET_NR_io_cancel (TARGET_NR_Linux + 204) -#define TARGET_NR_exit_group (TARGET_NR_Linux + 205) -#define TARGET_NR_lookup_dcookie (TARGET_NR_Linux + 206) -#define TARGET_NR_epoll_create (TARGET_NR_Linux + 207) -#define TARGET_NR_epoll_ctl (TARGET_NR_Linux + 208) -#define TARGET_NR_epoll_wait (TARGET_NR_Linux + 209) -#define TARGET_NR_remap_file_pages (TARGET_NR_Linux + 210) -#define TARGET_NR_rt_sigreturn (TARGET_NR_Linux + 211) -#define TARGET_NR_set_tid_address (TARGET_NR_Linux + 212) -#define TARGET_NR_restart_syscall (TARGET_NR_Linux + 213) -#define TARGET_NR_semtimedop (TARGET_NR_Linux + 214) -#define TARGET_NR_fadvise64 (TARGET_NR_Linux + 215) -#define TARGET_NR_timer_create (TARGET_NR_Linux + 216) -#define TARGET_NR_timer_settime (TARGET_NR_Linux + 217) -#define TARGET_NR_timer_gettime (TARGET_NR_Linux + 218) -#define TARGET_NR_timer_getoverrun (TARGET_NR_Linux + 219) -#define TARGET_NR_timer_delete (TARGET_NR_Linux + 220) -#define TARGET_NR_clock_settime (TARGET_NR_Linux + 221) -#define TARGET_NR_clock_gettime (TARGET_NR_Linux + 222) -#define TARGET_NR_clock_getres (TARGET_NR_Linux + 223) -#define TARGET_NR_clock_nanosleep (TARGET_NR_Linux + 224) -#define TARGET_NR_tgkill (TARGET_NR_Linux + 225) -#define TARGET_NR_utimes (TARGET_NR_Linux + 226) -#define TARGET_NR_mbind (TARGET_NR_Linux + 227) -#define TARGET_NR_get_mempolicy (TARGET_NR_Linux + 228) -#define TARGET_NR_set_mempolicy (TARGET_NR_Linux + 229) -#define TARGET_NR_mq_open (TARGET_NR_Linux + 230) -#define TARGET_NR_mq_unlink (TARGET_NR_Linux + 231) -#define TARGET_NR_mq_timedsend (TARGET_NR_Linux + 232) -#define TARGET_NR_mq_timedreceive (TARGET_NR_Linux + 233) -#define TARGET_NR_mq_notify (TARGET_NR_Linux + 234) -#define TARGET_NR_mq_getsetattr (TARGET_NR_Linux + 235) -#define TARGET_NR_vserver (TARGET_NR_Linux + 236) -#define TARGET_NR_waitid (TARGET_NR_Linux + 237) -/* #define TARGET_NR_sys_setaltroot (TARGET_NR_Linux + 238) */ -#define TARGET_NR_add_key (TARGET_NR_Linux + 239) -#define TARGET_NR_request_key (TARGET_NR_Linux + 240) -#define TARGET_NR_keyctl (TARGET_NR_Linux + 241) -#define TARGET_NR_set_thread_area (TARGET_NR_Linux + 242) -#define TARGET_NR_inotify_init (TARGET_NR_Linux + 243) -#define TARGET_NR_inotify_add_watch (TARGET_NR_Linux + 244) -#define TARGET_NR_inotify_rm_watch (TARGET_NR_Linux + 245) -#define TARGET_NR_migrate_pages (TARGET_NR_Linux + 246) -#define TARGET_NR_openat (TARGET_NR_Linux + 247) -#define TARGET_NR_mkdirat (TARGET_NR_Linux + 248) -#define TARGET_NR_mknodat (TARGET_NR_Linux + 249) -#define TARGET_NR_fchownat (TARGET_NR_Linux + 250) -#define TARGET_NR_futimesat (TARGET_NR_Linux + 251) -#define TARGET_NR_newfstatat (TARGET_NR_Linux + 252) -#define TARGET_NR_unlinkat (TARGET_NR_Linux + 253) -#define TARGET_NR_renameat (TARGET_NR_Linux + 254) -#define TARGET_NR_linkat (TARGET_NR_Linux + 255) -#define TARGET_NR_symlinkat (TARGET_NR_Linux + 256) -#define TARGET_NR_readlinkat (TARGET_NR_Linux + 257) -#define TARGET_NR_fchmodat (TARGET_NR_Linux + 258) -#define TARGET_NR_faccessat (TARGET_NR_Linux + 259) -#define TARGET_NR_pselect6 (TARGET_NR_Linux + 260) -#define TARGET_NR_ppoll (TARGET_NR_Linux + 261) -#define TARGET_NR_unshare (TARGET_NR_Linux + 262) -#define TARGET_NR_splice (TARGET_NR_Linux + 263) -#define TARGET_NR_sync_file_range (TARGET_NR_Linux + 264) -#define TARGET_NR_tee (TARGET_NR_Linux + 265) -#define TARGET_NR_vmsplice (TARGET_NR_Linux + 266) -#define TARGET_NR_move_pages (TARGET_NR_Linux + 267) -#define TARGET_NR_set_robust_list (TARGET_NR_Linux + 268) -#define TARGET_NR_get_robust_list (TARGET_NR_Linux + 269) -#define TARGET_NR_kexec_load (TARGET_NR_Linux + 270) -#define TARGET_NR_getcpu (TARGET_NR_Linux + 271) -#define TARGET_NR_epoll_pwait (TARGET_NR_Linux + 272) -#define TARGET_NR_ioprio_set (TARGET_NR_Linux + 273) -#define TARGET_NR_ioprio_get (TARGET_NR_Linux + 274) -#define TARGET_NR_utimensat (TARGET_NR_Linux + 275) -#define TARGET_NR_signalfd (TARGET_NR_Linux + 276) -#define TARGET_NR_timerfd (TARGET_NR_Linux + 277) -#define TARGET_NR_eventfd (TARGET_NR_Linux + 278) -#define TARGET_NR_fallocate (TARGET_NR_Linux + 279) -#define TARGET_NR_timerfd_create (TARGET_NR_Linux + 280) -#define TARGET_NR_timerfd_gettime (TARGET_NR_Linux + 281) -#define TARGET_NR_timerfd_settime (TARGET_NR_Linux + 282) -#define TARGET_NR_signalfd4 (TARGET_NR_Linux + 283) -#define TARGET_NR_eventfd2 (TARGET_NR_Linux + 284) -#define TARGET_NR_epoll_create1 (TARGET_NR_Linux + 285) -#define TARGET_NR_dup3 (TARGET_NR_Linux + 286) -#define TARGET_NR_pipe2 (TARGET_NR_Linux + 287) -#define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 288) -#define TARGET_NR_preadv (TARGET_NR_Linux + 289) -#define TARGET_NR_pwritev (TARGET_NR_Linux + 290) -#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 291) -#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 292) -#define TARGET_NR_accept4 (TARGET_NR_Linux + 293) -#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 294) -#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 295) -#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 296) -#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 297) -#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 298) -#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 299) -#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 300) -#define TARGET_NR_syncfs (TARGET_NR_Linux + 301) +#define TARGET_NR_Linux 5000 +#define TARGET_NR_read (TARGET_NR_Linux + 0) +#define TARGET_NR_write (TARGET_NR_Linux + 1) +#define TARGET_NR_open (TARGET_NR_Linux + 2) +#define TARGET_NR_close (TARGET_NR_Linux + 3) +#define TARGET_NR_stat (TARGET_NR_Linux + 4) +#define TARGET_NR_fstat (TARGET_NR_Linux + 5) +#define TARGET_NR_lstat (TARGET_NR_Linux + 6) +#define TARGET_NR_poll (TARGET_NR_Linux + 7) +#define TARGET_NR_lseek (TARGET_NR_Linux + 8) +#define TARGET_NR_mmap (TARGET_NR_Linux + 9) +#define TARGET_NR_mprotect (TARGET_NR_Linux + 10) +#define TARGET_NR_munmap (TARGET_NR_Linux + 11) +#define TARGET_NR_brk (TARGET_NR_Linux + 12) +#define TARGET_NR_rt_sigaction (TARGET_NR_Linux + 13) +#define TARGET_NR_rt_sigprocmask (TARGET_NR_Linux + 14) +#define TARGET_NR_ioctl (TARGET_NR_Linux + 15) +#define TARGET_NR_pread64 (TARGET_NR_Linux + 16) +#define TARGET_NR_pwrite64 (TARGET_NR_Linux + 17) +#define TARGET_NR_readv (TARGET_NR_Linux + 18) +#define TARGET_NR_writev (TARGET_NR_Linux + 19) +#define TARGET_NR_access (TARGET_NR_Linux + 20) +#define TARGET_NR_pipe (TARGET_NR_Linux + 21) +#define TARGET_NR__newselect (TARGET_NR_Linux + 22) +#define TARGET_NR_sched_yield (TARGET_NR_Linux + 23) +#define TARGET_NR_mremap (TARGET_NR_Linux + 24) +#define TARGET_NR_msync (TARGET_NR_Linux + 25) +#define TARGET_NR_mincore (TARGET_NR_Linux + 26) +#define TARGET_NR_madvise (TARGET_NR_Linux + 27) +#define TARGET_NR_shmget (TARGET_NR_Linux + 28) +#define TARGET_NR_shmat (TARGET_NR_Linux + 29) +#define TARGET_NR_shmctl (TARGET_NR_Linux + 30) +#define TARGET_NR_dup (TARGET_NR_Linux + 31) +#define TARGET_NR_dup2 (TARGET_NR_Linux + 32) +#define TARGET_NR_pause (TARGET_NR_Linux + 33) +#define TARGET_NR_nanosleep (TARGET_NR_Linux + 34) +#define TARGET_NR_getitimer (TARGET_NR_Linux + 35) +#define TARGET_NR_setitimer (TARGET_NR_Linux + 36) +#define TARGET_NR_alarm (TARGET_NR_Linux + 37) +#define TARGET_NR_getpid (TARGET_NR_Linux + 38) +#define TARGET_NR_sendfile (TARGET_NR_Linux + 39) +#define TARGET_NR_socket (TARGET_NR_Linux + 40) +#define TARGET_NR_connect (TARGET_NR_Linux + 41) +#define TARGET_NR_accept (TARGET_NR_Linux + 42) +#define TARGET_NR_sendto (TARGET_NR_Linux + 43) +#define TARGET_NR_recvfrom (TARGET_NR_Linux + 44) +#define TARGET_NR_sendmsg (TARGET_NR_Linux + 45) +#define TARGET_NR_recvmsg (TARGET_NR_Linux + 46) +#define TARGET_NR_shutdown (TARGET_NR_Linux + 47) +#define TARGET_NR_bind (TARGET_NR_Linux + 48) +#define TARGET_NR_listen (TARGET_NR_Linux + 49) +#define TARGET_NR_getsockname (TARGET_NR_Linux + 50) +#define TARGET_NR_getpeername (TARGET_NR_Linux + 51) +#define TARGET_NR_socketpair (TARGET_NR_Linux + 52) +#define TARGET_NR_setsockopt (TARGET_NR_Linux + 53) +#define TARGET_NR_getsockopt (TARGET_NR_Linux + 54) +#define TARGET_NR_clone (TARGET_NR_Linux + 55) +#define TARGET_NR_fork (TARGET_NR_Linux + 56) +#define TARGET_NR_execve (TARGET_NR_Linux + 57) +#define TARGET_NR_exit (TARGET_NR_Linux + 58) +#define TARGET_NR_wait4 (TARGET_NR_Linux + 59) +#define TARGET_NR_kill (TARGET_NR_Linux + 60) +#define TARGET_NR_uname (TARGET_NR_Linux + 61) +#define TARGET_NR_semget (TARGET_NR_Linux + 62) +#define TARGET_NR_semop (TARGET_NR_Linux + 63) +#define TARGET_NR_semctl (TARGET_NR_Linux + 64) +#define TARGET_NR_shmdt (TARGET_NR_Linux + 65) +#define TARGET_NR_msgget (TARGET_NR_Linux + 66) +#define TARGET_NR_msgsnd (TARGET_NR_Linux + 67) +#define TARGET_NR_msgrcv (TARGET_NR_Linux + 68) +#define TARGET_NR_msgctl (TARGET_NR_Linux + 69) +#define TARGET_NR_fcntl (TARGET_NR_Linux + 70) +#define TARGET_NR_flock (TARGET_NR_Linux + 71) +#define TARGET_NR_fsync (TARGET_NR_Linux + 72) +#define TARGET_NR_fdatasync (TARGET_NR_Linux + 73) +#define TARGET_NR_truncate (TARGET_NR_Linux + 74) +#define TARGET_NR_ftruncate (TARGET_NR_Linux + 75) +#define TARGET_NR_getdents (TARGET_NR_Linux + 76) +#define TARGET_NR_getcwd (TARGET_NR_Linux + 77) +#define TARGET_NR_chdir (TARGET_NR_Linux + 78) +#define TARGET_NR_fchdir (TARGET_NR_Linux + 79) +#define TARGET_NR_rename (TARGET_NR_Linux + 80) +#define TARGET_NR_mkdir (TARGET_NR_Linux + 81) +#define TARGET_NR_rmdir (TARGET_NR_Linux + 82) +#define TARGET_NR_creat (TARGET_NR_Linux + 83) +#define TARGET_NR_link (TARGET_NR_Linux + 84) +#define TARGET_NR_unlink (TARGET_NR_Linux + 85) +#define TARGET_NR_symlink (TARGET_NR_Linux + 86) +#define TARGET_NR_readlink (TARGET_NR_Linux + 87) +#define TARGET_NR_chmod (TARGET_NR_Linux + 88) +#define TARGET_NR_fchmod (TARGET_NR_Linux + 89) +#define TARGET_NR_chown (TARGET_NR_Linux + 90) +#define TARGET_NR_fchown (TARGET_NR_Linux + 91) +#define TARGET_NR_lchown (TARGET_NR_Linux + 92) +#define TARGET_NR_umask (TARGET_NR_Linux + 93) +#define TARGET_NR_gettimeofday (TARGET_NR_Linux + 94) +#define TARGET_NR_getrlimit (TARGET_NR_Linux + 95) +#define TARGET_NR_getrusage (TARGET_NR_Linux + 96) +#define TARGET_NR_sysinfo (TARGET_NR_Linux + 97) +#define TARGET_NR_times (TARGET_NR_Linux + 98) +#define TARGET_NR_ptrace (TARGET_NR_Linux + 99) +#define TARGET_NR_getuid (TARGET_NR_Linux + 100) +#define TARGET_NR_syslog (TARGET_NR_Linux + 101) +#define TARGET_NR_getgid (TARGET_NR_Linux + 102) +#define TARGET_NR_setuid (TARGET_NR_Linux + 103) +#define TARGET_NR_setgid (TARGET_NR_Linux + 104) +#define TARGET_NR_geteuid (TARGET_NR_Linux + 105) +#define TARGET_NR_getegid (TARGET_NR_Linux + 106) +#define TARGET_NR_setpgid (TARGET_NR_Linux + 107) +#define TARGET_NR_getppid (TARGET_NR_Linux + 108) +#define TARGET_NR_getpgrp (TARGET_NR_Linux + 109) +#define TARGET_NR_setsid (TARGET_NR_Linux + 110) +#define TARGET_NR_setreuid (TARGET_NR_Linux + 111) +#define TARGET_NR_setregid (TARGET_NR_Linux + 112) +#define TARGET_NR_getgroups (TARGET_NR_Linux + 113) +#define TARGET_NR_setgroups (TARGET_NR_Linux + 114) +#define TARGET_NR_setresuid (TARGET_NR_Linux + 115) +#define TARGET_NR_getresuid (TARGET_NR_Linux + 116) +#define TARGET_NR_setresgid (TARGET_NR_Linux + 117) +#define TARGET_NR_getresgid (TARGET_NR_Linux + 118) +#define TARGET_NR_getpgid (TARGET_NR_Linux + 119) +#define TARGET_NR_setfsuid (TARGET_NR_Linux + 120) +#define TARGET_NR_setfsgid (TARGET_NR_Linux + 121) +#define TARGET_NR_getsid (TARGET_NR_Linux + 122) +#define TARGET_NR_capget (TARGET_NR_Linux + 123) +#define TARGET_NR_capset (TARGET_NR_Linux + 124) +#define TARGET_NR_rt_sigpending (TARGET_NR_Linux + 125) +#define TARGET_NR_rt_sigtimedwait (TARGET_NR_Linux + 126) +#define TARGET_NR_rt_sigqueueinfo (TARGET_NR_Linux + 127) +#define TARGET_NR_rt_sigsuspend (TARGET_NR_Linux + 128) +#define TARGET_NR_sigaltstack (TARGET_NR_Linux + 129) +#define TARGET_NR_utime (TARGET_NR_Linux + 130) +#define TARGET_NR_mknod (TARGET_NR_Linux + 131) +#define TARGET_NR_personality (TARGET_NR_Linux + 132) +#define TARGET_NR_ustat (TARGET_NR_Linux + 133) +#define TARGET_NR_statfs (TARGET_NR_Linux + 134) +#define TARGET_NR_fstatfs (TARGET_NR_Linux + 135) +#define TARGET_NR_sysfs (TARGET_NR_Linux + 136) +#define TARGET_NR_getpriority (TARGET_NR_Linux + 137) +#define TARGET_NR_setpriority (TARGET_NR_Linux + 138) +#define TARGET_NR_sched_setparam (TARGET_NR_Linux + 139) +#define TARGET_NR_sched_getparam (TARGET_NR_Linux + 140) +#define TARGET_NR_sched_setscheduler (TARGET_NR_Linux + 141) +#define TARGET_NR_sched_getscheduler (TARGET_NR_Linux + 142) +#define TARGET_NR_sched_get_priority_max (TARGET_NR_Linux + 143) +#define TARGET_NR_sched_get_priority_min (TARGET_NR_Linux + 144) +#define TARGET_NR_sched_rr_get_interval (TARGET_NR_Linux + 145) +#define TARGET_NR_mlock (TARGET_NR_Linux + 146) +#define TARGET_NR_munlock (TARGET_NR_Linux + 147) +#define TARGET_NR_mlockall (TARGET_NR_Linux + 148) +#define TARGET_NR_munlockall (TARGET_NR_Linux + 149) +#define TARGET_NR_vhangup (TARGET_NR_Linux + 150) +#define TARGET_NR_pivot_root (TARGET_NR_Linux + 151) +#define TARGET_NR__sysctl (TARGET_NR_Linux + 152) +#define TARGET_NR_prctl (TARGET_NR_Linux + 153) +#define TARGET_NR_adjtimex (TARGET_NR_Linux + 154) +#define TARGET_NR_setrlimit (TARGET_NR_Linux + 155) +#define TARGET_NR_chroot (TARGET_NR_Linux + 156) +#define TARGET_NR_sync (TARGET_NR_Linux + 157) +#define TARGET_NR_acct (TARGET_NR_Linux + 158) +#define TARGET_NR_settimeofday (TARGET_NR_Linux + 159) +#define TARGET_NR_mount (TARGET_NR_Linux + 160) +#define TARGET_NR_umount2 (TARGET_NR_Linux + 161) +#define TARGET_NR_swapon (TARGET_NR_Linux + 162) +#define TARGET_NR_swapoff (TARGET_NR_Linux + 163) +#define TARGET_NR_reboot (TARGET_NR_Linux + 164) +#define TARGET_NR_sethostname (TARGET_NR_Linux + 165) +#define TARGET_NR_setdomainname (TARGET_NR_Linux + 166) +#define TARGET_NR_create_module (TARGET_NR_Linux + 167) +#define TARGET_NR_init_module (TARGET_NR_Linux + 168) +#define TARGET_NR_delete_module (TARGET_NR_Linux + 169) +#define TARGET_NR_get_kernel_syms (TARGET_NR_Linux + 170) +#define TARGET_NR_query_module (TARGET_NR_Linux + 171) +#define TARGET_NR_quotactl (TARGET_NR_Linux + 172) +#define TARGET_NR_nfsservctl (TARGET_NR_Linux + 173) +#define TARGET_NR_getpmsg (TARGET_NR_Linux + 174) +#define TARGET_NR_putpmsg (TARGET_NR_Linux + 175) +#define TARGET_NR_afs_syscall (TARGET_NR_Linux + 176) +#define TARGET_NR_reserved177 (TARGET_NR_Linux + 177) +#define TARGET_NR_gettid (TARGET_NR_Linux + 178) +#define TARGET_NR_readahead (TARGET_NR_Linux + 179) +#define TARGET_NR_setxattr (TARGET_NR_Linux + 180) +#define TARGET_NR_lsetxattr (TARGET_NR_Linux + 181) +#define TARGET_NR_fsetxattr (TARGET_NR_Linux + 182) +#define TARGET_NR_getxattr (TARGET_NR_Linux + 183) +#define TARGET_NR_lgetxattr (TARGET_NR_Linux + 184) +#define TARGET_NR_fgetxattr (TARGET_NR_Linux + 185) +#define TARGET_NR_listxattr (TARGET_NR_Linux + 186) +#define TARGET_NR_llistxattr (TARGET_NR_Linux + 187) +#define TARGET_NR_flistxattr (TARGET_NR_Linux + 188) +#define TARGET_NR_removexattr (TARGET_NR_Linux + 189) +#define TARGET_NR_lremovexattr (TARGET_NR_Linux + 190) +#define TARGET_NR_fremovexattr (TARGET_NR_Linux + 191) +#define TARGET_NR_tkill (TARGET_NR_Linux + 192) +#define TARGET_NR_reserved193 (TARGET_NR_Linux + 193) +#define TARGET_NR_futex (TARGET_NR_Linux + 194) +#define TARGET_NR_sched_setaffinity (TARGET_NR_Linux + 195) +#define TARGET_NR_sched_getaffinity (TARGET_NR_Linux + 196) +#define TARGET_NR_cacheflush (TARGET_NR_Linux + 197) +#define TARGET_NR_cachectl (TARGET_NR_Linux + 198) +#define TARGET_NR_sysmips (TARGET_NR_Linux + 199) +#define TARGET_NR_io_setup (TARGET_NR_Linux + 200) +#define TARGET_NR_io_destroy (TARGET_NR_Linux + 201) +#define TARGET_NR_io_getevents (TARGET_NR_Linux + 202) +#define TARGET_NR_io_submit (TARGET_NR_Linux + 203) +#define TARGET_NR_io_cancel (TARGET_NR_Linux + 204) +#define TARGET_NR_exit_group (TARGET_NR_Linux + 205) +#define TARGET_NR_lookup_dcookie (TARGET_NR_Linux + 206) +#define TARGET_NR_epoll_create (TARGET_NR_Linux + 207) +#define TARGET_NR_epoll_ctl (TARGET_NR_Linux + 208) +#define TARGET_NR_epoll_wait (TARGET_NR_Linux + 209) +#define TARGET_NR_remap_file_pages (TARGET_NR_Linux + 210) +#define TARGET_NR_rt_sigreturn (TARGET_NR_Linux + 211) +#define TARGET_NR_set_tid_address (TARGET_NR_Linux + 212) +#define TARGET_NR_restart_syscall (TARGET_NR_Linux + 213) +#define TARGET_NR_semtimedop (TARGET_NR_Linux + 214) +#define TARGET_NR_fadvise64 (TARGET_NR_Linux + 215) +#define TARGET_NR_timer_create (TARGET_NR_Linux + 216) +#define TARGET_NR_timer_settime (TARGET_NR_Linux + 217) +#define TARGET_NR_timer_gettime (TARGET_NR_Linux + 218) +#define TARGET_NR_timer_getoverrun (TARGET_NR_Linux + 219) +#define TARGET_NR_timer_delete (TARGET_NR_Linux + 220) +#define TARGET_NR_clock_settime (TARGET_NR_Linux + 221) +#define TARGET_NR_clock_gettime (TARGET_NR_Linux + 222) +#define TARGET_NR_clock_getres (TARGET_NR_Linux + 223) +#define TARGET_NR_clock_nanosleep (TARGET_NR_Linux + 224) +#define TARGET_NR_tgkill (TARGET_NR_Linux + 225) +#define TARGET_NR_utimes (TARGET_NR_Linux + 226) +#define TARGET_NR_mbind (TARGET_NR_Linux + 227) +#define TARGET_NR_get_mempolicy (TARGET_NR_Linux + 228) +#define TARGET_NR_set_mempolicy (TARGET_NR_Linux + 229) +#define TARGET_NR_mq_open (TARGET_NR_Linux + 230) +#define TARGET_NR_mq_unlink (TARGET_NR_Linux + 231) +#define TARGET_NR_mq_timedsend (TARGET_NR_Linux + 232) +#define TARGET_NR_mq_timedreceive (TARGET_NR_Linux + 233) +#define TARGET_NR_mq_notify (TARGET_NR_Linux + 234) +#define TARGET_NR_mq_getsetattr (TARGET_NR_Linux + 235) +#define TARGET_NR_vserver (TARGET_NR_Linux + 236) +#define TARGET_NR_waitid (TARGET_NR_Linux + 237) +/* #define TARGET_NR_sys_setaltroot (TARGET_NR_Linux + 238) */ +#define TARGET_NR_add_key (TARGET_NR_Linux + 239) +#define TARGET_NR_request_key (TARGET_NR_Linux + 240) +#define TARGET_NR_keyctl (TARGET_NR_Linux + 241) +#define TARGET_NR_set_thread_area (TARGET_NR_Linux + 242) +#define TARGET_NR_inotify_init (TARGET_NR_Linux + 243) +#define TARGET_NR_inotify_add_watch (TARGET_NR_Linux + 244) +#define TARGET_NR_inotify_rm_watch (TARGET_NR_Linux + 245) +#define TARGET_NR_migrate_pages (TARGET_NR_Linux + 246) +#define TARGET_NR_openat (TARGET_NR_Linux + 247) +#define TARGET_NR_mkdirat (TARGET_NR_Linux + 248) +#define TARGET_NR_mknodat (TARGET_NR_Linux + 249) +#define TARGET_NR_fchownat (TARGET_NR_Linux + 250) +#define TARGET_NR_futimesat (TARGET_NR_Linux + 251) +#define TARGET_NR_newfstatat (TARGET_NR_Linux + 252) +#define TARGET_NR_unlinkat (TARGET_NR_Linux + 253) +#define TARGET_NR_renameat (TARGET_NR_Linux + 254) +#define TARGET_NR_linkat (TARGET_NR_Linux + 255) +#define TARGET_NR_symlinkat (TARGET_NR_Linux + 256) +#define TARGET_NR_readlinkat (TARGET_NR_Linux + 257) +#define TARGET_NR_fchmodat (TARGET_NR_Linux + 258) +#define TARGET_NR_faccessat (TARGET_NR_Linux + 259) +#define TARGET_NR_pselect6 (TARGET_NR_Linux + 260) +#define TARGET_NR_ppoll (TARGET_NR_Linux + 261) +#define TARGET_NR_unshare (TARGET_NR_Linux + 262) +#define TARGET_NR_splice (TARGET_NR_Linux + 263) +#define TARGET_NR_sync_file_range (TARGET_NR_Linux + 264) +#define TARGET_NR_tee (TARGET_NR_Linux + 265) +#define TARGET_NR_vmsplice (TARGET_NR_Linux + 266) +#define TARGET_NR_move_pages (TARGET_NR_Linux + 267) +#define TARGET_NR_set_robust_list (TARGET_NR_Linux + 268) +#define TARGET_NR_get_robust_list (TARGET_NR_Linux + 269) +#define TARGET_NR_kexec_load (TARGET_NR_Linux + 270) +#define TARGET_NR_getcpu (TARGET_NR_Linux + 271) +#define TARGET_NR_epoll_pwait (TARGET_NR_Linux + 272) +#define TARGET_NR_ioprio_set (TARGET_NR_Linux + 273) +#define TARGET_NR_ioprio_get (TARGET_NR_Linux + 274) +#define TARGET_NR_utimensat (TARGET_NR_Linux + 275) +#define TARGET_NR_signalfd (TARGET_NR_Linux + 276) +#define TARGET_NR_timerfd (TARGET_NR_Linux + 277) +#define TARGET_NR_eventfd (TARGET_NR_Linux + 278) +#define TARGET_NR_fallocate (TARGET_NR_Linux + 279) +#define TARGET_NR_timerfd_create (TARGET_NR_Linux + 280) +#define TARGET_NR_timerfd_gettime (TARGET_NR_Linux + 281) +#define TARGET_NR_timerfd_settime (TARGET_NR_Linux + 282) +#define TARGET_NR_signalfd4 (TARGET_NR_Linux + 283) +#define TARGET_NR_eventfd2 (TARGET_NR_Linux + 284) +#define TARGET_NR_epoll_create1 (TARGET_NR_Linux + 285) +#define TARGET_NR_dup3 (TARGET_NR_Linux + 286) +#define TARGET_NR_pipe2 (TARGET_NR_Linux + 287) +#define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 288) +#define TARGET_NR_preadv (TARGET_NR_Linux + 289) +#define TARGET_NR_pwritev (TARGET_NR_Linux + 290) +#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 291) +#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 292) +#define TARGET_NR_accept4 (TARGET_NR_Linux + 293) +#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 294) +#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 295) +#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 296) +#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 297) +#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 298) +#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 299) +#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 300) +#define TARGET_NR_syncfs (TARGET_NR_Linux + 301) +#endif diff --git a/linux-user/mipsn32/syscall.h b/linux-user/mipsn32/syscall.h deleted file mode 100644 index ebe98f2070..0000000000 --- a/linux-user/mipsn32/syscall.h +++ /dev/null @@ -1,224 +0,0 @@ - -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - /* Saved main processor registers. */ - target_ulong regs[32]; - - /* Saved special registers. */ - target_ulong cp0_status; - target_ulong lo; - target_ulong hi; - target_ulong cp0_badvaddr; - target_ulong cp0_cause; - target_ulong cp0_epc; -}; - -/* Target errno definitions taken from asm-mips/errno.h */ -#undef TARGET_ENOMSG -#define TARGET_ENOMSG 35 /* Identifier removed */ -#undef TARGET_EIDRM -#define TARGET_EIDRM 36 /* Identifier removed */ -#undef TARGET_ECHRNG -#define TARGET_ECHRNG 37 /* Channel number out of range */ -#undef TARGET_EL2NSYNC -#define TARGET_EL2NSYNC 38 /* Level 2 not synchronized */ -#undef TARGET_EL3HLT -#define TARGET_EL3HLT 39 /* Level 3 halted */ -#undef TARGET_EL3RST -#define TARGET_EL3RST 40 /* Level 3 reset */ -#undef TARGET_ELNRNG -#define TARGET_ELNRNG 41 /* Link number out of range */ -#undef TARGET_EUNATCH -#define TARGET_EUNATCH 42 /* Protocol driver not attached */ -#undef TARGET_ENOCSI -#define TARGET_ENOCSI 43 /* No CSI structure available */ -#undef TARGET_EL2HLT -#define TARGET_EL2HLT 44 /* Level 2 halted */ -#undef TARGET_EDEADLK -#define TARGET_EDEADLK 45 /* Resource deadlock would occur */ -#undef TARGET_ENOLCK -#define TARGET_ENOLCK 46 /* No record locks available */ -#undef TARGET_EBADE -#define TARGET_EBADE 50 /* Invalid exchange */ -#undef TARGET_EBADR -#define TARGET_EBADR 51 /* Invalid request descriptor */ -#undef TARGET_EXFULL -#define TARGET_EXFULL 52 /* TARGET_Exchange full */ -#undef TARGET_ENOANO -#define TARGET_ENOANO 53 /* No anode */ -#undef TARGET_EBADRQC -#define TARGET_EBADRQC 54 /* Invalid request code */ -#undef TARGET_EBADSLT -#define TARGET_EBADSLT 55 /* Invalid slot */ -#undef TARGET_EDEADLOCK -#define TARGET_EDEADLOCK 56 /* File locking deadlock error */ -#undef TARGET_EBFONT -#define TARGET_EBFONT 59 /* Bad font file format */ -#undef TARGET_ENOSTR -#define TARGET_ENOSTR 60 /* Device not a stream */ -#undef TARGET_ENODATA -#define TARGET_ENODATA 61 /* No data available */ -#undef TARGET_ETIME -#define TARGET_ETIME 62 /* Timer expired */ -#undef TARGET_ENOSR -#define TARGET_ENOSR 63 /* Out of streams resources */ -#undef TARGET_ENONET -#define TARGET_ENONET 64 /* Machine is not on the network */ -#undef TARGET_ENOPKG -#define TARGET_ENOPKG 65 /* Package not installed */ -#undef TARGET_EREMOTE -#define TARGET_EREMOTE 66 /* Object is remote */ -#undef TARGET_ENOLINK -#define TARGET_ENOLINK 67 /* Link has been severed */ -#undef TARGET_EADV -#define TARGET_EADV 68 /* Advertise error */ -#undef TARGET_ESRMNT -#define TARGET_ESRMNT 69 /* Srmount error */ -#undef TARGET_ECOMM -#define TARGET_ECOMM 70 /* Communication error on send */ -#undef TARGET_EPROTO -#define TARGET_EPROTO 71 /* Protocol error */ -#undef TARGET_EDOTDOT -#define TARGET_EDOTDOT 73 /* RFS specific error */ -#undef TARGET_EMULTIHOP -#define TARGET_EMULTIHOP 74 /* Multihop attempted */ -#undef TARGET_EBADMSG -#define TARGET_EBADMSG 77 /* Not a data message */ -#undef TARGET_ENAMETOOLONG -#define TARGET_ENAMETOOLONG 78 /* File name too long */ -#undef TARGET_EOVERFLOW -#define TARGET_EOVERFLOW 79 /* Value too large for defined data type */ -#undef TARGET_ENOTUNIQ -#define TARGET_ENOTUNIQ 80 /* Name not unique on network */ -#undef TARGET_EBADFD -#define TARGET_EBADFD 81 /* File descriptor in bad state */ -#undef TARGET_EREMCHG -#define TARGET_EREMCHG 82 /* Remote address changed */ -#undef TARGET_ELIBACC -#define TARGET_ELIBACC 83 /* Can not access a needed shared library */ -#undef TARGET_ELIBBAD -#define TARGET_ELIBBAD 84 /* Accessing a corrupted shared library */ -#undef TARGET_ELIBSCN -#define TARGET_ELIBSCN 85 /* .lib section in a.out corrupted */ -#undef TARGET_ELIBMAX -#define TARGET_ELIBMAX 86 /* Attempting to link in too many shared libraries */ -#undef TARGET_ELIBEXEC -#define TARGET_ELIBEXEC 87 /* Cannot exec a shared library directly */ -#undef TARGET_EILSEQ -#define TARGET_EILSEQ 88 /* Illegal byte sequence */ -#undef TARGET_ENOSYS -#define TARGET_ENOSYS 89 /* Function not implemented */ -#undef TARGET_ELOOP -#define TARGET_ELOOP 90 /* Too many symbolic links encountered */ -#undef TARGET_ERESTART -#define TARGET_ERESTART 91 /* Interrupted system call should be restarted */ -#undef TARGET_ESTRPIPE -#define TARGET_ESTRPIPE 92 /* Streams pipe error */ -#undef TARGET_ENOTEMPTY -#define TARGET_ENOTEMPTY 93 /* Directory not empty */ -#undef TARGET_EUSERS -#define TARGET_EUSERS 94 /* Too many users */ -#undef TARGET_ENOTSOCK -#define TARGET_ENOTSOCK 95 /* Socket operation on non-socket */ -#undef TARGET_EDESTADDRREQ -#define TARGET_EDESTADDRREQ 96 /* Destination address required */ -#undef TARGET_EMSGSIZE -#define TARGET_EMSGSIZE 97 /* Message too long */ -#undef TARGET_EPROTOTYPE -#define TARGET_EPROTOTYPE 98 /* Protocol wrong type for socket */ -#undef TARGET_ENOPROTOOPT -#define TARGET_ENOPROTOOPT 99 /* Protocol not available */ -#undef TARGET_EPROTONOSUPPORT -#define TARGET_EPROTONOSUPPORT 120 /* Protocol not supported */ -#undef TARGET_ESOCKTNOSUPPORT -#define TARGET_ESOCKTNOSUPPORT 121 /* Socket type not supported */ -#undef TARGET_EOPNOTSUPP -#define TARGET_EOPNOTSUPP 122 /* Operation not supported on transport endpoint */ -#undef TARGET_EPFNOSUPPORT -#define TARGET_EPFNOSUPPORT 123 /* Protocol family not supported */ -#undef TARGET_EAFNOSUPPORT -#define TARGET_EAFNOSUPPORT 124 /* Address family not supported by protocol */ -#undef TARGET_EADDRINUSE -#define TARGET_EADDRINUSE 125 /* Address already in use */ -#undef TARGET_EADDRNOTAVAIL -#define TARGET_EADDRNOTAVAIL 126 /* Cannot assign requested address */ -#undef TARGET_ENETDOWN -#define TARGET_ENETDOWN 127 /* Network is down */ -#undef TARGET_ENETUNREACH -#define TARGET_ENETUNREACH 128 /* Network is unreachable */ -#undef TARGET_ENETRESET -#define TARGET_ENETRESET 129 /* Network dropped connection because of reset */ -#undef TARGET_ECONNABORTED -#define TARGET_ECONNABORTED 130 /* Software caused connection abort */ -#undef TARGET_ECONNRESET -#define TARGET_ECONNRESET 131 /* Connection reset by peer */ -#undef TARGET_ENOBUFS -#define TARGET_ENOBUFS 132 /* No buffer space available */ -#undef TARGET_EISCONN -#define TARGET_EISCONN 133 /* Transport endpoint is already connected */ -#undef TARGET_ENOTCONN -#define TARGET_ENOTCONN 134 /* Transport endpoint is not connected */ -#undef TARGET_EUCLEAN -#define TARGET_EUCLEAN 135 /* Structure needs cleaning */ -#undef TARGET_ENOTNAM -#define TARGET_ENOTNAM 137 /* Not a XENIX named type file */ -#undef TARGET_ENAVAIL -#define TARGET_ENAVAIL 138 /* No XENIX semaphores available */ -#undef TARGET_EISNAM -#define TARGET_EISNAM 139 /* Is a named type file */ -#undef TARGET_EREMOTEIO -#define TARGET_EREMOTEIO 140 /* Remote I/O error */ -#undef TARGET_EINIT -#define TARGET_EINIT 141 /* Reserved */ -#undef TARGET_EREMDEV -#define TARGET_EREMDEV 142 /* TARGET_Error 142 */ -#undef TARGET_ESHUTDOWN -#define TARGET_ESHUTDOWN 143 /* Cannot send after transport endpoint shutdown */ -#undef TARGET_ETOOMANYREFS -#define TARGET_ETOOMANYREFS 144 /* Too many references: cannot splice */ -#undef TARGET_ETIMEDOUT -#define TARGET_ETIMEDOUT 145 /* Connection timed out */ -#undef TARGET_ECONNREFUSED -#define TARGET_ECONNREFUSED 146 /* Connection refused */ -#undef TARGET_EHOSTDOWN -#define TARGET_EHOSTDOWN 147 /* Host is down */ -#undef TARGET_EHOSTUNREACH -#define TARGET_EHOSTUNREACH 148 /* No route to host */ -#undef TARGET_EALREADY -#define TARGET_EALREADY 149 /* Operation already in progress */ -#undef TARGET_EINPROGRESS -#define TARGET_EINPROGRESS 150 /* Operation now in progress */ -#undef TARGET_ESTALE -#define TARGET_ESTALE 151 /* Stale NFS file handle */ -#undef TARGET_ECANCELED -#define TARGET_ECANCELED 158 /* AIO operation canceled */ -/* - * These error are Linux extensions. - */ -#undef TARGET_ENOMEDIUM -#define TARGET_ENOMEDIUM 159 /* No medium found */ -#undef TARGET_EMEDIUMTYPE -#define TARGET_EMEDIUMTYPE 160 /* Wrong medium type */ -#undef TARGET_ENOKEY -#define TARGET_ENOKEY 161 /* Required key not available */ -#undef TARGET_EKEYEXPIRED -#define TARGET_EKEYEXPIRED 162 /* Key has expired */ -#undef TARGET_EKEYREVOKED -#define TARGET_EKEYREVOKED 163 /* Key has been revoked */ -#undef TARGET_EKEYREJECTED -#define TARGET_EKEYREJECTED 164 /* Key was rejected by service */ - -/* for robust mutexes */ -#undef TARGET_EOWNERDEAD -#define TARGET_EOWNERDEAD 165 /* Owner died */ -#undef TARGET_ENOTRECOVERABLE -#define TARGET_ENOTRECOVERABLE 166 /* State not recoverable */ - - - -/* Nasty hack: define a fake errno value for use by sigreturn. */ -#define TARGET_QEMU_ESIGRETURN 255 - -#define UNAME_MACHINE "mips64" diff --git a/linux-user/mipsn32/syscall_nr.h b/linux-user/mipsn32/syscall_nr.h deleted file mode 100644 index 4e1aca3a9b..0000000000 --- a/linux-user/mipsn32/syscall_nr.h +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Linux N32 syscalls are in the range from 6000 to 6999. - */ -#define TARGET_NR_Linux 6000 -#define TARGET_NR_read (TARGET_NR_Linux + 0) -#define TARGET_NR_write (TARGET_NR_Linux + 1) -#define TARGET_NR_open (TARGET_NR_Linux + 2) -#define TARGET_NR_close (TARGET_NR_Linux + 3) -#define TARGET_NR_stat (TARGET_NR_Linux + 4) -#define TARGET_NR_fstat (TARGET_NR_Linux + 5) -#define TARGET_NR_lstat (TARGET_NR_Linux + 6) -#define TARGET_NR_poll (TARGET_NR_Linux + 7) -#define TARGET_NR_lseek (TARGET_NR_Linux + 8) -#define TARGET_NR_mmap (TARGET_NR_Linux + 9) -#define TARGET_NR_mprotect (TARGET_NR_Linux + 10) -#define TARGET_NR_munmap (TARGET_NR_Linux + 11) -#define TARGET_NR_brk (TARGET_NR_Linux + 12) -#define TARGET_NR_rt_sigaction (TARGET_NR_Linux + 13) -#define TARGET_NR_rt_sigprocmask (TARGET_NR_Linux + 14) -#define TARGET_NR_ioctl (TARGET_NR_Linux + 15) -#define TARGET_NR_pread64 (TARGET_NR_Linux + 16) -#define TARGET_NR_pwrite64 (TARGET_NR_Linux + 17) -#define TARGET_NR_readv (TARGET_NR_Linux + 18) -#define TARGET_NR_writev (TARGET_NR_Linux + 19) -#define TARGET_NR_access (TARGET_NR_Linux + 20) -#define TARGET_NR_pipe (TARGET_NR_Linux + 21) -#define TARGET_NR__newselect (TARGET_NR_Linux + 22) -#define TARGET_NR_sched_yield (TARGET_NR_Linux + 23) -#define TARGET_NR_mremap (TARGET_NR_Linux + 24) -#define TARGET_NR_msync (TARGET_NR_Linux + 25) -#define TARGET_NR_mincore (TARGET_NR_Linux + 26) -#define TARGET_NR_madvise (TARGET_NR_Linux + 27) -#define TARGET_NR_shmget (TARGET_NR_Linux + 28) -#define TARGET_NR_shmat (TARGET_NR_Linux + 29) -#define TARGET_NR_shmctl (TARGET_NR_Linux + 30) -#define TARGET_NR_dup (TARGET_NR_Linux + 31) -#define TARGET_NR_dup2 (TARGET_NR_Linux + 32) -#define TARGET_NR_pause (TARGET_NR_Linux + 33) -#define TARGET_NR_nanosleep (TARGET_NR_Linux + 34) -#define TARGET_NR_getitimer (TARGET_NR_Linux + 35) -#define TARGET_NR_setitimer (TARGET_NR_Linux + 36) -#define TARGET_NR_alarm (TARGET_NR_Linux + 37) -#define TARGET_NR_getpid (TARGET_NR_Linux + 38) -#define TARGET_NR_sendfile (TARGET_NR_Linux + 39) -#define TARGET_NR_socket (TARGET_NR_Linux + 40) -#define TARGET_NR_connect (TARGET_NR_Linux + 41) -#define TARGET_NR_accept (TARGET_NR_Linux + 42) -#define TARGET_NR_sendto (TARGET_NR_Linux + 43) -#define TARGET_NR_recvfrom (TARGET_NR_Linux + 44) -#define TARGET_NR_sendmsg (TARGET_NR_Linux + 45) -#define TARGET_NR_recvmsg (TARGET_NR_Linux + 46) -#define TARGET_NR_shutdown (TARGET_NR_Linux + 47) -#define TARGET_NR_bind (TARGET_NR_Linux + 48) -#define TARGET_NR_listen (TARGET_NR_Linux + 49) -#define TARGET_NR_getsockname (TARGET_NR_Linux + 50) -#define TARGET_NR_getpeername (TARGET_NR_Linux + 51) -#define TARGET_NR_socketpair (TARGET_NR_Linux + 52) -#define TARGET_NR_setsockopt (TARGET_NR_Linux + 53) -#define TARGET_NR_getsockopt (TARGET_NR_Linux + 54) -#define TARGET_NR_clone (TARGET_NR_Linux + 55) -#define TARGET_NR_fork (TARGET_NR_Linux + 56) -#define TARGET_NR_execve (TARGET_NR_Linux + 57) -#define TARGET_NR_exit (TARGET_NR_Linux + 58) -#define TARGET_NR_wait4 (TARGET_NR_Linux + 59) -#define TARGET_NR_kill (TARGET_NR_Linux + 60) -#define TARGET_NR_uname (TARGET_NR_Linux + 61) -#define TARGET_NR_semget (TARGET_NR_Linux + 62) -#define TARGET_NR_semop (TARGET_NR_Linux + 63) -#define TARGET_NR_semctl (TARGET_NR_Linux + 64) -#define TARGET_NR_shmdt (TARGET_NR_Linux + 65) -#define TARGET_NR_msgget (TARGET_NR_Linux + 66) -#define TARGET_NR_msgsnd (TARGET_NR_Linux + 67) -#define TARGET_NR_msgrcv (TARGET_NR_Linux + 68) -#define TARGET_NR_msgctl (TARGET_NR_Linux + 69) -#define TARGET_NR_fcntl (TARGET_NR_Linux + 70) -#define TARGET_NR_flock (TARGET_NR_Linux + 71) -#define TARGET_NR_fsync (TARGET_NR_Linux + 72) -#define TARGET_NR_fdatasync (TARGET_NR_Linux + 73) -#define TARGET_NR_truncate (TARGET_NR_Linux + 74) -#define TARGET_NR_ftruncate (TARGET_NR_Linux + 75) -#define TARGET_NR_getdents (TARGET_NR_Linux + 76) -#define TARGET_NR_getcwd (TARGET_NR_Linux + 77) -#define TARGET_NR_chdir (TARGET_NR_Linux + 78) -#define TARGET_NR_fchdir (TARGET_NR_Linux + 79) -#define TARGET_NR_rename (TARGET_NR_Linux + 80) -#define TARGET_NR_mkdir (TARGET_NR_Linux + 81) -#define TARGET_NR_rmdir (TARGET_NR_Linux + 82) -#define TARGET_NR_creat (TARGET_NR_Linux + 83) -#define TARGET_NR_link (TARGET_NR_Linux + 84) -#define TARGET_NR_unlink (TARGET_NR_Linux + 85) -#define TARGET_NR_symlink (TARGET_NR_Linux + 86) -#define TARGET_NR_readlink (TARGET_NR_Linux + 87) -#define TARGET_NR_chmod (TARGET_NR_Linux + 88) -#define TARGET_NR_fchmod (TARGET_NR_Linux + 89) -#define TARGET_NR_chown (TARGET_NR_Linux + 90) -#define TARGET_NR_fchown (TARGET_NR_Linux + 91) -#define TARGET_NR_lchown (TARGET_NR_Linux + 92) -#define TARGET_NR_umask (TARGET_NR_Linux + 93) -#define TARGET_NR_gettimeofday (TARGET_NR_Linux + 94) -#define TARGET_NR_getrlimit (TARGET_NR_Linux + 95) -#define TARGET_NR_getrusage (TARGET_NR_Linux + 96) -#define TARGET_NR_sysinfo (TARGET_NR_Linux + 97) -#define TARGET_NR_times (TARGET_NR_Linux + 98) -#define TARGET_NR_ptrace (TARGET_NR_Linux + 99) -#define TARGET_NR_getuid (TARGET_NR_Linux + 100) -#define TARGET_NR_syslog (TARGET_NR_Linux + 101) -#define TARGET_NR_getgid (TARGET_NR_Linux + 102) -#define TARGET_NR_setuid (TARGET_NR_Linux + 103) -#define TARGET_NR_setgid (TARGET_NR_Linux + 104) -#define TARGET_NR_geteuid (TARGET_NR_Linux + 105) -#define TARGET_NR_getegid (TARGET_NR_Linux + 106) -#define TARGET_NR_setpgid (TARGET_NR_Linux + 107) -#define TARGET_NR_getppid (TARGET_NR_Linux + 108) -#define TARGET_NR_getpgrp (TARGET_NR_Linux + 109) -#define TARGET_NR_setsid (TARGET_NR_Linux + 110) -#define TARGET_NR_setreuid (TARGET_NR_Linux + 111) -#define TARGET_NR_setregid (TARGET_NR_Linux + 112) -#define TARGET_NR_getgroups (TARGET_NR_Linux + 113) -#define TARGET_NR_setgroups (TARGET_NR_Linux + 114) -#define TARGET_NR_setresuid (TARGET_NR_Linux + 115) -#define TARGET_NR_getresuid (TARGET_NR_Linux + 116) -#define TARGET_NR_setresgid (TARGET_NR_Linux + 117) -#define TARGET_NR_getresgid (TARGET_NR_Linux + 118) -#define TARGET_NR_getpgid (TARGET_NR_Linux + 119) -#define TARGET_NR_setfsuid (TARGET_NR_Linux + 120) -#define TARGET_NR_setfsgid (TARGET_NR_Linux + 121) -#define TARGET_NR_getsid (TARGET_NR_Linux + 122) -#define TARGET_NR_capget (TARGET_NR_Linux + 123) -#define TARGET_NR_capset (TARGET_NR_Linux + 124) -#define TARGET_NR_rt_sigpending (TARGET_NR_Linux + 125) -#define TARGET_NR_rt_sigtimedwait (TARGET_NR_Linux + 126) -#define TARGET_NR_rt_sigqueueinfo (TARGET_NR_Linux + 127) -#define TARGET_NR_rt_sigsuspend (TARGET_NR_Linux + 128) -#define TARGET_NR_sigaltstack (TARGET_NR_Linux + 129) -#define TARGET_NR_utime (TARGET_NR_Linux + 130) -#define TARGET_NR_mknod (TARGET_NR_Linux + 131) -#define TARGET_NR_personality (TARGET_NR_Linux + 132) -#define TARGET_NR_ustat (TARGET_NR_Linux + 133) -#define TARGET_NR_statfs (TARGET_NR_Linux + 134) -#define TARGET_NR_fstatfs (TARGET_NR_Linux + 135) -#define TARGET_NR_sysfs (TARGET_NR_Linux + 136) -#define TARGET_NR_getpriority (TARGET_NR_Linux + 137) -#define TARGET_NR_setpriority (TARGET_NR_Linux + 138) -#define TARGET_NR_sched_setparam (TARGET_NR_Linux + 139) -#define TARGET_NR_sched_getparam (TARGET_NR_Linux + 140) -#define TARGET_NR_sched_setscheduler (TARGET_NR_Linux + 141) -#define TARGET_NR_sched_getscheduler (TARGET_NR_Linux + 142) -#define TARGET_NR_sched_get_priority_max (TARGET_NR_Linux + 143) -#define TARGET_NR_sched_get_priority_min (TARGET_NR_Linux + 144) -#define TARGET_NR_sched_rr_get_interval (TARGET_NR_Linux + 145) -#define TARGET_NR_mlock (TARGET_NR_Linux + 146) -#define TARGET_NR_munlock (TARGET_NR_Linux + 147) -#define TARGET_NR_mlockall (TARGET_NR_Linux + 148) -#define TARGET_NR_munlockall (TARGET_NR_Linux + 149) -#define TARGET_NR_vhangup (TARGET_NR_Linux + 150) -#define TARGET_NR_pivot_root (TARGET_NR_Linux + 151) -#define TARGET_NR__sysctl (TARGET_NR_Linux + 152) -#define TARGET_NR_prctl (TARGET_NR_Linux + 153) -#define TARGET_NR_adjtimex (TARGET_NR_Linux + 154) -#define TARGET_NR_setrlimit (TARGET_NR_Linux + 155) -#define TARGET_NR_chroot (TARGET_NR_Linux + 156) -#define TARGET_NR_sync (TARGET_NR_Linux + 157) -#define TARGET_NR_acct (TARGET_NR_Linux + 158) -#define TARGET_NR_settimeofday (TARGET_NR_Linux + 159) -#define TARGET_NR_mount (TARGET_NR_Linux + 160) -#define TARGET_NR_umount2 (TARGET_NR_Linux + 161) -#define TARGET_NR_swapon (TARGET_NR_Linux + 162) -#define TARGET_NR_swapoff (TARGET_NR_Linux + 163) -#define TARGET_NR_reboot (TARGET_NR_Linux + 164) -#define TARGET_NR_sethostname (TARGET_NR_Linux + 165) -#define TARGET_NR_setdomainname (TARGET_NR_Linux + 166) -#define TARGET_NR_create_module (TARGET_NR_Linux + 167) -#define TARGET_NR_init_module (TARGET_NR_Linux + 168) -#define TARGET_NR_delete_module (TARGET_NR_Linux + 169) -#define TARGET_NR_get_kernel_syms (TARGET_NR_Linux + 170) -#define TARGET_NR_query_module (TARGET_NR_Linux + 171) -#define TARGET_NR_quotactl (TARGET_NR_Linux + 172) -#define TARGET_NR_nfsservctl (TARGET_NR_Linux + 173) -#define TARGET_NR_getpmsg (TARGET_NR_Linux + 174) -#define TARGET_NR_putpmsg (TARGET_NR_Linux + 175) -#define TARGET_NR_afs_syscall (TARGET_NR_Linux + 176) -#define TARGET_NR_reserved177 (TARGET_NR_Linux + 177) -#define TARGET_NR_gettid (TARGET_NR_Linux + 178) -#define TARGET_NR_readahead (TARGET_NR_Linux + 179) -#define TARGET_NR_setxattr (TARGET_NR_Linux + 180) -#define TARGET_NR_lsetxattr (TARGET_NR_Linux + 181) -#define TARGET_NR_fsetxattr (TARGET_NR_Linux + 182) -#define TARGET_NR_getxattr (TARGET_NR_Linux + 183) -#define TARGET_NR_lgetxattr (TARGET_NR_Linux + 184) -#define TARGET_NR_fgetxattr (TARGET_NR_Linux + 185) -#define TARGET_NR_listxattr (TARGET_NR_Linux + 186) -#define TARGET_NR_llistxattr (TARGET_NR_Linux + 187) -#define TARGET_NR_flistxattr (TARGET_NR_Linux + 188) -#define TARGET_NR_removexattr (TARGET_NR_Linux + 189) -#define TARGET_NR_lremovexattr (TARGET_NR_Linux + 190) -#define TARGET_NR_fremovexattr (TARGET_NR_Linux + 191) -#define TARGET_NR_tkill (TARGET_NR_Linux + 192) -#define TARGET_NR_reserved193 (TARGET_NR_Linux + 193) -#define TARGET_NR_futex (TARGET_NR_Linux + 194) -#define TARGET_NR_sched_setaffinity (TARGET_NR_Linux + 195) -#define TARGET_NR_sched_getaffinity (TARGET_NR_Linux + 196) -#define TARGET_NR_cacheflush (TARGET_NR_Linux + 197) -#define TARGET_NR_cachectl (TARGET_NR_Linux + 198) -#define TARGET_NR_sysmips (TARGET_NR_Linux + 199) -#define TARGET_NR_io_setup (TARGET_NR_Linux + 200) -#define TARGET_NR_io_destroy (TARGET_NR_Linux + 201) -#define TARGET_NR_io_getevents (TARGET_NR_Linux + 202) -#define TARGET_NR_io_submit (TARGET_NR_Linux + 203) -#define TARGET_NR_io_cancel (TARGET_NR_Linux + 204) -#define TARGET_NR_exit_group (TARGET_NR_Linux + 205) -#define TARGET_NR_lookup_dcookie (TARGET_NR_Linux + 206) -#define TARGET_NR_epoll_create (TARGET_NR_Linux + 207) -#define TARGET_NR_epoll_ctl (TARGET_NR_Linux + 208) -#define TARGET_NR_epoll_wait (TARGET_NR_Linux + 209) -#define TARGET_NR_remap_file_pages (TARGET_NR_Linux + 210) -#define TARGET_NR_rt_sigreturn (TARGET_NR_Linux + 211) -#define TARGET_NR_fcntl64 (TARGET_NR_Linux + 212) -#define TARGET_NR_set_tid_address (TARGET_NR_Linux + 213) -#define TARGET_NR_restart_syscall (TARGET_NR_Linux + 214) -#define TARGET_NR_semtimedop (TARGET_NR_Linux + 215) -#define TARGET_NR_fadvise64 (TARGET_NR_Linux + 216) -#define TARGET_NR_statfs64 (TARGET_NR_Linux + 217) -#define TARGET_NR_fstatfs64 (TARGET_NR_Linux + 218) -#define TARGET_NR_sendfile64 (TARGET_NR_Linux + 219) -#define TARGET_NR_timer_create (TARGET_NR_Linux + 220) -#define TARGET_NR_timer_settime (TARGET_NR_Linux + 221) -#define TARGET_NR_timer_gettime (TARGET_NR_Linux + 222) -#define TARGET_NR_timer_getoverrun (TARGET_NR_Linux + 223) -#define TARGET_NR_timer_delete (TARGET_NR_Linux + 224) -#define TARGET_NR_clock_settime (TARGET_NR_Linux + 225) -#define TARGET_NR_clock_gettime (TARGET_NR_Linux + 226) -#define TARGET_NR_clock_getres (TARGET_NR_Linux + 227) -#define TARGET_NR_clock_nanosleep (TARGET_NR_Linux + 228) -#define TARGET_NR_tgkill (TARGET_NR_Linux + 229) -#define TARGET_NR_utimes (TARGET_NR_Linux + 230) -#define TARGET_NR_mbind (TARGET_NR_Linux + 231) -#define TARGET_NR_get_mempolicy (TARGET_NR_Linux + 232) -#define TARGET_NR_set_mempolicy (TARGET_NR_Linux + 233) -#define TARGET_NR_mq_open (TARGET_NR_Linux + 234) -#define TARGET_NR_mq_unlink (TARGET_NR_Linux + 235) -#define TARGET_NR_mq_timedsend (TARGET_NR_Linux + 236) -#define TARGET_NR_mq_timedreceive (TARGET_NR_Linux + 237) -#define TARGET_NR_mq_notify (TARGET_NR_Linux + 238) -#define TARGET_NR_mq_getsetattr (TARGET_NR_Linux + 239) -#define TARGET_NR_vserver (TARGET_NR_Linux + 240) -#define TARGET_NR_waitid (TARGET_NR_Linux + 241) -/* #define TARGET_NR_sys_setaltroot (TARGET_NR_Linux + 242) */ -#define TARGET_NR_add_key (TARGET_NR_Linux + 243) -#define TARGET_NR_request_key (TARGET_NR_Linux + 244) -#define TARGET_NR_keyctl (TARGET_NR_Linux + 245) -#define TARGET_NR_set_thread_area (TARGET_NR_Linux + 246) -#define TARGET_NR_inotify_init (TARGET_NR_Linux + 247) -#define TARGET_NR_inotify_add_watch (TARGET_NR_Linux + 248) -#define TARGET_NR_inotify_rm_watch (TARGET_NR_Linux + 249) -#define TARGET_NR_migrate_pages (TARGET_NR_Linux + 250) -#define TARGET_NR_openat (TARGET_NR_Linux + 251) -#define TARGET_NR_mkdirat (TARGET_NR_Linux + 252) -#define TARGET_NR_mknodat (TARGET_NR_Linux + 253) -#define TARGET_NR_fchownat (TARGET_NR_Linux + 254) -#define TARGET_NR_futimesat (TARGET_NR_Linux + 255) -#define TARGET_NR_newfstatat (TARGET_NR_Linux + 256) -#define TARGET_NR_unlinkat (TARGET_NR_Linux + 257) -#define TARGET_NR_renameat (TARGET_NR_Linux + 258) -#define TARGET_NR_linkat (TARGET_NR_Linux + 259) -#define TARGET_NR_symlinkat (TARGET_NR_Linux + 260) -#define TARGET_NR_readlinkat (TARGET_NR_Linux + 261) -#define TARGET_NR_fchmodat (TARGET_NR_Linux + 262) -#define TARGET_NR_faccessat (TARGET_NR_Linux + 263) -#define TARGET_NR_pselect6 (TARGET_NR_Linux + 264) -#define TARGET_NR_ppoll (TARGET_NR_Linux + 265) -#define TARGET_NR_unshare (TARGET_NR_Linux + 266) -#define TARGET_NR_splice (TARGET_NR_Linux + 267) -#define TARGET_NR_sync_file_range (TARGET_NR_Linux + 268) -#define TARGET_NR_tee (TARGET_NR_Linux + 269) -#define TARGET_NR_vmsplice (TARGET_NR_Linux + 270) -#define TARGET_NR_move_pages (TARGET_NR_Linux + 271) -#define TARGET_NR_set_robust_list (TARGET_NR_Linux + 272) -#define TARGET_NR_get_robust_list (TARGET_NR_Linux + 273) -#define TARGET_NR_kexec_load (TARGET_NR_Linux + 274) -#define TARGET_NR_getcpu (TARGET_NR_Linux + 275) -#define TARGET_NR_epoll_pwait (TARGET_NR_Linux + 276) -#define TARGET_NR_ioprio_set (TARGET_NR_Linux + 277) -#define TARGET_NR_ioprio_get (TARGET_NR_Linux + 278) -#define TARGET_NR_utimensat (TARGET_NR_Linux + 279) -#define TARGET_NR_signalfd (TARGET_NR_Linux + 280) -#define TARGET_NR_timerfd (TARGET_NR_Linux + 281) -#define TARGET_NR_eventfd (TARGET_NR_Linux + 282) -#define TARGET_NR_fallocate (TARGET_NR_Linux + 283) -#define TARGET_NR_timerfd_create (TARGET_NR_Linux + 284) -#define TARGET_NR_timerfd_gettime (TARGET_NR_Linux + 285) -#define TARGET_NR_timerfd_settime (TARGET_NR_Linux + 286) -#define TARGET_NR_signalfd4 (TARGET_NR_Linux + 287) -#define TARGET_NR_eventfd2 (TARGET_NR_Linux + 288) -#define TARGET_NR_epoll_create1 (TARGET_NR_Linux + 289) -#define TARGET_NR_dup3 (TARGET_NR_Linux + 290) -#define TARGET_NR_pipe2 (TARGET_NR_Linux + 291) -#define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 292) -#define TARGET_NR_preadv (TARGET_NR_Linux + 293) -#define TARGET_NR_pwritev (TARGET_NR_Linux + 294) -#define TARGET_NR_rt_tgsigqueueinfo (TARGET_NR_Linux + 295) -#define TARGET_NR_perf_event_open (TARGET_NR_Linux + 296) -#define TARGET_NR_accept4 (TARGET_NR_Linux + 297) -#define TARGET_NR_recvmmsg (TARGET_NR_Linux + 298) -#define TARGET_NR_getdents64 (TARGET_NR_Linux + 299) -#define TARGET_NR_fanotify_init (TARGET_NR_Linux + 300) -#define TARGET_NR_fanotify_mark (TARGET_NR_Linux + 301) -#define TARGET_NR_prlimit64 (TARGET_NR_Linux + 302) -#define TARGET_NR_name_to_handle_at (TARGET_NR_Linux + 303) -#define TARGET_NR_open_by_handle_at (TARGET_NR_Linux + 304) -#define TARGET_NR_clock_adjtime (TARGET_NR_Linux + 305) -#define TARGET_NR_syncfs (TARGET_NR_Linux + 306) diff --git a/linux-user/mipsn32/target_signal.h b/linux-user/mipsn32/target_signal.h deleted file mode 100644 index ff20d9e33e..0000000000 --- a/linux-user/mipsn32/target_signal.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef TARGET_SIGNAL_H -#define TARGET_SIGNAL_H - -#include "cpu.h" - -/* this struct defines a stack used during syscall handling */ - -typedef struct target_sigaltstack { - int32_t ss_sp; - uint32_t ss_size; - int32_t ss_flags; -} target_stack_t; - - -/* - * sigaltstack controls - */ -#define TARGET_SS_ONSTACK 1 -#define TARGET_SS_DISABLE 2 - -#define TARGET_MINSIGSTKSZ 2048 -#define TARGET_SIGSTKSZ 8192 - -static inline target_ulong get_sp_from_cpustate(CPUMIPSState *state) -{ - return state->active_tc.gpr[29]; -} - -#endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/mipsn32/termbits.h b/linux-user/mipsn32/termbits.h deleted file mode 100644 index d3a6cf8f91..0000000000 --- a/linux-user/mipsn32/termbits.h +++ /dev/null @@ -1,245 +0,0 @@ -/* from asm/termbits.h */ - -#define TARGET_NCCS 23 - -struct target_termios { - unsigned int c_iflag; /* input mode flags */ - unsigned int c_oflag; /* output mode flags */ - unsigned int c_cflag; /* control mode flags */ - unsigned int c_lflag; /* local mode flags */ - unsigned char c_line; /* line discipline */ - unsigned char c_cc[TARGET_NCCS]; /* control characters */ -}; - -/* c_iflag bits */ -#define TARGET_IGNBRK 0000001 -#define TARGET_BRKINT 0000002 -#define TARGET_IGNPAR 0000004 -#define TARGET_PARMRK 0000010 -#define TARGET_INPCK 0000020 -#define TARGET_ISTRIP 0000040 -#define TARGET_INLCR 0000100 -#define TARGET_IGNCR 0000200 -#define TARGET_ICRNL 0000400 -#define TARGET_IUCLC 0001000 -#define TARGET_IXON 0002000 -#define TARGET_IXANY 0004000 -#define TARGET_IXOFF 0010000 -#define TARGET_IMAXBEL 0020000 -#define TARGET_IUTF8 0040000 - -/* c_oflag bits */ -#define TARGET_OPOST 0000001 -#define TARGET_OLCUC 0000002 -#define TARGET_ONLCR 0000004 -#define TARGET_OCRNL 0000010 -#define TARGET_ONOCR 0000020 -#define TARGET_ONLRET 0000040 -#define TARGET_OFILL 0000100 -#define TARGET_OFDEL 0000200 -#define TARGET_NLDLY 0000400 -#define TARGET_NL0 0000000 -#define TARGET_NL1 0000400 -#define TARGET_CRDLY 0003000 -#define TARGET_CR0 0000000 -#define TARGET_CR1 0001000 -#define TARGET_CR2 0002000 -#define TARGET_CR3 0003000 -#define TARGET_TABDLY 0014000 -#define TARGET_TAB0 0000000 -#define TARGET_TAB1 0004000 -#define TARGET_TAB2 0010000 -#define TARGET_TAB3 0014000 -#define TARGET_XTABS 0014000 -#define TARGET_BSDLY 0020000 -#define TARGET_BS0 0000000 -#define TARGET_BS1 0020000 -#define TARGET_VTDLY 0040000 -#define TARGET_VT0 0000000 -#define TARGET_VT1 0040000 -#define TARGET_FFDLY 0100000 -#define TARGET_FF0 0000000 -#define TARGET_FF1 0100000 - -/* c_cflag bit meaning */ -#define TARGET_CBAUD 0010017 -#define TARGET_B0 0000000 /* hang up */ -#define TARGET_B50 0000001 -#define TARGET_B75 0000002 -#define TARGET_B110 0000003 -#define TARGET_B134 0000004 -#define TARGET_B150 0000005 -#define TARGET_B200 0000006 -#define TARGET_B300 0000007 -#define TARGET_B600 0000010 -#define TARGET_B1200 0000011 -#define TARGET_B1800 0000012 -#define TARGET_B2400 0000013 -#define TARGET_B4800 0000014 -#define TARGET_B9600 0000015 -#define TARGET_B19200 0000016 -#define TARGET_B38400 0000017 -#define TARGET_EXTA B19200 -#define TARGET_EXTB B38400 -#define TARGET_CSIZE 0000060 -#define TARGET_CS5 0000000 -#define TARGET_CS6 0000020 -#define TARGET_CS7 0000040 -#define TARGET_CS8 0000060 -#define TARGET_CSTOPB 0000100 -#define TARGET_CREAD 0000200 -#define TARGET_PARENB 0000400 -#define TARGET_PARODD 0001000 -#define TARGET_HUPCL 0002000 -#define TARGET_CLOCAL 0004000 -#define TARGET_CBAUDEX 0010000 -#define TARGET_BOTHER 0010000 -#define TARGET_B57600 0010001 -#define TARGET_B115200 0010002 -#define TARGET_B230400 0010003 -#define TARGET_B460800 0010004 -#define TARGET_B500000 0010005 -#define TARGET_B576000 0010006 -#define TARGET_B921600 0010007 -#define TARGET_B1000000 0010010 -#define TARGET_B1152000 0010011 -#define TARGET_B1500000 0010012 -#define TARGET_B2000000 0010013 -#define TARGET_B2500000 0010014 -#define TARGET_B3000000 0010015 -#define TARGET_B3500000 0010016 -#define TARGET_B4000000 0010017 -#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */ -#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */ -#define TARGET_CRTSCTS 020000000000 /* flow control */ - -/* c_lflag bits */ -#define TARGET_ISIG 0000001 -#define TARGET_ICANON 0000002 -#define TARGET_XCASE 0000004 -#define TARGET_ECHO 0000010 -#define TARGET_ECHOE 0000020 -#define TARGET_ECHOK 0000040 -#define TARGET_ECHONL 0000100 -#define TARGET_NOFLSH 0000200 -#define TARGET_IEXTEN 0000400 -#define TARGET_ECHOCTL 0001000 -#define TARGET_ECHOPRT 0002000 -#define TARGET_ECHOKE 0004000 -#define TARGET_FLUSHO 0010000 -#define TARGET_PENDIN 0040000 -#define TARGET_TOSTOP 0100000 -#define TARGET_ITOSTOP TARGET_TOSTOP - -/* c_cc character offsets */ -#define TARGET_VINTR 0 -#define TARGET_VQUIT 1 -#define TARGET_VERASE 2 -#define TARGET_VKILL 3 -#define TARGET_VMIN 4 -#define TARGET_VTIME 5 -#define TARGET_VEOL2 6 -#define TARGET_VSWTC 7 -#define TARGET_VSTART 8 -#define TARGET_VSTOP 9 -#define TARGET_VSUSP 10 -/* VDSUSP not supported */ -#define TARGET_VREPRINT 12 -#define TARGET_VDISCARD 13 -#define TARGET_VWERASE 14 -#define TARGET_VLNEXT 15 -#define TARGET_VEOF 16 -#define TARGET_VEOL 17 - -/* ioctls */ - -#define TARGET_TCGETA 0x5401 -#define TARGET_TCSETA 0x5402 /* Clashes with SNDCTL_TMR_START sound ioctl */ -#define TARGET_TCSETAW 0x5403 -#define TARGET_TCSETAF 0x5404 - -#define TARGET_TCSBRK 0x5405 -#define TARGET_TCXONC 0x5406 -#define TARGET_TCFLSH 0x5407 - -#define TARGET_TCGETS 0x540d -#define TARGET_TCSETS 0x540e -#define TARGET_TCSETSW 0x540f -#define TARGET_TCSETSF 0x5410 - -#define TARGET_TIOCEXCL 0x740d /* set exclusive use of tty */ -#define TARGET_TIOCNXCL 0x740e /* reset exclusive use of tty */ -#define TARGET_TIOCOUTQ 0x7472 /* output queue size */ -#define TARGET_TIOCSTI 0x5472 /* simulate terminal input */ -#define TARGET_TIOCMGET 0x741d /* get all modem bits */ -#define TARGET_TIOCMBIS 0x741b /* bis modem bits */ -#define TARGET_TIOCMBIC 0x741c /* bic modem bits */ -#define TARGET_TIOCMSET 0x741a /* set all modem bits */ -#define TARGET_TIOCPKT 0x5470 /* pty: set/clear packet mode */ -#define TARGET_TIOCPKT_DATA 0x00 /* data packet */ -#define TARGET_TIOCPKT_FLUSHREAD 0x01 /* flush packet */ -#define TARGET_TIOCPKT_FLUSHWRITE 0x02 /* flush packet */ -#define TARGET_TIOCPKT_STOP 0x04 /* stop output */ -#define TARGET_TIOCPKT_START 0x08 /* start output */ -#define TARGET_TIOCPKT_NOSTOP 0x10 /* no more ^S, ^Q */ -#define TARGET_TIOCPKT_DOSTOP 0x20 /* now do ^S ^Q */ -/* #define TIOCPKT_IOCTL 0x40 state change of pty driver */ -#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize) /* set window size */ -#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize) /* get window size */ -#define TARGET_TIOCNOTTY 0x5471 /* void tty association */ -#define TARGET_TIOCSETD 0x7401 -#define TARGET_TIOCGETD 0x7400 - -#define TARGET_FIOCLEX 0x6601 -#define TARGET_FIONCLEX 0x6602 -#define TARGET_FIOASYNC 0x667d -#define TARGET_FIONBIO 0x667e -#define TARGET_FIOQSIZE 0x667f - -#define TARGET_TIOCGLTC 0x7474 /* get special local chars */ -#define TARGET_TIOCSLTC 0x7475 /* set special local chars */ -#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int) /* set pgrp of tty */ -#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int) /* get pgrp of tty */ -#define TARGET_TIOCCONS TARGET_IOW('t', 120, int) /* become virtual console */ - -#define TARGET_FIONREAD 0x467f -#define TARGET_TIOCINQ TARGET_FIONREAD - -#define TARGET_TIOCGETP 0x7408 -#define TARGET_TIOCSETP 0x7409 -#define TARGET_TIOCSETN 0x740a /* TIOCSETP wo flush */ - -/* #define TARGET_TIOCSETA TARGET_IOW('t', 20, struct termios) set termios struct */ -/* #define TARGET_TIOCSETAW TARGET_IOW('t', 21, struct termios) drain output, set */ -/* #define TARGET_TIOCSETAF TARGET_IOW('t', 22, struct termios) drn out, fls in, set */ -/* #define TARGET_TIOCGETD TARGET_IOR('t', 26, int) get line discipline */ -/* #define TARGET_TIOCSETD TARGET_IOW('t', 27, int) set line discipline */ - /* 127-124 compat */ - -#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */ -#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */ -#define TARGET_TIOCGSID 0x7416 /* Return the session ID of FD */ -#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ -#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */ - -/* I hope the range from 0x5480 on is free ... */ -#define TARGET_TIOCSCTTY 0x5480 /* become controlling tty */ -#define TARGET_TIOCGSOFTCAR 0x5481 -#define TARGET_TIOCSSOFTCAR 0x5482 -#define TARGET_TIOCLINUX 0x5483 -#define TARGET_TIOCGSERIAL 0x5484 -#define TARGET_TIOCSSERIAL 0x5485 -#define TARGET_TCSBRKP 0x5486 /* Needed for POSIX tcsendbreak() */ -#define TARGET_TIOCSERCONFIG 0x5488 -#define TARGET_TIOCSERGWILD 0x5489 -#define TARGET_TIOCSERSWILD 0x548a -#define TARGET_TIOCGLCKTRMIOS 0x548b -#define TARGET_TIOCSLCKTRMIOS 0x548c -#define TARGET_TIOCSERGSTRUCT 0x548d /* For debugging only */ -#define TARGET_TIOCSERGETLSR 0x548e /* Get line status register */ -#define TARGET_TIOCSERGETMULTI 0x548f /* Get multiport config */ -#define TARGET_TIOCSERSETMULTI 0x5490 /* Set multiport config */ -#define TARGET_TIOCMIWAIT 0x5491 /* wait for a change on serial input line(s) */ -#define TARGET_TIOCGICOUNT 0x5492 /* read serial port inline interrupt counts */ -#define TARGET_TIOCGHAYESESP 0x5493 /* Get Hayes ESP configuration */ -#define TARGET_TIOCSHAYESESP 0x5494 /* Set Hayes ESP configuration */ diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 8a3538c631..b10e9572a9 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -287,36 +287,39 @@ static inline int access_ok(int type, abi_ulong addr, abi_ulong size) (type == VERIFY_READ) ? PAGE_READ : (PAGE_READ | PAGE_WRITE)) == 0; } -/* NOTE __get_user and __put_user use host pointers and don't check access. */ -/* These are usually used to access struct data members once the - * struct has been locked - usually with lock_user_struct(). - */ -#define __put_user(x, hptr)\ -({ __typeof(*hptr) pu_ = (x);\ - switch(sizeof(*hptr)) {\ - case 1: break;\ - case 2: pu_ = tswap16(pu_); break; \ - case 4: pu_ = tswap32(pu_); break; \ - case 8: pu_ = tswap64(pu_); break; \ - default: abort();\ - }\ - memcpy(hptr, &pu_, sizeof(pu_)); \ - 0;\ -}) +/* NOTE __get_user and __put_user use host pointers and don't check access. + These are usually used to access struct data members once the struct has + been locked - usually with lock_user_struct. */ -#define __get_user(x, hptr) \ -({ __typeof(*hptr) gu_; \ - memcpy(&gu_, hptr, sizeof(gu_)); \ - switch(sizeof(*hptr)) {\ - case 1: break; \ - case 2: gu_ = tswap16(gu_); break; \ - case 4: gu_ = tswap32(gu_); break; \ - case 8: gu_ = tswap64(gu_); break; \ - default: abort();\ - }\ - (x) = gu_; \ - 0;\ -}) +/* Tricky points: + - Use __builtin_choose_expr to avoid type promotion from ?:, + - Invalid sizes result in a compile time error stemming from + the fact that abort has no parameters. + - It's easier to use the endian-specific unaligned load/store + functions than host-endian unaligned load/store plus tswapN. */ + +#define __put_user_e(x, hptr, e) \ + (__builtin_choose_expr(sizeof(*(hptr)) == 1, stb_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 2, stw_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 4, stl_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 8, stq_##e##_p, abort)))) \ + ((hptr), (x)), 0) + +#define __get_user_e(x, hptr, e) \ + ((x) = (typeof(*hptr))( \ + __builtin_choose_expr(sizeof(*(hptr)) == 1, ldub_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 2, lduw_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 4, ldl_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 8, ldq_##e##_p, abort)))) \ + (hptr)), 0) + +#ifdef TARGET_WORDS_BIGENDIAN +# define __put_user(x, hptr) __put_user_e(x, hptr, be) +# define __get_user(x, hptr) __get_user_e(x, hptr, be) +#else +# define __put_user(x, hptr) __put_user_e(x, hptr, le) +# define __get_user(x, hptr) __get_user_e(x, hptr, le) +#endif /* put_user()/get_user() take a guest address and check access */ /* These are usually used to access an atomic data type, such as an int, diff --git a/linux-user/s390x/syscall.h b/linux-user/s390x/syscall.h index c2ea151ea5..e4603b79c3 100644 --- a/linux-user/s390x/syscall.h +++ b/linux-user/s390x/syscall.h @@ -16,7 +16,7 @@ struct target_pt_regs { target_psw_t psw; abi_ulong gprs[TARGET_NUM_GPRS]; abi_ulong orig_gpr2; - unsigned short ilc; + unsigned short ilen; unsigned short trap; }; diff --git a/linux-user/signal.c b/linux-user/signal.c index 95e2ffa007..1055507224 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -607,28 +607,22 @@ int do_sigaction(int sig, const struct target_sigaction *act, sig, act, oact); #endif if (oact) { - oact->_sa_handler = tswapal(k->_sa_handler); -#if defined(TARGET_MIPS) || defined (TARGET_ALPHA) - oact->sa_flags = bswap32(k->sa_flags); -#else - oact->sa_flags = tswapal(k->sa_flags); -#endif + __put_user(k->_sa_handler, &oact->_sa_handler); + __put_user(k->sa_flags, &oact->sa_flags); #if !defined(TARGET_MIPS) - oact->sa_restorer = tswapal(k->sa_restorer); + __put_user(k->sa_restorer, &oact->sa_restorer); #endif + /* Not swapped. */ oact->sa_mask = k->sa_mask; } if (act) { /* FIXME: This is not threadsafe. */ - k->_sa_handler = tswapal(act->_sa_handler); -#if defined(TARGET_MIPS) || defined (TARGET_ALPHA) - k->sa_flags = bswap32(act->sa_flags); -#else - k->sa_flags = tswapal(act->sa_flags); -#endif + __get_user(k->_sa_handler, &act->_sa_handler); + __get_user(k->sa_flags, &act->sa_flags); #if !defined(TARGET_MIPS) - k->sa_restorer = tswapal(act->sa_restorer); + __get_user(k->sa_restorer, &act->sa_restorer); #endif + /* To be swapped in target_to_host_sigset. */ k->sa_mask = act->sa_mask; /* we update the host linux signal state */ @@ -2444,66 +2438,9 @@ void sparc64_get_context(CPUSPARCState *env) force_sig(TARGET_SIGSEGV); } #endif -#elif defined(TARGET_ABI_MIPSN64) - -# warning signal handling not implemented - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUMIPSState *env) -{ - fprintf(stderr, "setup_frame: not implemented\n"); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUMIPSState *env) -{ - fprintf(stderr, "setup_rt_frame: not implemented\n"); -} - -long do_sigreturn(CPUMIPSState *env) -{ - fprintf(stderr, "do_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -long do_rt_sigreturn(CPUMIPSState *env) -{ - fprintf(stderr, "do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -#elif defined(TARGET_ABI_MIPSN32) - -# warning signal handling not implemented - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUMIPSState *env) -{ - fprintf(stderr, "setup_frame: not implemented\n"); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUMIPSState *env) -{ - fprintf(stderr, "setup_rt_frame: not implemented\n"); -} - -long do_sigreturn(CPUMIPSState *env) -{ - fprintf(stderr, "do_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -long do_rt_sigreturn(CPUMIPSState *env) -{ - fprintf(stderr, "do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -#elif defined(TARGET_ABI_MIPSO32) +#elif defined(TARGET_MIPS) || defined(TARGET_MIPS64) +# if defined(TARGET_ABI_MIPSO32) struct target_sigcontext { uint32_t sc_regmask; /* Unused */ uint32_t sc_status; @@ -2525,6 +2462,25 @@ struct target_sigcontext { target_ulong sc_hi3; target_ulong sc_lo3; }; +# else /* N32 || N64 */ +struct target_sigcontext { + uint64_t sc_regs[32]; + uint64_t sc_fpregs[32]; + uint64_t sc_mdhi; + uint64_t sc_hi1; + uint64_t sc_hi2; + uint64_t sc_hi3; + uint64_t sc_mdlo; + uint64_t sc_lo1; + uint64_t sc_lo2; + uint64_t sc_lo3; + uint64_t sc_pc; + uint32_t sc_fpc_csr; + uint32_t sc_used_math; + uint32_t sc_dsp; + uint32_t sc_reserved; +}; +# endif /* O32 */ struct sigframe { uint32_t sf_ass[4]; /* argument save space for o32 */ @@ -2552,18 +2508,17 @@ struct target_rt_sigframe { /* Install trampoline to jump back from signal handler */ static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall) { - int err; + int err = 0; /* - * Set up the return code ... - * - * li v0, __NR__foo_sigreturn - * syscall - */ + * Set up the return code ... + * + * li v0, __NR__foo_sigreturn + * syscall + */ - err = __put_user(0x24020000 + syscall, tramp + 0); + err |= __put_user(0x24020000 + syscall, tramp + 0); err |= __put_user(0x0000000c , tramp + 1); - /* flush_cache_sigtramp((unsigned long) tramp); */ return err; } @@ -2571,74 +2526,37 @@ static inline int setup_sigcontext(CPUMIPSState *regs, struct target_sigcontext *sc) { int err = 0; + int i; err |= __put_user(regs->active_tc.PC, &sc->sc_pc); -#define save_gp_reg(i) do { \ - err |= __put_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); \ - } while(0) - __put_user(0, &sc->sc_regs[0]); save_gp_reg(1); save_gp_reg(2); - save_gp_reg(3); save_gp_reg(4); save_gp_reg(5); save_gp_reg(6); - save_gp_reg(7); save_gp_reg(8); save_gp_reg(9); save_gp_reg(10); - save_gp_reg(11); save_gp_reg(12); save_gp_reg(13); save_gp_reg(14); - save_gp_reg(15); save_gp_reg(16); save_gp_reg(17); save_gp_reg(18); - save_gp_reg(19); save_gp_reg(20); save_gp_reg(21); save_gp_reg(22); - save_gp_reg(23); save_gp_reg(24); save_gp_reg(25); save_gp_reg(26); - save_gp_reg(27); save_gp_reg(28); save_gp_reg(29); save_gp_reg(30); - save_gp_reg(31); -#undef save_gp_reg + __put_user(0, &sc->sc_regs[0]); + for (i = 1; i < 32; ++i) { + err |= __put_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); + } err |= __put_user(regs->active_tc.HI[0], &sc->sc_mdhi); err |= __put_user(regs->active_tc.LO[0], &sc->sc_mdlo); - /* Not used yet, but might be useful if we ever have DSP suppport */ -#if 0 - if (cpu_has_dsp) { - err |= __put_user(mfhi1(), &sc->sc_hi1); - err |= __put_user(mflo1(), &sc->sc_lo1); - err |= __put_user(mfhi2(), &sc->sc_hi2); - err |= __put_user(mflo2(), &sc->sc_lo2); - err |= __put_user(mfhi3(), &sc->sc_hi3); - err |= __put_user(mflo3(), &sc->sc_lo3); - err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); + /* Rather than checking for dsp existence, always copy. The storage + would just be garbage otherwise. */ + err |= __put_user(regs->active_tc.HI[1], &sc->sc_hi1); + err |= __put_user(regs->active_tc.HI[2], &sc->sc_hi2); + err |= __put_user(regs->active_tc.HI[3], &sc->sc_hi3); + err |= __put_user(regs->active_tc.LO[1], &sc->sc_lo1); + err |= __put_user(regs->active_tc.LO[2], &sc->sc_lo2); + err |= __put_user(regs->active_tc.LO[3], &sc->sc_lo3); + { + uint32_t dsp = cpu_rddsp(0x3ff, regs); + err |= __put_user(dsp, &sc->sc_dsp); } - /* same with 64 bit */ -#ifdef CONFIG_64BIT - err |= __put_user(regs->hi, &sc->sc_hi[0]); - err |= __put_user(regs->lo, &sc->sc_lo[0]); - if (cpu_has_dsp) { - err |= __put_user(mfhi1(), &sc->sc_hi[1]); - err |= __put_user(mflo1(), &sc->sc_lo[1]); - err |= __put_user(mfhi2(), &sc->sc_hi[2]); - err |= __put_user(mflo2(), &sc->sc_lo[2]); - err |= __put_user(mfhi3(), &sc->sc_hi[3]); - err |= __put_user(mflo3(), &sc->sc_lo[3]); - err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp); + + err |= __put_user(1, &sc->sc_used_math); + + for (i = 0; i < 32; ++i) { + err |= __put_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); } -#endif -#endif -#if 0 - err |= __put_user(!!used_math(), &sc->sc_used_math); - - if (!used_math()) - goto out; - - /* - * Save FPU state to signal context. Signal handler will "inherit" - * current FPU state. - */ - preempt_disable(); - - if (!is_fpu_owner()) { - own_fpu(); - restore_fp(current); - } - err |= save_fp_context(sc); - - preempt_enable(); - out: -#endif return err; } @@ -2646,70 +2564,36 @@ static inline int restore_sigcontext(CPUMIPSState *regs, struct target_sigcontext *sc) { int err = 0; + int i; err |= __get_user(regs->CP0_EPC, &sc->sc_pc); err |= __get_user(regs->active_tc.HI[0], &sc->sc_mdhi); err |= __get_user(regs->active_tc.LO[0], &sc->sc_mdlo); -#define restore_gp_reg(i) do { \ - err |= __get_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); \ - } while(0) - restore_gp_reg( 1); restore_gp_reg( 2); restore_gp_reg( 3); - restore_gp_reg( 4); restore_gp_reg( 5); restore_gp_reg( 6); - restore_gp_reg( 7); restore_gp_reg( 8); restore_gp_reg( 9); - restore_gp_reg(10); restore_gp_reg(11); restore_gp_reg(12); - restore_gp_reg(13); restore_gp_reg(14); restore_gp_reg(15); - restore_gp_reg(16); restore_gp_reg(17); restore_gp_reg(18); - restore_gp_reg(19); restore_gp_reg(20); restore_gp_reg(21); - restore_gp_reg(22); restore_gp_reg(23); restore_gp_reg(24); - restore_gp_reg(25); restore_gp_reg(26); restore_gp_reg(27); - restore_gp_reg(28); restore_gp_reg(29); restore_gp_reg(30); - restore_gp_reg(31); -#undef restore_gp_reg - -#if 0 - if (cpu_has_dsp) { - err |= __get_user(treg, &sc->sc_hi1); mthi1(treg); - err |= __get_user(treg, &sc->sc_lo1); mtlo1(treg); - err |= __get_user(treg, &sc->sc_hi2); mthi2(treg); - err |= __get_user(treg, &sc->sc_lo2); mtlo2(treg); - err |= __get_user(treg, &sc->sc_hi3); mthi3(treg); - err |= __get_user(treg, &sc->sc_lo3); mtlo3(treg); - err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK); - } -#ifdef CONFIG_64BIT - err |= __get_user(regs->hi, &sc->sc_hi[0]); - err |= __get_user(regs->lo, &sc->sc_lo[0]); - if (cpu_has_dsp) { - err |= __get_user(treg, &sc->sc_hi[1]); mthi1(treg); - err |= __get_user(treg, &sc->sc_lo[1]); mthi1(treg); - err |= __get_user(treg, &sc->sc_hi[2]); mthi2(treg); - err |= __get_user(treg, &sc->sc_lo[2]); mthi2(treg); - err |= __get_user(treg, &sc->sc_hi[3]); mthi3(treg); - err |= __get_user(treg, &sc->sc_lo[3]); mthi3(treg); - err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK); - } -#endif - - err |= __get_user(used_math, &sc->sc_used_math); - conditional_used_math(used_math); - - preempt_disable(); - - if (used_math()) { - /* restore fpu context if we have used it before */ - own_fpu(); - err |= restore_fp_context(sc); - } else { - /* signal handler may have used FPU. Give it up. */ - lose_fpu(); + for (i = 1; i < 32; ++i) { + err |= __get_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); + } + + err |= __get_user(regs->active_tc.HI[1], &sc->sc_hi1); + err |= __get_user(regs->active_tc.HI[2], &sc->sc_hi2); + err |= __get_user(regs->active_tc.HI[3], &sc->sc_hi3); + err |= __get_user(regs->active_tc.LO[1], &sc->sc_lo1); + err |= __get_user(regs->active_tc.LO[2], &sc->sc_lo2); + err |= __get_user(regs->active_tc.LO[3], &sc->sc_lo3); + { + uint32_t dsp; + err |= __get_user(dsp, &sc->sc_dsp); + cpu_wrdsp(dsp, 0x3ff, regs); + } + + for (i = 0; i < 32; ++i) { + err |= __get_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); } - preempt_enable(); -#endif return err; } + /* * Determine which stack to use.. */ @@ -2736,6 +2620,7 @@ get_sigframe(struct target_sigaction *ka, CPUMIPSState *regs, size_t frame_size) return (sp - frame_size) & ~7; } +# if defined(TARGET_ABI_MIPSO32) /* compare linux/arch/mips/kernel/signal.c:setup_frame() */ static void setup_frame(int sig, struct target_sigaction * ka, target_sigset_t *set, CPUMIPSState *regs) @@ -2833,6 +2718,7 @@ badframe: force_sig(TARGET_SIGSEGV/*, current*/); return 0; } +# endif /* O32 */ static void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, @@ -4584,7 +4470,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, signal = current_exec_domain_sig(sig); - err |= __put_user(h2g(ka->_sa_handler), &sc->handler); + err |= __put_user(ka->_sa_handler, &sc->handler); err |= __put_user(set->sig[0], &sc->oldmask); #if defined(TARGET_PPC64) err |= __put_user(set->sig[0] >> 32, &sc->_unused[3]); @@ -4606,7 +4492,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, /* Create a stack frame for the caller of the handler. */ newsp = frame_addr - SIGNAL_FRAMESIZE; - err |= __put_user(env->gpr[1], (target_ulong *)(uintptr_t) newsp); + err |= put_user(env->gpr[1], newsp, target_ulong); if (err) goto sigsegv; @@ -4614,7 +4500,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, /* Set up registers for signal handler. */ env->gpr[1] = newsp; env->gpr[3] = signal; - env->gpr[4] = (target_ulong) h2g(sc); + env->gpr[4] = frame_addr + offsetof(struct target_sigframe, sctx); env->nip = (target_ulong) ka->_sa_handler; /* Signal handlers are entered in big-endian mode. */ env->msr &= ~MSR_LE; @@ -5563,10 +5449,15 @@ void process_pending_signals(CPUArchState *cpu_env) } #endif /* prepare the stack frame of the virtual CPU */ +#if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64) + /* These targets do not have traditional signals. */ + setup_rt_frame(sig, sa, &q->info, &target_old_set, cpu_env); +#else if (sa->sa_flags & TARGET_SA_SIGINFO) setup_rt_frame(sig, sa, &q->info, &target_old_set, cpu_env); else setup_frame(sig, sa, &target_old_set, cpu_env); +#endif if (sa->sa_flags & TARGET_SA_RESETHAND) sa->_sa_handler = TARGET_SIG_DFL; } diff --git a/linux-user/socket.h b/linux-user/socket.h index 93d47823d8..339cae5a16 100644 --- a/linux-user/socket.h +++ b/linux-user/socket.h @@ -87,6 +87,75 @@ #define TARGET_SOCK_MAX (SOCK_PACKET + 1) +#elif defined(TARGET_ALPHA) + + /* For setsockopt(2) */ + #define TARGET_SOL_SOCKET 0xffff + + #define TARGET_SO_DEBUG 0x0001 + #define TARGET_SO_REUSEADDR 0x0004 + #define TARGET_SO_KEEPALIVE 0x0008 + #define TARGET_SO_DONTROUTE 0x0010 + #define TARGET_SO_BROADCAST 0x0020 + #define TARGET_SO_LINGER 0x0080 + #define TARGET_SO_OOBINLINE 0x0100 + /* To add :#define TARGET_SO_REUSEPORT 0x0200 */ + + #define TARGET_SO_TYPE 0x1008 + #define TARGET_SO_ERROR 0x1007 + #define TARGET_SO_SNDBUF 0x1001 + #define TARGET_SO_RCVBUF 0x1002 + #define TARGET_SO_SNDBUFFORCE 0x100a + #define TARGET_SO_RCVBUFFORCE 0x100b + #define TARGET_SO_RCVLOWAT 0x1010 + #define TARGET_SO_SNDLOWAT 0x1011 + #define TARGET_SO_RCVTIMEO 0x1012 + #define TARGET_SO_SNDTIMEO 0x1013 + #define TARGET_SO_ACCEPTCONN 0x1014 + #define TARGET_SO_PROTOCOL 0x1028 + #define TARGET_SO_DOMAIN 0x1029 + + /* linux-specific, might as well be the same as on i386 */ + #define TARGET_SO_NO_CHECK 11 + #define TARGET_SO_PRIORITY 12 + #define TARGET_SO_BSDCOMPAT 14 + + #define TARGET_SO_PASSCRED 17 + #define TARGET_SO_PEERCRED 18 + #define TARGET_SO_BINDTODEVICE 25 + + /* Socket filtering */ + #define TARGET_SO_ATTACH_FILTER 26 + #define TARGET_SO_DETACH_FILTER 27 + + #define TARGET_SO_PEERNAME 28 + #define TARGET_SO_TIMESTAMP 29 + #define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP + + #define TARGET_SO_PEERSEC 30 + #define TARGET_SO_PASSSEC 34 + #define TARGET_SO_TIMESTAMPNS 35 + #define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS + + /* Security levels - as per NRL IPv6 - don't actually do anything */ + #define TARGET_SO_SECURITY_AUTHENTICATION 19 + #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 20 + #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 21 + + #define TARGET_SO_MARK 36 + + #define TARGET_SO_TIMESTAMPING 37 + #define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING + + #define TARGET_SO_RXQ_OVFL 40 + + #define TARGET_SO_WIFI_STATUS 41 + #define TARGET_SCM_WIFI_STATUS TARGET_SO_WIFI_STATUS + #define TARGET_SO_PEEK_OFF 42 + + /* Instruct lower device to use last 4-bytes of skb data as FCS */ + #define TARGET_SO_NOFCS 43 + #else /* For setsockopt(2) */ diff --git a/linux-user/sparc/syscall_nr.h b/linux-user/sparc/syscall_nr.h index 061711cc03..534e6e9963 100644 --- a/linux-user/sparc/syscall_nr.h +++ b/linux-user/sparc/syscall_nr.h @@ -200,6 +200,8 @@ #define TARGET_NR__newselect 230 /* Linux Specific */ #define TARGET_NR_time 231 /* Linux Specific */ #define TARGET_NR_stime 233 /* Linux Specific */ +#define TARGET_NR_statfs64 234 /* Linux Specific */ +#define TARGET_NR_fstatfs64 235 /* Linux Specific */ #define TARGET_NR__llseek 236 /* Linux Specific */ #define TARGET_NR_mlock 237 #define TARGET_NR_munlock 238 diff --git a/linux-user/strace.c b/linux-user/strace.c index 6ec90e8974..0fbae3c2f6 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -462,18 +462,6 @@ UNUSED static struct flags mmap_flags[] = { FLAG_END, }; -UNUSED static struct flags fcntl_flags[] = { - FLAG_TARGET(F_DUPFD), - FLAG_TARGET(F_GETFD), - FLAG_TARGET(F_SETFD), - FLAG_TARGET(F_GETFL), - FLAG_TARGET(F_SETFL), - FLAG_TARGET(F_GETLK), - FLAG_TARGET(F_SETLK), - FLAG_TARGET(F_SETLKW), - FLAG_END, -}; - UNUSED static struct flags clone_flags[] = { FLAG_GENERIC(CLONE_VM), FLAG_GENERIC(CLONE_FS), @@ -682,7 +670,7 @@ print_timeval(abi_ulong tv_addr, int last) if (!tv) return; gemu_log("{" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "}%s", - tv->tv_sec, tv->tv_usec, get_comma(last)); + tswapal(tv->tv_sec), tswapal(tv->tv_usec), get_comma(last)); unlock_user(tv, tv_addr, 0); } else gemu_log("NULL%s", get_comma(last)); @@ -867,12 +855,85 @@ print_fcntl(const struct syscallname *name, { print_syscall_prologue(name); print_raw_param("%d", arg0, 0); - print_flags(fcntl_flags, arg1, 0); - /* - * TODO: check flags and print following argument only - * when needed. - */ - print_pointer(arg2, 1); + switch(arg1) { + case TARGET_F_DUPFD: + gemu_log("F_DUPFD,"); + print_raw_param(TARGET_ABI_FMT_ld, arg2, 1); + break; + case TARGET_F_GETFD: + gemu_log("F_GETFD"); + break; + case TARGET_F_SETFD: + gemu_log("F_SETFD,"); + print_raw_param(TARGET_ABI_FMT_ld, arg2, 1); + break; + case TARGET_F_GETFL: + gemu_log("F_GETFL"); + break; + case TARGET_F_SETFL: + gemu_log("F_SETFL,"); + print_open_flags(arg2, 1); + break; + case TARGET_F_GETLK: + gemu_log("F_GETLK,"); + print_pointer(arg2, 1); + break; + case TARGET_F_SETLK: + gemu_log("F_SETLK,"); + print_pointer(arg2, 1); + break; + case TARGET_F_SETLKW: + gemu_log("F_SETLKW,"); + print_pointer(arg2, 1); + break; + case TARGET_F_GETOWN: + gemu_log("F_GETOWN"); + break; + case TARGET_F_SETOWN: + gemu_log("F_SETOWN,"); + print_raw_param(TARGET_ABI_FMT_ld, arg2, 0); + break; + case TARGET_F_GETSIG: + gemu_log("F_GETSIG"); + break; + case TARGET_F_SETSIG: + gemu_log("F_SETSIG,"); + print_raw_param(TARGET_ABI_FMT_ld, arg2, 0); + break; +#if TARGET_ABI_BITS == 32 + case TARGET_F_GETLK64: + gemu_log("F_GETLK64,"); + print_pointer(arg2, 1); + break; + case TARGET_F_SETLK64: + gemu_log("F_SETLK64,"); + print_pointer(arg2, 1); + break; + case TARGET_F_SETLKW64: + gemu_log("F_SETLKW64,"); + print_pointer(arg2, 1); + break; +#endif + case TARGET_F_SETLEASE: + gemu_log("F_SETLEASE,"); + print_raw_param(TARGET_ABI_FMT_ld, arg2, 0); + break; + case TARGET_F_GETLEASE: + gemu_log("F_GETLEASE"); + break; + case TARGET_F_DUPFD_CLOEXEC: + gemu_log("F_DUPFD_CLOEXEC,"); + print_raw_param(TARGET_ABI_FMT_ld, arg2, 1); + break; + case TARGET_F_NOTIFY: + gemu_log("F_NOTIFY,"); + print_raw_param(TARGET_ABI_FMT_ld, arg2, 0); + break; + default: + print_raw_param(TARGET_ABI_FMT_ld, arg1, 0); + print_pointer(arg2, 1); + break; + } print_syscall_epilogue(name); } #define print_fcntl64 print_fcntl @@ -1436,6 +1497,12 @@ if( cmd == val ) { \ gemu_log("FUTEX_PRIVATE_FLAG|"); cmd &= ~FUTEX_PRIVATE_FLAG; } +#endif +#ifdef FUTEX_CLOCK_REALTIME + if (cmd & FUTEX_CLOCK_REALTIME) { + gemu_log("FUTEX_CLOCK_REALTIME|"); + cmd &= ~FUTEX_CLOCK_REALTIME; + } #endif print_op(FUTEX_WAIT) print_op(FUTEX_WAKE) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e99adab492..ee82a2da4e 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -36,6 +36,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -75,6 +78,9 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #ifdef CONFIG_ATTR #include "qemu/xattr.h" #endif +#ifdef CONFIG_SENDFILE +#include +#endif #define termios host_termios #define winsize host_winsize @@ -98,6 +104,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #include #include #include +#include #include "linux_loop.h" #include "cpu-uname.h" @@ -581,11 +588,6 @@ _syscall4(int, sys_prlimit64, pid_t, pid, int, resource, struct host_rlimit64 *, old_limit) #endif -extern int personality(int); -extern int flock(int, int); -extern int setfsuid(int); -extern int setfsgid(int); - /* ARM EABI and MIPS expect 64bit types aligned even on pairs or registers */ #ifdef TARGET_ARM static inline int regpairs_aligned(void *cpu_env) { @@ -1491,6 +1493,28 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, break; case TARGET_SOL_SOCKET: switch (optname) { + case TARGET_SO_RCVTIMEO: + { + struct timeval tv; + + optname = SO_RCVTIMEO; + +set_timeout: + if (optlen != sizeof(struct target_timeval)) { + return -TARGET_EINVAL; + } + + if (copy_from_user_timeval(&tv, optval_addr)) { + return -TARGET_EFAULT; + } + + ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, + &tv, sizeof(tv))); + return ret; + } + case TARGET_SO_SNDTIMEO: + optname = SO_SNDTIMEO; + goto set_timeout; /* Options with 'int' argument. */ case TARGET_SO_DEBUG: optname = SO_DEBUG; @@ -1542,12 +1566,6 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, case TARGET_SO_RCVLOWAT: optname = SO_RCVLOWAT; break; - case TARGET_SO_RCVTIMEO: - optname = SO_RCVTIMEO; - break; - case TARGET_SO_SNDTIMEO: - optname = SO_SNDTIMEO; - break; break; default: goto unimplemented; @@ -1761,7 +1779,7 @@ static struct iovec *lock_iovec(int type, abi_ulong target_addr, errno = 0; return NULL; } - if (count > IOV_MAX) { + if (count < 0 || count > IOV_MAX) { errno = EINVAL; return NULL; } @@ -1986,16 +2004,30 @@ out2: return ret; } -/* do_accept() Must return target values and target errnos. */ -static abi_long do_accept(int fd, abi_ulong target_addr, - abi_ulong target_addrlen_addr) +/* If we don't have a system accept4() then just call accept. + * The callsites to do_accept4() will ensure that they don't + * pass a non-zero flags argument in this config. + */ +#ifndef CONFIG_ACCEPT4 +static inline int accept4(int sockfd, struct sockaddr *addr, + socklen_t *addrlen, int flags) +{ + assert(flags == 0); + return accept(sockfd, addr, addrlen); +} +#endif + +/* do_accept4() Must return target values and target errnos. */ +static abi_long do_accept4(int fd, abi_ulong target_addr, + abi_ulong target_addrlen_addr, int flags) { socklen_t addrlen; void *addr; abi_long ret; - if (target_addr == 0) - return get_errno(accept(fd, NULL, NULL)); + if (target_addr == 0) { + return get_errno(accept4(fd, NULL, NULL, flags)); + } /* linux returns EINVAL if addrlen pointer is invalid */ if (get_user_u32(addrlen, target_addrlen_addr)) @@ -2010,7 +2042,7 @@ static abi_long do_accept(int fd, abi_ulong target_addr, addr = alloca(addrlen); - ret = get_errno(accept(fd, addr, &addrlen)); + ret = get_errno(accept4(fd, addr, &addrlen, flags)); if (!is_error(ret)) { host_to_target_sockaddr(target_addr, addr, addrlen); if (put_user_u32(addrlen, target_addrlen_addr)) @@ -2236,7 +2268,7 @@ static abi_long do_socketcall(int num, abi_ulong vptr) || get_user_ual(target_addrlen, vptr + 2 * n)) return -TARGET_EFAULT; - ret = do_accept(sockfd, target_addr, target_addrlen); + ret = do_accept4(sockfd, target_addr, target_addrlen, 0); } break; case SOCKOP_getsockname: @@ -2899,7 +2931,7 @@ static inline abi_long do_msgrcv(int msqid, abi_long msgp, return -TARGET_EFAULT; host_mb = g_malloc(msgsz+sizeof(long)); - ret = get_errno(msgrcv(msqid, host_mb, msgsz, tswapal(msgtyp), msgflg)); + ret = get_errno(msgrcv(msqid, host_mb, msgsz, msgtyp, msgflg)); if (ret > 0) { abi_ulong target_mtext_addr = msgp + sizeof(abi_ulong); @@ -3191,7 +3223,7 @@ static abi_long do_ipc(unsigned int call, int first, break; } - ret = do_msgrcv(first, tmp->msgp, second, tmp->msgtyp, third); + ret = do_msgrcv(first, tswapal(tmp->msgp), second, tswapal(tmp->msgtyp), third); unlock_user_struct(tmp, ptr, 0); break; @@ -4297,13 +4329,15 @@ static void *clone_func(void *arg) { new_thread_info *info = arg; CPUArchState *env; + CPUState *cpu; TaskState *ts; env = info->env; + cpu = ENV_GET_CPU(env); thread_env = env; ts = (TaskState *)thread_env->opaque; info->tid = gettid(); - env->host_tid = info->tid; + cpu->host_tid = info->tid; task_settid(ts); if (info->child_tidptr) put_user_u32(info->tid, info->child_tidptr); @@ -4514,6 +4548,16 @@ static int target_to_host_fcntl_cmd(int cmd) return -TARGET_EINVAL; } +#define TRANSTBL_CONVERT(a) { -1, TARGET_##a, -1, a } +static const bitmask_transtbl flock_tbl[] = { + TRANSTBL_CONVERT(F_RDLCK), + TRANSTBL_CONVERT(F_WRLCK), + TRANSTBL_CONVERT(F_UNLCK), + TRANSTBL_CONVERT(F_EXLCK), + TRANSTBL_CONVERT(F_SHLCK), + { 0, 0, 0, 0 } +}; + static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) { struct flock fl; @@ -4530,7 +4574,8 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) case TARGET_F_GETLK: if (!lock_user_struct(VERIFY_READ, target_fl, arg, 1)) return -TARGET_EFAULT; - fl.l_type = tswap16(target_fl->l_type); + fl.l_type = + target_to_host_bitmask(tswap16(target_fl->l_type), flock_tbl); fl.l_whence = tswap16(target_fl->l_whence); fl.l_start = tswapal(target_fl->l_start); fl.l_len = tswapal(target_fl->l_len); @@ -4540,7 +4585,8 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) if (ret == 0) { if (!lock_user_struct(VERIFY_WRITE, target_fl, arg, 0)) return -TARGET_EFAULT; - target_fl->l_type = tswap16(fl.l_type); + target_fl->l_type = + host_to_target_bitmask(tswap16(fl.l_type), flock_tbl); target_fl->l_whence = tswap16(fl.l_whence); target_fl->l_start = tswapal(fl.l_start); target_fl->l_len = tswapal(fl.l_len); @@ -4553,7 +4599,8 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) case TARGET_F_SETLKW: if (!lock_user_struct(VERIFY_READ, target_fl, arg, 1)) return -TARGET_EFAULT; - fl.l_type = tswap16(target_fl->l_type); + fl.l_type = + target_to_host_bitmask(tswap16(target_fl->l_type), flock_tbl); fl.l_whence = tswap16(target_fl->l_whence); fl.l_start = tswapal(target_fl->l_start); fl.l_len = tswapal(target_fl->l_len); @@ -4565,7 +4612,8 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) case TARGET_F_GETLK64: if (!lock_user_struct(VERIFY_READ, target_fl64, arg, 1)) return -TARGET_EFAULT; - fl64.l_type = tswap16(target_fl64->l_type) >> 1; + fl64.l_type = + target_to_host_bitmask(tswap16(target_fl64->l_type), flock_tbl) >> 1; fl64.l_whence = tswap16(target_fl64->l_whence); fl64.l_start = tswap64(target_fl64->l_start); fl64.l_len = tswap64(target_fl64->l_len); @@ -4575,7 +4623,8 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) if (ret == 0) { if (!lock_user_struct(VERIFY_WRITE, target_fl64, arg, 0)) return -TARGET_EFAULT; - target_fl64->l_type = tswap16(fl64.l_type) >> 1; + target_fl64->l_type = + host_to_target_bitmask(tswap16(fl64.l_type), flock_tbl) >> 1; target_fl64->l_whence = tswap16(fl64.l_whence); target_fl64->l_start = tswap64(fl64.l_start); target_fl64->l_len = tswap64(fl64.l_len); @@ -4587,7 +4636,8 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) case TARGET_F_SETLKW64: if (!lock_user_struct(VERIFY_READ, target_fl64, arg, 1)) return -TARGET_EFAULT; - fl64.l_type = tswap16(target_fl64->l_type) >> 1; + fl64.l_type = + target_to_host_bitmask(tswap16(target_fl64->l_type), flock_tbl) >> 1; fl64.l_whence = tswap16(target_fl64->l_whence); fl64.l_start = tswap64(target_fl64->l_start); fl64.l_len = tswap64(target_fl64->l_len); @@ -4889,6 +4939,7 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, #endif switch (base_op) { case FUTEX_WAIT: + case FUTEX_WAIT_BITSET: if (timeout) { pts = &ts; target_to_host_timespec(pts, timeout); @@ -4896,7 +4947,7 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, pts = NULL; } return get_errno(sys_futex(g2h(uaddr), op, tswap32(val), - pts, NULL, 0)); + pts, NULL, val3)); case FUTEX_WAKE: return get_errno(sys_futex(g2h(uaddr), op, val, NULL, NULL, 0)); case FUTEX_FD: @@ -5188,7 +5239,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, NULL, NULL, 0); } thread_env = NULL; - object_delete(OBJECT(ENV_GET_CPU(cpu_env))); + object_unref(OBJECT(ENV_GET_CPU(cpu_env))); g_free(ts); pthread_exit(NULL); } @@ -6213,8 +6264,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(settimeofday(&tv, NULL)); } break; -#if defined(TARGET_NR_select) && !defined(TARGET_S390X) && !defined(TARGET_S390) +#if defined(TARGET_NR_select) case TARGET_NR_select: +#if defined(TARGET_S390X) || defined(TARGET_ALPHA) + ret = do_select(arg1, arg2, arg3, arg4, arg5); +#else { struct target_sel_arg_struct *sel; abi_ulong inp, outp, exp, tvp; @@ -6230,6 +6284,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user_struct(sel, arg1, 0); ret = do_select(nsel, inp, outp, exp, tvp); } +#endif break; #endif #ifdef TARGET_NR_pselect6 @@ -6417,10 +6472,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; #endif case TARGET_NR_reboot: - if (!(p = lock_user_string(arg4))) - goto efault; - ret = reboot(arg1, arg2, arg3, p); - unlock_user(p, arg4, 0); + if (arg3 == LINUX_REBOOT_CMD_RESTART2) { + /* arg4 must be ignored in all other cases */ + p = lock_user_string(arg4); + if (!p) { + goto efault; + } + ret = get_errno(reboot(arg1, arg2, arg3, p)); + unlock_user(p, arg4, 0); + } else { + ret = get_errno(reboot(arg1, arg2, arg3, NULL)); + } break; #ifdef TARGET_NR_readdir case TARGET_NR_readdir: @@ -6629,7 +6691,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_accept case TARGET_NR_accept: - ret = do_accept(arg1, arg2, arg3); + ret = do_accept4(arg1, arg2, arg3, 0); + break; +#endif +#ifdef TARGET_NR_accept4 + case TARGET_NR_accept4: +#ifdef CONFIG_ACCEPT4 + ret = do_accept4(arg1, arg2, arg3, arg4); +#else + goto unimplemented; +#endif break; #endif #ifdef TARGET_NR_bind @@ -7153,12 +7224,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } break; #endif /* TARGET_NR_getdents64 */ -#if defined(TARGET_NR__newselect) || defined(TARGET_S390X) -#ifdef TARGET_S390X - case TARGET_NR_select: -#else +#if defined(TARGET_NR__newselect) case TARGET_NR__newselect: -#endif ret = do_select(arg1, arg2, arg3, arg4, arg5); break; #endif @@ -7490,8 +7557,58 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #else goto unimplemented; #endif + +#ifdef CONFIG_SENDFILE case TARGET_NR_sendfile: + { + off_t *offp = NULL; + off_t off; + if (arg3) { + ret = get_user_sal(off, arg3); + if (is_error(ret)) { + break; + } + offp = &off; + } + ret = get_errno(sendfile(arg1, arg2, offp, arg4)); + if (!is_error(ret) && arg3) { + abi_long ret2 = put_user_sal(off, arg3); + if (is_error(ret2)) { + ret = ret2; + } + } + break; + } +#ifdef TARGET_NR_sendfile64 + case TARGET_NR_sendfile64: + { + off_t *offp = NULL; + off_t off; + if (arg3) { + ret = get_user_s64(off, arg3); + if (is_error(ret)) { + break; + } + offp = &off; + } + ret = get_errno(sendfile(arg1, arg2, offp, arg4)); + if (!is_error(ret) && arg3) { + abi_long ret2 = put_user_s64(off, arg3); + if (is_error(ret2)) { + ret = ret2; + } + } + break; + } +#endif +#else + case TARGET_NR_sendfile: +#ifdef TARGET_NR_sendfile64: + case TARGET_NR_sendfile64: +#endif goto unimplemented; +#endif + #ifdef TARGET_NR_getpmsg case TARGET_NR_getpmsg: goto unimplemented; @@ -7639,18 +7756,20 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, { int gidsetsize = arg1; target_id *target_grouplist; - gid_t *grouplist; + gid_t *grouplist = NULL; int i; - - grouplist = alloca(gidsetsize * sizeof(gid_t)); - target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 2, 1); - if (!target_grouplist) { - ret = -TARGET_EFAULT; - goto fail; + if (gidsetsize) { + grouplist = alloca(gidsetsize * sizeof(gid_t)); + target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 2, 1); + if (!target_grouplist) { + ret = -TARGET_EFAULT; + goto fail; + } + for (i = 0; i < gidsetsize; i++) { + grouplist[i] = low2highgid(tswapid(target_grouplist[i])); + } + unlock_user(target_grouplist, arg2, 0); } - for(i = 0;i < gidsetsize; i++) - grouplist[i] = low2highgid(tswapid(target_grouplist[i])); - unlock_user(target_grouplist, arg2, 0); ret = get_errno(setgroups(gidsetsize, grouplist)); } break; @@ -8512,7 +8631,20 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_set_robust_list case TARGET_NR_set_robust_list: - goto unimplemented_nowarn; + case TARGET_NR_get_robust_list: + /* The ABI for supporting robust futexes has userspace pass + * the kernel a pointer to a linked list which is updated by + * userspace after the syscall; the list is walked by the kernel + * when the thread exits. Since the linked list in QEMU guest + * memory isn't a valid linked list for the host and we have + * no way to reliably intercept the thread-death event, we can't + * support these. Silently return ENOSYS so that guest userspace + * falls back to a non-robust futex implementation (which should + * be OK except in the corner case of the guest crashing while + * holding a mutex that is shared with another process via + * shared memory). + */ + goto unimplemented_nowarn; #endif #if defined(TARGET_NR_utimensat) && defined(__NR_utimensat) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index d4589e7906..92c01a9603 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -544,7 +544,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, struct target_old_sigaction { abi_ulong _sa_handler; abi_ulong sa_mask; - abi_ulong sa_flags; + int32_t sa_flags; }; struct target_rt_sigaction { @@ -2017,6 +2017,12 @@ struct target_statfs64 { #define TARGET_F_SETLKW 9 #define TARGET_F_SETOWN 5 /* for sockets. */ #define TARGET_F_GETOWN 6 /* for sockets. */ + +#define TARGET_F_RDLCK 1 +#define TARGET_F_WRLCK 2 +#define TARGET_F_UNLCK 8 +#define TARGET_F_EXLCK 16 +#define TARGET_F_SHLCK 32 #elif defined(TARGET_MIPS) #define TARGET_F_GETLK 14 #define TARGET_F_SETLK 6 @@ -2031,6 +2037,18 @@ struct target_statfs64 { #define TARGET_F_GETOWN 9 /* for sockets. */ #endif +#ifndef TARGET_F_RDLCK +#define TARGET_F_RDLCK 0 +#define TARGET_F_WRLCK 1 +#define TARGET_F_UNLCK 2 +#endif + +#ifndef TARGET_F_EXLCK +#define TARGET_F_EXLCK 4 +#define TARGET_F_SHLCK 8 +#endif + + #define TARGET_F_SETSIG 10 /* for sockets. */ #define TARGET_F_GETSIG 11 /* for sockets. */ diff --git a/main-loop.c b/main-loop.c index ea9e3c15ef..83b4f71237 100644 --- a/main-loop.c +++ b/main-loop.c @@ -116,6 +116,11 @@ static int qemu_signal_init(void) static AioContext *qemu_aio_context; +AioContext *qemu_get_aio_context(void) +{ + return qemu_aio_context; +} + void qemu_notify_event(void) { if (!qemu_aio_context) { @@ -124,6 +129,8 @@ void qemu_notify_event(void) aio_notify(qemu_aio_context); } +static GArray *gpollfds; + int qemu_init_main_loop(void) { int ret; @@ -140,6 +147,7 @@ int qemu_init_main_loop(void) return ret; } + gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); qemu_aio_context = aio_context_new(); src = aio_get_g_source(qemu_aio_context); g_source_attach(src, NULL); @@ -147,100 +155,63 @@ int qemu_init_main_loop(void) return 0; } -static fd_set rfds, wfds, xfds; -static int nfds; -static GPollFD poll_fds[1024 * 2]; /* this is probably overkill */ -static int n_poll_fds; static int max_priority; #ifndef _WIN32 -static void glib_select_fill(int *max_fd, fd_set *rfds, fd_set *wfds, - fd_set *xfds, uint32_t *cur_timeout) +static int glib_pollfds_idx; +static int glib_n_poll_fds; + +static void glib_pollfds_fill(uint32_t *cur_timeout) { GMainContext *context = g_main_context_default(); - int i; int timeout = 0; + int n; g_main_context_prepare(context, &max_priority); - n_poll_fds = g_main_context_query(context, max_priority, &timeout, - poll_fds, ARRAY_SIZE(poll_fds)); - g_assert(n_poll_fds <= ARRAY_SIZE(poll_fds)); - - for (i = 0; i < n_poll_fds; i++) { - GPollFD *p = &poll_fds[i]; - - if ((p->events & G_IO_IN)) { - FD_SET(p->fd, rfds); - *max_fd = MAX(*max_fd, p->fd); - } - if ((p->events & G_IO_OUT)) { - FD_SET(p->fd, wfds); - *max_fd = MAX(*max_fd, p->fd); - } - if ((p->events & G_IO_ERR)) { - FD_SET(p->fd, xfds); - *max_fd = MAX(*max_fd, p->fd); - } - } + glib_pollfds_idx = gpollfds->len; + n = glib_n_poll_fds; + do { + GPollFD *pfds; + glib_n_poll_fds = n; + g_array_set_size(gpollfds, glib_pollfds_idx + glib_n_poll_fds); + pfds = &g_array_index(gpollfds, GPollFD, glib_pollfds_idx); + n = g_main_context_query(context, max_priority, &timeout, pfds, + glib_n_poll_fds); + } while (n != glib_n_poll_fds); if (timeout >= 0 && timeout < *cur_timeout) { *cur_timeout = timeout; } } -static void glib_select_poll(fd_set *rfds, fd_set *wfds, fd_set *xfds, - bool err) +static void glib_pollfds_poll(void) { GMainContext *context = g_main_context_default(); + GPollFD *pfds = &g_array_index(gpollfds, GPollFD, glib_pollfds_idx); - if (!err) { - int i; - - for (i = 0; i < n_poll_fds; i++) { - GPollFD *p = &poll_fds[i]; - - if ((p->events & G_IO_IN) && FD_ISSET(p->fd, rfds)) { - p->revents |= G_IO_IN; - } - if ((p->events & G_IO_OUT) && FD_ISSET(p->fd, wfds)) { - p->revents |= G_IO_OUT; - } - if ((p->events & G_IO_ERR) && FD_ISSET(p->fd, xfds)) { - p->revents |= G_IO_ERR; - } - } - } - - if (g_main_context_check(context, max_priority, poll_fds, n_poll_fds)) { + if (g_main_context_check(context, max_priority, pfds, glib_n_poll_fds)) { g_main_context_dispatch(context); } } static int os_host_main_loop_wait(uint32_t timeout) { - struct timeval tv, *tvarg = NULL; int ret; - glib_select_fill(&nfds, &rfds, &wfds, &xfds, &timeout); - - if (timeout < UINT32_MAX) { - tvarg = &tv; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - } + glib_pollfds_fill(&timeout); if (timeout > 0) { qemu_mutex_unlock_iothread(); } - ret = select(nfds + 1, &rfds, &wfds, &xfds, tvarg); + ret = g_poll((GPollFD *)gpollfds->data, gpollfds->len, timeout); if (timeout > 0) { qemu_mutex_lock_iothread(); } - glib_select_poll(&rfds, &wfds, &xfds, (ret < 0)); + glib_pollfds_poll(); return ret; } #else @@ -334,14 +305,67 @@ void qemu_fd_register(int fd) FD_CONNECT | FD_WRITE | FD_OOB); } +static int pollfds_fill(GArray *pollfds, fd_set *rfds, fd_set *wfds, + fd_set *xfds) +{ + int nfds = -1; + int i; + + for (i = 0; i < pollfds->len; i++) { + GPollFD *pfd = &g_array_index(pollfds, GPollFD, i); + int fd = pfd->fd; + int events = pfd->events; + if (events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) { + FD_SET(fd, rfds); + nfds = MAX(nfds, fd); + } + if (events & (G_IO_OUT | G_IO_ERR)) { + FD_SET(fd, wfds); + nfds = MAX(nfds, fd); + } + if (events & G_IO_PRI) { + FD_SET(fd, xfds); + nfds = MAX(nfds, fd); + } + } + return nfds; +} + +static void pollfds_poll(GArray *pollfds, int nfds, fd_set *rfds, + fd_set *wfds, fd_set *xfds) +{ + int i; + + for (i = 0; i < pollfds->len; i++) { + GPollFD *pfd = &g_array_index(pollfds, GPollFD, i); + int fd = pfd->fd; + int revents = 0; + + if (FD_ISSET(fd, rfds)) { + revents |= G_IO_IN | G_IO_HUP | G_IO_ERR; + } + if (FD_ISSET(fd, wfds)) { + revents |= G_IO_OUT | G_IO_ERR; + } + if (FD_ISSET(fd, xfds)) { + revents |= G_IO_PRI; + } + pfd->revents = revents & pfd->events; + } +} + static int os_host_main_loop_wait(uint32_t timeout) { GMainContext *context = g_main_context_default(); - int ret, i; + GPollFD poll_fds[1024 * 2]; /* this is probably overkill */ + int select_ret = 0; + int g_poll_ret, ret, i, n_poll_fds; PollingEntry *pe; WaitObjects *w = &wait_objects; gint poll_timeout; static struct timeval tv0; + fd_set rfds, wfds, xfds; + int nfds; /* XXX: need to suppress polling by better using win32 events */ ret = 0; @@ -352,13 +376,6 @@ static int os_host_main_loop_wait(uint32_t timeout) return ret; } - if (nfds >= 0) { - ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv0); - if (ret != 0) { - timeout = 0; - } - } - g_main_context_prepare(context, &max_priority); n_poll_fds = g_main_context_query(context, max_priority, &poll_timeout, poll_fds, ARRAY_SIZE(poll_fds)); @@ -374,9 +391,9 @@ static int os_host_main_loop_wait(uint32_t timeout) } qemu_mutex_unlock_iothread(); - ret = g_poll(poll_fds, n_poll_fds + w->num, poll_timeout); + g_poll_ret = g_poll(poll_fds, n_poll_fds + w->num, poll_timeout); qemu_mutex_lock_iothread(); - if (ret > 0) { + if (g_poll_ret > 0) { for (i = 0; i < w->num; i++) { w->revents[i] = poll_fds[n_poll_fds + i].revents; } @@ -391,12 +408,25 @@ static int os_host_main_loop_wait(uint32_t timeout) g_main_context_dispatch(context); } - /* If an edge-triggered socket event occurred, select will return a - * positive result on the next iteration. We do not need to do anything - * here. + /* Call select after g_poll to avoid a useless iteration and therefore + * improve socket latency. */ - return ret; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&xfds); + nfds = pollfds_fill(gpollfds, &rfds, &wfds, &xfds); + if (nfds >= 0) { + select_ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv0); + if (select_ret != 0) { + timeout = 0; + } + if (select_ret > 0) { + pollfds_poll(gpollfds, nfds, &rfds, &wfds, &xfds); + } + } + + return select_ret || g_poll_ret; } #endif @@ -410,21 +440,17 @@ int main_loop_wait(int nonblocking) } /* poll any events */ + g_array_set_size(gpollfds, 0); /* reset for new iteration */ /* XXX: separate device handlers from system ones */ - nfds = -1; - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_ZERO(&xfds); - #ifdef CONFIG_SLIRP slirp_update_timeout(&timeout); - slirp_select_fill(&nfds, &rfds, &wfds, &xfds); + slirp_pollfds_fill(gpollfds); #endif - qemu_iohandler_fill(&nfds, &rfds, &wfds, &xfds); + qemu_iohandler_fill(gpollfds); ret = os_host_main_loop_wait(timeout); - qemu_iohandler_poll(&rfds, &wfds, &xfds, ret); + qemu_iohandler_poll(gpollfds, ret); #ifdef CONFIG_SLIRP - slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0)); + slirp_pollfds_poll(gpollfds, (ret < 0)); #endif qemu_run_all_timers(); diff --git a/memory.c b/memory.c index 410c5f80b4..92a2196b7e 100644 --- a/memory.c +++ b/memory.c @@ -855,7 +855,7 @@ static uint64_t memory_region_dispatch_read1(MemoryRegion *mr, } if (!mr->ops->read) { - return mr->ops->old_mmio.read[bitops_ffsl(size)](mr->opaque, addr); + return mr->ops->old_mmio.read[ctz32(size)](mr->opaque, addr); } /* FIXME: support unaligned access */ @@ -908,7 +908,7 @@ static void memory_region_dispatch_write(MemoryRegion *mr, adjust_endianness(mr, &data, size); if (!mr->ops->write) { - mr->ops->old_mmio.write[bitops_ffsl(size)](mr->opaque, addr, data); + mr->ops->old_mmio.write[ctz32(size)](mr->opaque, addr, data); return; } diff --git a/migration-exec.c b/migration-exec.c index a051a6e668..deab4e378e 100644 --- a/migration-exec.c +++ b/migration-exec.c @@ -33,49 +33,14 @@ do { } while (0) #endif -static int file_errno(MigrationState *s) -{ - return errno; -} - -static int file_write(MigrationState *s, const void * buf, size_t size) -{ - return write(s->fd, buf, size); -} - -static int exec_close(MigrationState *s) -{ - int ret = 0; - DPRINTF("exec_close\n"); - ret = qemu_fclose(s->opaque); - s->opaque = NULL; - s->fd = -1; - if (ret >= 0 && !(WIFEXITED(ret) && WEXITSTATUS(ret) == 0)) { - /* close succeeded, but non-zero exit code: */ - ret = -EIO; /* fake errno value */ - } - return ret; -} - void exec_start_outgoing_migration(MigrationState *s, const char *command, Error **errp) { - FILE *f; - - f = popen(command, "w"); - if (f == NULL) { + s->file = qemu_popen_cmd(command, "w"); + if (s->file == NULL) { error_setg_errno(errp, errno, "failed to popen the migration target"); return; } - s->fd = fileno(f); - assert(s->fd != -1); - - s->opaque = qemu_popen(f, "w"); - - s->close = exec_close; - s->get_error = file_errno; - s->write = file_write; - migrate_fd_connect(s); } diff --git a/migration-fd.c b/migration-fd.c index a99e0e3971..3d4613cbaf 100644 --- a/migration-fd.c +++ b/migration-fd.c @@ -30,54 +30,13 @@ do { } while (0) #endif -static int fd_errno(MigrationState *s) -{ - return errno; -} - -static int fd_write(MigrationState *s, const void * buf, size_t size) -{ - return write(s->fd, buf, size); -} - -static int fd_close(MigrationState *s) -{ - struct stat st; - int ret; - - DPRINTF("fd_close\n"); - ret = fstat(s->fd, &st); - if (ret == 0 && S_ISREG(st.st_mode)) { - /* - * If the file handle is a regular file make sure the - * data is flushed to disk before signaling success. - */ - ret = fsync(s->fd); - if (ret != 0) { - ret = -errno; - perror("migration-fd: fsync"); - return ret; - } - } - ret = close(s->fd); - s->fd = -1; - if (ret != 0) { - ret = -errno; - perror("migration-fd: close"); - } - return ret; -} - void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **errp) { - s->fd = monitor_get_fd(cur_mon, fdname, errp); - if (s->fd == -1) { + int fd = monitor_get_fd(cur_mon, fdname, errp); + if (fd == -1) { return; } - - s->get_error = fd_errno; - s->write = fd_write; - s->close = fd_close; + s->file = qemu_fdopen(fd, "wb"); migrate_fd_connect(s); } diff --git a/migration-tcp.c b/migration-tcp.c index e78a296137..b20ee58f55 100644 --- a/migration-tcp.c +++ b/migration-tcp.c @@ -29,49 +29,24 @@ do { } while (0) #endif -static int socket_errno(MigrationState *s) -{ - return socket_error(); -} - -static int socket_write(MigrationState *s, const void * buf, size_t size) -{ - return send(s->fd, buf, size, 0); -} - -static int tcp_close(MigrationState *s) -{ - int r = 0; - DPRINTF("tcp_close\n"); - if (closesocket(s->fd) < 0) { - r = -socket_error(); - } - return r; -} - static void tcp_wait_for_connect(int fd, void *opaque) { MigrationState *s = opaque; if (fd < 0) { DPRINTF("migrate connect error\n"); - s->fd = -1; + s->file = NULL; migrate_fd_error(s); } else { DPRINTF("migrate connect success\n"); - s->fd = fd; - socket_set_block(s->fd); + s->file = qemu_fopen_socket(fd, "wb"); migrate_fd_connect(s); } } void tcp_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp) { - s->get_error = socket_errno; - s->write = socket_write; - s->close = tcp_close; - - s->fd = inet_nonblocking_connect(host_port, tcp_wait_for_connect, s, errp); + inet_nonblocking_connect(host_port, tcp_wait_for_connect, s, errp); } static void tcp_accept_incoming_migration(void *opaque) @@ -95,7 +70,7 @@ static void tcp_accept_incoming_migration(void *opaque) goto out; } - f = qemu_fopen_socket(c); + f = qemu_fopen_socket(c, "rb"); if (f == NULL) { fprintf(stderr, "could not qemu_fopen socket\n"); goto out; diff --git a/migration-unix.c b/migration-unix.c index 218835a7a4..94b7022fc8 100644 --- a/migration-unix.c +++ b/migration-unix.c @@ -29,49 +29,24 @@ do { } while (0) #endif -static int unix_errno(MigrationState *s) -{ - return errno; -} - -static int unix_write(MigrationState *s, const void * buf, size_t size) -{ - return write(s->fd, buf, size); -} - -static int unix_close(MigrationState *s) -{ - int r = 0; - DPRINTF("unix_close\n"); - if (close(s->fd) < 0) { - r = -errno; - } - return r; -} - static void unix_wait_for_connect(int fd, void *opaque) { MigrationState *s = opaque; if (fd < 0) { DPRINTF("migrate connect error\n"); - s->fd = -1; + s->file = NULL; migrate_fd_error(s); } else { DPRINTF("migrate connect success\n"); - s->fd = fd; - socket_set_block(s->fd); + s->file = qemu_fopen_socket(fd, "wb"); migrate_fd_connect(s); } } void unix_start_outgoing_migration(MigrationState *s, const char *path, Error **errp) { - s->get_error = unix_errno; - s->write = unix_write; - s->close = unix_close; - - s->fd = unix_nonblocking_connect(path, unix_wait_for_connect, s, errp); + unix_nonblocking_connect(path, unix_wait_for_connect, s, errp); } static void unix_accept_incoming_migration(void *opaque) @@ -95,7 +70,7 @@ static void unix_accept_incoming_migration(void *opaque) goto out; } - f = qemu_fopen_socket(c); + f = qemu_fopen_socket(c, "rb"); if (f == NULL) { fprintf(stderr, "could not qemu_fopen socket\n"); goto out; diff --git a/migration.c b/migration.c index c69e864fcd..185d11260d 100644 --- a/migration.c +++ b/migration.c @@ -23,6 +23,7 @@ #include "migration/block.h" #include "qemu/thread.h" #include "qmp-commands.h" +#include "trace.h" //#define DEBUG_MIGRATION @@ -85,7 +86,7 @@ void qemu_start_incoming_migration(const char *uri, Error **errp) fd_start_incoming_migration(p, errp); #endif else { - error_setg(errp, "unknown migration protocol: %s\n", uri); + error_setg(errp, "unknown migration protocol: %s", uri); } } @@ -95,7 +96,6 @@ static void process_incoming_migration_co(void *opaque) int ret; ret = qemu_loadvm_state(f); - qemu_set_fd_handler(qemu_get_fd(f), NULL, NULL, NULL); qemu_fclose(f); if (ret < 0) { fprintf(stderr, "load of migration failed\n"); @@ -115,12 +115,6 @@ static void process_incoming_migration_co(void *opaque) } } -static void enter_migration_coroutine(void *opaque) -{ - Coroutine *co = opaque; - qemu_coroutine_enter(co, NULL); -} - void process_incoming_migration(QEMUFile *f) { Coroutine *co = qemu_coroutine_create(process_incoming_migration_co); @@ -128,7 +122,6 @@ void process_incoming_migration(QEMUFile *f) assert(fd != -1); socket_set_nonblock(fd); - qemu_set_fd_handler(fd, enter_migration_coroutine, NULL, co); qemu_coroutine_enter(co, f); } @@ -268,81 +261,54 @@ void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, /* shared migration helpers */ -static int migrate_fd_cleanup(MigrationState *s) +static void migrate_fd_cleanup(void *opaque) { - int ret = 0; + MigrationState *s = opaque; + + qemu_bh_delete(s->cleanup_bh); + s->cleanup_bh = NULL; if (s->file) { DPRINTF("closing file\n"); - ret = qemu_fclose(s->file); + qemu_mutex_unlock_iothread(); + qemu_thread_join(&s->thread); + qemu_mutex_lock_iothread(); + + qemu_fclose(s->file); s->file = NULL; } - assert(s->fd == -1); - return ret; + assert(s->state != MIG_STATE_ACTIVE); + + if (s->state != MIG_STATE_COMPLETED) { + qemu_savevm_state_cancel(); + } + + notifier_list_notify(&migration_state_notifiers, s); +} + +static void migrate_finish_set_state(MigrationState *s, int new_state) +{ + if (__sync_val_compare_and_swap(&s->state, MIG_STATE_ACTIVE, + new_state) == new_state) { + trace_migrate_set_state(new_state); + } } void migrate_fd_error(MigrationState *s) { DPRINTF("setting error state\n"); + assert(s->file == NULL); s->state = MIG_STATE_ERROR; + trace_migrate_set_state(MIG_STATE_ERROR); notifier_list_notify(&migration_state_notifiers, s); - migrate_fd_cleanup(s); -} - -static void migrate_fd_completed(MigrationState *s) -{ - DPRINTF("setting completed state\n"); - if (migrate_fd_cleanup(s) < 0) { - s->state = MIG_STATE_ERROR; - } else { - s->state = MIG_STATE_COMPLETED; - runstate_set(RUN_STATE_POSTMIGRATE); - } - notifier_list_notify(&migration_state_notifiers, s); -} - -ssize_t migrate_fd_put_buffer(MigrationState *s, const void *data, - size_t size) -{ - ssize_t ret; - - if (s->state != MIG_STATE_ACTIVE) { - return -EIO; - } - - do { - ret = s->write(s, data, size); - } while (ret == -1 && ((s->get_error(s)) == EINTR)); - - if (ret == -1) - ret = -(s->get_error(s)); - - return ret; } static void migrate_fd_cancel(MigrationState *s) { - if (s->state != MIG_STATE_ACTIVE) - return; - DPRINTF("cancelling migration\n"); - s->state = MIG_STATE_CANCELLED; - notifier_list_notify(&migration_state_notifiers, s); - qemu_savevm_state_cancel(s->file); - - migrate_fd_cleanup(s); -} - -int migrate_fd_close(MigrationState *s) -{ - int rc = 0; - if (s->fd != -1) { - rc = s->close(s); - s->fd = -1; - } - return rc; + migrate_finish_set_state(s, MIG_STATE_CANCELLED); } void add_migration_state_change_notifier(Notifier *notify) @@ -390,8 +356,9 @@ static MigrationState *migrate_init(const MigrationParams *params) s->bandwidth_limit = bandwidth_limit; s->state = MIG_STATE_SETUP; - s->total_time = qemu_get_clock_ms(rt_clock); + trace_migrate_set_state(MIG_STATE_SETUP); + s->total_time = qemu_get_clock_ms(rt_clock); return s; } @@ -488,10 +455,15 @@ void qmp_migrate_set_speed(int64_t value, Error **errp) if (value < 0) { value = 0; } + if (value > SIZE_MAX) { + value = SIZE_MAX; + } s = migrate_get_current(); s->bandwidth_limit = value; - qemu_file_set_rate_limit(s->file, s->bandwidth_limit); + if (s->file) { + qemu_file_set_rate_limit(s->file, s->bandwidth_limit / XFER_LIMIT_RATIO); + } } void qmp_migrate_set_downtime(double value, Error **errp) @@ -521,283 +493,108 @@ int64_t migrate_xbzrle_cache_size(void) /* migration thread support */ - -static ssize_t buffered_flush(MigrationState *s) -{ - size_t offset = 0; - ssize_t ret = 0; - - DPRINTF("flushing %zu byte(s) of data\n", s->buffer_size); - - while (s->bytes_xfer < s->xfer_limit && offset < s->buffer_size) { - size_t to_send = MIN(s->buffer_size - offset, s->xfer_limit - s->bytes_xfer); - ret = migrate_fd_put_buffer(s, s->buffer + offset, to_send); - if (ret <= 0) { - DPRINTF("error flushing data, %zd\n", ret); - break; - } else { - DPRINTF("flushed %zd byte(s)\n", ret); - offset += ret; - s->bytes_xfer += ret; - } - } - - DPRINTF("flushed %zu of %zu byte(s)\n", offset, s->buffer_size); - memmove(s->buffer, s->buffer + offset, s->buffer_size - offset); - s->buffer_size -= offset; - - if (ret < 0) { - return ret; - } - return offset; -} - -static int buffered_put_buffer(void *opaque, const uint8_t *buf, - int64_t pos, int size) -{ - MigrationState *s = opaque; - ssize_t error; - - DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos); - - error = qemu_file_get_error(s->file); - if (error) { - DPRINTF("flush when error, bailing: %s\n", strerror(-error)); - return error; - } - - if (size <= 0) { - return size; - } - - if (size > (s->buffer_capacity - s->buffer_size)) { - DPRINTF("increasing buffer capacity from %zu by %zu\n", - s->buffer_capacity, size + 1024); - - s->buffer_capacity += size + 1024; - - s->buffer = g_realloc(s->buffer, s->buffer_capacity); - } - - memcpy(s->buffer + s->buffer_size, buf, size); - s->buffer_size += size; - - return size; -} - -static int buffered_close(void *opaque) -{ - MigrationState *s = opaque; - ssize_t ret = 0; - int ret2; - - DPRINTF("closing\n"); - - s->xfer_limit = INT_MAX; - while (!qemu_file_get_error(s->file) && s->buffer_size) { - ret = buffered_flush(s); - if (ret < 0) { - break; - } - } - - ret2 = migrate_fd_close(s); - if (ret >= 0) { - ret = ret2; - } - ret = migrate_fd_close(s); - s->complete = true; - return ret; -} - -static int buffered_get_fd(void *opaque) -{ - MigrationState *s = opaque; - - return s->fd; -} - -/* - * The meaning of the return values is: - * 0: We can continue sending - * 1: Time to stop - * negative: There has been an error - */ -static int buffered_rate_limit(void *opaque) -{ - MigrationState *s = opaque; - int ret; - - ret = qemu_file_get_error(s->file); - if (ret) { - return ret; - } - - if (s->bytes_xfer > s->xfer_limit) { - return 1; - } - - return 0; -} - -static int64_t buffered_set_rate_limit(void *opaque, int64_t new_rate) -{ - MigrationState *s = opaque; - if (qemu_file_get_error(s->file)) { - goto out; - } - if (new_rate > SIZE_MAX) { - new_rate = SIZE_MAX; - } - - s->xfer_limit = new_rate / 10; - -out: - return s->xfer_limit; -} - -static int64_t buffered_get_rate_limit(void *opaque) -{ - MigrationState *s = opaque; - - return s->xfer_limit; -} - -static bool migrate_fd_put_ready(MigrationState *s, uint64_t max_size) -{ - int ret; - uint64_t pending_size; - bool last_round = false; - - qemu_mutex_lock_iothread(); - if (s->state != MIG_STATE_ACTIVE) { - DPRINTF("put_ready returning because of non-active state\n"); - qemu_mutex_unlock_iothread(); - return false; - } - if (s->first_time) { - s->first_time = false; - DPRINTF("beginning savevm\n"); - ret = qemu_savevm_state_begin(s->file, &s->params); - if (ret < 0) { - DPRINTF("failed, %d\n", ret); - migrate_fd_error(s); - qemu_mutex_unlock_iothread(); - return false; - } - } - - DPRINTF("iterate\n"); - pending_size = qemu_savevm_state_pending(s->file, max_size); - DPRINTF("pending size %lu max %lu\n", pending_size, max_size); - if (pending_size >= max_size) { - ret = qemu_savevm_state_iterate(s->file); - if (ret < 0) { - migrate_fd_error(s); - } - } else { - int old_vm_running = runstate_is_running(); - int64_t start_time, end_time; - - DPRINTF("done iterating\n"); - start_time = qemu_get_clock_ms(rt_clock); - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); - if (old_vm_running) { - vm_stop(RUN_STATE_FINISH_MIGRATE); - } else { - vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); - } - - if (qemu_savevm_state_complete(s->file) < 0) { - migrate_fd_error(s); - } else { - migrate_fd_completed(s); - } - end_time = qemu_get_clock_ms(rt_clock); - s->total_time = end_time - s->total_time; - s->downtime = end_time - start_time; - if (s->state != MIG_STATE_COMPLETED) { - if (old_vm_running) { - vm_start(); - } - } - last_round = true; - } - qemu_mutex_unlock_iothread(); - - return last_round; -} - -static void *buffered_file_thread(void *opaque) +static void *migration_thread(void *opaque) { MigrationState *s = opaque; int64_t initial_time = qemu_get_clock_ms(rt_clock); + int64_t sleep_time = 0; + int64_t initial_bytes = 0; int64_t max_size = 0; - bool last_round = false; + int64_t start_time = initial_time; + bool old_vm_running = false; - while (true) { - int64_t current_time = qemu_get_clock_ms(rt_clock); + DPRINTF("beginning savevm\n"); + qemu_savevm_state_begin(s->file, &s->params); - if (s->complete) { + while (s->state == MIG_STATE_ACTIVE) { + int64_t current_time; + uint64_t pending_size; + + if (!qemu_file_rate_limit(s->file)) { + DPRINTF("iterate\n"); + pending_size = qemu_savevm_state_pending(s->file, max_size); + DPRINTF("pending size %lu max %lu\n", pending_size, max_size); + if (pending_size && pending_size >= max_size) { + qemu_savevm_state_iterate(s->file); + } else { + DPRINTF("done iterating\n"); + qemu_mutex_lock_iothread(); + start_time = qemu_get_clock_ms(rt_clock); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + old_vm_running = runstate_is_running(); + vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); + qemu_file_set_rate_limit(s->file, INT_MAX); + qemu_savevm_state_complete(s->file); + qemu_mutex_unlock_iothread(); + if (!qemu_file_get_error(s->file)) { + migrate_finish_set_state(s, MIG_STATE_COMPLETED); + break; + } + } + } + + if (qemu_file_get_error(s->file)) { + migrate_finish_set_state(s, MIG_STATE_ERROR); break; } + current_time = qemu_get_clock_ms(rt_clock); if (current_time >= initial_time + BUFFER_DELAY) { - uint64_t transferred_bytes = s->bytes_xfer; - uint64_t time_spent = current_time - initial_time; + uint64_t transferred_bytes = qemu_ftell(s->file) - initial_bytes; + uint64_t time_spent = current_time - initial_time - sleep_time; double bandwidth = transferred_bytes / time_spent; max_size = bandwidth * migrate_max_downtime() / 1000000; DPRINTF("transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %g max_size %" PRId64 "\n", transferred_bytes, time_spent, bandwidth, max_size); + /* if we haven't sent anything, we don't want to recalculate + 10000 is a small enough number for our purposes */ + if (s->dirty_bytes_rate && transferred_bytes > 10000) { + s->expected_downtime = s->dirty_bytes_rate / bandwidth; + } - s->bytes_xfer = 0; + qemu_file_reset_rate_limit(s->file); + sleep_time = 0; initial_time = current_time; + initial_bytes = qemu_ftell(s->file); } - if (!last_round && (s->bytes_xfer >= s->xfer_limit)) { + if (qemu_file_rate_limit(s->file)) { /* usleep expects microseconds */ g_usleep((initial_time + BUFFER_DELAY - current_time)*1000); - } - if (buffered_flush(s) < 0) { - break; - } - - DPRINTF("file is ready\n"); - if (s->bytes_xfer < s->xfer_limit) { - DPRINTF("notifying client\n"); - last_round = migrate_fd_put_ready(s, max_size); + sleep_time += qemu_get_clock_ms(rt_clock) - current_time; } } - g_free(s->buffer); + qemu_mutex_lock_iothread(); + if (s->state == MIG_STATE_COMPLETED) { + int64_t end_time = qemu_get_clock_ms(rt_clock); + s->total_time = end_time - s->total_time; + s->downtime = end_time - start_time; + runstate_set(RUN_STATE_POSTMIGRATE); + } else { + if (old_vm_running) { + vm_start(); + } + } + qemu_bh_schedule(s->cleanup_bh); + qemu_mutex_unlock_iothread(); + return NULL; } -static const QEMUFileOps buffered_file_ops = { - .get_fd = buffered_get_fd, - .put_buffer = buffered_put_buffer, - .close = buffered_close, - .rate_limit = buffered_rate_limit, - .get_rate_limit = buffered_get_rate_limit, - .set_rate_limit = buffered_set_rate_limit, -}; - void migrate_fd_connect(MigrationState *s) { s->state = MIG_STATE_ACTIVE; - s->bytes_xfer = 0; - s->buffer = NULL; - s->buffer_size = 0; - s->buffer_capacity = 0; + trace_migrate_set_state(MIG_STATE_ACTIVE); - s->first_time = true; + /* This is a best 1st approximation. ns to ms */ + s->expected_downtime = max_downtime/1000000; + s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s); - s->xfer_limit = s->bandwidth_limit / XFER_LIMIT_RATIO; - s->complete = false; + qemu_file_set_rate_limit(s->file, + s->bandwidth_limit / XFER_LIMIT_RATIO); - s->file = qemu_fopen_ops(s, &buffered_file_ops); - - qemu_thread_create(&s->thread, buffered_file_thread, s, - QEMU_THREAD_DETACHED); + qemu_thread_create(&s->thread, migration_thread, s, + QEMU_THREAD_JOINABLE); notifier_list_notify(&migration_state_notifiers, s); } diff --git a/monitor.c b/monitor.c index 9cf419bb1d..112e92064d 100644 --- a/monitor.c +++ b/monitor.c @@ -23,7 +23,7 @@ */ #include #include "hw/hw.h" -#include "hw/qdev.h" +#include "monitor/qdev.h" #include "hw/usb.h" #include "hw/pcmcia.h" #include "hw/pc.h" @@ -47,6 +47,7 @@ #include "migration/migration.h" #include "sysemu/kvm.h" #include "qemu/acl.h" +#include "tpm/tpm.h" #include "qapi/qmp/qint.h" #include "qapi/qmp/qfloat.h" #include "qapi/qmp/qlist.h" @@ -123,13 +124,17 @@ typedef struct mon_cmd_t { const char *help; void (*user_print)(Monitor *mon, const QObject *data); union { - void (*info)(Monitor *mon); void (*cmd)(Monitor *mon, const QDict *qdict); int (*cmd_new)(Monitor *mon, const QDict *params, QObject **ret_data); int (*cmd_async)(Monitor *mon, const QDict *params, MonitorCompletion *cb, void *opaque); } mhandler; int flags; + /* @sub_table is a list of 2nd level of commands. If it do not exist, + * mhandler should be used. If it exist, sub_table[?].mhandler should be + * used, and mhandler of 1st level plays the role of help function. + */ + struct mon_cmd_t *sub_table; } mon_cmd_t; /* file descriptors passed via SCM_RIGHTS */ @@ -270,6 +275,7 @@ static void monitor_puts(Monitor *mon, const char *str) char c; for(;;) { + assert(mon->outbuf_index < sizeof(mon->outbuf) - 1); c = *str++; if (c == '\0') break; @@ -716,10 +722,10 @@ static void help_cmd(Monitor *mon, const char *name) } else { help_cmd_dump(mon, mon_cmds, "", name); if (name && !strcmp(name, "log")) { - const CPULogItem *item; + const QEMULogItem *item; monitor_printf(mon, "Log items (comma separated):\n"); monitor_printf(mon, "%-10s %s\n", "none", "remove all logs"); - for(item = cpu_log_items; item->mask != 0; item++) { + for (item = qemu_log_items; item->mask != 0; item++) { monitor_printf(mon, "%-10s %s\n", item->name, item->help); } } @@ -806,28 +812,8 @@ static void user_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd, } } -static void do_info(Monitor *mon, const QDict *qdict) +static void do_info_help(Monitor *mon, const QDict *qdict) { - const mon_cmd_t *cmd; - const char *item = qdict_get_try_str(qdict, "item"); - - if (!item) { - goto help; - } - - for (cmd = info_cmds; cmd->name != NULL; cmd++) { - if (compare_cmd(item, cmd->name)) - break; - } - - if (cmd->name == NULL) { - goto help; - } - - cmd->mhandler.info(mon); - return; - -help: help_cmd(mon, "info"); } @@ -870,15 +856,14 @@ EventInfoList *qmp_query_events(Error **errp) /* set the current CPU defined by the user */ int monitor_set_cpu(int cpu_index) { - CPUArchState *env; + CPUState *cpu; - for(env = first_cpu; env != NULL; env = env->next_cpu) { - if (env->cpu_index == cpu_index) { - cur_mon->mon_cpu = env; - return 0; - } + cpu = qemu_get_cpu(cpu_index); + if (cpu == NULL) { + return -1; } - return -1; + cur_mon->mon_cpu = cpu->env_ptr; + return 0; } static CPUArchState *mon_get_cpu(void) @@ -892,22 +877,23 @@ static CPUArchState *mon_get_cpu(void) int monitor_get_cpu_index(void) { - return mon_get_cpu()->cpu_index; + CPUState *cpu = ENV_GET_CPU(mon_get_cpu()); + return cpu->cpu_index; } -static void do_info_registers(Monitor *mon) +static void do_info_registers(Monitor *mon, const QDict *qdict) { CPUArchState *env; env = mon_get_cpu(); cpu_dump_state(env, (FILE *)mon, monitor_fprintf, CPU_DUMP_FPU); } -static void do_info_jit(Monitor *mon) +static void do_info_jit(Monitor *mon, const QDict *qdict) { dump_exec_info((FILE *)mon, monitor_fprintf); } -static void do_info_history(Monitor *mon) +static void do_info_history(Monitor *mon, const QDict *qdict) { int i; const char *str; @@ -926,7 +912,7 @@ static void do_info_history(Monitor *mon) #if defined(TARGET_PPC) /* XXX: not implemented in other targets */ -static void do_info_cpu_stats(Monitor *mon) +static void do_info_cpu_stats(Monitor *mon, const QDict *qdict) { CPUArchState *env; @@ -935,7 +921,7 @@ static void do_info_cpu_stats(Monitor *mon) } #endif -static void do_trace_print_events(Monitor *mon) +static void do_trace_print_events(Monitor *mon, const QDict *qdict) { trace_print_events((FILE *)mon, &monitor_fprintf); } @@ -976,7 +962,7 @@ static int client_migrate_info(Monitor *mon, const QDict *qdict, static void do_logfile(Monitor *mon, const QDict *qdict) { - cpu_set_log_filename(qdict_get_str(qdict, "filename")); + qemu_set_log_filename(qdict_get_str(qdict, "filename")); } static void do_log(Monitor *mon, const QDict *qdict) @@ -987,13 +973,13 @@ static void do_log(Monitor *mon, const QDict *qdict) if (!strcmp(items, "none")) { mask = 0; } else { - mask = cpu_str_to_log_mask(items); + mask = qemu_str_to_log_mask(items); if (!mask) { help_cmd(mon, "log"); return; } } - cpu_set_log(mask); + qemu_set_log(mask); } static void do_singlestep(Monitor *mon, const QDict *qdict) @@ -1487,7 +1473,7 @@ static void tlb_info_64(Monitor *mon, CPUArchState *env) } #endif -static void tlb_info(Monitor *mon) +static void tlb_info(Monitor *mon, const QDict *qdict) { CPUArchState *env; @@ -1710,7 +1696,7 @@ static void mem_info_64(Monitor *mon, CPUArchState *env) } #endif -static void mem_info(Monitor *mon) +static void mem_info(Monitor *mon, const QDict *qdict) { CPUArchState *env; @@ -1749,7 +1735,7 @@ static void print_tlb(Monitor *mon, int idx, tlb_t *tlb) tlb->d, tlb->wt); } -static void tlb_info(Monitor *mon) +static void tlb_info(Monitor *mon, const QDict *qdict) { CPUArchState *env = mon_get_cpu(); int i; @@ -1765,7 +1751,7 @@ static void tlb_info(Monitor *mon) #endif #if defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_XTENSA) -static void tlb_info(Monitor *mon) +static void tlb_info(Monitor *mon, const QDict *qdict) { CPUArchState *env1 = mon_get_cpu(); @@ -1773,22 +1759,24 @@ static void tlb_info(Monitor *mon) } #endif -static void do_info_mtree(Monitor *mon) +static void do_info_mtree(Monitor *mon, const QDict *qdict) { mtree_info((fprintf_function)monitor_printf, mon); } -static void do_info_numa(Monitor *mon) +static void do_info_numa(Monitor *mon, const QDict *qdict) { int i; CPUArchState *env; + CPUState *cpu; monitor_printf(mon, "%d nodes\n", nb_numa_nodes); for (i = 0; i < nb_numa_nodes; i++) { monitor_printf(mon, "node %d cpus:", i); for (env = first_cpu; env != NULL; env = env->next_cpu) { - if (env->numa_node == i) { - monitor_printf(mon, " %d", env->cpu_index); + cpu = ENV_GET_CPU(env); + if (cpu->numa_node == i) { + monitor_printf(mon, " %d", cpu->cpu_index); } } monitor_printf(mon, "\n"); @@ -1802,7 +1790,7 @@ static void do_info_numa(Monitor *mon) int64_t qemu_time; int64_t dev_time; -static void do_info_profile(Monitor *mon) +static void do_info_profile(Monitor *mon, const QDict *qdict) { int64_t total; total = qemu_time; @@ -1816,7 +1804,7 @@ static void do_info_profile(Monitor *mon) dev_time = 0; } #else -static void do_info_profile(Monitor *mon) +static void do_info_profile(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "Internal profiler not compiled\n"); } @@ -1825,7 +1813,7 @@ static void do_info_profile(Monitor *mon) /* Capture support */ static QLIST_HEAD (capture_list_head, CaptureState) capture_head; -static void do_info_capture(Monitor *mon) +static void do_info_capture(Monitor *mon, const QDict *qdict) { int i; CaptureState *s; @@ -1990,6 +1978,7 @@ static void do_inject_mce(Monitor *mon, const QDict *qdict) { X86CPU *cpu; CPUX86State *cenv; + CPUState *cs; int cpu_index = qdict_get_int(qdict, "cpu_index"); int bank = qdict_get_int(qdict, "bank"); uint64_t status = qdict_get_int(qdict, "status"); @@ -2003,7 +1992,8 @@ static void do_inject_mce(Monitor *mon, const QDict *qdict) } for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) { cpu = x86_env_get_cpu(cenv); - if (cenv->cpu_index == cpu_index) { + cs = CPU(cpu); + if (cs->cpu_index == cpu_index) { cpu_x86_inject_mce(mon, cpu, bank, status, mcg_status, addr, misc, flags); break; @@ -2421,12 +2411,6 @@ int monitor_handle_fd_param(Monitor *mon, const char *fdname) return fd; } -/* mon_cmds and info_cmds would be sorted at runtime */ -static mon_cmd_t mon_cmds[] = { -#include "hmp-commands.h" - { NULL, NULL, }, -}; - /* Please update hmp-commands.hx when adding or changing commands */ static mon_cmd_t info_cmds[] = { { @@ -2434,63 +2418,63 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show the version of QEMU", - .mhandler.info = hmp_info_version, + .mhandler.cmd = hmp_info_version, }, { .name = "network", .args_type = "", .params = "", .help = "show the network state", - .mhandler.info = do_info_network, + .mhandler.cmd = do_info_network, }, { .name = "chardev", .args_type = "", .params = "", .help = "show the character devices", - .mhandler.info = hmp_info_chardev, + .mhandler.cmd = hmp_info_chardev, }, { .name = "block", .args_type = "", .params = "", .help = "show the block devices", - .mhandler.info = hmp_info_block, + .mhandler.cmd = hmp_info_block, }, { .name = "blockstats", .args_type = "", .params = "", .help = "show block device statistics", - .mhandler.info = hmp_info_blockstats, + .mhandler.cmd = hmp_info_blockstats, }, { .name = "block-jobs", .args_type = "", .params = "", .help = "show progress of ongoing block device operations", - .mhandler.info = hmp_info_block_jobs, + .mhandler.cmd = hmp_info_block_jobs, }, { .name = "registers", .args_type = "", .params = "", .help = "show the cpu registers", - .mhandler.info = do_info_registers, + .mhandler.cmd = do_info_registers, }, { .name = "cpus", .args_type = "", .params = "", .help = "show infos for each CPU", - .mhandler.info = hmp_info_cpus, + .mhandler.cmd = hmp_info_cpus, }, { .name = "history", .args_type = "", .params = "", .help = "show the command line history", - .mhandler.info = do_info_history, + .mhandler.cmd = do_info_history, }, #if defined(TARGET_I386) || defined(TARGET_PPC) || defined(TARGET_MIPS) || \ defined(TARGET_LM32) || (defined(TARGET_SPARC) && !defined(TARGET_SPARC64)) @@ -2500,11 +2484,11 @@ static mon_cmd_t info_cmds[] = { .params = "", .help = "show the interrupts statistics (if available)", #ifdef TARGET_SPARC - .mhandler.info = sun4m_irq_info, + .mhandler.cmd = sun4m_irq_info, #elif defined(TARGET_LM32) - .mhandler.info = lm32_irq_info, + .mhandler.cmd = lm32_irq_info, #else - .mhandler.info = irq_info, + .mhandler.cmd = irq_info, #endif }, { @@ -2513,11 +2497,11 @@ static mon_cmd_t info_cmds[] = { .params = "", .help = "show i8259 (PIC) state", #ifdef TARGET_SPARC - .mhandler.info = sun4m_pic_info, + .mhandler.cmd = sun4m_pic_info, #elif defined(TARGET_LM32) - .mhandler.info = lm32_do_pic_info, + .mhandler.cmd = lm32_do_pic_info, #else - .mhandler.info = pic_info, + .mhandler.cmd = pic_info, #endif }, #endif @@ -2526,7 +2510,7 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show PCI info", - .mhandler.info = hmp_info_pci, + .mhandler.cmd = hmp_info_pci, }, #if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) || \ defined(TARGET_PPC) || defined(TARGET_XTENSA) @@ -2535,7 +2519,7 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show virtual to physical memory mappings", - .mhandler.info = tlb_info, + .mhandler.cmd = tlb_info, }, #endif #if defined(TARGET_I386) @@ -2544,7 +2528,7 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show the active virtual memory mappings", - .mhandler.info = mem_info, + .mhandler.cmd = mem_info, }, #endif { @@ -2552,91 +2536,91 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show memory tree", - .mhandler.info = do_info_mtree, + .mhandler.cmd = do_info_mtree, }, { .name = "jit", .args_type = "", .params = "", .help = "show dynamic compiler info", - .mhandler.info = do_info_jit, + .mhandler.cmd = do_info_jit, }, { .name = "kvm", .args_type = "", .params = "", .help = "show KVM information", - .mhandler.info = hmp_info_kvm, + .mhandler.cmd = hmp_info_kvm, }, { .name = "numa", .args_type = "", .params = "", .help = "show NUMA information", - .mhandler.info = do_info_numa, + .mhandler.cmd = do_info_numa, }, { .name = "usb", .args_type = "", .params = "", .help = "show guest USB devices", - .mhandler.info = usb_info, + .mhandler.cmd = usb_info, }, { .name = "usbhost", .args_type = "", .params = "", .help = "show host USB devices", - .mhandler.info = usb_host_info, + .mhandler.cmd = usb_host_info, }, { .name = "profile", .args_type = "", .params = "", .help = "show profiling information", - .mhandler.info = do_info_profile, + .mhandler.cmd = do_info_profile, }, { .name = "capture", .args_type = "", .params = "", .help = "show capture information", - .mhandler.info = do_info_capture, + .mhandler.cmd = do_info_capture, }, { .name = "snapshots", .args_type = "", .params = "", .help = "show the currently saved VM snapshots", - .mhandler.info = do_info_snapshots, + .mhandler.cmd = do_info_snapshots, }, { .name = "status", .args_type = "", .params = "", .help = "show the current VM status (running|paused)", - .mhandler.info = hmp_info_status, + .mhandler.cmd = hmp_info_status, }, { .name = "pcmcia", .args_type = "", .params = "", .help = "show guest PCMCIA status", - .mhandler.info = pcmcia_info, + .mhandler.cmd = pcmcia_info, }, { .name = "mice", .args_type = "", .params = "", .help = "show which guest mouse is receiving events", - .mhandler.info = hmp_info_mice, + .mhandler.cmd = hmp_info_mice, }, { .name = "vnc", .args_type = "", .params = "", .help = "show the vnc server status", - .mhandler.info = hmp_info_vnc, + .mhandler.cmd = hmp_info_vnc, }, #if defined(CONFIG_SPICE) { @@ -2644,7 +2628,7 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show the spice server status", - .mhandler.info = hmp_info_spice, + .mhandler.cmd = hmp_info_spice, }, #endif { @@ -2652,14 +2636,14 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show the current VM name", - .mhandler.info = hmp_info_name, + .mhandler.cmd = hmp_info_name, }, { .name = "uuid", .args_type = "", .params = "", .help = "show the current VM UUID", - .mhandler.info = hmp_info_uuid, + .mhandler.cmd = hmp_info_uuid, }, #if defined(TARGET_PPC) { @@ -2667,7 +2651,7 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show CPU statistics", - .mhandler.info = do_info_cpu_stats, + .mhandler.cmd = do_info_cpu_stats, }, #endif #if defined(CONFIG_SLIRP) @@ -2676,7 +2660,7 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show user network stack connection states", - .mhandler.info = do_info_usernet, + .mhandler.cmd = do_info_usernet, }, #endif { @@ -2684,62 +2668,75 @@ static mon_cmd_t info_cmds[] = { .args_type = "", .params = "", .help = "show migration status", - .mhandler.info = hmp_info_migrate, + .mhandler.cmd = hmp_info_migrate, }, { .name = "migrate_capabilities", .args_type = "", .params = "", .help = "show current migration capabilities", - .mhandler.info = hmp_info_migrate_capabilities, + .mhandler.cmd = hmp_info_migrate_capabilities, }, { .name = "migrate_cache_size", .args_type = "", .params = "", .help = "show current migration xbzrle cache size", - .mhandler.info = hmp_info_migrate_cache_size, + .mhandler.cmd = hmp_info_migrate_cache_size, }, { .name = "balloon", .args_type = "", .params = "", .help = "show balloon information", - .mhandler.info = hmp_info_balloon, + .mhandler.cmd = hmp_info_balloon, }, { .name = "qtree", .args_type = "", .params = "", .help = "show device tree", - .mhandler.info = do_info_qtree, + .mhandler.cmd = do_info_qtree, }, { .name = "qdm", .args_type = "", .params = "", .help = "show qdev device model list", - .mhandler.info = do_info_qdm, + .mhandler.cmd = do_info_qdm, }, { .name = "roms", .args_type = "", .params = "", .help = "show roms", - .mhandler.info = do_info_roms, + .mhandler.cmd = do_info_roms, }, { .name = "trace-events", .args_type = "", .params = "", .help = "show available trace-events & their state", - .mhandler.info = do_trace_print_events, + .mhandler.cmd = do_trace_print_events, + }, + { + .name = "tpm", + .args_type = "", + .params = "", + .help = "show the TPM device", + .mhandler.cmd = hmp_info_tpm, }, { .name = NULL, }, }; +/* mon_cmds and info_cmds would be sorted at runtime */ +static mon_cmd_t mon_cmds[] = { +#include "hmp-commands.h" + { NULL, NULL, }, +}; + static const mon_cmd_t qmp_cmds[] = { #include "qmp-commands-old.h" { /* NULL */ }, @@ -2748,7 +2745,7 @@ static const mon_cmd_t qmp_cmds[] = { /*******************************************************************/ static const char *pch; -static jmp_buf expr_env; +static sigjmp_buf expr_env; #define MD_TLONG 0 #define MD_I32 1 @@ -3143,7 +3140,7 @@ static const MonitorDef monitor_defs[] = { static void expr_error(Monitor *mon, const char *msg) { monitor_printf(mon, "%s\n", msg); - longjmp(expr_env, 1); + siglongjmp(expr_env, 1); } /* return 0 if OK, -1 if not found */ @@ -3353,7 +3350,7 @@ static int64_t expr_sum(Monitor *mon) static int get_expr(Monitor *mon, int64_t *pval, const char **pp) { pch = *pp; - if (setjmp(expr_env)) { + if (sigsetjmp(expr_env, 0)) { *pp = pch; return -1; } @@ -3534,18 +3531,27 @@ static const mon_cmd_t *search_dispatch_table(const mon_cmd_t *disp_table, return NULL; } -static const mon_cmd_t *monitor_find_command(const char *cmdname) -{ - return search_dispatch_table(mon_cmds, cmdname); -} - static const mon_cmd_t *qmp_find_cmd(const char *cmdname) { return search_dispatch_table(qmp_cmds, cmdname); } +/* + * Parse @cmdline according to command table @table. + * If @cmdline is blank, return NULL. + * If it can't be parsed, report to @mon, and return NULL. + * Else, insert command arguments into @qdict, and return the command. + * If sub-command table exist, and if @cmdline contains addtional string for + * sub-command, this function will try search sub-command table. if no + * addtional string for sub-command exist, this function will return the found + * one in @table. + * Do not assume the returned command points into @table! It doesn't + * when the command is a sub-command. + */ static const mon_cmd_t *monitor_parse_command(Monitor *mon, const char *cmdline, + int start, + mon_cmd_t *table, QDict *qdict) { const char *p, *typestr; @@ -3556,20 +3562,35 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon, char *key; #ifdef DEBUG - monitor_printf(mon, "command='%s'\n", cmdline); + monitor_printf(mon, "command='%s', start='%d'\n", cmdline, start); #endif /* extract the command name */ - p = get_command_name(cmdline, cmdname, sizeof(cmdname)); + p = get_command_name(cmdline + start, cmdname, sizeof(cmdname)); if (!p) return NULL; - cmd = monitor_find_command(cmdname); + cmd = search_dispatch_table(table, cmdname); if (!cmd) { - monitor_printf(mon, "unknown command: '%s'\n", cmdname); + monitor_printf(mon, "unknown command: '%.*s'\n", + (int)(p - cmdline), cmdline); return NULL; } + /* filter out following useless space */ + while (qemu_isspace(*p)) { + p++; + } + /* search sub command */ + if (cmd->sub_table != NULL) { + /* check if user set additional command */ + if (*p == '\0') { + return cmd; + } + return monitor_parse_command(mon, cmdline, p - cmdline, + cmd->sub_table, qdict); + } + /* parse the parameters */ typestr = cmd->args_type; for(;;) { @@ -3925,7 +3946,7 @@ static void handle_user_command(Monitor *mon, const char *cmdline) qdict = qdict_new(); - cmd = monitor_parse_command(mon, cmdline, qdict); + cmd = monitor_parse_command(mon, cmdline, 0, mon_cmds, qdict); if (!cmd) goto out; @@ -4790,3 +4811,25 @@ int monitor_read_block_device_key(Monitor *mon, const char *device, return monitor_read_bdrv_key_start(mon, bs, completion_cb, opaque); } + +QemuOptsList qemu_mon_opts = { + .name = "mon", + .implied_opt_name = "chardev", + .head = QTAILQ_HEAD_INITIALIZER(qemu_mon_opts.head), + .desc = { + { + .name = "mode", + .type = QEMU_OPT_STRING, + },{ + .name = "chardev", + .type = QEMU_OPT_STRING, + },{ + .name = "default", + .type = QEMU_OPT_BOOL, + },{ + .name = "pretty", + .type = QEMU_OPT_BOOL, + }, + { /* end of list */ } + }, +}; diff --git a/net/hub.c b/net/hub.c index a24c9d17f7..df32074de0 100644 --- a/net/hub.c +++ b/net/hub.c @@ -338,3 +338,17 @@ void net_hub_check_clients(void) } } } + +bool net_hub_flush(NetClientState *nc) +{ + NetHubPort *port; + NetHubPort *source_port = DO_UPCAST(NetHubPort, nc, nc); + int ret = 0; + + QLIST_FOREACH(port, &source_port->hub->ports, next) { + if (port != source_port) { + ret += qemu_net_queue_flush(port->nc.send_queue); + } + } + return ret ? true : false; +} diff --git a/net/hub.h b/net/hub.h index 583ada89d8..a625effe00 100644 --- a/net/hub.h +++ b/net/hub.h @@ -21,5 +21,6 @@ NetClientState *net_hub_add_port(int hub_id, const char *name); NetClientState *net_hub_find_client_by_name(int hub_id, const char *name); void net_hub_info(Monitor *mon); void net_hub_check_clients(void); +bool net_hub_flush(NetClientState *nc); #endif /* NET_HUB_H */ diff --git a/net/net.c b/net/net.c index dbf3e1b003..f3d67f8322 100644 --- a/net/net.c +++ b/net/net.c @@ -182,17 +182,18 @@ static char *assign_name(NetClientState *nc1, const char *model) return g_strdup(buf); } -NetClientState *qemu_new_net_client(NetClientInfo *info, - NetClientState *peer, - const char *model, - const char *name) +static void qemu_net_client_destructor(NetClientState *nc) { - NetClientState *nc; - - assert(info->size >= sizeof(NetClientState)); - - nc = g_malloc0(info->size); + g_free(nc); +} +static void qemu_net_client_setup(NetClientState *nc, + NetClientInfo *info, + NetClientState *peer, + const char *model, + const char *name, + NetClientDestructor *destructor) +{ nc->info = info; nc->model = g_strdup(model); if (name) { @@ -209,6 +210,21 @@ NetClientState *qemu_new_net_client(NetClientInfo *info, QTAILQ_INSERT_TAIL(&net_clients, nc, next); nc->send_queue = qemu_new_net_queue(nc); + nc->destructor = destructor; +} + +NetClientState *qemu_new_net_client(NetClientInfo *info, + NetClientState *peer, + const char *model, + const char *name) +{ + NetClientState *nc; + + assert(info->size >= sizeof(NetClientState)); + + nc = g_malloc0(info->size); + qemu_net_client_setup(nc, info, peer, model, name, + qemu_net_client_destructor); return nc; } @@ -219,21 +235,51 @@ NICState *qemu_new_nic(NetClientInfo *info, const char *name, void *opaque) { - NetClientState *nc; + NetClientState **peers = conf->peers.ncs; NICState *nic; + int i, queues = MAX(1, conf->queues); assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC); assert(info->size >= sizeof(NICState)); - nc = qemu_new_net_client(info, conf->peer, model, name); - - nic = DO_UPCAST(NICState, nc, nc); + nic = g_malloc0(info->size + sizeof(NetClientState) * queues); + nic->ncs = (void *)nic + info->size; nic->conf = conf; nic->opaque = opaque; + for (i = 0; i < queues; i++) { + qemu_net_client_setup(&nic->ncs[i], info, peers[i], model, name, + NULL); + nic->ncs[i].queue_index = i; + } + return nic; } +NetClientState *qemu_get_subqueue(NICState *nic, int queue_index) +{ + return nic->ncs + queue_index; +} + +NetClientState *qemu_get_queue(NICState *nic) +{ + return qemu_get_subqueue(nic, 0); +} + +NICState *qemu_get_nic(NetClientState *nc) +{ + NetClientState *nc0 = nc - nc->queue_index; + + return (NICState *)((void *)nc0 - nc->info->size); +} + +void *qemu_get_nic_opaque(NetClientState *nc) +{ + NICState *nic = qemu_get_nic(nc); + + return nic->opaque; +} + static void qemu_cleanup_net_client(NetClientState *nc) { QTAILQ_REMOVE(&net_clients, nc, next); @@ -253,37 +299,74 @@ static void qemu_free_net_client(NetClientState *nc) } g_free(nc->name); g_free(nc->model); - g_free(nc); + if (nc->destructor) { + nc->destructor(nc); + } } void qemu_del_net_client(NetClientState *nc) { + NetClientState *ncs[MAX_QUEUE_NUM]; + int queues, i; + + /* If the NetClientState belongs to a multiqueue backend, we will change all + * other NetClientStates also. + */ + queues = qemu_find_net_clients_except(nc->name, ncs, + NET_CLIENT_OPTIONS_KIND_NIC, + MAX_QUEUE_NUM); + assert(queues != 0); + /* If there is a peer NIC, delete and cleanup client, but do not free. */ if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { - NICState *nic = DO_UPCAST(NICState, nc, nc->peer); + NICState *nic = qemu_get_nic(nc->peer); if (nic->peer_deleted) { return; } nic->peer_deleted = true; - /* Let NIC know peer is gone. */ - nc->peer->link_down = true; + + for (i = 0; i < queues; i++) { + ncs[i]->peer->link_down = true; + } + if (nc->peer->info->link_status_changed) { nc->peer->info->link_status_changed(nc->peer); } - qemu_cleanup_net_client(nc); + + for (i = 0; i < queues; i++) { + qemu_cleanup_net_client(ncs[i]); + } + return; } + assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC); + + for (i = 0; i < queues; i++) { + qemu_cleanup_net_client(ncs[i]); + qemu_free_net_client(ncs[i]); + } +} + +void qemu_del_nic(NICState *nic) +{ + int i, queues = MAX(nic->conf->queues, 1); + /* If this is a peer NIC and peer has already been deleted, free it now. */ - if (nc->peer && nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { - NICState *nic = DO_UPCAST(NICState, nc, nc); - if (nic->peer_deleted) { - qemu_free_net_client(nc->peer); + if (nic->peer_deleted) { + for (i = 0; i < queues; i++) { + qemu_free_net_client(qemu_get_subqueue(nic, i)->peer); } } - qemu_cleanup_net_client(nc); - qemu_free_net_client(nc); + for (i = queues - 1; i >= 0; i--) { + NetClientState *nc = qemu_get_subqueue(nic, i); + + qemu_cleanup_net_client(nc); + qemu_free_net_client(nc); + } + + g_free(nic); } void qemu_foreach_nic(qemu_nic_foreach func, void *opaque) @@ -292,7 +375,9 @@ void qemu_foreach_nic(qemu_nic_foreach func, void *opaque) QTAILQ_FOREACH(nc, &net_clients, next) { if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { - func(DO_UPCAST(NICState, nc, nc), opaque); + if (nc->queue_index == 0) { + func(qemu_get_nic(nc), opaque); + } } } } @@ -355,6 +440,12 @@ void qemu_flush_queued_packets(NetClientState *nc) { nc->receive_disabled = 0; + if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) { + if (net_hub_flush(nc->peer)) { + qemu_notify_event(); + } + return; + } if (qemu_net_queue_flush(nc->send_queue)) { /* We emptied the queue successfully, signal to the IO thread to repoll * the file descriptor (for tap, for example). @@ -482,6 +573,27 @@ NetClientState *qemu_find_netdev(const char *id) return NULL; } +int qemu_find_net_clients_except(const char *id, NetClientState **ncs, + NetClientOptionsKind type, int max) +{ + NetClientState *nc; + int ret = 0; + + QTAILQ_FOREACH(nc, &net_clients, next) { + if (nc->info->type == type) { + continue; + } + if (!strcmp(nc->name, id)) { + if (ret < max) { + ncs[ret] = nc; + } + ret++; + } + } + + return ret; +} + static int nic_get_free_idx(void) { int index; @@ -566,9 +678,7 @@ static int net_init_nic(const NetClientOptions *opts, const char *name, assert(peer); nd->netdev = peer; } - if (name) { - nd->name = g_strdup(name); - } + nd->name = g_strdup(name); if (nic->has_model) { nd->model = g_strdup(nic->model); } @@ -848,11 +958,13 @@ void qmp_netdev_del(const char *id, Error **errp) void print_net_client(Monitor *mon, NetClientState *nc) { - monitor_printf(mon, "%s: type=%s,%s\n", nc->name, - NetClientOptionsKind_lookup[nc->info->type], nc->info_str); + monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name, + nc->queue_index, + NetClientOptionsKind_lookup[nc->info->type], + nc->info_str); } -void do_info_network(Monitor *mon) +void do_info_network(Monitor *mon, const QDict *qdict) { NetClientState *nc, *peer; NetClientOptionsKind type; @@ -880,20 +992,23 @@ void do_info_network(Monitor *mon) void qmp_set_link(const char *name, bool up, Error **errp) { - NetClientState *nc = NULL; + NetClientState *ncs[MAX_QUEUE_NUM]; + NetClientState *nc; + int queues, i; - QTAILQ_FOREACH(nc, &net_clients, next) { - if (!strcmp(nc->name, name)) { - goto done; - } - } -done: - if (!nc) { + queues = qemu_find_net_clients_except(name, ncs, + NET_CLIENT_OPTIONS_KIND_MAX, + MAX_QUEUE_NUM); + + if (queues == 0) { error_set(errp, QERR_DEVICE_NOT_FOUND, name); return; } + nc = ncs[0]; - nc->link_down = !up; + for (i = 0; i < queues; i++) { + ncs[i]->link_down = !up; + } if (nc->info->link_status_changed) { nc->info->link_status_changed(nc); @@ -913,10 +1028,18 @@ done: void net_cleanup(void) { - NetClientState *nc, *next_vc; + NetClientState *nc; - QTAILQ_FOREACH_SAFE(nc, &net_clients, next, next_vc) { - qemu_del_net_client(nc); + /* We may del multiple entries during qemu_del_net_client(), + * so QTAILQ_FOREACH_SAFE() is also not safe here. + */ + while (!QTAILQ_EMPTY(&net_clients)) { + nc = QTAILQ_FIRST(&net_clients); + if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { + qemu_del_nic(qemu_get_nic(nc)); + } else { + qemu_del_net_client(nc); + } } } @@ -1054,3 +1177,29 @@ unsigned compute_mcast_idx(const uint8_t *ep) } return crc >> 26; } + +QemuOptsList qemu_netdev_opts = { + .name = "netdev", + .implied_opt_name = "type", + .head = QTAILQ_HEAD_INITIALIZER(qemu_netdev_opts.head), + .desc = { + /* + * no elements => accept any params + * validation will happen later + */ + { /* end of list */ } + }, +}; + +QemuOptsList qemu_net_opts = { + .name = "net", + .implied_opt_name = "type", + .head = QTAILQ_HEAD_INITIALIZER(qemu_net_opts.head), + .desc = { + /* + * no elements => accept any params + * validation will happen later + */ + { /* end of list */ } + }, +}; diff --git a/net/queue.c b/net/queue.c index 6eaf5b63c0..859d02a136 100644 --- a/net/queue.c +++ b/net/queue.c @@ -50,6 +50,8 @@ struct NetPacket { struct NetQueue { void *opaque; + uint32_t nq_maxlen; + uint32_t nq_count; QTAILQ_HEAD(packets, NetPacket) packets; @@ -63,6 +65,8 @@ NetQueue *qemu_new_net_queue(void *opaque) queue = g_malloc0(sizeof(NetQueue)); queue->opaque = opaque; + queue->nq_maxlen = 10000; + queue->nq_count = 0; QTAILQ_INIT(&queue->packets); @@ -92,6 +96,9 @@ static void qemu_net_queue_append(NetQueue *queue, { NetPacket *packet; + if (queue->nq_count >= queue->nq_maxlen && !sent_cb) { + return; /* drop if queue full and no callback */ + } packet = g_malloc(sizeof(NetPacket) + size); packet->sender = sender; packet->flags = flags; @@ -99,6 +106,7 @@ static void qemu_net_queue_append(NetQueue *queue, packet->sent_cb = sent_cb; memcpy(packet->data, buf, size); + queue->nq_count++; QTAILQ_INSERT_TAIL(&queue->packets, packet, entry); } @@ -113,6 +121,9 @@ static void qemu_net_queue_append_iov(NetQueue *queue, size_t max_len = 0; int i; + if (queue->nq_count >= queue->nq_maxlen && !sent_cb) { + return; /* drop if queue full and no callback */ + } for (i = 0; i < iovcnt; i++) { max_len += iov[i].iov_len; } @@ -130,6 +141,7 @@ static void qemu_net_queue_append_iov(NetQueue *queue, packet->size += len; } + queue->nq_count++; QTAILQ_INSERT_TAIL(&queue->packets, packet, entry); } @@ -220,6 +232,7 @@ void qemu_net_queue_purge(NetQueue *queue, NetClientState *from) QTAILQ_FOREACH_SAFE(packet, &queue->packets, entry, next) { if (packet->sender == from) { QTAILQ_REMOVE(&queue->packets, packet, entry); + queue->nq_count--; g_free(packet); } } @@ -233,6 +246,7 @@ bool qemu_net_queue_flush(NetQueue *queue) packet = QTAILQ_FIRST(&queue->packets); QTAILQ_REMOVE(&queue->packets, packet, entry); + queue->nq_count--; ret = qemu_net_queue_deliver(queue, packet->sender, @@ -240,6 +254,7 @@ bool qemu_net_queue_flush(NetQueue *queue) packet->data, packet->size); if (ret == 0) { + queue->nq_count++; QTAILQ_INSERT_HEAD(&queue->packets, packet, entry); return false; } diff --git a/net/slirp.c b/net/slirp.c index c14259f004..4df550faf6 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -670,7 +670,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, return -1; } -void do_info_usernet(Monitor *mon) +void do_info_usernet(Monitor *mon, const QDict *qdict) { SlirpState *s; diff --git a/net/tap-aix.c b/net/tap-aix.c index aff6c527e9..804d16448d 100644 --- a/net/tap-aix.c +++ b/net/tap-aix.c @@ -25,7 +25,8 @@ #include "tap_int.h" #include -int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, + int vnet_hdr_required, int mq_required) { fprintf(stderr, "no tap on AIX\n"); return -1; @@ -59,3 +60,19 @@ void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo) { } + +int tap_fd_enable(int fd) +{ + return -1; +} + +int tap_fd_disable(int fd) +{ + return -1; +} + +int tap_fd_get_ifname(int fd, char *ifname) +{ + return -1; +} + diff --git a/net/tap-bsd.c b/net/tap-bsd.c index 01c705b4c0..bcdb2682b5 100644 --- a/net/tap-bsd.c +++ b/net/tap-bsd.c @@ -33,7 +33,8 @@ #include #endif -int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, + int vnet_hdr_required, int mq_required) { int fd; #ifdef TAPGIFNAME @@ -145,3 +146,18 @@ void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo) { } + +int tap_fd_enable(int fd) +{ + return -1; +} + +int tap_fd_disable(int fd) +{ + return -1; +} + +int tap_fd_get_ifname(int fd, char *ifname) +{ + return -1; +} diff --git a/net/tap-haiku.c b/net/tap-haiku.c index 08cc034cee..e5ce436d24 100644 --- a/net/tap-haiku.c +++ b/net/tap-haiku.c @@ -25,7 +25,8 @@ #include "tap_int.h" #include -int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, + int vnet_hdr_required, int mq_required) { fprintf(stderr, "no tap on Haiku\n"); return -1; @@ -59,3 +60,18 @@ void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo) { } + +int tap_fd_enable(int fd) +{ + return -1; +} + +int tap_fd_disable(int fd) +{ + return -1; +} + +int tap_fd_get_ifname(int fd, char *ifname) +{ + return -1; +} diff --git a/net/tap-linux.c b/net/tap-linux.c index 059f5f34ab..36c09e24d8 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -36,11 +36,13 @@ #define PATH_NET_TUN "/dev/net/tun" -int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, + int vnet_hdr_required, int mq_required) { struct ifreq ifr; int fd, ret; int len = sizeof(struct virtio_net_hdr); + unsigned int features; TFR(fd = open(PATH_NET_TUN, O_RDWR)); if (fd < 0) { @@ -50,9 +52,12 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - if (*vnet_hdr) { - unsigned int features; + if (ioctl(fd, TUNGETFEATURES, &features) == 0 && + features & IFF_ONE_QUEUE) { + ifr.ifr_flags |= IFF_ONE_QUEUE; + } + if (*vnet_hdr) { if (ioctl(fd, TUNGETFEATURES, &features) == 0 && features & IFF_VNET_HDR) { *vnet_hdr = 1; @@ -76,6 +81,18 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required ioctl(fd, TUNSETVNETHDRSZ, &len); } + if (mq_required) { + if ((ioctl(fd, TUNGETFEATURES, &features) != 0) || + !(features & IFF_MULTI_QUEUE)) { + error_report("multiqueue required, but no kernel " + "support for IFF_MULTI_QUEUE available"); + close(fd); + return -1; + } else { + ifr.ifr_flags |= IFF_MULTI_QUEUE; + } + } + if (ifname[0] != '\0') pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname); else @@ -164,7 +181,7 @@ int tap_probe_vnet_hdr_len(int fd, int len) if (ioctl(fd, TUNSETVNETHDRSZ, &orig) == -1) { fprintf(stderr, "TUNGETVNETHDRSZ ioctl() failed: %s. Exiting.\n", strerror(errno)); - assert(0); + abort(); return -errno; } return 1; @@ -175,7 +192,7 @@ void tap_fd_set_vnet_hdr_len(int fd, int len) if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) { fprintf(stderr, "TUNSETVNETHDRSZ ioctl() failed: %s. Exiting.\n", strerror(errno)); - assert(0); + abort(); } } @@ -209,3 +226,53 @@ void tap_fd_set_offload(int fd, int csum, int tso4, } } } + +/* Enable a specific queue of tap. */ +int tap_fd_enable(int fd) +{ + struct ifreq ifr; + int ret; + + memset(&ifr, 0, sizeof(ifr)); + + ifr.ifr_flags = IFF_ATTACH_QUEUE; + ret = ioctl(fd, TUNSETQUEUE, (void *) &ifr); + + if (ret != 0) { + error_report("could not enable queue"); + } + + return ret; +} + +/* Disable a specific queue of tap/ */ +int tap_fd_disable(int fd) +{ + struct ifreq ifr; + int ret; + + memset(&ifr, 0, sizeof(ifr)); + + ifr.ifr_flags = IFF_DETACH_QUEUE; + ret = ioctl(fd, TUNSETQUEUE, (void *) &ifr); + + if (ret != 0) { + error_report("could not disable queue"); + } + + return ret; +} + +int tap_fd_get_ifname(int fd, char *ifname) +{ + struct ifreq ifr; + + if (ioctl(fd, TUNGETIFF, &ifr) != 0) { + error_report("TUNGETIFF ioctl() failed: %s", + strerror(errno)); + return -1; + } + + pstrcpy(ifname, sizeof(ifr.ifr_name), ifr.ifr_name); + return 0; +} diff --git a/net/tap-linux.h b/net/tap-linux.h index cb2a6d480a..1cf35d41bd 100644 --- a/net/tap-linux.h +++ b/net/tap-linux.h @@ -29,13 +29,18 @@ #define TUNSETSNDBUF _IOW('T', 212, int) #define TUNGETVNETHDRSZ _IOR('T', 215, int) #define TUNSETVNETHDRSZ _IOW('T', 216, int) +#define TUNSETQUEUE _IOW('T', 217, int) #endif /* TUNSETIFF ifr flags */ -#define IFF_TAP 0x0002 -#define IFF_NO_PI 0x1000 -#define IFF_VNET_HDR 0x4000 +#define IFF_TAP 0x0002 +#define IFF_NO_PI 0x1000 +#define IFF_ONE_QUEUE 0x2000 +#define IFF_VNET_HDR 0x4000 +#define IFF_MULTI_QUEUE 0x0100 +#define IFF_ATTACH_QUEUE 0x0200 +#define IFF_DETACH_QUEUE 0x0400 /* Features for GSO (TUNSETOFFLOAD). */ #define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */ diff --git a/net/tap-solaris.c b/net/tap-solaris.c index 486a7ea838..9c7278f1bf 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c @@ -173,7 +173,8 @@ static int tap_alloc(char *dev, size_t dev_size) return tap_fd; } -int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, + int vnet_hdr_required, int mq_required) { char dev[10]=""; int fd; @@ -225,3 +226,18 @@ void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo) { } + +int tap_fd_enable(int fd) +{ + return -1; +} + +int tap_fd_disable(int fd) +{ + return -1; +} + +int tap_fd_get_ifname(int fd, char *ifname) +{ + return -1; +} diff --git a/net/tap-win32.c b/net/tap-win32.c index 265369c3c5..91e9e844a0 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -722,9 +722,9 @@ int net_init_tap(const NetClientOptions *opts, const char *name, return 0; } -int tap_has_ufo(NetClientState *nc) +bool tap_has_ufo(NetClientState *nc) { - return 0; + return false; } int tap_has_vnet_hdr(NetClientState *nc) @@ -741,7 +741,7 @@ void tap_fd_set_vnet_hdr_len(int fd, int len) { } -void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr) +void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) { } @@ -762,5 +762,15 @@ int tap_has_vnet_hdr_len(NetClientState *nc, int len) void tap_set_vnet_hdr_len(NetClientState *nc, int len) { - assert(0); + abort(); +} + +int tap_enable(NetClientState *nc) +{ + abort(); +} + +int tap_disable(NetClientState *nc) +{ + abort(); } diff --git a/net/tap.c b/net/tap.c index eb40c42d7d..daab350efc 100644 --- a/net/tap.c +++ b/net/tap.c @@ -55,10 +55,11 @@ typedef struct TAPState { char down_script[1024]; char down_script_arg[128]; uint8_t buf[TAP_BUFSIZE]; - unsigned int read_poll : 1; - unsigned int write_poll : 1; - unsigned int using_vnet_hdr : 1; - unsigned int has_ufo: 1; + bool read_poll; + bool write_poll; + bool using_vnet_hdr; + bool has_ufo; + bool enabled; VHostNetState *vhost_net; unsigned host_vnet_hdr_len; } TAPState; @@ -72,21 +73,21 @@ static void tap_writable(void *opaque); static void tap_update_fd_handler(TAPState *s) { qemu_set_fd_handler2(s->fd, - s->read_poll ? tap_can_send : NULL, - s->read_poll ? tap_send : NULL, - s->write_poll ? tap_writable : NULL, + s->read_poll && s->enabled ? tap_can_send : NULL, + s->read_poll && s->enabled ? tap_send : NULL, + s->write_poll && s->enabled ? tap_writable : NULL, s); } -static void tap_read_poll(TAPState *s, int enable) +static void tap_read_poll(TAPState *s, bool enable) { - s->read_poll = !!enable; + s->read_poll = enable; tap_update_fd_handler(s); } -static void tap_write_poll(TAPState *s, int enable) +static void tap_write_poll(TAPState *s, bool enable) { - s->write_poll = !!enable; + s->write_poll = enable; tap_update_fd_handler(s); } @@ -94,7 +95,7 @@ static void tap_writable(void *opaque) { TAPState *s = opaque; - tap_write_poll(s, 0); + tap_write_poll(s, false); qemu_flush_queued_packets(&s->nc); } @@ -108,7 +109,7 @@ static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt } while (len == -1 && errno == EINTR); if (len == -1 && errno == EAGAIN) { - tap_write_poll(s, 1); + tap_write_poll(s, true); return 0; } @@ -186,7 +187,7 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen) static void tap_send_completed(NetClientState *nc, ssize_t len) { TAPState *s = DO_UPCAST(TAPState, nc, nc); - tap_read_poll(s, 1); + tap_read_poll(s, true); } static void tap_send(void *opaque) @@ -209,12 +210,12 @@ static void tap_send(void *opaque) size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed); if (size == 0) { - tap_read_poll(s, 0); + tap_read_poll(s, false); } } while (size > 0 && qemu_can_send_packet(&s->nc)); } -int tap_has_ufo(NetClientState *nc) +bool tap_has_ufo(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); @@ -253,12 +254,10 @@ void tap_set_vnet_hdr_len(NetClientState *nc, int len) s->host_vnet_hdr_len = len; } -void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr) +void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) { TAPState *s = DO_UPCAST(TAPState, nc, nc); - using_vnet_hdr = using_vnet_hdr != 0; - assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP); assert(!!s->host_vnet_hdr_len == using_vnet_hdr); @@ -290,8 +289,8 @@ static void tap_cleanup(NetClientState *nc) if (s->down_script[0]) launch_script(s->down_script, s->down_script_arg, s->fd); - tap_read_poll(s, 0); - tap_write_poll(s, 0); + tap_read_poll(s, false); + tap_write_poll(s, false); close(s->fd); s->fd = -1; } @@ -337,8 +336,9 @@ static TAPState *net_tap_fd_init(NetClientState *peer, s->fd = fd; s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; - s->using_vnet_hdr = 0; + s->using_vnet_hdr = false; s->has_ufo = tap_probe_has_ufo(s->fd); + s->enabled = true; tap_set_offload(&s->nc, 0, 0, 0, 0, 0); /* * Make sure host header length is set correctly in tap: @@ -347,7 +347,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer, if (tap_probe_vnet_hdr_len(s->fd, s->host_vnet_hdr_len)) { tap_fd_set_vnet_hdr_len(s->fd, s->host_vnet_hdr_len); } - tap_read_poll(s, 1); + tap_read_poll(s, true); s->vhost_net = NULL; return s; } @@ -558,17 +558,10 @@ int net_init_bridge(const NetClientOptions *opts, const char *name, static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr, const char *setup_script, char *ifname, - size_t ifname_sz) + size_t ifname_sz, int mq_required) { int fd, vnet_hdr_required; - if (tap->has_ifname) { - pstrcpy(ifname, ifname_sz, tap->ifname); - } else { - assert(ifname_sz > 0); - ifname[0] = '\0'; - } - if (tap->has_vnet_hdr) { *vnet_hdr = tap->vnet_hdr; vnet_hdr_required = *vnet_hdr; @@ -577,7 +570,8 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr, vnet_hdr_required = 0; } - TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required)); + TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required, + mq_required)); if (fd < 0) { return -1; } @@ -593,70 +587,16 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr, return fd; } -int net_init_tap(const NetClientOptions *opts, const char *name, - NetClientState *peer) +#define MAX_TAP_QUEUES 1024 + +static int net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, + const char *model, const char *name, + const char *ifname, const char *script, + const char *downscript, const char *vhostfdname, + int vnet_hdr, int fd) { - const NetdevTapOptions *tap; - - int fd, vnet_hdr = 0; - const char *model; TAPState *s; - /* for the no-fd, no-helper case */ - const char *script = NULL; /* suppress wrong "uninit'd use" gcc warning */ - char ifname[128]; - - assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP); - tap = opts->tap; - - if (tap->has_fd) { - if (tap->has_ifname || tap->has_script || tap->has_downscript || - tap->has_vnet_hdr || tap->has_helper) { - error_report("ifname=, script=, downscript=, vnet_hdr=, " - "and helper= are invalid with fd="); - return -1; - } - - fd = monitor_handle_fd_param(cur_mon, tap->fd); - if (fd == -1) { - return -1; - } - - fcntl(fd, F_SETFL, O_NONBLOCK); - - vnet_hdr = tap_probe_vnet_hdr(fd); - - model = "tap"; - - } else if (tap->has_helper) { - if (tap->has_ifname || tap->has_script || tap->has_downscript || - tap->has_vnet_hdr) { - error_report("ifname=, script=, downscript=, and vnet_hdr= " - "are invalid with helper="); - return -1; - } - - fd = net_bridge_run_helper(tap->helper, DEFAULT_BRIDGE_INTERFACE); - if (fd == -1) { - return -1; - } - - fcntl(fd, F_SETFL, O_NONBLOCK); - - vnet_hdr = tap_probe_vnet_hdr(fd); - - model = "bridge"; - - } else { - script = tap->has_script ? tap->script : DEFAULT_NETWORK_SCRIPT; - fd = net_tap_init(tap, &vnet_hdr, script, ifname, sizeof ifname); - if (fd == -1) { - return -1; - } - - model = "tap"; - } - s = net_tap_fd_init(peer, model, name, fd, vnet_hdr); if (!s) { close(fd); @@ -667,33 +607,29 @@ int net_init_tap(const NetClientOptions *opts, const char *name, return -1; } - if (tap->has_fd) { + if (tap->has_fd || tap->has_fds) { snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd); } else if (tap->has_helper) { snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s", tap->helper); } else { - const char *downscript; - - downscript = tap->has_downscript ? tap->downscript : - DEFAULT_NETWORK_DOWN_SCRIPT; - snprintf(s->nc.info_str, sizeof(s->nc.info_str), "ifname=%s,script=%s,downscript=%s", ifname, script, downscript); if (strcmp(downscript, "no") != 0) { snprintf(s->down_script, sizeof(s->down_script), "%s", downscript); - snprintf(s->down_script_arg, sizeof(s->down_script_arg), "%s", ifname); + snprintf(s->down_script_arg, sizeof(s->down_script_arg), + "%s", ifname); } } if (tap->has_vhost ? tap->vhost : - tap->has_vhostfd || (tap->has_vhostforce && tap->vhostforce)) { + vhostfdname || (tap->has_vhostforce && tap->vhostforce)) { int vhostfd; if (tap->has_vhostfd) { - vhostfd = monitor_handle_fd_param(cur_mon, tap->vhostfd); + vhostfd = monitor_handle_fd_param(cur_mon, vhostfdname); if (vhostfd == -1) { return -1; } @@ -707,7 +643,7 @@ int net_init_tap(const NetClientOptions *opts, const char *name, error_report("vhost-net requested but could not be initialized"); return -1; } - } else if (tap->has_vhostfd) { + } else if (tap->has_vhostfd || tap->has_vhostfds) { error_report("vhostfd= is not valid without vhost"); return -1; } @@ -715,9 +651,219 @@ int net_init_tap(const NetClientOptions *opts, const char *name, return 0; } +static int get_fds(char *str, char *fds[], int max) +{ + char *ptr = str, *this; + size_t len = strlen(str); + int i = 0; + + while (i < max && ptr < str + len) { + this = strchr(ptr, ':'); + + if (this == NULL) { + fds[i] = g_strdup(ptr); + } else { + fds[i] = g_strndup(ptr, this - ptr); + } + + i++; + if (this == NULL) { + break; + } else { + ptr = this + 1; + } + } + + return i; +} + +int net_init_tap(const NetClientOptions *opts, const char *name, + NetClientState *peer) +{ + const NetdevTapOptions *tap; + int fd, vnet_hdr = 0, i = 0, queues; + /* for the no-fd, no-helper case */ + const char *script = NULL; /* suppress wrong "uninit'd use" gcc warning */ + const char *downscript = NULL; + const char *vhostfdname; + char ifname[128]; + + assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP); + tap = opts->tap; + queues = tap->has_queues ? tap->queues : 1; + vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL; + + /* QEMU vlans does not support multiqueue tap, in this case peer is set. + * For -netdev, peer is always NULL. */ + if (peer && (tap->has_queues || tap->has_fds || tap->has_vhostfds)) { + error_report("Multiqueue tap cannnot be used with QEMU vlans"); + return -1; + } + + if (tap->has_fd) { + if (tap->has_ifname || tap->has_script || tap->has_downscript || + tap->has_vnet_hdr || tap->has_helper || tap->has_queues || + tap->has_fds) { + error_report("ifname=, script=, downscript=, vnet_hdr=, " + "helper=, queues=, and fds= are invalid with fd="); + return -1; + } + + fd = monitor_handle_fd_param(cur_mon, tap->fd); + if (fd == -1) { + return -1; + } + + fcntl(fd, F_SETFL, O_NONBLOCK); + + vnet_hdr = tap_probe_vnet_hdr(fd); + + if (net_init_tap_one(tap, peer, "tap", name, NULL, + script, downscript, + vhostfdname, vnet_hdr, fd)) { + return -1; + } + } else if (tap->has_fds) { + char *fds[MAX_TAP_QUEUES]; + char *vhost_fds[MAX_TAP_QUEUES]; + int nfds, nvhosts; + + if (tap->has_ifname || tap->has_script || tap->has_downscript || + tap->has_vnet_hdr || tap->has_helper || tap->has_queues || + tap->has_fd) { + error_report("ifname=, script=, downscript=, vnet_hdr=, " + "helper=, queues=, and fd= are invalid with fds="); + return -1; + } + + nfds = get_fds(tap->fds, fds, MAX_TAP_QUEUES); + if (tap->has_vhostfds) { + nvhosts = get_fds(tap->vhostfds, vhost_fds, MAX_TAP_QUEUES); + if (nfds != nvhosts) { + error_report("The number of fds passed does not match the " + "number of vhostfds passed"); + return -1; + } + } + + for (i = 0; i < nfds; i++) { + fd = monitor_handle_fd_param(cur_mon, fds[i]); + if (fd == -1) { + return -1; + } + + fcntl(fd, F_SETFL, O_NONBLOCK); + + if (i == 0) { + vnet_hdr = tap_probe_vnet_hdr(fd); + } else if (vnet_hdr != tap_probe_vnet_hdr(fd)) { + error_report("vnet_hdr not consistent across given tap fds"); + return -1; + } + + if (net_init_tap_one(tap, peer, "tap", name, ifname, + script, downscript, + tap->has_vhostfds ? vhost_fds[i] : NULL, + vnet_hdr, fd)) { + return -1; + } + } + } else if (tap->has_helper) { + if (tap->has_ifname || tap->has_script || tap->has_downscript || + tap->has_vnet_hdr || tap->has_queues || tap->has_fds) { + error_report("ifname=, script=, downscript=, and vnet_hdr= " + "queues=, and fds= are invalid with helper="); + return -1; + } + + fd = net_bridge_run_helper(tap->helper, DEFAULT_BRIDGE_INTERFACE); + if (fd == -1) { + return -1; + } + + fcntl(fd, F_SETFL, O_NONBLOCK); + vnet_hdr = tap_probe_vnet_hdr(fd); + + if (net_init_tap_one(tap, peer, "bridge", name, ifname, + script, downscript, vhostfdname, + vnet_hdr, fd)) { + return -1; + } + } else { + script = tap->has_script ? tap->script : DEFAULT_NETWORK_SCRIPT; + downscript = tap->has_downscript ? tap->downscript : + DEFAULT_NETWORK_DOWN_SCRIPT; + + if (tap->has_ifname) { + pstrcpy(ifname, sizeof ifname, tap->ifname); + } else { + ifname[0] = '\0'; + } + + for (i = 0; i < queues; i++) { + fd = net_tap_init(tap, &vnet_hdr, i >= 1 ? "no" : script, + ifname, sizeof ifname, queues > 1); + if (fd == -1) { + return -1; + } + + if (queues > 1 && i == 0 && !tap->has_ifname) { + if (tap_fd_get_ifname(fd, ifname)) { + error_report("Fail to get ifname"); + return -1; + } + } + + if (net_init_tap_one(tap, peer, "tap", name, ifname, + i >= 1 ? "no" : script, + i >= 1 ? "no" : downscript, + vhostfdname, vnet_hdr, fd)) { + return -1; + } + } + } + + return 0; +} + VHostNetState *tap_get_vhost_net(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP); return s->vhost_net; } + +int tap_enable(NetClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + int ret; + + if (s->enabled) { + return 0; + } else { + ret = tap_fd_enable(s->fd); + if (ret == 0) { + s->enabled = true; + tap_update_fd_handler(s); + } + return ret; + } +} + +int tap_disable(NetClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + int ret; + + if (s->enabled == 0) { + return 0; + } else { + ret = tap_fd_disable(s->fd); + if (ret == 0) { + qemu_purge_queued_packets(nc); + s->enabled = false; + tap_update_fd_handler(s); + } + return ret; + } +} diff --git a/net/tap_int.h b/net/tap_int.h index 1dffe12a45..86bb224bc8 100644 --- a/net/tap_int.h +++ b/net/tap_int.h @@ -32,7 +32,8 @@ #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown" -int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required); +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, + int vnet_hdr_required, int mq_required); ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen); @@ -42,5 +43,8 @@ int tap_probe_vnet_hdr_len(int fd, int len); int tap_probe_has_ufo(int fd); void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo); void tap_fd_set_vnet_hdr_len(int fd, int len); +int tap_fd_enable(int fd); +int tap_fd_disable(int fd); +int tap_fd_get_ifname(int fd, char *ifname); #endif /* QEMU_TAP_H */ diff --git a/page_cache.c b/page_cache.c index ba5640bd73..938a79c9ea 100644 --- a/page_cache.c +++ b/page_cache.c @@ -152,11 +152,14 @@ void cache_insert(PageCache *cache, uint64_t addr, uint8_t *pdata) /* actual update of entry */ it = cache_get_by_addr(cache, addr); + /* free old cached data if any */ + g_free(it->it_data); + if (!it->it_data) { cache->num_items++; } - it->it_data = pdata; + it->it_data = g_memdup(pdata, cache->page_size); it->it_age = ++cache->max_item_age; it->it_addr = addr; } @@ -192,22 +195,22 @@ int64_t cache_resize(PageCache *cache, int64_t new_num_pages) if (old_it->it_addr != -1) { /* check for collision, if there is, keep MRU page */ new_it = cache_get_by_addr(new_cache, old_it->it_addr); - if (new_it->it_data) { + if (new_it->it_data && new_it->it_age >= old_it->it_age) { /* keep the MRU page */ - if (new_it->it_age >= old_it->it_age) { - g_free(old_it->it_data); - } else { - g_free(new_it->it_data); - new_it->it_data = old_it->it_data; - new_it->it_age = old_it->it_age; - new_it->it_addr = old_it->it_addr; - } + g_free(old_it->it_data); } else { - cache_insert(new_cache, old_it->it_addr, old_it->it_data); + if (!new_it->it_data) { + new_cache->num_items++; + } + g_free(new_it->it_data); + new_it->it_data = old_it->it_data; + new_it->it_age = old_it->it_age; + new_it->it_addr = old_it->it_addr; } } } + g_free(cache->page_cache); cache->page_cache = new_cache->page_cache; cache->max_num_items = new_cache->max_num_items; cache->num_items = new_cache->num_items; diff --git a/pc-bios/acpi-dsdt.aml b/pc-bios/acpi-dsdt.aml index 18b4dc1aa5..75dfd1e310 100644 Binary files a/pc-bios/acpi-dsdt.aml and b/pc-bios/acpi-dsdt.aml differ diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin index 3eefff4cf8..ec9eeb12c6 100644 Binary files a/pc-bios/bios.bin and b/pc-bios/bios.bin differ diff --git a/pc-bios/q35-acpi-dsdt.aml b/pc-bios/q35-acpi-dsdt.aml index 8a50559514..cf7b085762 100644 Binary files a/pc-bios/q35-acpi-dsdt.aml and b/pc-bios/q35-acpi-dsdt.aml differ diff --git a/pci-ids.txt b/pci-ids.txt deleted file mode 100644 index 73125a8bd7..0000000000 --- a/pci-ids.txt +++ /dev/null @@ -1,31 +0,0 @@ - -PCI IDs for qemu -================ - -Red Hat, Inc. donates a part of its device ID range to qemu, to be used for -virtual devices. The vendor ID is 1af4 (formerly Qumranet ID). - -The 1000 -> 10ff device ID range is used for VirtIO devices. - -The 1100 device ID is used as PCI Subsystem ID for existing hardware -devices emulated by qemu. - -All other device IDs are reserved. - - -VirtIO Device IDs ------------------ - -1af4:1000 network device -1af4:1001 block device -1af4:1002 balloon device -1af4:1003 console device - -1af4:1004 Reserved. - to Contact Gerd Hoffmann to get a -1af4:10ef device ID assigned for your new virtio device. - -1af4:10f0 Available for experimental usage without registration. Must get - to official ID when the code leaves the test lab (i.e. when seeking -1af4:10ff upstream merge or shipping a distro/product) to avoid conflicts. - diff --git a/po/Makefile b/po/Makefile new file mode 100644 index 0000000000..2b4420f178 --- /dev/null +++ b/po/Makefile @@ -0,0 +1,46 @@ +# This makefile is very special as it's meant to build as part of the build +# process and also within the source tree to update the translation files. + +VERSION=$(shell cat ../VERSION) +TRANSLATIONS=de_DE it +SRCS=$(addsuffix .po, $(TRANSLATIONS)) +OBJS=$(addsuffix .mo, $(TRANSLATIONS)) + +SRC_PATH=.. + +-include ../config-host.mak + +vpath %.po $(SRC_PATH)/po + +all: + @echo Use 'make update' to update translation files + @echo or us 'make build' or 'make install' to build and install + @echo the translation files + +update: $(SRCS) + +build: $(OBJS) + +clean: + $(RM) $(OBJS) + +install: $(OBJS) + for obj in $(OBJS); do \ + base=`basename $$obj .mo`; \ + $(INSTALL) -d $(DESTDIR)$(prefix)/share/locale/$$base/LC_MESSAGES; \ + $(INSTALL) -m644 $$obj $(DESTDIR)$(prefix)/share/locale/$$base/LC_MESSAGES/qemu.mo; \ + done + +%.mo: + @msgfmt -o $@ $(SRC_PATH)/po/`basename $@ .mo`.po + +messages.po: $(SRC_PATH)/ui/gtk.c + @xgettext -o $@ --foreign-user --package-name=QEMU --package-version=1.0.50 --msgid-bugs-address=qemu-devel@nongnu.org -k_ -C $< + +de_DE.po: messages.po $(SRC_PATH)/ui/gtk.c + @msgmerge $@ $< > $@.bak && mv $@.bak $@ + +it.po: messages.po $(SRC_PATH)/ui/gtk.c + @msgmerge $@ $< > $@.bak && mv $@.bak $@ + +.PHONY: $(SRCS) clean all diff --git a/po/de_DE.po b/po/de_DE.po new file mode 100644 index 0000000000..875578349d --- /dev/null +++ b/po/de_DE.po @@ -0,0 +1,41 @@ +# German translation for QEMU. +# This file is put in the public domain. +# Kevin Wolf , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: QEMU 1.4.50\n" +"Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" +"POT-Creation-Date: 2013-02-08 09:21-0600\n" +"PO-Revision-Date: 2012-02-28 16:00+0100\n" +"Last-Translator: Kevin Wolf \n" +"Language-Team: Deutsch \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n!=1);\n" + +#: ../ui/gtk.c:990 +msgid "_File" +msgstr "_Datei" + +#: ../ui/gtk.c:1000 +msgid "_View" +msgstr "_Ansicht" + +#: ../ui/gtk.c:1029 +msgid "Zoom To _Fit" +msgstr "Auf _Fenstergröße skalieren" + +#: ../ui/gtk.c:1035 +msgid "Grab On _Hover" +msgstr "Tastatur _automatisch einfangen" + +#: ../ui/gtk.c:1038 +msgid "_Grab Input" +msgstr "_Eingabegeräte einfangen" + +#: ../ui/gtk.c:1064 +msgid "Show _Tabs" +msgstr "_Tableiste anzeigen" diff --git a/po/it.po b/po/it.po new file mode 100644 index 0000000000..7d77fff2d3 --- /dev/null +++ b/po/it.po @@ -0,0 +1,41 @@ +# Italian translation for QEMU. +# This file is put in the public domain. +# Paolo Bonzini , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: QEMU 1.4.50\n" +"Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" +"POT-Creation-Date: 2013-02-08 09:21-0600\n" +"PO-Revision-Date: 2012-02-27 08:23+0100\n" +"Last-Translator: Paolo Bonzini \n" +"Language-Team: Italian \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +#: ../ui/gtk.c:990 +msgid "_File" +msgstr "_File" + +#: ../ui/gtk.c:1000 +msgid "_View" +msgstr "_Visualizza" + +#: ../ui/gtk.c:1029 +msgid "Zoom To _Fit" +msgstr "Adatta alla _finestra" + +#: ../ui/gtk.c:1035 +msgid "Grab On _Hover" +msgstr "Cattura _automatica input" + +#: ../ui/gtk.c:1038 +msgid "_Grab Input" +msgstr "_Cattura input" + +#: ../ui/gtk.c:1064 +msgid "Show _Tabs" +msgstr "Mostra _tab" diff --git a/po/messages.po b/po/messages.po new file mode 100644 index 0000000000..191e81cc7c --- /dev/null +++ b/po/messages.po @@ -0,0 +1,41 @@ +# SOME DESCRIPTIVE TITLE. +# This file is put in the public domain. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: QEMU 1.4.50\n" +"Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" +"POT-Creation-Date: 2013-02-08 09:21-0600\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../ui/gtk.c:990 +msgid "_File" +msgstr "" + +#: ../ui/gtk.c:1000 +msgid "_View" +msgstr "" + +#: ../ui/gtk.c:1029 +msgid "Zoom To _Fit" +msgstr "" + +#: ../ui/gtk.c:1035 +msgid "Grab On _Hover" +msgstr "" + +#: ../ui/gtk.c:1038 +msgid "_Grab Input" +msgstr "" + +#: ../ui/gtk.c:1064 +msgid "Show _Tabs" +msgstr "" diff --git a/qapi-schema.json b/qapi-schema.json index 5dfa052391..fdaa9da133 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -244,6 +244,56 @@ '*backing-filename': 'str', '*full-backing-filename': 'str', '*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'] } } +## +# @ImageCheck: +# +# Information about a QEMU image file check +# +# @filename: name of the image file checked +# +# @format: format of the image file checked +# +# @check-errors: number of unexpected errors occurred during check +# +# @image-end-offset: #optional offset (in bytes) where the image ends, this +# field is present if the driver for the image format +# supports it +# +# @corruptions: #optional number of corruptions found during the check if any +# +# @leaks: #optional number of leaks found during the check if any +# +# @corruptions-fixed: #optional number of corruptions fixed during the check +# if any +# +# @leaks-fixed: #optional number of leaks fixed during the check if any +# +# @total-clusters: #optional total number of clusters, this field is present +# if the driver for the image format supports it +# +# @allocated-clusters: #optional total number of allocated clusters, this +# field is present if the driver for the image format +# supports it +# +# @fragmented-clusters: #optional total number of fragmented clusters, this +# field is present if the driver for the image format +# supports it +# +# @compressed-clusters: #optional total number of compressed clusters, this +# field is present if the driver for the image format +# supports it +# +# Since: 1.4 +# +## + +{ 'type': 'ImageCheck', + 'data': {'filename': 'str', 'format': 'str', 'check-errors': 'int', + '*image-end-offset': 'int', '*corruptions': 'int', '*leaks': 'int', + '*corruptions-fixed': 'int', '*leaks-fixed': 'int', + '*total-clusters': 'int', '*allocated-clusters': 'int', + '*fragmented-clusters': 'int', '*compressed-clusters': 'int' } } + ## # @StatusInfo: # @@ -324,6 +374,73 @@ ## { 'command': 'query-chardev', 'returns': ['ChardevInfo'] } +## +# @DataFormat: +# +# An enumeration of data format. +# +# @utf8: Data is a UTF-8 string (RFC 3629) +# +# @base64: Data is Base64 encoded binary (RFC 3548) +# +# Since: 1.4 +## +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } + +## +# @ringbuf-write: +# +# Write to a ring buffer character device. +# +# @device: the ring buffer character device name +# +# @data: data to write +# +# @format: #optional data encoding (default 'utf8'). +# - base64: data must be base64 encoded text. Its binary +# decoding gets written. +# Bug: invalid base64 is currently not rejected. +# Whitespace *is* invalid. +# - utf8: data's UTF-8 encoding is written +# - data itself is always Unicode regardless of format, like +# any other string. +# +# Returns: Nothing on success +# +# Since: 1.4 +## +{ 'command': 'ringbuf-write', + 'data': {'device': 'str', 'data': 'str', + '*format': 'DataFormat'} } + +## +# @ringbuf-read: +# +# Read from a ring buffer character device. +# +# @device: the ring buffer character device name +# +# @size: how many bytes to read at most +# +# @format: #optional data encoding (default 'utf8'). +# - base64: the data read is returned in base64 encoding. +# - utf8: the data read is interpreted as UTF-8. +# Bug: can screw up when the buffer contains invalid UTF-8 +# sequences, NUL characters, after the ring buffer lost +# data, and when reading stops because the size limit is +# reached. +# - The return value is always Unicode regardless of format, +# like any other string. +# +# Returns: data read from the device +# +# Since: 1.4 +## +{ 'command': 'ringbuf-read', + 'data': {'device': 'str', 'size': 'int', '*format': 'DataFormat'}, + 'returns': 'str' } + ## # @CommandInfo: # @@ -667,10 +784,12 @@ # # @count: number of dirty bytes according to the dirty bitmap # +# @granularity: granularity of the dirty bitmap in bytes (since 1.4) +# # Since: 1.3 ## { 'type': 'BlockDirtyInfo', - 'data': {'count': 'int'} } + 'data': {'count': 'int', 'granularity': 'int'} } ## # @BlockInfo: @@ -977,28 +1096,10 @@ # # @actual: the number of bytes the balloon currently contains # -# @mem_swapped_in: #optional number of pages swapped in within the guest -# -# @mem_swapped_out: #optional number of pages swapped out within the guest -# -# @major_page_faults: #optional number of major page faults within the guest -# -# @minor_page_faults: #optional number of minor page faults within the guest -# -# @free_mem: #optional amount of memory (in bytes) free in the guest -# -# @total_mem: #optional amount of memory (in bytes) visible to the guest -# # Since: 0.14.0 # -# Notes: all current versions of QEMU do not fill out optional information in -# this structure. ## -{ 'type': 'BalloonInfo', - 'data': {'actual': 'int', '*mem_swapped_in': 'int', - '*mem_swapped_out': 'int', '*major_page_faults': 'int', - '*minor_page_faults': 'int', '*free_mem': 'int', - '*total_mem': 'int'} } +{ 'type': 'BalloonInfo', 'data': {'actual': 'int' } } ## # @query-balloon: @@ -1634,6 +1735,14 @@ # (all the disk, only the sectors allocated in the topmost image, or # only new I/O). # +# @granularity: #optional granularity of the dirty bitmap, default is 64K +# if the image format doesn't have clusters, 4K if the clusters +# are smaller than that, else the cluster size. Must be a +# power of 2 between 512 and 64M (since 1.4). +# +# @buf-size: #optional maximum amount of data in flight from source to +# target (since 1.4). +# # @on-source-error: #optional the action to take on an error on the source, # default 'report'. 'stop' and 'enospc' can only be used # if the block device supports io-status (see BlockInfo). @@ -1650,7 +1759,8 @@ { 'command': 'drive-mirror', 'data': { 'device': 'str', 'target': 'str', '*format': 'str', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode', - '*speed': 'int', '*on-source-error': 'BlockdevOnError', + '*speed': 'int', '*granularity': 'uint32', + '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError' } } ## @@ -2444,6 +2554,9 @@ # # @fd: #optional file descriptor of an already opened tap # +# @fds: #optional multiple file descriptors of already opened multiqueue capable +# tap +# # @script: #optional script to initialize the interface # # @downscript: #optional script to shut down the interface @@ -2458,6 +2571,9 @@ # # @vhostfd: #optional file descriptor of an already opened vhost net device # +# @vhostfds: #optional file descriptors of multiple already opened vhost net +# devices +# # @vhostforce: #optional vhost on for non-MSIX virtio guests # # Since 1.2 @@ -2466,6 +2582,7 @@ 'data': { '*ifname': 'str', '*fd': 'str', + '*fds': 'str', '*script': 'str', '*downscript': 'str', '*helper': 'str', @@ -2473,7 +2590,9 @@ '*vnet_hdr': 'bool', '*vhost': 'bool', '*vhostfd': 'str', - '*vhostforce': 'bool' } } + '*vhostfds': 'str', + '*vhostforce': 'bool', + '*queues': 'uint32'} } ## # @NetdevSocketOptions @@ -3017,3 +3136,309 @@ # Since: 1.3.0 ## { 'command': 'nbd-server-stop' } + +## +# @ChardevFile: +# +# Configuration info for file chardevs. +# +# @in: #optional The name of the input file +# @out: The name of the output file +# +# Since: 1.4 +## +{ 'type': 'ChardevFile', 'data': { '*in' : 'str', + 'out' : 'str' } } + +## +# @ChardevHostdev: +# +# Configuration info for device and pipe chardevs. +# +# @device: The name of the special file for the device, +# i.e. /dev/ttyS0 on Unix or COM1: on Windows +# @type: What kind of device this is. +# +# Since: 1.4 +## +{ 'type': 'ChardevHostdev', 'data': { 'device' : 'str' } } + +## +# @ChardevSocket: +# +# Configuration info for (stream) socket chardevs. +# +# @addr: socket address to listen on (server=true) +# or connect to (server=false) +# @server: #optional create server socket (default: true) +# @wait: #optional wait for connect (not used for server +# sockets, default: false) +# @nodelay: #optional set TCP_NODELAY socket option (default: false) +# @telnet: #optional enable telnet protocol (default: false) +# +# Since: 1.4 +## +{ 'type': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress', + '*server' : 'bool', + '*wait' : 'bool', + '*nodelay' : 'bool', + '*telnet' : 'bool' } } + +## +# @ChardevDgram: +# +# Configuration info for datagram socket chardevs. +# +# @remote: remote address +# @local: #optional local address +# +# Since: 1.5 +## +{ 'type': 'ChardevDgram', 'data': { 'remote' : 'SocketAddress', + '*local' : 'SocketAddress' } } + +## +# @ChardevMux: +# +# Configuration info for mux chardevs. +# +# @chardev: name of the base chardev. +# +# Since: 1.5 +## +{ 'type': 'ChardevMux', 'data': { 'chardev' : 'str' } } + +## +# @ChardevStdio: +# +# Configuration info for stdio chardevs. +# +# @signal: #optional Allow signals (such as SIGINT triggered by ^C) +# be delivered to qemu. Default: true in -nographic mode, +# false otherwise. +# +# Since: 1.5 +## +{ 'type': 'ChardevStdio', 'data': { '*signal' : 'bool' } } + +## +# @ChardevSpiceChannel: +# +# Configuration info for spice vm channel chardevs. +# +# @type: kind of channel (for example vdagent). +# +# Since: 1.5 +## +{ 'type': 'ChardevSpiceChannel', 'data': { 'type' : 'str' } } + +## +# @ChardevSpicePort: +# +# Configuration info for spice port chardevs. +# +# @fqdn: name of the channel (see docs/spice-port-fqdn.txt) +# +# Since: 1.5 +## +{ 'type': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' } } + +## +# @ChardevVC: +# +# Configuration info for virtual console chardevs. +# +# @width: console width, in pixels +# @height: console height, in pixels +# @cols: console width, in chars +# @rows: console height, in chars +# +# Since: 1.5 +## +{ 'type': 'ChardevVC', 'data': { '*width' : 'int', + '*height' : 'int', + '*cols' : 'int', + '*rows' : 'int' } } + +## +# @ChardevRingbuf: +# +# Configuration info for memory chardevs +# +# @size: #optional Ringbuffer size, must be power of two, default is 65536 +# +# Since: 1.5 +## +{ 'type': 'ChardevRingbuf', 'data': { '*size' : 'int' } } + +## +# @ChardevBackend: +# +# Configuration info for the new chardev backend. +# +# Since: 1.4 +## +{ 'type': 'ChardevDummy', 'data': { } } + +{ 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile', + 'serial' : 'ChardevHostdev', + 'parallel': 'ChardevHostdev', + 'pipe' : 'ChardevHostdev', + 'socket' : 'ChardevSocket', + 'dgram' : 'ChardevDgram', + 'pty' : 'ChardevDummy', + 'null' : 'ChardevDummy', + 'mux' : 'ChardevMux', + 'msmouse': 'ChardevDummy', + 'braille': 'ChardevDummy', + 'stdio' : 'ChardevStdio', + 'console': 'ChardevDummy', + 'spicevmc' : 'ChardevSpiceChannel', + 'spiceport' : 'ChardevSpicePort', + 'vc' : 'ChardevVC', + 'memory' : 'ChardevRingbuf' } } + +## +# @ChardevReturn: +# +# Return info about the chardev backend just created. +# +# @pty: #optional name of the slave pseudoterminal device, present if +# and only if a chardev of type 'pty' was created +# +# Since: 1.4 +## +{ 'type' : 'ChardevReturn', 'data': { '*pty' : 'str' } } + +## +# @chardev-add: +# +# Add a character device backend +# +# @id: the chardev's ID, must be unique +# @backend: backend type and parameters +# +# Returns: ChardevReturn. +# +# Since: 1.4 +## +{ 'command': 'chardev-add', 'data': {'id' : 'str', + 'backend' : 'ChardevBackend' }, + 'returns': 'ChardevReturn' } + +## +# @chardev-remove: +# +# Remove a character device backend +# +# @id: the chardev's ID, must exist and not be in use +# +# Returns: Nothing on success +# +# Since: 1.4 +## +{ 'command': 'chardev-remove', 'data': {'id': 'str'} } + +## +# @TpmModel: +# +# An enumeration of TPM models +# +# @tpm-tis: TPM TIS model +# +# Since: 1.5 +## +{ 'enum': 'TpmModel', 'data': [ 'tpm-tis' ] } + +## +# @query-tpm-models: +# +# Return a list of supported TPM models +# +# Returns: a list of TpmModel +# +# Since: 1.5 +## +{ 'command': 'query-tpm-models', 'returns': ['TpmModel'] } + +## +# @TpmType: +# +# An enumeration of TPM types +# +# @passthrough: TPM passthrough type +# +# Since: 1.5 +## +{ 'enum': 'TpmType', 'data': [ 'passthrough' ] } + +## +# @query-tpm-types: +# +# Return a list of supported TPM types +# +# Returns: a list of TpmType +# +# Since: 1.5 +## +{ 'command': 'query-tpm-types', 'returns': ['TpmType'] } + +## +# @TPMPassthroughOptions: +# +# Information about the TPM passthrough type +# +# @path: #optional string describing the path used for accessing the TPM device +# +# @cancel-path: #optional string showing the TPM's sysfs cancel file +# for cancellation of TPM commands while they are executing +# +# Since: 1.5 +## +{ 'type': 'TPMPassthroughOptions', 'data': { '*path' : 'str', + '*cancel-path' : 'str'} } + +## +# @TpmTypeOptions: +# +# A union referencing different TPM backend types' configuration options +# +# @tpm-passthough-options: TPMPassthroughOptions describing the TPM +# passthrough configuration options +# +# Since: 1.5 +## +{ 'union': 'TpmTypeOptions', + 'data': { 'tpm-passthrough-options' : 'TPMPassthroughOptions' } } + +## +# @TpmInfo: +# +# Information about the TPM +# +# @id: The Id of the TPM +# +# @model: The TPM frontend model +# +# @type: The TPM (backend) type being used +# +# @tpm-options: The TPM (backend) type configuration options +# +# Since: 1.5 +## +{ 'type': 'TPMInfo', + 'data': {'id': 'str', + 'model': 'TpmModel', + 'type': 'TpmType', + 'tpm-options': 'TpmTypeOptions' } } + +## +# @query-tpm: +# +# Return information about the TPM device +# +# Returns: @TPMInfo on success +# +# Since: 1.5 +## +{ 'command': 'query-tpm', 'returns': ['TPMInfo'] } diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs index f9bd3b9910..1f9c97342c 100644 --- a/qapi/Makefile.objs +++ b/qapi/Makefile.objs @@ -1,5 +1,5 @@ -qapi-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o -qapi-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o -qapi-obj-y += string-input-visitor.o string-output-visitor.o +util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o +util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o +util-obj-y += string-input-visitor.o string-output-visitor.o -common-obj-y += opts-visitor.o +util-obj-y += opts-visitor.o diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index 70cdbca470..28bbbe849e 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -92,7 +92,7 @@ char **qmp_get_command_list(void) list_head = list = g_malloc0(count * sizeof(char *)); QTAILQ_FOREACH(cmd, &qmp_commands, node) { - *list = strdup(cmd->name); + *list = g_strdup(cmd->name); list++; } diff --git a/hw/qdev-monitor.c b/qdev-monitor.c similarity index 89% rename from hw/qdev-monitor.c rename to qdev-monitor.c index b73986759b..9a78ccff6d 100644 --- a/hw/qdev-monitor.c +++ b/qdev-monitor.c @@ -17,8 +17,9 @@ * License along with this library; if not, see . */ -#include "qdev.h" +#include "hw/qdev.h" #include "monitor/monitor.h" +#include "monitor/qdev.h" #include "qmp-commands.h" #include "sysemu/arch_init.h" #include "qemu/config-file.h" @@ -283,6 +284,7 @@ static DeviceState *qbus_find_dev(BusState *bus, char *elem) static BusState *qbus_find_recursive(BusState *bus, const char *name, const char *bus_typename) { + BusClass *bus_class = BUS_GET_CLASS(bus); BusChild *kid; BusState *child, *ret; int match = 1; @@ -293,6 +295,17 @@ static BusState *qbus_find_recursive(BusState *bus, const char *name, if (bus_typename && !object_dynamic_cast(OBJECT(bus), bus_typename)) { match = 0; } + if ((bus_class->max_dev != 0) && (bus_class->max_dev <= bus->max_index)) { + if (name != NULL) { + /* bus was explicitly specified: return an error. */ + qerror_report(ERROR_CLASS_GENERIC_ERROR, "Bus '%s' is full", + bus->name); + return NULL; + } else { + /* bus was not specified: try to find another one. */ + match = 0; + } + } if (match) { return bus; } @@ -564,13 +577,13 @@ static void qbus_print(Monitor *mon, BusState *bus, int indent) } #undef qdev_printf -void do_info_qtree(Monitor *mon) +void do_info_qtree(Monitor *mon, const QDict *qdict) { if (sysbus_get_default()) qbus_print(mon, sysbus_get_default(), 0); } -void do_info_qdm(Monitor *mon) +void do_info_qdm(Monitor *mon, const QDict *qdict) { object_class_foreach(qdev_print_devinfo, TYPE_DEVICE, false, NULL); } @@ -579,6 +592,7 @@ int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data) { Error *local_err = NULL; QemuOpts *opts; + DeviceState *dev; opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &local_err); if (error_is_set(&local_err)) { @@ -590,10 +604,12 @@ int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data) qemu_opts_del(opts); return 0; } - if (!qdev_device_add(opts)) { + dev = qdev_device_add(opts); + if (!dev) { qemu_opts_del(opts); return -1; } + object_unref(OBJECT(dev)); return 0; } @@ -615,3 +631,54 @@ void qdev_machine_init(void) qdev_get_peripheral_anon(); qdev_get_peripheral(); } + +QemuOptsList qemu_device_opts = { + .name = "device", + .implied_opt_name = "driver", + .head = QTAILQ_HEAD_INITIALIZER(qemu_device_opts.head), + .desc = { + /* + * no elements => accept any + * sanity checking will happen later + * when setting device properties + */ + { /* end of list */ } + }, +}; + +QemuOptsList qemu_global_opts = { + .name = "global", + .head = QTAILQ_HEAD_INITIALIZER(qemu_global_opts.head), + .desc = { + { + .name = "driver", + .type = QEMU_OPT_STRING, + },{ + .name = "property", + .type = QEMU_OPT_STRING, + },{ + .name = "value", + .type = QEMU_OPT_STRING, + }, + { /* end of list */ } + }, +}; + +int qemu_global_option(const char *str) +{ + char driver[64], property[64]; + QemuOpts *opts; + int rc, offset; + + rc = sscanf(str, "%63[^.].%63[^=]%n", driver, property, &offset); + if (rc < 2 || str[offset] != '=') { + error_report("can't parse: \"%s\"", str); + return -1; + } + + opts = qemu_opts_create_nofail(&qemu_global_opts); + qemu_opt_set(opts, "driver", driver); + qemu_opt_set(opts, "property", property); + qemu_opt_set(opts, "value", str+offset+1); + return 0; +} diff --git a/qemu-char.c b/qemu-char.c index 6113d0ab60..e6337971a5 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -28,8 +28,6 @@ #include "qemu/timer.h" #include "char/char.h" #include "hw/usb.h" -#include "hw/baum.h" -#include "hw/msmouse.h" #include "qmp-commands.h" #include @@ -122,20 +120,18 @@ void qemu_chr_be_event(CharDriverState *s, int event) s->chr_event(s->handler_opaque, event); } -static void qemu_chr_fire_open_event(void *opaque) +static gboolean qemu_chr_generic_open_bh(gpointer opaque) { CharDriverState *s = opaque; qemu_chr_be_event(s, CHR_EVENT_OPENED); - qemu_free_timer(s->open_timer); - s->open_timer = NULL; + s->idle_tag = 0; + return FALSE; } void qemu_chr_generic_open(CharDriverState *s) { - if (s->open_timer == NULL) { - s->open_timer = qemu_new_timer_ms(rt_clock, - qemu_chr_fire_open_event, s); - qemu_mod_timer(s->open_timer, qemu_get_clock_ms(rt_clock) - 1); + if (s->idle_tag == 0) { + s->idle_tag = g_idle_add(qemu_chr_generic_open_bh, s); } } @@ -221,7 +217,7 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return len; } -static CharDriverState *qemu_chr_open_null(QemuOpts *opts) +static CharDriverState *qemu_chr_open_null(void) { CharDriverState *chr; @@ -537,23 +533,241 @@ int send_all(int fd, const void *_buf, int len1) } return len1 - len; } + +int recv_all(int fd, void *_buf, int len1, bool single_read) +{ + int ret, len; + uint8_t *buf = _buf; + + len = len1; + while ((len > 0) && (ret = read(fd, buf, len)) != 0) { + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) { + return -1; + } + continue; + } else { + if (single_read) { + return ret; + } + buf += ret; + len -= ret; + } + } + return len1 - len; +} + #endif /* !_WIN32 */ -#define STDIO_MAX_CLIENTS 1 -static int stdio_nb_clients; +typedef struct IOWatchPoll +{ + GSource *src; + int max_size; + + IOCanReadHandler *fd_can_read; + void *opaque; + + QTAILQ_ENTRY(IOWatchPoll) node; +} IOWatchPoll; + +static QTAILQ_HEAD(, IOWatchPoll) io_watch_poll_list = + QTAILQ_HEAD_INITIALIZER(io_watch_poll_list); + +static IOWatchPoll *io_watch_poll_from_source(GSource *source) +{ + IOWatchPoll *i; + + QTAILQ_FOREACH(i, &io_watch_poll_list, node) { + if (i->src == source) { + return i; + } + } + + return NULL; +} + +static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_) +{ + IOWatchPoll *iwp = io_watch_poll_from_source(source); + + iwp->max_size = iwp->fd_can_read(iwp->opaque); + if (iwp->max_size == 0) { + return FALSE; + } + + return g_io_watch_funcs.prepare(source, timeout_); +} + +static gboolean io_watch_poll_check(GSource *source) +{ + IOWatchPoll *iwp = io_watch_poll_from_source(source); + + if (iwp->max_size == 0) { + return FALSE; + } + + return g_io_watch_funcs.check(source); +} + +static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback, + gpointer user_data) +{ + return g_io_watch_funcs.dispatch(source, callback, user_data); +} + +static void io_watch_poll_finalize(GSource *source) +{ + IOWatchPoll *iwp = io_watch_poll_from_source(source); + QTAILQ_REMOVE(&io_watch_poll_list, iwp, node); + g_io_watch_funcs.finalize(source); +} + +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(GIOChannel *channel, + IOCanReadHandler *fd_can_read, + GIOFunc fd_read, + gpointer user_data) +{ + IOWatchPoll *iwp; + GSource *src; + guint tag; + + src = g_io_create_watch(channel, G_IO_IN | G_IO_ERR | G_IO_HUP); + g_source_set_funcs(src, &io_watch_poll_funcs); + g_source_set_callback(src, (GSourceFunc)fd_read, user_data, NULL); + tag = g_source_attach(src, NULL); + g_source_unref(src); + + iwp = g_malloc0(sizeof(*iwp)); + iwp->src = src; + iwp->max_size = 0; + iwp->fd_can_read = fd_can_read; + iwp->opaque = user_data; + + QTAILQ_INSERT_HEAD(&io_watch_poll_list, iwp, node); + + return tag; +} + +#ifndef _WIN32 +static GIOChannel *io_channel_from_fd(int fd) +{ + GIOChannel *chan; + + if (fd == -1) { + return NULL; + } + + chan = g_io_channel_unix_new(fd); + + g_io_channel_set_encoding(chan, NULL, NULL); + g_io_channel_set_buffered(chan, FALSE); + + return chan; +} +#endif + +static GIOChannel *io_channel_from_socket(int fd) +{ + GIOChannel *chan; + + if (fd == -1) { + return NULL; + } + +#ifdef _WIN32 + chan = g_io_channel_win32_new_socket(fd); +#else + chan = g_io_channel_unix_new(fd); +#endif + + g_io_channel_set_encoding(chan, NULL, NULL); + g_io_channel_set_buffered(chan, FALSE); + + return chan; +} + +static int io_channel_send_all(GIOChannel *fd, const void *_buf, int len1) +{ + GIOStatus status; + gsize bytes_written; + int len; + const uint8_t *buf = _buf; + + len = len1; + while (len > 0) { + status = g_io_channel_write_chars(fd, (const gchar *)buf, len, + &bytes_written, NULL); + if (status != G_IO_STATUS_NORMAL) { + if (status == G_IO_STATUS_AGAIN) { + errno = EAGAIN; + return -1; + } else { + errno = EINVAL; + return -1; + } + } else if (status == G_IO_STATUS_EOF) { + break; + } else { + buf += bytes_written; + len -= bytes_written; + } + } + return len1 - len; +} #ifndef _WIN32 -typedef struct { - int fd_in, fd_out; +typedef struct FDCharDriver { + CharDriverState *chr; + GIOChannel *fd_in, *fd_out; + guint fd_in_tag; int max_size; + QTAILQ_ENTRY(FDCharDriver) node; } FDCharDriver; - static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { FDCharDriver *s = chr->opaque; - return send_all(s->fd_out, buf, len); + + return io_channel_send_all(s->fd_out, buf, len); +} + +static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) +{ + CharDriverState *chr = opaque; + FDCharDriver *s = chr->opaque; + int len; + uint8_t buf[READ_BUF_LEN]; + GIOStatus status; + gsize bytes_read; + + len = sizeof(buf); + if (len > s->max_size) { + len = s->max_size; + } + if (len == 0) { + return FALSE; + } + + status = g_io_channel_read_chars(chan, (gchar *)buf, + len, &bytes_read, NULL); + if (status == G_IO_STATUS_EOF) { + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); + return FALSE; + } + if (status == G_IO_STATUS_NORMAL) { + qemu_chr_be_write(chr, buf, bytes_read); + } + + return TRUE; } static int fd_chr_read_poll(void *opaque) @@ -565,40 +779,22 @@ static int fd_chr_read_poll(void *opaque) return s->max_size; } -static void fd_chr_read(void *opaque) +static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond) { - CharDriverState *chr = opaque; FDCharDriver *s = chr->opaque; - int size, len; - uint8_t buf[READ_BUF_LEN]; - - len = sizeof(buf); - if (len > s->max_size) - len = s->max_size; - if (len == 0) - return; - size = read(s->fd_in, buf, len); - if (size == 0) { - /* FD has been closed. Remove it from the active list. */ - qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL); - qemu_chr_be_event(chr, CHR_EVENT_CLOSED); - return; - } - if (size > 0) { - qemu_chr_be_write(chr, buf, size); - } + return g_io_create_watch(s->fd_out, cond); } static void fd_chr_update_read_handler(CharDriverState *chr) { FDCharDriver *s = chr->opaque; - if (s->fd_in >= 0) { - if (display_type == DT_NOGRAPHIC && s->fd_in == 0) { - } else { - qemu_set_fd_handler2(s->fd_in, fd_chr_read_poll, - fd_chr_read, NULL, chr); - } + if (s->fd_in_tag) { + g_source_remove(s->fd_in_tag); + } + + if (s->fd_in) { + s->fd_in_tag = io_add_watch_poll(s->fd_in, fd_chr_read_poll, fd_chr_read, chr); } } @@ -606,11 +802,16 @@ static void fd_chr_close(struct CharDriverState *chr) { FDCharDriver *s = chr->opaque; - if (s->fd_in >= 0) { - if (display_type == DT_NOGRAPHIC && s->fd_in == 0) { - } else { - qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL); - } + if (s->fd_in_tag) { + g_source_remove(s->fd_in_tag); + s->fd_in_tag = 0; + } + + if (s->fd_in) { + g_io_channel_unref(s->fd_in); + } + if (s->fd_out) { + g_io_channel_unref(s->fd_out); } g_free(s); @@ -625,9 +826,12 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) chr = g_malloc0(sizeof(CharDriverState)); s = g_malloc0(sizeof(FDCharDriver)); - s->fd_in = fd_in; - s->fd_out = fd_out; + s->fd_in = io_channel_from_fd(fd_in); + s->fd_out = io_channel_from_fd(fd_out); + fcntl(fd_out, F_SETFL, O_NONBLOCK); + 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_close = fd_chr_close; @@ -637,23 +841,11 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) return chr; } -static CharDriverState *qemu_chr_open_file_out(QemuOpts *opts) -{ - int fd_out; - - TFR(fd_out = qemu_open(qemu_opt_get(opts, "path"), - O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666)); - if (fd_out < 0) { - return NULL; - } - return qemu_chr_open_fd(-1, fd_out); -} - -static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) +static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts) { int fd_in, fd_out; char filename_in[256], filename_out[256]; - const char *filename = qemu_opt_get(opts, "path"); + const char *filename = opts->device; if (filename == NULL) { fprintf(stderr, "chardev: pipe: no filename given\n"); @@ -677,53 +869,6 @@ static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) return qemu_chr_open_fd(fd_in, fd_out); } - -/* for STDIO, we handle the case where several clients use it - (nographic mode) */ - -#define TERM_FIFO_MAX_SIZE 1 - -static uint8_t term_fifo[TERM_FIFO_MAX_SIZE]; -static int term_fifo_size; - -static int stdio_read_poll(void *opaque) -{ - CharDriverState *chr = opaque; - - /* try to flush the queue if needed */ - if (term_fifo_size != 0 && qemu_chr_be_can_write(chr) > 0) { - qemu_chr_be_write(chr, term_fifo, 1); - term_fifo_size = 0; - } - /* see if we can absorb more chars */ - if (term_fifo_size == 0) - return 1; - else - return 0; -} - -static void stdio_read(void *opaque) -{ - int size; - uint8_t buf[1]; - CharDriverState *chr = opaque; - - size = read(0, buf, 1); - if (size == 0) { - /* stdin has been closed. Remove it from the active list. */ - qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL); - qemu_chr_be_event(chr, CHR_EVENT_CLOSED); - return; - } - if (size > 0) { - if (qemu_chr_be_can_write(chr) > 0) { - qemu_chr_be_write(chr, buf, 1); - } else if (term_fifo_size == 0) { - term_fifo[term_fifo_size++] = buf[0]; - } - } -} - /* init terminal so that we can grab keys */ static struct termios oldtty; static int old_fd0_flags; @@ -760,32 +905,29 @@ static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo) static void qemu_chr_close_stdio(struct CharDriverState *chr) { term_exit(); - stdio_nb_clients--; - qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL); fd_chr_close(chr); } -static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) +static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) { CharDriverState *chr; - if (stdio_nb_clients >= STDIO_MAX_CLIENTS) { + if (is_daemonized()) { + error_report("cannot use stdio with -daemonize"); return NULL; } - if (stdio_nb_clients == 0) { - old_fd0_flags = fcntl(0, F_GETFL); - tcgetattr (0, &oldtty); - fcntl(0, F_SETFL, O_NONBLOCK); - atexit(term_exit); - } + old_fd0_flags = fcntl(0, F_GETFL); + tcgetattr (0, &oldtty); + fcntl(0, F_SETFL, O_NONBLOCK); + atexit(term_exit); chr = qemu_chr_open_fd(0, 1); chr->chr_close = qemu_chr_close_stdio; chr->chr_set_echo = qemu_chr_set_echo_stdio; - qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr); - stdio_nb_clients++; - stdio_allow_signal = qemu_opt_get_bool(opts, "signal", - display_type != DT_NOGRAPHIC); + stdio_allow_signal = display_type != DT_NOGRAPHIC; + if (opts->has_signal) { + stdio_allow_signal = opts->signal; + } qemu_chr_fe_set_echo(chr, false); return chr; @@ -852,17 +994,58 @@ static void cfmakeraw (struct termios *termios_p) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ || defined(__GLIBC__) +#define HAVE_CHARDEV_TTY 1 + typedef struct { - int fd; + GIOChannel *fd; + guint fd_tag; int connected; int polling; int read_bytes; - QEMUTimer *timer; + guint timer_tag; } PtyCharDriver; static void pty_chr_update_read_handler(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; + + if (s->connected) { + goto out; + } + if (s->polling) { + /* If we arrive here without polling being cleared due + * read returning -EIO, then we are (re-)connected */ + pty_chr_state(chr, 1); + goto out; + } + + /* Next poll ... */ + pty_chr_update_read_handler(chr); + +out: + return FALSE; +} + +static void pty_chr_rearm_timer(CharDriverState *chr, int ms) +{ + PtyCharDriver *s = chr->opaque; + + if (s->timer_tag) { + g_source_remove(s->timer_tag); + s->timer_tag = 0; + } + + if (ms == 1000) { + s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr); + } else { + s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr); + } +} + static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { PtyCharDriver *s = chr->opaque; @@ -872,7 +1055,13 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) pty_chr_update_read_handler(chr); return 0; } - return send_all(s->fd, buf, len); + return io_channel_send_all(s->fd, buf, len); +} + +static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond) +{ + PtyCharDriver *s = chr->opaque; + return g_io_create_watch(s->fd, cond); } static int pty_chr_read_poll(void *opaque) @@ -884,36 +1073,39 @@ static int pty_chr_read_poll(void *opaque) return s->read_bytes; } -static void pty_chr_read(void *opaque) +static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; PtyCharDriver *s = chr->opaque; - int size, len; + gsize size, len; uint8_t buf[READ_BUF_LEN]; + GIOStatus status; len = sizeof(buf); if (len > s->read_bytes) len = s->read_bytes; if (len == 0) - return; - size = read(s->fd, buf, len); - if ((size == -1 && errno == EIO) || - (size == 0)) { + return FALSE; + status = g_io_channel_read_chars(s->fd, (gchar *)buf, len, &size, NULL); + if (status != G_IO_STATUS_NORMAL) { pty_chr_state(chr, 0); - return; - } - if (size > 0) { + return FALSE; + } else { pty_chr_state(chr, 1); qemu_chr_be_write(chr, buf, size); } + return TRUE; } static void pty_chr_update_read_handler(CharDriverState *chr) { PtyCharDriver *s = chr->opaque; - qemu_set_fd_handler2(s->fd, pty_chr_read_poll, - pty_chr_read, NULL, chr); + if (s->fd_tag) { + g_source_remove(s->fd_tag); + } + + s->fd_tag = io_add_watch_poll(s->fd, pty_chr_read_poll, pty_chr_read, chr); s->polling = 1; /* * Short timeout here: just need wait long enougth that qemu makes @@ -923,7 +1115,7 @@ static void pty_chr_update_read_handler(CharDriverState *chr) * timeout to the normal (much longer) poll interval before the * timer triggers. */ - qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 10); + pty_chr_rearm_timer(chr, 10); } static void pty_chr_state(CharDriverState *chr, int connected) @@ -931,13 +1123,14 @@ static void pty_chr_state(CharDriverState *chr, int connected) PtyCharDriver *s = chr->opaque; if (!connected) { - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + g_source_remove(s->fd_tag); + s->fd_tag = 0; s->connected = 0; s->polling = 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. */ - qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 1000); + pty_chr_rearm_timer(chr, 1000); } else { if (!s->connected) qemu_chr_generic_open(chr); @@ -945,42 +1138,32 @@ static void pty_chr_state(CharDriverState *chr, int connected) } } -static void pty_chr_timer(void *opaque) -{ - struct CharDriverState *chr = opaque; - PtyCharDriver *s = chr->opaque; - - if (s->connected) - return; - if (s->polling) { - /* If we arrive here without polling being cleared due - * read returning -EIO, then we are (re-)connected */ - pty_chr_state(chr, 1); - return; - } - - /* Next poll ... */ - pty_chr_update_read_handler(chr); -} static void pty_chr_close(struct CharDriverState *chr) { PtyCharDriver *s = chr->opaque; + int fd; - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); - close(s->fd); - qemu_del_timer(s->timer); - qemu_free_timer(s->timer); + if (s->fd_tag) { + g_source_remove(s->fd_tag); + } + fd = g_io_channel_unix_get_fd(s->fd); + g_io_channel_unref(s->fd); + close(fd); + if (s->timer_tag) { + g_source_remove(s->timer_tag); + } g_free(s); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) +static CharDriverState *qemu_chr_open_pty(const char *id, + ChardevReturn *ret) { CharDriverState *chr; PtyCharDriver *s; struct termios tty; - int master_fd, slave_fd, len; + int master_fd, slave_fd; #if defined(__OpenBSD__) || defined(__DragonFly__) char pty_name[PATH_MAX]; #define q_ptsname(x) pty_name @@ -1001,20 +1184,22 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) chr = g_malloc0(sizeof(CharDriverState)); - len = strlen(q_ptsname(master_fd)) + 5; - chr->filename = g_malloc(len); - snprintf(chr->filename, len, "pty:%s", q_ptsname(master_fd)); - qemu_opt_set(opts, "path", q_ptsname(master_fd)); - fprintf(stderr, "char device redirected to %s\n", q_ptsname(master_fd)); + chr->filename = g_strdup_printf("pty:%s", q_ptsname(master_fd)); + ret->pty = g_strdup(q_ptsname(master_fd)); + ret->has_pty = true; + + fprintf(stderr, "char device redirected to %s (label %s)\n", + q_ptsname(master_fd), id); s = g_malloc0(sizeof(PtyCharDriver)); chr->opaque = s; chr->chr_write = pty_chr_write; chr->chr_update_read_handler = pty_chr_update_read_handler; chr->chr_close = pty_chr_close; + chr->chr_add_watch = pty_chr_add_watch; - s->fd = master_fd; - s->timer = qemu_new_timer_ms(rt_clock, pty_chr_timer, chr); + s->fd = io_channel_from_fd(master_fd); + s->timer_tag = 0; return chr; } @@ -1142,22 +1327,24 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) case CHR_IOCTL_SERIAL_SET_PARAMS: { QEMUSerialSetParams *ssp = arg; - tty_serial_init(s->fd_in, ssp->speed, ssp->parity, + tty_serial_init(g_io_channel_unix_get_fd(s->fd_in), + ssp->speed, ssp->parity, ssp->data_bits, ssp->stop_bits); } break; case CHR_IOCTL_SERIAL_SET_BREAK: { int enable = *(int *)arg; - if (enable) - tcsendbreak(s->fd_in, 1); + if (enable) { + tcsendbreak(g_io_channel_unix_get_fd(s->fd_in), 1); + } } break; case CHR_IOCTL_SERIAL_GET_TIOCM: { int sarg = 0; int *targ = (int *)arg; - ioctl(s->fd_in, TIOCMGET, &sarg); + ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMGET, &sarg); *targ = 0; if (sarg & TIOCM_CTS) *targ |= CHR_TIOCM_CTS; @@ -1177,7 +1364,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) { int sarg = *(int *)arg; int targ = 0; - ioctl(s->fd_in, TIOCMGET, &targ); + ioctl(g_io_channel_unix_get_fd(s->fd_in), 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) @@ -1192,7 +1379,7 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) targ |= TIOCM_DTR; if (sarg & CHR_TIOCM_RTS) targ |= TIOCM_RTS; - ioctl(s->fd_in, TIOCMSET, &targ); + ioctl(g_io_channel_unix_get_fd(s->fd_in), TIOCMSET, &targ); } break; default: @@ -1207,7 +1394,7 @@ static void qemu_chr_close_tty(CharDriverState *chr) int fd = -1; if (s) { - fd = s->fd_in; + fd = g_io_channel_unix_get_fd(s->fd_in); } fd_chr_close(chr); @@ -1217,30 +1404,22 @@ static void qemu_chr_close_tty(CharDriverState *chr) } } -static CharDriverState *qemu_chr_open_tty(QemuOpts *opts) +static CharDriverState *qemu_chr_open_tty_fd(int fd) { - const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; - int fd; - TFR(fd = qemu_open(filename, O_RDWR | O_NONBLOCK)); - if (fd < 0) { - return NULL; - } tty_serial_init(fd, 115200, 'N', 8, 1); chr = qemu_chr_open_fd(fd, fd); chr->chr_ioctl = tty_serial_ioctl; chr->chr_close = qemu_chr_close_tty; return chr; } -#else /* ! __linux__ && ! __sun__ */ -static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) -{ - return NULL; -} #endif /* __linux__ || __sun__ */ #if defined(__linux__) + +#define HAVE_CHARDEV_PARPORT 1 + typedef struct { int fd; int mode; @@ -1350,17 +1529,10 @@ static void pp_close(CharDriverState *chr) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) +static CharDriverState *qemu_chr_open_pp_fd(int fd) { - const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; ParallelCharDriver *drv; - int fd; - - TFR(fd = qemu_open(filename, O_RDWR)); - if (fd < 0) { - return NULL; - } if (ioctl(fd, PPCLAIM) < 0) { close(fd); @@ -1384,6 +1556,9 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) #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; @@ -1421,16 +1596,9 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) +static CharDriverState *qemu_chr_open_pp_fd(int fd) { - const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; - int fd; - - fd = qemu_open(filename, O_RDWR); - if (fd < 0) { - return NULL; - } chr = g_malloc0(sizeof(CharDriverState)); chr->opaque = (void *)(intptr_t)fd; @@ -1442,8 +1610,6 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) #else /* _WIN32 */ -static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS]; - typedef struct { int max_size; HANDLE hcom, hrecv, hsend; @@ -1652,9 +1818,8 @@ static int win_chr_poll(void *opaque) return 0; } -static CharDriverState *qemu_chr_open_win(QemuOpts *opts) +static CharDriverState *qemu_chr_open_win_path(const char *filename) { - const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; WinCharState *s; @@ -1752,9 +1917,9 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename) } -static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts) +static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts) { - const char *filename = qemu_opt_get(opts, "path"); + const char *filename = opts->device; CharDriverState *chr; WinCharState *s; @@ -1787,25 +1952,11 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) return chr; } -static CharDriverState *qemu_chr_open_win_con(QemuOpts *opts) +static CharDriverState *qemu_chr_open_win_con(void) { return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE)); } -static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts) -{ - const char *file_out = qemu_opt_get(opts, "path"); - HANDLE fd_out; - - fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (fd_out == INVALID_HANDLE_VALUE) { - return NULL; - } - - return qemu_chr_open_win_file(fd_out); -} - static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len) { HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); @@ -1941,21 +2092,15 @@ static void win_stdio_close(CharDriverState *chr) g_free(chr->opaque); g_free(chr); - stdio_nb_clients--; } -static CharDriverState *qemu_chr_open_win_stdio(QemuOpts *opts) +static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts) { CharDriverState *chr; WinStdioCharState *stdio; DWORD dwMode; int is_console = 0; - if (stdio_nb_clients >= STDIO_MAX_CLIENTS - || ((display_type != DT_NOGRAPHIC) && (stdio_nb_clients != 0))) { - return NULL; - } - chr = g_malloc0(sizeof(CharDriverState)); stdio = g_malloc0(sizeof(WinStdioCharState)); @@ -1971,37 +2116,34 @@ static CharDriverState *qemu_chr_open_win_stdio(QemuOpts *opts) chr->chr_write = win_stdio_write; chr->chr_close = win_stdio_close; - if (stdio_nb_clients == 0) { - if (is_console) { - if (qemu_add_wait_object(stdio->hStdIn, - win_stdio_wait_func, chr)) { - fprintf(stderr, "qemu_add_wait_object: failed\n"); - } - } else { - DWORD dwId; + if (is_console) { + if (qemu_add_wait_object(stdio->hStdIn, + win_stdio_wait_func, chr)) { + fprintf(stderr, "qemu_add_wait_object: failed\n"); + } + } else { + DWORD dwId; + + stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread, + chr, 0, &dwId); - stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread, - chr, 0, &dwId); - - if (stdio->hInputThread == INVALID_HANDLE_VALUE - || stdio->hInputReadyEvent == INVALID_HANDLE_VALUE - || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) { - fprintf(stderr, "cannot create stdio thread or event\n"); - exit(1); - } - if (qemu_add_wait_object(stdio->hInputReadyEvent, - win_stdio_thread_wait_func, chr)) { - fprintf(stderr, "qemu_add_wait_object: failed\n"); - } + if (stdio->hInputThread == INVALID_HANDLE_VALUE + || stdio->hInputReadyEvent == INVALID_HANDLE_VALUE + || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) { + fprintf(stderr, "cannot create stdio thread or event\n"); + exit(1); + } + if (qemu_add_wait_object(stdio->hInputReadyEvent, + win_stdio_thread_wait_func, chr)) { + fprintf(stderr, "qemu_add_wait_object: failed\n"); } } dwMode |= ENABLE_LINE_INPUT; - stdio_clients[stdio_nb_clients++] = chr; - if (stdio_nb_clients == 1 && is_console) { + if (is_console) { /* set the terminal in raw mode */ /* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */ dwMode |= ENABLE_PROCESSED_INPUT; @@ -2016,11 +2158,14 @@ static CharDriverState *qemu_chr_open_win_stdio(QemuOpts *opts) } #endif /* !_WIN32 */ + /***********************************************************/ /* UDP Net console */ typedef struct { int fd; + GIOChannel *chan; + guint tag; uint8_t buf[READ_BUF_LEN]; int bufcnt; int bufptr; @@ -2030,8 +2175,17 @@ typedef struct { static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { NetCharDriver *s = chr->opaque; + gsize bytes_written; + GIOStatus status; - return send(s->fd, (const void *)buf, len, 0); + status = g_io_channel_write_chars(s->chan, (const gchar *)buf, len, &bytes_written, NULL); + if (status == G_IO_STATUS_EOF) { + return 0; + } else if (status != G_IO_STATUS_NORMAL) { + return -1; + } + + return bytes_written; } static int udp_chr_read_poll(void *opaque) @@ -2052,17 +2206,22 @@ static int udp_chr_read_poll(void *opaque) return s->max_size; } -static void udp_chr_read(void *opaque) +static gboolean udp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; NetCharDriver *s = chr->opaque; + gsize bytes_read = 0; + GIOStatus status; if (s->max_size == 0) - return; - s->bufcnt = qemu_recv(s->fd, s->buf, sizeof(s->buf), 0); + return FALSE; + status = g_io_channel_read_chars(s->chan, (gchar *)s->buf, sizeof(s->buf), + &bytes_read, NULL); + s->bufcnt = bytes_read; s->bufptr = s->bufcnt; - if (s->bufcnt <= 0) - return; + if (status != G_IO_STATUS_NORMAL) { + return FALSE; + } s->bufptr = 0; while (s->max_size > 0 && s->bufptr < s->bufcnt) { @@ -2070,45 +2229,48 @@ static void udp_chr_read(void *opaque) s->bufptr++; s->max_size = qemu_chr_be_can_write(chr); } + + return TRUE; } static void udp_chr_update_read_handler(CharDriverState *chr) { NetCharDriver *s = chr->opaque; - if (s->fd >= 0) { - qemu_set_fd_handler2(s->fd, udp_chr_read_poll, - udp_chr_read, NULL, chr); + if (s->tag) { + g_source_remove(s->tag); + s->tag = 0; + } + + if (s->chan) { + s->tag = io_add_watch_poll(s->chan, udp_chr_read_poll, udp_chr_read, chr); } } static void udp_chr_close(CharDriverState *chr) { NetCharDriver *s = chr->opaque; - if (s->fd >= 0) { - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + if (s->tag) { + g_source_remove(s->tag); + } + if (s->chan) { + g_io_channel_unref(s->chan); closesocket(s->fd); } g_free(s); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) +static CharDriverState *qemu_chr_open_udp_fd(int fd) { CharDriverState *chr = NULL; NetCharDriver *s = NULL; - Error *local_err = NULL; - int fd = -1; chr = g_malloc0(sizeof(CharDriverState)); s = g_malloc0(sizeof(NetCharDriver)); - fd = inet_dgram_opts(opts, &local_err); - if (fd < 0) { - goto return_err; - } - s->fd = fd; + s->chan = io_channel_from_socket(s->fd); s->bufcnt = 0; s->bufptr = 0; chr->opaque = s; @@ -2116,24 +2278,27 @@ static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) chr->chr_update_read_handler = udp_chr_update_read_handler; chr->chr_close = udp_chr_close; return chr; +} -return_err: - if (local_err) { - qerror_report_err(local_err); - error_free(local_err); +static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) +{ + Error *local_err = NULL; + int fd = -1; + + fd = inet_dgram_opts(opts, &local_err); + if (fd < 0) { + return NULL; } - g_free(chr); - g_free(s); - if (fd >= 0) { - closesocket(fd); - } - return NULL; + return qemu_chr_open_udp_fd(fd); } /***********************************************************/ /* TCP Net console */ typedef struct { + + GIOChannel *chan, *listen_chan; + guint tag, listen_tag; int fd, listen_fd; int connected; int max_size; @@ -2143,13 +2308,13 @@ typedef struct { int msgfd; } TCPCharDriver; -static void tcp_chr_accept(void *opaque); +static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque); static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { TCPCharDriver *s = chr->opaque; if (s->connected) { - return send_all(s->fd, buf, len); + return io_channel_send_all(s->chan, buf, len); } else { /* XXX: indicate an error ? */ return len; @@ -2289,15 +2454,22 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) } #endif -static void tcp_chr_read(void *opaque) +static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond) +{ + TCPCharDriver *s = chr->opaque; + return g_io_create_watch(s->chan, cond); +} + +static gboolean tcp_chr_read(GIOChannel *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; + if (!s->connected || s->max_size <= 0) { + return FALSE; + } len = sizeof(buf); if (len > s->max_size) len = s->max_size; @@ -2305,10 +2477,13 @@ static void tcp_chr_read(void *opaque) if (size == 0) { /* connection closed */ s->connected = 0; - if (s->listen_fd >= 0) { - qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr); + if (s->listen_chan) { + s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr); } - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + g_source_remove(s->tag); + s->tag = 0; + g_io_channel_unref(s->chan); + s->chan = NULL; closesocket(s->fd); s->fd = -1; qemu_chr_be_event(chr, CHR_EVENT_CLOSED); @@ -2318,6 +2493,8 @@ static void tcp_chr_read(void *opaque) if (size > 0) qemu_chr_be_write(chr, buf, size); } + + return TRUE; } #ifndef _WIN32 @@ -2333,9 +2510,8 @@ static void tcp_chr_connect(void *opaque) TCPCharDriver *s = chr->opaque; s->connected = 1; - if (s->fd >= 0) { - qemu_set_fd_handler2(s->fd, tcp_chr_read_poll, - tcp_chr_read, NULL, chr); + if (s->chan) { + s->tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, tcp_chr_read, chr); } qemu_chr_generic_open(chr); } @@ -2355,12 +2531,6 @@ static void tcp_chr_telnet_init(int fd) send(fd, (char *)buf, 3, 0); } -static void socket_set_nodelay(int fd) -{ - int val = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val)); -} - static int tcp_chr_add_client(CharDriverState *chr, int fd) { TCPCharDriver *s = chr->opaque; @@ -2371,13 +2541,15 @@ static int tcp_chr_add_client(CharDriverState *chr, int fd) if (s->do_nodelay) socket_set_nodelay(fd); s->fd = fd; - qemu_set_fd_handler2(s->listen_fd, NULL, NULL, NULL, NULL); + s->chan = io_channel_from_socket(fd); + g_source_remove(s->listen_tag); + s->listen_tag = 0; tcp_chr_connect(chr); return 0; } -static void tcp_chr_accept(void *opaque) +static gboolean tcp_chr_accept(GIOChannel *channel, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; @@ -2402,7 +2574,7 @@ static void tcp_chr_accept(void *opaque) } fd = qemu_accept(s->listen_fd, addr, &len); if (fd < 0 && errno != EINTR) { - return; + return FALSE; } else if (fd >= 0) { if (s->do_telnetopt) tcp_chr_telnet_init(fd); @@ -2411,27 +2583,120 @@ static void tcp_chr_accept(void *opaque) } if (tcp_chr_add_client(chr, fd) < 0) close(fd); + + return TRUE; } static void tcp_chr_close(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; if (s->fd >= 0) { - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + if (s->tag) { + g_source_remove(s->tag); + } + if (s->chan) { + g_io_channel_unref(s->chan); + } closesocket(s->fd); } if (s->listen_fd >= 0) { - qemu_set_fd_handler2(s->listen_fd, NULL, NULL, NULL, NULL); + if (s->listen_tag) { + g_source_remove(s->listen_tag); + } + if (s->listen_chan) { + g_io_channel_unref(s->listen_chan); + } closesocket(s->listen_fd); } g_free(s); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) +static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, + bool is_listen, bool is_telnet, + bool is_waitconnect, + Error **errp) { CharDriverState *chr = NULL; TCPCharDriver *s = NULL; + char host[NI_MAXHOST], serv[NI_MAXSERV]; + const char *left = "", *right = ""; + struct sockaddr_storage ss; + socklen_t ss_len = sizeof(ss); + + memset(&ss, 0, ss_len); + if (getsockname(fd, (struct sockaddr *) &ss, &ss_len) != 0) { + error_setg(errp, "getsockname: %s", strerror(errno)); + return NULL; + } + + chr = g_malloc0(sizeof(CharDriverState)); + s = g_malloc0(sizeof(TCPCharDriver)); + + s->connected = 0; + s->fd = -1; + s->listen_fd = -1; + s->msgfd = -1; + + chr->filename = g_malloc(256); + switch (ss.ss_family) { +#ifndef _WIN32 + case AF_UNIX: + s->is_unix = 1; + snprintf(chr->filename, 256, "unix:%s%s", + ((struct sockaddr_un *)(&ss))->sun_path, + is_listen ? ",server" : ""); + break; +#endif + case AF_INET6: + left = "["; + right = "]"; + /* fall through */ + case AF_INET: + s->do_nodelay = do_nodelay; + getnameinfo((struct sockaddr *) &ss, ss_len, host, sizeof(host), + serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); + snprintf(chr->filename, 256, "%s:%s%s%s:%s%s", + is_telnet ? "telnet" : "tcp", + left, host, right, serv, + is_listen ? ",server" : ""); + break; + } + + chr->opaque = s; + chr->chr_write = tcp_chr_write; + chr->chr_close = tcp_chr_close; + chr->get_msgfd = tcp_get_msgfd; + chr->chr_add_client = tcp_chr_add_client; + chr->chr_add_watch = tcp_chr_add_watch; + + if (is_listen) { + s->listen_fd = fd; + s->listen_chan = io_channel_from_socket(s->listen_fd); + s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr); + if (is_telnet) { + s->do_telnetopt = 1; + } + } else { + s->connected = 1; + s->fd = fd; + socket_set_nodelay(fd); + s->chan = io_channel_from_socket(s->fd); + tcp_chr_connect(chr); + } + + if (is_listen && is_waitconnect) { + printf("QEMU waiting for connection on: %s\n", + chr->filename); + tcp_chr_accept(s->listen_chan, G_IO_IN, chr); + socket_set_nonblock(s->listen_fd); + } + return chr; +} + +static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) +{ + CharDriverState *chr = NULL; Error *local_err = NULL; int fd = -1; int is_listen; @@ -2448,9 +2713,6 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) if (!is_listen) is_waitconnect = 0; - chr = g_malloc0(sizeof(CharDriverState)); - s = g_malloc0(sizeof(TCPCharDriver)); - if (is_unix) { if (is_listen) { fd = unix_listen_opts(opts, &local_err); @@ -2471,56 +2733,14 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) if (!is_waitconnect) socket_set_nonblock(fd); - s->connected = 0; - s->fd = -1; - s->listen_fd = -1; - s->msgfd = -1; - s->is_unix = is_unix; - s->do_nodelay = do_nodelay && !is_unix; - - chr->opaque = s; - chr->chr_write = tcp_chr_write; - chr->chr_close = tcp_chr_close; - chr->get_msgfd = tcp_get_msgfd; - chr->chr_add_client = tcp_chr_add_client; - - if (is_listen) { - s->listen_fd = fd; - qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr); - if (is_telnet) - s->do_telnetopt = 1; - - } else { - s->connected = 1; - s->fd = fd; - socket_set_nodelay(fd); - tcp_chr_connect(chr); - } - - /* for "info chardev" monitor command */ - chr->filename = g_malloc(256); - if (is_unix) { - snprintf(chr->filename, 256, "unix:%s%s", - qemu_opt_get(opts, "path"), - qemu_opt_get_bool(opts, "server", 0) ? ",server" : ""); - } else if (is_telnet) { - snprintf(chr->filename, 256, "telnet:%s:%s%s", - qemu_opt_get(opts, "host"), qemu_opt_get(opts, "port"), - qemu_opt_get_bool(opts, "server", 0) ? ",server" : ""); - } else { - snprintf(chr->filename, 256, "tcp:%s:%s%s", - qemu_opt_get(opts, "host"), qemu_opt_get(opts, "port"), - qemu_opt_get_bool(opts, "server", 0) ? ",server" : ""); - } - - if (is_listen && is_waitconnect) { - printf("QEMU waiting for connection on: %s\n", - chr->filename); - tcp_chr_accept(chr); - socket_set_nonblock(s->listen_fd); + chr = qemu_chr_open_socket_fd(fd, do_nodelay, is_listen, is_telnet, + is_waitconnect, &local_err); + if (error_is_set(&local_err)) { + goto fail; } return chr; + fail: if (local_err) { qerror_report_err(local_err); @@ -2529,8 +2749,10 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) if (fd >= 0) { closesocket(fd); } - g_free(s); - g_free(chr); + if (chr) { + g_free(chr->opaque); + g_free(chr); + } return NULL; } @@ -2598,6 +2820,189 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->outbuf_size; } +/*********************************************************/ +/* 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; +} + +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 0; +} + +static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ + RingBufCharDriver *d = chr->opaque; + int i; + + for (i = 0; i < len && d->cons != d->prod; i++) { + buf[i] = d->cbuf[d->cons++ & (d->size - 1)]; + } + + return i; +} + +static void ringbuf_chr_close(struct CharDriverState *chr) +{ + RingBufCharDriver *d = chr->opaque; + + g_free(d->cbuf); + g_free(d); + chr->opaque = NULL; +} + +static CharDriverState *qemu_chr_open_ringbuf(ChardevRingbuf *opts, + Error **errp) +{ + CharDriverState *chr; + RingBufCharDriver *d; + + chr = g_malloc0(sizeof(CharDriverState)); + 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_close = ringbuf_chr_close; + + return chr; + +fail: + g_free(d); + g_free(chr); + return NULL; +} + +static 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; + size_t 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 = g_base64_decode(data, &write_count); + } 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]; @@ -2726,78 +3131,197 @@ fail: return NULL; } -static const struct { +static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + const char *path = qemu_opt_get(opts, "path"); + + if (path == NULL) { + error_setg(errp, "chardev: file: no filename given"); + return; + } + backend->file = g_new0(ChardevFile, 1); + backend->file->out = g_strdup(path); +} + +static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + backend->stdio = g_new0(ChardevStdio, 1); + backend->stdio->has_signal = true; + backend->stdio->signal = + qemu_opt_get_bool(opts, "signal", display_type != DT_NOGRAPHIC); +} + +static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + const char *device = qemu_opt_get(opts, "path"); + + if (device == NULL) { + error_setg(errp, "chardev: serial/tty: no device path given"); + return; + } + backend->serial = g_new0(ChardevHostdev, 1); + backend->serial->device = g_strdup(device); +} + +static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + const char *device = qemu_opt_get(opts, "path"); + + if (device == NULL) { + error_setg(errp, "chardev: parallel: no device path given"); + return; + } + backend->parallel = g_new0(ChardevHostdev, 1); + backend->parallel->device = g_strdup(device); +} + +static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + const char *device = qemu_opt_get(opts, "path"); + + if (device == NULL) { + error_setg(errp, "chardev: pipe: no device path given"); + return; + } + backend->pipe = g_new0(ChardevHostdev, 1); + backend->pipe->device = g_strdup(device); +} + +static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + int val; + + backend->memory = g_new0(ChardevRingbuf, 1); + + val = qemu_opt_get_number(opts, "size", 0); + if (val != 0) { + backend->memory->has_size = true; + backend->memory->size = val; + } +} + +typedef struct CharDriver { const char *name; + /* old, pre qapi */ CharDriverState *(*open)(QemuOpts *opts); -} backend_table[] = { - { .name = "null", .open = qemu_chr_open_null }, - { .name = "socket", .open = qemu_chr_open_socket }, - { .name = "udp", .open = qemu_chr_open_udp }, - { .name = "msmouse", .open = qemu_chr_open_msmouse }, - { .name = "vc", .open = text_console_init }, -#ifdef _WIN32 - { .name = "file", .open = qemu_chr_open_win_file_out }, - { .name = "pipe", .open = qemu_chr_open_win_pipe }, - { .name = "console", .open = qemu_chr_open_win_con }, - { .name = "serial", .open = qemu_chr_open_win }, - { .name = "stdio", .open = qemu_chr_open_win_stdio }, -#else - { .name = "file", .open = qemu_chr_open_file_out }, - { .name = "pipe", .open = qemu_chr_open_pipe }, - { .name = "pty", .open = qemu_chr_open_pty }, - { .name = "stdio", .open = qemu_chr_open_stdio }, -#endif -#ifdef CONFIG_BRLAPI - { .name = "braille", .open = chr_baum_init }, -#endif -#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ - || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ - || defined(__FreeBSD_kernel__) - { .name = "tty", .open = qemu_chr_open_tty }, -#endif -#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) \ - || defined(__FreeBSD_kernel__) - { .name = "parport", .open = qemu_chr_open_pp }, -#endif -#ifdef CONFIG_SPICE - { .name = "spicevmc", .open = qemu_chr_open_spice }, -#if SPICE_SERVER_VERSION >= 0x000c02 - { .name = "spiceport", .open = qemu_chr_open_spice_port }, -#endif -#endif -}; + /* new, qapi-based */ + int kind; + void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp); +} CharDriver; + +static GSList *backends; + +void register_char_driver(const char *name, CharDriverState *(*open)(QemuOpts *)) +{ + CharDriver *s; + + s = g_malloc0(sizeof(*s)); + s->name = g_strdup(name); + s->open = open; + + backends = g_slist_append(backends, s); +} + +void register_char_driver_qapi(const char *name, int kind, + void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp)) +{ + CharDriver *s; + + s = g_malloc0(sizeof(*s)); + s->name = g_strdup(name); + s->kind = kind; + s->parse = parse; + + backends = g_slist_append(backends, s); +} CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, - void (*init)(struct CharDriverState *s)) + void (*init)(struct CharDriverState *s), + Error **errp) { + CharDriver *cd; CharDriverState *chr; - int i; + GSList *i; if (qemu_opts_id(opts) == NULL) { - fprintf(stderr, "chardev: no id specified\n"); - return NULL; + error_setg(errp, "chardev: no id specified"); + goto err; } if (qemu_opt_get(opts, "backend") == NULL) { - fprintf(stderr, "chardev: \"%s\" missing backend\n", - qemu_opts_id(opts)); - return NULL; + error_setg(errp, "chardev: \"%s\" missing backend", + qemu_opts_id(opts)); + goto err; } - for (i = 0; i < ARRAY_SIZE(backend_table); i++) { - if (strcmp(backend_table[i].name, qemu_opt_get(opts, "backend")) == 0) + for (i = backends; i; i = i->next) { + cd = i->data; + + if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) { break; + } } - if (i == ARRAY_SIZE(backend_table)) { - fprintf(stderr, "chardev: backend \"%s\" not found\n", - qemu_opt_get(opts, "backend")); + if (i == NULL) { + error_setg(errp, "chardev: backend \"%s\" not found", + qemu_opt_get(opts, "backend")); return NULL; } - chr = backend_table[i].open(opts); + if (!cd->open) { + /* using new, qapi init */ + ChardevBackend *backend = g_new0(ChardevBackend, 1); + ChardevReturn *ret = NULL; + const char *id = qemu_opts_id(opts); + const char *bid = NULL; + + if (qemu_opt_get_bool(opts, "mux", 0)) { + bid = g_strdup_printf("%s-base", id); + } + + chr = NULL; + backend->kind = cd->kind; + if (cd->parse) { + cd->parse(opts, backend, errp); + if (error_is_set(errp)) { + goto qapi_out; + } + } + ret = qmp_chardev_add(bid ? bid : id, backend, errp); + if (error_is_set(errp)) { + goto qapi_out; + } + + if (bid) { + qapi_free_ChardevBackend(backend); + qapi_free_ChardevReturn(ret); + backend = g_new0(ChardevBackend, 1); + backend->mux = g_new0(ChardevMux, 1); + backend->kind = CHARDEV_BACKEND_KIND_MUX; + backend->mux->chardev = g_strdup(bid); + ret = qmp_chardev_add(id, backend, errp); + if (error_is_set(errp)) { + goto qapi_out; + } + } + + chr = qemu_chr_find(id); + + qapi_out: + qapi_free_ChardevBackend(backend); + qapi_free_ChardevReturn(ret); + return chr; + } + + chr = cd->open(opts); if (!chr) { - fprintf(stderr, "chardev: opening backend \"%s\" failed\n", - qemu_opt_get(opts, "backend")); - return NULL; + error_setg(errp, "chardev: opening backend \"%s\" failed", + qemu_opt_get(opts, "backend")); + goto err; } if (!chr->filename) @@ -2818,7 +3342,12 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, chr->avail_connections = 1; } chr->label = g_strdup(qemu_opts_id(opts)); + chr->opts = opts; return chr; + +err: + qemu_opts_del(opts); + return NULL; } CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s)) @@ -2826,6 +3355,7 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*in const char *p; CharDriverState *chr; QemuOpts *opts; + Error *err = NULL; if (strstart(filename, "chardev:", &p)) { return qemu_chr_find(p); @@ -2835,11 +3365,14 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*in if (!opts) return NULL; - chr = qemu_chr_new_from_opts(opts, init); + chr = qemu_chr_new_from_opts(opts, init, &err); + if (error_is_set(&err)) { + fprintf(stderr, "%s\n", error_get_pretty(err)); + error_free(err); + } if (chr && qemu_opt_get_bool(opts, "mux", 0)) { monitor_init(chr, MONITOR_USE_READLINE); } - qemu_opts_del(opts); return chr; } @@ -2864,13 +3397,35 @@ void qemu_chr_fe_close(struct CharDriverState *chr) } } +guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond, + GIOFunc func, void *user_data) +{ + GSource *src; + guint tag; + + if (s->chr_add_watch == NULL) { + return -ENOSYS; + } + + src = s->chr_add_watch(s, cond); + 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_delete(CharDriverState *chr) { QTAILQ_REMOVE(&chardevs, chr, next); - if (chr->chr_close) + if (chr->chr_close) { chr->chr_close(chr); + } g_free(chr->filename); g_free(chr->label); + if (chr->opts) { + qemu_opts_del(chr->opts); + } g_free(chr); } @@ -2913,3 +3468,361 @@ CharDriverState *qemu_char_get_next_serial(void) return serial_hds[next_serial++]; } +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 = "telnet", + .type = QEMU_OPT_BOOL, + },{ + .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, + }, + { /* end of list */ } + }, +}; + +#ifdef _WIN32 + +static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp) +{ + HANDLE out; + + if (file->in) { + error_setg(errp, "input file not supported"); + return NULL; + } + + out = CreateFile(file->out, GENERIC_WRITE, FILE_SHARE_READ, NULL, + OPEN_ALWAYS, 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); +} + +static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial, + Error **errp) +{ + return qemu_chr_open_win_path(serial->device); +} + +static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel, + Error **errp) +{ + error_setg(errp, "character device backend type 'parallel' not supported"); + return NULL; +} + +#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(errp, "open %s: %s", src, strerror(errno)); + } + return fd; +} + +static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp) +{ + int flags, in = -1, out = -1; + + flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY; + out = qmp_chardev_open_file_source(file->out, flags, errp); + if (error_is_set(errp)) { + return NULL; + } + + if (file->in) { + flags = O_RDONLY; + in = qmp_chardev_open_file_source(file->in, flags, errp); + if (error_is_set(errp)) { + qemu_close(out); + return NULL; + } + } + + return qemu_chr_open_fd(in, out); +} + +static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial, + Error **errp) +{ +#ifdef HAVE_CHARDEV_TTY + int fd; + + fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp); + if (error_is_set(errp)) { + return NULL; + } + socket_set_nonblock(fd); + return qemu_chr_open_tty_fd(fd); +#else + error_setg(errp, "character device backend type 'serial' not supported"); + return NULL; +#endif +} + +static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel, + Error **errp) +{ +#ifdef HAVE_CHARDEV_PARPORT + int fd; + + fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp); + if (error_is_set(errp)) { + return NULL; + } + return qemu_chr_open_pp_fd(fd); +#else + error_setg(errp, "character device backend type 'parallel' not supported"); + return NULL; +#endif +} + +#endif /* WIN32 */ + +static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, + Error **errp) +{ + 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; + int fd; + + if (is_listen) { + fd = socket_listen(addr, errp); + } else { + fd = socket_connect(addr, errp, NULL, NULL); + } + if (error_is_set(errp)) { + return NULL; + } + return qemu_chr_open_socket_fd(fd, do_nodelay, is_listen, + is_telnet, is_waitconnect, errp); +} + +static CharDriverState *qmp_chardev_open_dgram(ChardevDgram *dgram, + Error **errp) +{ + int fd; + + fd = socket_dgram(dgram->remote, dgram->local, errp); + if (error_is_set(errp)) { + return NULL; + } + return qemu_chr_open_udp_fd(fd); +} + +ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, + Error **errp) +{ + ChardevReturn *ret = g_new0(ChardevReturn, 1); + CharDriverState *base, *chr = NULL; + + chr = qemu_chr_find(id); + if (chr) { + error_setg(errp, "Chardev '%s' already exists", id); + g_free(ret); + return NULL; + } + + switch (backend->kind) { + case CHARDEV_BACKEND_KIND_FILE: + chr = qmp_chardev_open_file(backend->file, errp); + break; + case CHARDEV_BACKEND_KIND_SERIAL: + chr = qmp_chardev_open_serial(backend->serial, errp); + break; + case CHARDEV_BACKEND_KIND_PARALLEL: + chr = qmp_chardev_open_parallel(backend->parallel, errp); + break; + case CHARDEV_BACKEND_KIND_PIPE: + chr = qemu_chr_open_pipe(backend->pipe); + break; + case CHARDEV_BACKEND_KIND_SOCKET: + chr = qmp_chardev_open_socket(backend->socket, errp); + break; + case CHARDEV_BACKEND_KIND_DGRAM: + chr = qmp_chardev_open_dgram(backend->dgram, errp); + break; +#ifdef HAVE_CHARDEV_TTY + case CHARDEV_BACKEND_KIND_PTY: + chr = qemu_chr_open_pty(id, ret); + break; +#endif + case CHARDEV_BACKEND_KIND_NULL: + chr = qemu_chr_open_null(); + break; + case CHARDEV_BACKEND_KIND_MUX: + base = qemu_chr_find(backend->mux->chardev); + if (base == NULL) { + error_setg(errp, "mux: base chardev %s not found", + backend->mux->chardev); + break; + } + chr = qemu_chr_open_mux(base); + break; + case CHARDEV_BACKEND_KIND_MSMOUSE: + chr = qemu_chr_open_msmouse(); + break; +#ifdef CONFIG_BRLAPI + case CHARDEV_BACKEND_KIND_BRAILLE: + chr = chr_baum_init(); + break; +#endif + case CHARDEV_BACKEND_KIND_STDIO: + chr = qemu_chr_open_stdio(backend->stdio); + break; +#ifdef _WIN32 + case CHARDEV_BACKEND_KIND_CONSOLE: + chr = qemu_chr_open_win_con(); + break; +#endif +#ifdef CONFIG_SPICE + case CHARDEV_BACKEND_KIND_SPICEVMC: + chr = qemu_chr_open_spice_vmc(backend->spicevmc->type); + break; + case CHARDEV_BACKEND_KIND_SPICEPORT: + chr = qemu_chr_open_spice_port(backend->spiceport->fqdn); + break; +#endif + case CHARDEV_BACKEND_KIND_VC: + chr = vc_init(backend->vc); + break; + case CHARDEV_BACKEND_KIND_MEMORY: + chr = qemu_chr_open_ringbuf(backend->memory, errp); + break; + default: + error_setg(errp, "unknown chardev backend (%d)", backend->kind); + break; + } + + if (chr == NULL && !error_is_set(errp)) { + error_setg(errp, "Failed to create chardev"); + } + if (chr) { + chr->label = g_strdup(id); + chr->avail_connections = + (backend->kind == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1; + QTAILQ_INSERT_TAIL(&chardevs, chr, next); + return ret; + } else { + g_free(ret); + return NULL; + } +} + +void qmp_chardev_remove(const char *id, Error **errp) +{ + CharDriverState *chr; + + chr = qemu_chr_find(id); + if (NULL == chr) { + error_setg(errp, "Chardev '%s' not found", id); + return; + } + if (chr->chr_can_read || chr->chr_read || + chr->chr_event || chr->handler_opaque) { + error_setg(errp, "Chardev '%s' is busy", id); + return; + } + qemu_chr_delete(chr); +} + +static void register_types(void) +{ + register_char_driver_qapi("null", CHARDEV_BACKEND_KIND_NULL, NULL); + register_char_driver("socket", qemu_chr_open_socket); + register_char_driver("udp", qemu_chr_open_udp); + register_char_driver_qapi("memory", CHARDEV_BACKEND_KIND_MEMORY, + qemu_chr_parse_ringbuf); + register_char_driver_qapi("file", CHARDEV_BACKEND_KIND_FILE, + qemu_chr_parse_file_out); + register_char_driver_qapi("stdio", CHARDEV_BACKEND_KIND_STDIO, + qemu_chr_parse_stdio); + register_char_driver_qapi("serial", CHARDEV_BACKEND_KIND_SERIAL, + qemu_chr_parse_serial); + register_char_driver_qapi("tty", CHARDEV_BACKEND_KIND_SERIAL, + qemu_chr_parse_serial); + register_char_driver_qapi("parallel", CHARDEV_BACKEND_KIND_PARALLEL, + qemu_chr_parse_parallel); + register_char_driver_qapi("parport", CHARDEV_BACKEND_KIND_PARALLEL, + qemu_chr_parse_parallel); + register_char_driver_qapi("pty", CHARDEV_BACKEND_KIND_PTY, NULL); + register_char_driver_qapi("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL); + register_char_driver_qapi("pipe", CHARDEV_BACKEND_KIND_PIPE, + qemu_chr_parse_pipe); +} + +type_init(register_types); diff --git a/qemu-config.c b/qemu-config.c deleted file mode 100644 index 44f23ddd45..0000000000 --- a/qemu-config.c +++ /dev/null @@ -1,898 +0,0 @@ -#include "qemu-common.h" -#include "qemu/error-report.h" -#include "qemu/option.h" -#include "qemu/config-file.h" -#include "hw/qdev.h" -#include "qapi/error.h" - -static QemuOptsList qemu_drive_opts = { - .name = "drive", - .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head), - .desc = { - { - .name = "bus", - .type = QEMU_OPT_NUMBER, - .help = "bus number", - },{ - .name = "unit", - .type = QEMU_OPT_NUMBER, - .help = "unit number (i.e. lun for scsi)", - },{ - .name = "if", - .type = QEMU_OPT_STRING, - .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", - },{ - .name = "index", - .type = QEMU_OPT_NUMBER, - .help = "index number", - },{ - .name = "cyls", - .type = QEMU_OPT_NUMBER, - .help = "number of cylinders (ide disk geometry)", - },{ - .name = "heads", - .type = QEMU_OPT_NUMBER, - .help = "number of heads (ide disk geometry)", - },{ - .name = "secs", - .type = QEMU_OPT_NUMBER, - .help = "number of sectors (ide disk geometry)", - },{ - .name = "trans", - .type = QEMU_OPT_STRING, - .help = "chs translation (auto, lba. none)", - },{ - .name = "media", - .type = QEMU_OPT_STRING, - .help = "media type (disk, cdrom)", - },{ - .name = "snapshot", - .type = QEMU_OPT_BOOL, - .help = "enable/disable snapshot mode", - },{ - .name = "file", - .type = QEMU_OPT_STRING, - .help = "disk image", - },{ - .name = "cache", - .type = QEMU_OPT_STRING, - .help = "host cache usage (none, writeback, writethrough, " - "directsync, unsafe)", - },{ - .name = "aio", - .type = QEMU_OPT_STRING, - .help = "host AIO implementation (threads, native)", - },{ - .name = "format", - .type = QEMU_OPT_STRING, - .help = "disk format (raw, qcow2, ...)", - },{ - .name = "serial", - .type = QEMU_OPT_STRING, - .help = "disk serial number", - },{ - .name = "rerror", - .type = QEMU_OPT_STRING, - .help = "read error action", - },{ - .name = "werror", - .type = QEMU_OPT_STRING, - .help = "write error action", - },{ - .name = "addr", - .type = QEMU_OPT_STRING, - .help = "pci address (virtio only)", - },{ - .name = "readonly", - .type = QEMU_OPT_BOOL, - .help = "open drive file as read-only", - },{ - .name = "iops", - .type = QEMU_OPT_NUMBER, - .help = "limit total I/O operations per second", - },{ - .name = "iops_rd", - .type = QEMU_OPT_NUMBER, - .help = "limit read operations per second", - },{ - .name = "iops_wr", - .type = QEMU_OPT_NUMBER, - .help = "limit write operations per second", - },{ - .name = "bps", - .type = QEMU_OPT_NUMBER, - .help = "limit total bytes per second", - },{ - .name = "bps_rd", - .type = QEMU_OPT_NUMBER, - .help = "limit read bytes per second", - },{ - .name = "bps_wr", - .type = QEMU_OPT_NUMBER, - .help = "limit write bytes per second", - },{ - .name = "copy-on-read", - .type = QEMU_OPT_BOOL, - .help = "copy read data from backing file into image file", - },{ - .name = "locked", - .type = QEMU_OPT_BOOL, - .help = "emulate a security locked drive", - },{ - .name = "boot", - .type = QEMU_OPT_BOOL, - .help = "(deprecated, ignored)", - }, - { /* end of list */ } - }, -}; - -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 = "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", - }, - { /* end of list */ } - }, -}; - -static 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 = "telnet", - .type = QEMU_OPT_BOOL, - },{ - .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, - }, - { /* end of list */ } - }, -}; - -QemuOptsList qemu_fsdev_opts = { - .name = "fsdev", - .implied_opt_name = "fsdriver", - .head = QTAILQ_HEAD_INITIALIZER(qemu_fsdev_opts.head), - .desc = { - { - .name = "fsdriver", - .type = QEMU_OPT_STRING, - }, { - .name = "path", - .type = QEMU_OPT_STRING, - }, { - .name = "security_model", - .type = QEMU_OPT_STRING, - }, { - .name = "writeout", - .type = QEMU_OPT_STRING, - }, { - .name = "readonly", - .type = QEMU_OPT_BOOL, - - }, { - .name = "socket", - .type = QEMU_OPT_STRING, - }, { - .name = "sock_fd", - .type = QEMU_OPT_NUMBER, - }, - - { /*End of list */ } - }, -}; - -QemuOptsList qemu_virtfs_opts = { - .name = "virtfs", - .implied_opt_name = "fsdriver", - .head = QTAILQ_HEAD_INITIALIZER(qemu_virtfs_opts.head), - .desc = { - { - .name = "fsdriver", - .type = QEMU_OPT_STRING, - }, { - .name = "path", - .type = QEMU_OPT_STRING, - }, { - .name = "mount_tag", - .type = QEMU_OPT_STRING, - }, { - .name = "security_model", - .type = QEMU_OPT_STRING, - }, { - .name = "writeout", - .type = QEMU_OPT_STRING, - }, { - .name = "readonly", - .type = QEMU_OPT_BOOL, - }, { - .name = "socket", - .type = QEMU_OPT_STRING, - }, { - .name = "sock_fd", - .type = QEMU_OPT_NUMBER, - }, - - { /*End of list */ } - }, -}; - -static QemuOptsList qemu_device_opts = { - .name = "device", - .implied_opt_name = "driver", - .head = QTAILQ_HEAD_INITIALIZER(qemu_device_opts.head), - .desc = { - /* - * no elements => accept any - * sanity checking will happen later - * when setting device properties - */ - { /* end of list */ } - }, -}; - -static QemuOptsList qemu_netdev_opts = { - .name = "netdev", - .implied_opt_name = "type", - .head = QTAILQ_HEAD_INITIALIZER(qemu_netdev_opts.head), - .desc = { - /* - * no elements => accept any params - * validation will happen later - */ - { /* end of list */ } - }, -}; - -static QemuOptsList qemu_net_opts = { - .name = "net", - .implied_opt_name = "type", - .head = QTAILQ_HEAD_INITIALIZER(qemu_net_opts.head), - .desc = { - /* - * no elements => accept any params - * validation will happen later - */ - { /* end of list */ } - }, -}; - -static QemuOptsList qemu_rtc_opts = { - .name = "rtc", - .head = QTAILQ_HEAD_INITIALIZER(qemu_rtc_opts.head), - .desc = { - { - .name = "base", - .type = QEMU_OPT_STRING, - },{ - .name = "clock", - .type = QEMU_OPT_STRING, - },{ - .name = "driftfix", - .type = QEMU_OPT_STRING, - }, - { /* end of list */ } - }, -}; - -static QemuOptsList qemu_global_opts = { - .name = "global", - .head = QTAILQ_HEAD_INITIALIZER(qemu_global_opts.head), - .desc = { - { - .name = "driver", - .type = QEMU_OPT_STRING, - },{ - .name = "property", - .type = QEMU_OPT_STRING, - },{ - .name = "value", - .type = QEMU_OPT_STRING, - }, - { /* end of list */ } - }, -}; - -QemuOptsList qemu_sandbox_opts = { - .name = "sandbox", - .implied_opt_name = "enable", - .head = QTAILQ_HEAD_INITIALIZER(qemu_sandbox_opts.head), - .desc = { - { - .name = "enable", - .type = QEMU_OPT_BOOL, - }, - { /* end of list */ } - }, -}; - -static QemuOptsList qemu_mon_opts = { - .name = "mon", - .implied_opt_name = "chardev", - .head = QTAILQ_HEAD_INITIALIZER(qemu_mon_opts.head), - .desc = { - { - .name = "mode", - .type = QEMU_OPT_STRING, - },{ - .name = "chardev", - .type = QEMU_OPT_STRING, - },{ - .name = "default", - .type = QEMU_OPT_BOOL, - },{ - .name = "pretty", - .type = QEMU_OPT_BOOL, - }, - { /* end of list */ } - }, -}; - -static QemuOptsList qemu_trace_opts = { - .name = "trace", - .implied_opt_name = "trace", - .head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head), - .desc = { - { - .name = "events", - .type = QEMU_OPT_STRING, - },{ - .name = "file", - .type = QEMU_OPT_STRING, - }, - { /* end of list */ } - }, -}; - -QemuOptsList qemu_spice_opts = { - .name = "spice", - .head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head), - .desc = { - { - .name = "port", - .type = QEMU_OPT_NUMBER, - },{ - .name = "tls-port", - .type = QEMU_OPT_NUMBER, - },{ - .name = "addr", - .type = QEMU_OPT_STRING, - },{ - .name = "ipv4", - .type = QEMU_OPT_BOOL, - },{ - .name = "ipv6", - .type = QEMU_OPT_BOOL, - },{ - .name = "password", - .type = QEMU_OPT_STRING, - },{ - .name = "disable-ticketing", - .type = QEMU_OPT_BOOL, - },{ - .name = "disable-copy-paste", - .type = QEMU_OPT_BOOL, - },{ - .name = "sasl", - .type = QEMU_OPT_BOOL, - },{ - .name = "x509-dir", - .type = QEMU_OPT_STRING, - },{ - .name = "x509-key-file", - .type = QEMU_OPT_STRING, - },{ - .name = "x509-key-password", - .type = QEMU_OPT_STRING, - },{ - .name = "x509-cert-file", - .type = QEMU_OPT_STRING, - },{ - .name = "x509-cacert-file", - .type = QEMU_OPT_STRING, - },{ - .name = "x509-dh-key-file", - .type = QEMU_OPT_STRING, - },{ - .name = "tls-ciphers", - .type = QEMU_OPT_STRING, - },{ - .name = "tls-channel", - .type = QEMU_OPT_STRING, - },{ - .name = "plaintext-channel", - .type = QEMU_OPT_STRING, - },{ - .name = "image-compression", - .type = QEMU_OPT_STRING, - },{ - .name = "jpeg-wan-compression", - .type = QEMU_OPT_STRING, - },{ - .name = "zlib-glz-wan-compression", - .type = QEMU_OPT_STRING, - },{ - .name = "streaming-video", - .type = QEMU_OPT_STRING, - },{ - .name = "agent-mouse", - .type = QEMU_OPT_BOOL, - },{ - .name = "playback-compression", - .type = QEMU_OPT_BOOL, - }, { - .name = "seamless-migration", - .type = QEMU_OPT_BOOL, - }, - { /* end of list */ } - }, -}; - -QemuOptsList qemu_option_rom_opts = { - .name = "option-rom", - .implied_opt_name = "romfile", - .head = QTAILQ_HEAD_INITIALIZER(qemu_option_rom_opts.head), - .desc = { - { - .name = "bootindex", - .type = QEMU_OPT_NUMBER, - }, { - .name = "romfile", - .type = QEMU_OPT_STRING, - }, - { /* end of list */ } - }, -}; - -static QemuOptsList qemu_machine_opts = { - .name = "machine", - .implied_opt_name = "type", - .merge_lists = true, - .head = QTAILQ_HEAD_INITIALIZER(qemu_machine_opts.head), - .desc = { - { - .name = "type", - .type = QEMU_OPT_STRING, - .help = "emulated machine" - }, { - .name = "accel", - .type = QEMU_OPT_STRING, - .help = "accelerator list", - }, { - .name = "kernel_irqchip", - .type = QEMU_OPT_BOOL, - .help = "use KVM in-kernel irqchip", - }, { - .name = "kvm_shadow_mem", - .type = QEMU_OPT_SIZE, - .help = "KVM shadow MMU size", - }, { - .name = "kernel", - .type = QEMU_OPT_STRING, - .help = "Linux kernel image file", - }, { - .name = "initrd", - .type = QEMU_OPT_STRING, - .help = "Linux initial ramdisk file", - }, { - .name = "append", - .type = QEMU_OPT_STRING, - .help = "Linux kernel command line", - }, { - .name = "dtb", - .type = QEMU_OPT_STRING, - .help = "Linux kernel device tree file", - }, { - .name = "dumpdtb", - .type = QEMU_OPT_STRING, - .help = "Dump current dtb to a file and quit", - }, { - .name = "phandle_start", - .type = QEMU_OPT_STRING, - .help = "The first phandle ID we may generate dynamically", - }, { - .name = "dt_compatible", - .type = QEMU_OPT_STRING, - .help = "Overrides the \"compatible\" property of the dt root node", - }, { - .name = "dump-guest-core", - .type = QEMU_OPT_BOOL, - .help = "Include guest memory in a core dump", - }, { - .name = "mem-merge", - .type = QEMU_OPT_BOOL, - .help = "enable/disable memory merge support", - },{ - .name = "usb", - .type = QEMU_OPT_BOOL, - .help = "Set on/off to enable/disable usb", - }, { - .name = "nvram", - .type = QEMU_OPT_STRING, - .help = "Drive backing persistent NVRAM", - }, - { /* End of list */ } - }, -}; - -QemuOptsList qemu_boot_opts = { - .name = "boot-opts", - .head = QTAILQ_HEAD_INITIALIZER(qemu_boot_opts.head), - .desc = { - /* the three names below are not used now */ - { - .name = "order", - .type = QEMU_OPT_STRING, - }, { - .name = "once", - .type = QEMU_OPT_STRING, - }, { - .name = "menu", - .type = QEMU_OPT_STRING, - /* following are really used */ - }, { - .name = "splash", - .type = QEMU_OPT_STRING, - }, { - .name = "splash-time", - .type = QEMU_OPT_STRING, - }, { - .name = "reboot-timeout", - .type = QEMU_OPT_STRING, - }, - { /*End of list */ } - }, -}; - -static QemuOptsList qemu_add_fd_opts = { - .name = "add-fd", - .head = QTAILQ_HEAD_INITIALIZER(qemu_add_fd_opts.head), - .desc = { - { - .name = "fd", - .type = QEMU_OPT_NUMBER, - .help = "file descriptor of which a duplicate is added to fd set", - },{ - .name = "set", - .type = QEMU_OPT_NUMBER, - .help = "ID of the fd set to add fd to", - },{ - .name = "opaque", - .type = QEMU_OPT_STRING, - .help = "free-form string used to describe fd", - }, - { /* end of list */ } - }, -}; - -static QemuOptsList qemu_object_opts = { - .name = "object", - .implied_opt_name = "qom-type", - .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head), - .desc = { - { } - }, -}; - -static QemuOptsList *vm_config_groups[32] = { - &qemu_drive_opts, - &qemu_chardev_opts, - &qemu_device_opts, - &qemu_netdev_opts, - &qemu_net_opts, - &qemu_rtc_opts, - &qemu_global_opts, - &qemu_mon_opts, - &qemu_trace_opts, - &qemu_option_rom_opts, - &qemu_machine_opts, - &qemu_boot_opts, - &qemu_iscsi_opts, - &qemu_sandbox_opts, - &qemu_add_fd_opts, - &qemu_object_opts, - NULL, -}; - -static QemuOptsList *find_list(QemuOptsList **lists, const char *group, - Error **errp) -{ - int i; - - for (i = 0; lists[i] != NULL; i++) { - if (strcmp(lists[i]->name, group) == 0) - break; - } - if (lists[i] == NULL) { - error_set(errp, QERR_INVALID_OPTION_GROUP, group); - } - return lists[i]; -} - -QemuOptsList *qemu_find_opts(const char *group) -{ - QemuOptsList *ret; - Error *local_err = NULL; - - ret = find_list(vm_config_groups, group, &local_err); - if (error_is_set(&local_err)) { - error_report("%s\n", error_get_pretty(local_err)); - error_free(local_err); - } - - return ret; -} - -QemuOptsList *qemu_find_opts_err(const char *group, Error **errp) -{ - return find_list(vm_config_groups, group, errp); -} - -void qemu_add_opts(QemuOptsList *list) -{ - int entries, i; - - entries = ARRAY_SIZE(vm_config_groups); - entries--; /* keep list NULL terminated */ - for (i = 0; i < entries; i++) { - if (vm_config_groups[i] == NULL) { - vm_config_groups[i] = list; - return; - } - } - fprintf(stderr, "ran out of space in vm_config_groups"); - abort(); -} - -int qemu_set_option(const char *str) -{ - char group[64], id[64], arg[64]; - QemuOptsList *list; - QemuOpts *opts; - int rc, offset; - - rc = sscanf(str, "%63[^.].%63[^.].%63[^=]%n", group, id, arg, &offset); - if (rc < 3 || str[offset] != '=') { - error_report("can't parse: \"%s\"", str); - return -1; - } - - list = qemu_find_opts(group); - if (list == NULL) { - return -1; - } - - opts = qemu_opts_find(list, id); - if (!opts) { - error_report("there is no %s \"%s\" defined", - list->name, id); - return -1; - } - - if (qemu_opt_set(opts, arg, str+offset+1) == -1) { - return -1; - } - return 0; -} - -int qemu_global_option(const char *str) -{ - char driver[64], property[64]; - QemuOpts *opts; - int rc, offset; - - rc = sscanf(str, "%63[^.].%63[^=]%n", driver, property, &offset); - if (rc < 2 || str[offset] != '=') { - error_report("can't parse: \"%s\"", str); - return -1; - } - - opts = qemu_opts_create_nofail(&qemu_global_opts); - qemu_opt_set(opts, "driver", driver); - qemu_opt_set(opts, "property", property); - qemu_opt_set(opts, "value", str+offset+1); - return 0; -} - -struct ConfigWriteData { - QemuOptsList *list; - FILE *fp; -}; - -static int config_write_opt(const char *name, const char *value, void *opaque) -{ - struct ConfigWriteData *data = opaque; - - fprintf(data->fp, " %s = \"%s\"\n", name, value); - return 0; -} - -static int config_write_opts(QemuOpts *opts, void *opaque) -{ - struct ConfigWriteData *data = opaque; - const char *id = qemu_opts_id(opts); - - if (id) { - fprintf(data->fp, "[%s \"%s\"]\n", data->list->name, id); - } else { - fprintf(data->fp, "[%s]\n", data->list->name); - } - qemu_opt_foreach(opts, config_write_opt, data, 0); - fprintf(data->fp, "\n"); - return 0; -} - -void qemu_config_write(FILE *fp) -{ - struct ConfigWriteData data = { .fp = fp }; - QemuOptsList **lists = vm_config_groups; - int i; - - fprintf(fp, "# qemu config file\n\n"); - for (i = 0; lists[i] != NULL; i++) { - data.list = lists[i]; - qemu_opts_foreach(data.list, config_write_opts, &data, 0); - } -} - -int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname) -{ - char line[1024], group[64], id[64], arg[64], value[1024]; - Location loc; - QemuOptsList *list = NULL; - Error *local_err = NULL; - QemuOpts *opts = NULL; - int res = -1, lno = 0; - - loc_push_none(&loc); - while (fgets(line, sizeof(line), fp) != NULL) { - loc_set_file(fname, ++lno); - if (line[0] == '\n') { - /* skip empty lines */ - continue; - } - if (line[0] == '#') { - /* comment */ - continue; - } - if (sscanf(line, "[%63s \"%63[^\"]\"]", group, id) == 2) { - /* group with id */ - list = find_list(lists, group, &local_err); - if (error_is_set(&local_err)) { - error_report("%s\n", error_get_pretty(local_err)); - error_free(local_err); - goto out; - } - opts = qemu_opts_create(list, id, 1, NULL); - continue; - } - if (sscanf(line, "[%63[^]]]", group) == 1) { - /* group without id */ - list = find_list(lists, group, &local_err); - if (error_is_set(&local_err)) { - error_report("%s\n", error_get_pretty(local_err)); - error_free(local_err); - goto out; - } - opts = qemu_opts_create_nofail(list); - continue; - } - if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2) { - /* arg = value */ - if (opts == NULL) { - error_report("no group defined"); - goto out; - } - if (qemu_opt_set(opts, arg, value) != 0) { - goto out; - } - continue; - } - error_report("parse error"); - goto out; - } - if (ferror(fp)) { - error_report("error reading file"); - goto out; - } - res = 0; -out: - loc_pop(&loc); - return res; -} - -int qemu_read_config_file(const char *filename) -{ - FILE *f = fopen(filename, "r"); - int ret; - - if (f == NULL) { - return -errno; - } - - ret = qemu_config_parse(f, vm_config_groups, filename); - fclose(f); - - if (ret == 0) { - return 0; - } else { - return -EINVAL; - } -} diff --git a/qemu-coroutine-lock.c b/qemu-coroutine-lock.c index 97ef01c796..86efe1f86a 100644 --- a/qemu-coroutine-lock.c +++ b/qemu-coroutine-lock.c @@ -29,28 +29,36 @@ #include "block/aio.h" #include "trace.h" -static QTAILQ_HEAD(, Coroutine) unlock_bh_queue = - QTAILQ_HEAD_INITIALIZER(unlock_bh_queue); -static QEMUBH* unlock_bh; +/* Coroutines are awoken from a BH to allow the current coroutine to complete + * its flow of execution. The BH may run after the CoQueue has been destroyed, + * so keep BH data in a separate heap-allocated struct. + */ +typedef struct { + QEMUBH *bh; + QTAILQ_HEAD(, Coroutine) entries; +} CoQueueNextData; static void qemu_co_queue_next_bh(void *opaque) { + CoQueueNextData *data = opaque; Coroutine *next; trace_qemu_co_queue_next_bh(); - while ((next = QTAILQ_FIRST(&unlock_bh_queue))) { - QTAILQ_REMOVE(&unlock_bh_queue, next, co_queue_next); + while ((next = QTAILQ_FIRST(&data->entries))) { + QTAILQ_REMOVE(&data->entries, next, co_queue_next); qemu_coroutine_enter(next, NULL); } + + qemu_bh_delete(data->bh); + g_slice_free(CoQueueNextData, data); } void qemu_co_queue_init(CoQueue *queue) { QTAILQ_INIT(&queue->entries); - if (!unlock_bh) { - unlock_bh = qemu_bh_new(qemu_co_queue_next_bh, NULL); - } + /* This will be exposed to callers once there are multiple AioContexts */ + queue->ctx = qemu_get_aio_context(); } void coroutine_fn qemu_co_queue_wait(CoQueue *queue) @@ -69,26 +77,39 @@ void coroutine_fn qemu_co_queue_wait_insert_head(CoQueue *queue) assert(qemu_in_coroutine()); } -bool qemu_co_queue_next(CoQueue *queue) +static bool qemu_co_queue_do_restart(CoQueue *queue, bool single) { Coroutine *next; + CoQueueNextData *data; - next = QTAILQ_FIRST(&queue->entries); - if (next) { - QTAILQ_REMOVE(&queue->entries, next, co_queue_next); - QTAILQ_INSERT_TAIL(&unlock_bh_queue, next, co_queue_next); - trace_qemu_co_queue_next(next); - qemu_bh_schedule(unlock_bh); + if (QTAILQ_EMPTY(&queue->entries)) { + return false; } - return (next != NULL); + data = g_slice_new(CoQueueNextData); + data->bh = aio_bh_new(queue->ctx, qemu_co_queue_next_bh, data); + QTAILQ_INIT(&data->entries); + qemu_bh_schedule(data->bh); + + while ((next = QTAILQ_FIRST(&queue->entries)) != NULL) { + QTAILQ_REMOVE(&queue->entries, next, co_queue_next); + QTAILQ_INSERT_TAIL(&data->entries, next, co_queue_next); + trace_qemu_co_queue_next(next); + if (single) { + break; + } + } + return true; +} + +bool qemu_co_queue_next(CoQueue *queue) +{ + return qemu_co_queue_do_restart(queue, true); } void qemu_co_queue_restart_all(CoQueue *queue) { - while (qemu_co_queue_next(queue)) { - /* Do nothing */ - } + qemu_co_queue_do_restart(queue, false); } bool qemu_co_queue_empty(CoQueue *queue) diff --git a/qemu-coroutine.c b/qemu-coroutine.c index 0f6e268574..25a14c605d 100644 --- a/qemu-coroutine.c +++ b/qemu-coroutine.c @@ -17,13 +17,54 @@ #include "block/coroutine.h" #include "block/coroutine_int.h" +enum { + /* Maximum free pool size prevents holding too many freed coroutines */ + POOL_MAX_SIZE = 64, +}; + +/** Free list to speed up creation */ +static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool); +static unsigned int pool_size; + Coroutine *qemu_coroutine_create(CoroutineEntry *entry) { - Coroutine *co = qemu_coroutine_new(); + Coroutine *co; + + co = QSLIST_FIRST(&pool); + if (co) { + QSLIST_REMOVE_HEAD(&pool, pool_next); + pool_size--; + } else { + co = qemu_coroutine_new(); + } + co->entry = entry; return co; } +static void coroutine_delete(Coroutine *co) +{ + if (pool_size < POOL_MAX_SIZE) { + QSLIST_INSERT_HEAD(&pool, co, pool_next); + co->caller = NULL; + pool_size++; + return; + } + + qemu_coroutine_delete(co); +} + +static void __attribute__((destructor)) coroutine_cleanup(void) +{ + Coroutine *co; + Coroutine *tmp; + + QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) { + QSLIST_REMOVE_HEAD(&pool, pool_next); + qemu_coroutine_delete(co); + } +} + static void coroutine_swap(Coroutine *from, Coroutine *to) { CoroutineAction ret; @@ -35,7 +76,7 @@ static void coroutine_swap(Coroutine *from, Coroutine *to) return; case COROUTINE_TERMINATE: trace_qemu_coroutine_terminate(to); - qemu_coroutine_delete(to); + coroutine_delete(to); return; default: abort(); diff --git a/qemu-doc.texi b/qemu-doc.texi index 6d7f50d832..af84bef0e9 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -830,7 +830,7 @@ QEMU-based virtual machines. You can create a Sheepdog disk image with the command: @example -qemu-img create sheepdog:@var{image} @var{size} +qemu-img create sheepdog:///@var{image} @var{size} @end example where @var{image} is the Sheepdog image name and @var{size} is its size. @@ -838,38 +838,44 @@ size. To import the existing @var{filename} to Sheepdog, you can use a convert command. @example -qemu-img convert @var{filename} sheepdog:@var{image} +qemu-img convert @var{filename} sheepdog:///@var{image} @end example You can boot from the Sheepdog disk image with the command: @example -qemu-system-i386 sheepdog:@var{image} +qemu-system-i386 sheepdog:///@var{image} @end example You can also create a snapshot of the Sheepdog image like qcow2. @example -qemu-img snapshot -c @var{tag} sheepdog:@var{image} +qemu-img snapshot -c @var{tag} sheepdog:///@var{image} @end example where @var{tag} is a tag name of the newly created snapshot. To boot from the Sheepdog snapshot, specify the tag name of the snapshot. @example -qemu-system-i386 sheepdog:@var{image}:@var{tag} +qemu-system-i386 sheepdog:///@var{image}#@var{tag} @end example You can create a cloned image from the existing snapshot. @example -qemu-img create -b sheepdog:@var{base}:@var{tag} sheepdog:@var{image} +qemu-img create -b sheepdog:///@var{base}#@var{tag} sheepdog:///@var{image} @end example where @var{base} is a image name of the source snapshot and @var{tag} is its tag name. +You can use an unix socket instead of an inet socket: + +@example +qemu-system-i386 sheepdog+unix:///@var{image}?socket=@var{path} +@end example + If the Sheepdog daemon doesn't run on the local host, you need to specify one of the Sheepdog servers to connect to. @example -qemu-img create sheepdog:@var{hostname}:@var{port}:@var{image} @var{size} -qemu-system-i386 sheepdog:@var{hostname}:@var{port}:@var{image} +qemu-img create sheepdog://@var{hostname}:@var{port}/@var{image} @var{size} +qemu-system-i386 sheepdog://@var{hostname}:@var{port}/@var{image} @end example @node disk_images_iscsi @@ -2642,8 +2648,8 @@ Pre-allocate a guest virtual address space of the given size (in bytes). Debug options: @table @option -@item -d -Activate log (logfile=/tmp/qemu.log) +@item -d item1,... +Activate logging of the specified items (use '-d help' for a list of log items) @item -p pagesize Act as if the host page size was 'pagesize' bytes @item -g port @@ -2781,8 +2787,8 @@ FreeBSD, NetBSD and OpenBSD (default). Debug options: @table @option -@item -d -Activate log (logfile=/tmp/qemu.log) +@item -d item1,... +Activate logging of the specified items (use '-d help' for a list of log items) @item -p pagesize Act as if the host page size was 'pagesize' bytes @item -singlestep diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index a18136302d..4ca7e95655 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -10,27 +10,33 @@ STEXI ETEXI DEF("check", img_check, - "check [-f fmt] [-r [leaks | all]] filename") + "check [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] filename") STEXI -@item check [-f @var{fmt}] [-r [leaks | all]] @var{filename} +@item check [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] @var{filename} ETEXI DEF("create", img_create, - "create [-f fmt] [-o options] filename [size]") + "create [-q] [-f fmt] [-o options] filename [size]") STEXI -@item create [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}] +@item create [-q] [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}] ETEXI DEF("commit", img_commit, - "commit [-f fmt] [-t cache] filename") + "commit [-q] [-f fmt] [-t cache] filename") STEXI -@item commit [-f @var{fmt}] [-t @var{cache}] @var{filename} +@item commit [-q] [-f @var{fmt}] [-t @var{cache}] @var{filename} +ETEXI + +DEF("compare", img_compare, + "compare [-f fmt] [-F fmt] [-p] [-q] [-s] filename1 filename2") +STEXI +@item compare [-f @var{fmt}] [-F @var{fmt}] [-p] [-q] [-s] @var{filename1} @var{filename2} ETEXI DEF("convert", img_convert, - "convert [-c] [-p] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] [-S sparse_size] filename [filename2 [...]] output_filename") + "convert [-c] [-p] [-q] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] [-S sparse_size] filename [filename2 [...]] output_filename") STEXI -@item convert [-c] [-p] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [-c] [-p] [-q] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI DEF("info", img_info, @@ -40,20 +46,20 @@ STEXI ETEXI DEF("snapshot", img_snapshot, - "snapshot [-l | -a snapshot | -c snapshot | -d snapshot] filename") + "snapshot [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename") STEXI -@item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename} +@item snapshot [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename} ETEXI DEF("rebase", img_rebase, - "rebase [-f fmt] [-t cache] [-p] [-u] -b backing_file [-F backing_fmt] filename") + "rebase [-q] [-f fmt] [-t cache] [-p] [-u] -b backing_file [-F backing_fmt] filename") STEXI -@item rebase [-f @var{fmt}] [-t @var{cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} +@item rebase [-q] [-f @var{fmt}] [-t @var{cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} ETEXI DEF("resize", img_resize, - "resize filename [+ | -]size") + "resize [-q] filename [+ | -]size") STEXI -@item resize @var{filename} [+ | -]@var{size} +@item resize [-q] @var{filename} [+ | -]@var{size} @end table ETEXI diff --git a/qemu-img.c b/qemu-img.c index 69cc02871b..31627b0da8 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -32,6 +32,7 @@ #include "block/block_int.h" #include #include +#include #ifdef _WIN32 #include @@ -42,6 +43,16 @@ typedef struct img_cmd_t { int (*handler)(int argc, char **argv); } img_cmd_t; +enum { + OPTION_OUTPUT = 256, + OPTION_BACKING_CHAIN = 257, +}; + +typedef enum OutputFormat { + OFORMAT_JSON, + OFORMAT_HUMAN, +} OutputFormat; + /* Default to cache=writeback as data integrity is not important for qemu-tcg. */ #define BDRV_O_FLAGS BDRV_O_CACHE_WB #define BDRV_DEFAULT_CACHE "writeback" @@ -86,6 +97,7 @@ static void help(void) " rebasing in this case (useful for renaming the backing file)\n" " '-h' with or without a command shows this help and lists the supported formats\n" " '-p' show progress of command (only certain commands)\n" + " '-q' use Quiet mode - do not print any output (except errors)\n" " '-S' indicates the consecutive number of bytes that must contain only zeros\n" " for qemu-img to create a sparse image during conversion\n" " '--output' takes the format in which the output must be done (human or json)\n" @@ -101,7 +113,12 @@ static void help(void) " '-a' applies a snapshot (revert disk to saved state)\n" " '-c' creates a snapshot\n" " '-d' deletes a snapshot\n" - " '-l' lists all snapshots in the given image\n"; + " '-l' lists all snapshots in the given image\n" + "\n" + "Parameters to compare subcommand:\n" + " '-f' first image format\n" + " '-F' second image format\n" + " '-s' run in Strict mode - fail on different image size or sector allocation\n"; printf("%s\nSupported formats:", help_msg); bdrv_iterate_format(format_print, NULL); @@ -109,6 +126,18 @@ static void help(void) exit(1); } +static int qprintf(bool quiet, const char *fmt, ...) +{ + int ret = 0; + if (!quiet) { + va_list args; + va_start(args, fmt); + ret = vprintf(fmt, args); + va_end(args); + } + return ret; +} + #if defined(WIN32) /* XXX: put correct support for win32 */ static int read_password(char *buf, int buf_size) @@ -227,7 +256,8 @@ static int print_block_option_help(const char *filename, const char *fmt) static BlockDriverState *bdrv_new_open(const char *filename, const char *fmt, int flags, - bool require_io) + bool require_io, + bool quiet) { BlockDriverState *bs; BlockDriver *drv; @@ -246,14 +276,14 @@ static BlockDriverState *bdrv_new_open(const char *filename, drv = NULL; } - ret = bdrv_open(bs, filename, flags, drv); + ret = bdrv_open(bs, filename, NULL, flags, drv); if (ret < 0) { error_report("Could not open '%s': %s", filename, strerror(-ret)); goto fail; } if (bdrv_is_encrypted(bs) && require_io) { - printf("Disk image '%s' is encrypted.\n", filename); + qprintf(quiet, "Disk image '%s' is encrypted.\n", filename); if (read_password(password, sizeof(password)) < 0) { error_report("No password given"); goto fail; @@ -302,9 +332,10 @@ static int img_create(int argc, char **argv) const char *base_filename = NULL; char *options = NULL; Error *local_err = NULL; + bool quiet = false; for(;;) { - c = getopt(argc, argv, "F:b:f:he6o:"); + c = getopt(argc, argv, "F:b:f:he6o:q"); if (c == -1) { break; } @@ -333,6 +364,9 @@ static int img_create(int argc, char **argv) case 'o': options = optarg; break; + case 'q': + quiet = true; + break; } } @@ -348,9 +382,13 @@ static int img_create(int argc, char **argv) char *end; sval = strtosz_suffix(argv[optind++], &end, STRTOSZ_DEFSUFFIX_B); if (sval < 0 || *end) { - error_report("Invalid image size specified! You may use k, M, G or " - "T suffixes for "); - error_report("kilobytes, megabytes, gigabytes and terabytes."); + if (sval == -ERANGE) { + error_report("Image size must be less than 8 EiB!"); + } else { + error_report("Invalid image size specified! You may use k, M, " + "G or T suffixes for "); + error_report("kilobytes, megabytes, gigabytes and terabytes."); + } return 1; } img_size = (uint64_t)sval; @@ -361,7 +399,7 @@ static int img_create(int argc, char **argv) } bdrv_img_create(filename, fmt, base_filename, base_fmt, - options, img_size, BDRV_O_FLAGS, &local_err); + options, img_size, BDRV_O_FLAGS, &local_err, quiet); if (error_is_set(&local_err)) { error_report("%s", error_get_pretty(local_err)); error_free(local_err); @@ -371,6 +409,105 @@ static int img_create(int argc, char **argv) return 0; } +static void dump_json_image_check(ImageCheck *check, bool quiet) +{ + Error *errp = NULL; + QString *str; + QmpOutputVisitor *ov = qmp_output_visitor_new(); + QObject *obj; + visit_type_ImageCheck(qmp_output_get_visitor(ov), + &check, NULL, &errp); + obj = qmp_output_get_qobject(ov); + str = qobject_to_json_pretty(obj); + assert(str != NULL); + qprintf(quiet, "%s\n", qstring_get_str(str)); + qobject_decref(obj); + qmp_output_visitor_cleanup(ov); + QDECREF(str); +} + +static void dump_human_image_check(ImageCheck *check, bool quiet) +{ + if (!(check->corruptions || check->leaks || check->check_errors)) { + qprintf(quiet, "No errors were found on the image.\n"); + } else { + if (check->corruptions) { + qprintf(quiet, "\n%" PRId64 " errors were found on the image.\n" + "Data may be corrupted, or further writes to the image " + "may corrupt it.\n", + check->corruptions); + } + + if (check->leaks) { + qprintf(quiet, + "\n%" PRId64 " leaked clusters were found on the image.\n" + "This means waste of disk space, but no harm to data.\n", + check->leaks); + } + + if (check->check_errors) { + qprintf(quiet, + "\n%" PRId64 + " internal errors have occurred during the check.\n", + check->check_errors); + } + } + + if (check->total_clusters != 0 && check->allocated_clusters != 0) { + qprintf(quiet, "%" PRId64 "/%" PRId64 " = %0.2f%% allocated, " + "%0.2f%% fragmented, %0.2f%% compressed clusters\n", + check->allocated_clusters, check->total_clusters, + check->allocated_clusters * 100.0 / check->total_clusters, + check->fragmented_clusters * 100.0 / check->allocated_clusters, + check->compressed_clusters * 100.0 / + check->allocated_clusters); + } + + if (check->image_end_offset) { + qprintf(quiet, + "Image end offset: %" PRId64 "\n", check->image_end_offset); + } +} + +static int collect_image_check(BlockDriverState *bs, + ImageCheck *check, + const char *filename, + const char *fmt, + int fix) +{ + int ret; + BdrvCheckResult result; + + ret = bdrv_check(bs, &result, fix); + if (ret < 0) { + return ret; + } + + check->filename = g_strdup(filename); + check->format = g_strdup(bdrv_get_format_name(bs)); + check->check_errors = result.check_errors; + check->corruptions = result.corruptions; + check->has_corruptions = result.corruptions != 0; + check->leaks = result.leaks; + check->has_leaks = result.leaks != 0; + check->corruptions_fixed = result.corruptions_fixed; + check->has_corruptions_fixed = result.corruptions != 0; + check->leaks_fixed = result.leaks_fixed; + check->has_leaks_fixed = result.leaks != 0; + check->image_end_offset = result.image_end_offset; + check->has_image_end_offset = result.image_end_offset != 0; + check->total_clusters = result.bfi.total_clusters; + check->has_total_clusters = result.bfi.total_clusters != 0; + check->allocated_clusters = result.bfi.allocated_clusters; + check->has_allocated_clusters = result.bfi.allocated_clusters != 0; + check->fragmented_clusters = result.bfi.fragmented_clusters; + check->has_fragmented_clusters = result.bfi.fragmented_clusters != 0; + check->compressed_clusters = result.bfi.compressed_clusters; + check->has_compressed_clusters = result.bfi.compressed_clusters != 0; + + return 0; +} + /* * Checks an image for consistency. Exit codes: * @@ -382,15 +519,27 @@ static int img_create(int argc, char **argv) static int img_check(int argc, char **argv) { int c, ret; - const char *filename, *fmt; + OutputFormat output_format = OFORMAT_HUMAN; + const char *filename, *fmt, *output; BlockDriverState *bs; - BdrvCheckResult result; int fix = 0; int flags = BDRV_O_FLAGS | BDRV_O_CHECK; + ImageCheck *check; + bool quiet = false; fmt = NULL; + output = NULL; for(;;) { - c = getopt(argc, argv, "f:hr:"); + int option_index = 0; + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"format", required_argument, 0, 'f'}, + {"repair", no_argument, 0, 'r'}, + {"output", required_argument, 0, OPTION_OUTPUT}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "f:hr:q", + long_options, &option_index); if (c == -1) { break; } @@ -413,6 +562,12 @@ static int img_check(int argc, char **argv) help(); } break; + case OPTION_OUTPUT: + output = optarg; + break; + case 'q': + quiet = true; + break; } } if (optind >= argc) { @@ -420,73 +575,80 @@ static int img_check(int argc, char **argv) } filename = argv[optind++]; - bs = bdrv_new_open(filename, fmt, flags, true); + if (output && !strcmp(output, "json")) { + output_format = OFORMAT_JSON; + } else if (output && !strcmp(output, "human")) { + output_format = OFORMAT_HUMAN; + } else if (output) { + error_report("--output must be used with human or json as argument."); + return 1; + } + + bs = bdrv_new_open(filename, fmt, flags, true, quiet); if (!bs) { return 1; } - ret = bdrv_check(bs, &result, fix); + + check = g_new0(ImageCheck, 1); + ret = collect_image_check(bs, check, filename, fmt, fix); if (ret == -ENOTSUP) { - error_report("This image format does not support checks"); - bdrv_delete(bs); - return 1; + if (output_format == OFORMAT_HUMAN) { + error_report("This image format does not support checks"); + } + ret = 1; + goto fail; } - if (result.corruptions_fixed || result.leaks_fixed) { - printf("The following inconsistencies were found and repaired:\n\n" - " %d leaked clusters\n" - " %d corruptions\n\n" - "Double checking the fixed image now...\n", - result.leaks_fixed, - result.corruptions_fixed); - ret = bdrv_check(bs, &result, 0); + if (check->corruptions_fixed || check->leaks_fixed) { + int corruptions_fixed, leaks_fixed; + + leaks_fixed = check->leaks_fixed; + corruptions_fixed = check->corruptions_fixed; + + if (output_format == OFORMAT_HUMAN) { + qprintf(quiet, + "The following inconsistencies were found and repaired:\n\n" + " %" PRId64 " leaked clusters\n" + " %" PRId64 " corruptions\n\n" + "Double checking the fixed image now...\n", + check->leaks_fixed, + check->corruptions_fixed); + } + + ret = collect_image_check(bs, check, filename, fmt, 0); + + check->leaks_fixed = leaks_fixed; + check->corruptions_fixed = corruptions_fixed; } - if (!(result.corruptions || result.leaks || result.check_errors)) { - printf("No errors were found on the image.\n"); + switch (output_format) { + case OFORMAT_HUMAN: + dump_human_image_check(check, quiet); + break; + case OFORMAT_JSON: + dump_json_image_check(check, quiet); + break; + } + + if (ret || check->check_errors) { + ret = 1; + goto fail; + } + + if (check->corruptions) { + ret = 2; + } else if (check->leaks) { + ret = 3; } else { - if (result.corruptions) { - printf("\n%d errors were found on the image.\n" - "Data may be corrupted, or further writes to the image " - "may corrupt it.\n", - result.corruptions); - } - - if (result.leaks) { - printf("\n%d leaked clusters were found on the image.\n" - "This means waste of disk space, but no harm to data.\n", - result.leaks); - } - - if (result.check_errors) { - printf("\n%d internal errors have occurred during the check.\n", - result.check_errors); - } - } - - if (result.bfi.total_clusters != 0 && result.bfi.allocated_clusters != 0) { - printf("%" PRId64 "/%" PRId64 "= %0.2f%% allocated, %0.2f%% fragmented\n", - result.bfi.allocated_clusters, result.bfi.total_clusters, - result.bfi.allocated_clusters * 100.0 / result.bfi.total_clusters, - result.bfi.fragmented_clusters * 100.0 / result.bfi.allocated_clusters); + ret = 0; } +fail: + qapi_free_ImageCheck(check); bdrv_delete(bs); - if (ret < 0 || result.check_errors) { - printf("\nAn error has occurred during the check: %s\n" - "The check is not complete and may have missed error.\n", - strerror(-ret)); - return 1; - } - - if (result.corruptions) { - return 2; - } else if (result.leaks) { - return 3; - } else { - return 0; - } + return ret; } static int img_commit(int argc, char **argv) @@ -494,11 +656,12 @@ static int img_commit(int argc, char **argv) int c, ret, flags; const char *filename, *fmt, *cache; BlockDriverState *bs; + bool quiet = false; fmt = NULL; cache = BDRV_DEFAULT_CACHE; for(;;) { - c = getopt(argc, argv, "f:ht:"); + c = getopt(argc, argv, "f:ht:q"); if (c == -1) { break; } @@ -513,6 +676,9 @@ static int img_commit(int argc, char **argv) case 't': cache = optarg; break; + case 'q': + quiet = true; + break; } } if (optind >= argc) { @@ -527,14 +693,14 @@ static int img_commit(int argc, char **argv) return -1; } - bs = bdrv_new_open(filename, fmt, flags, true); + bs = bdrv_new_open(filename, fmt, flags, true, quiet); if (!bs) { return 1; } ret = bdrv_commit(bs); switch(ret) { case 0: - printf("Image committed.\n"); + qprintf(quiet, "Image committed.\n"); break; case -ENOENT: error_report("No disk inserted"); @@ -659,6 +825,289 @@ static int compare_sectors(const uint8_t *buf1, const uint8_t *buf2, int n, #define IO_BUF_SIZE (2 * 1024 * 1024) +static int64_t sectors_to_bytes(int64_t sectors) +{ + return sectors << BDRV_SECTOR_BITS; +} + +static int64_t sectors_to_process(int64_t total, int64_t from) +{ + return MIN(total - from, IO_BUF_SIZE >> BDRV_SECTOR_BITS); +} + +/* + * Check if passed sectors are empty (not allocated or contain only 0 bytes) + * + * Returns 0 in case sectors are filled with 0, 1 if sectors contain non-zero + * data and negative value on error. + * + * @param bs: Driver used for accessing file + * @param sect_num: Number of first sector to check + * @param sect_count: Number of sectors to check + * @param filename: Name of disk file we are checking (logging purpose) + * @param buffer: Allocated buffer for storing read data + * @param quiet: Flag for quiet mode + */ +static int check_empty_sectors(BlockDriverState *bs, int64_t sect_num, + int sect_count, const char *filename, + uint8_t *buffer, bool quiet) +{ + int pnum, ret = 0; + ret = bdrv_read(bs, sect_num, buffer, sect_count); + if (ret < 0) { + error_report("Error while reading offset %" PRId64 " of %s: %s", + sectors_to_bytes(sect_num), filename, strerror(-ret)); + return ret; + } + ret = is_allocated_sectors(buffer, sect_count, &pnum); + if (ret || pnum != sect_count) { + qprintf(quiet, "Content mismatch at offset %" PRId64 "!\n", + sectors_to_bytes(ret ? sect_num : sect_num + pnum)); + return 1; + } + + return 0; +} + +/* + * Compares two images. Exit codes: + * + * 0 - Images are identical + * 1 - Images differ + * >1 - Error occurred + */ +static int img_compare(int argc, char **argv) +{ + const char *fmt1 = NULL, *fmt2 = NULL, *filename1, *filename2; + BlockDriverState *bs1, *bs2; + int64_t total_sectors1, total_sectors2; + uint8_t *buf1 = NULL, *buf2 = NULL; + int pnum1, pnum2; + int allocated1, allocated2; + int ret = 0; /* return value - 0 Ident, 1 Different, >1 Error */ + bool progress = false, quiet = false, strict = false; + int64_t total_sectors; + int64_t sector_num = 0; + int64_t nb_sectors; + int c, pnum; + uint64_t bs_sectors; + uint64_t progress_base; + + for (;;) { + c = getopt(argc, argv, "hpf:F:sq"); + if (c == -1) { + break; + } + switch (c) { + case '?': + case 'h': + help(); + break; + case 'f': + fmt1 = optarg; + break; + case 'F': + fmt2 = optarg; + break; + case 'p': + progress = true; + break; + case 'q': + quiet = true; + break; + case 's': + strict = true; + break; + } + } + + /* Progress is not shown in Quiet mode */ + if (quiet) { + progress = false; + } + + + if (optind > argc - 2) { + help(); + } + filename1 = argv[optind++]; + filename2 = argv[optind++]; + + /* Initialize before goto out */ + qemu_progress_init(progress, 2.0); + + bs1 = bdrv_new_open(filename1, fmt1, BDRV_O_FLAGS, true, quiet); + if (!bs1) { + error_report("Can't open file %s", filename1); + ret = 2; + goto out3; + } + + bs2 = bdrv_new_open(filename2, fmt2, BDRV_O_FLAGS, true, quiet); + if (!bs2) { + error_report("Can't open file %s", filename2); + ret = 2; + goto out2; + } + + buf1 = qemu_blockalign(bs1, IO_BUF_SIZE); + buf2 = qemu_blockalign(bs2, IO_BUF_SIZE); + bdrv_get_geometry(bs1, &bs_sectors); + total_sectors1 = bs_sectors; + bdrv_get_geometry(bs2, &bs_sectors); + total_sectors2 = bs_sectors; + total_sectors = MIN(total_sectors1, total_sectors2); + progress_base = MAX(total_sectors1, total_sectors2); + + qemu_progress_print(0, 100); + + if (strict && total_sectors1 != total_sectors2) { + ret = 1; + qprintf(quiet, "Strict mode: Image size mismatch!\n"); + goto out; + } + + for (;;) { + nb_sectors = sectors_to_process(total_sectors, sector_num); + if (nb_sectors <= 0) { + break; + } + allocated1 = bdrv_is_allocated_above(bs1, NULL, sector_num, nb_sectors, + &pnum1); + if (allocated1 < 0) { + ret = 3; + error_report("Sector allocation test failed for %s", filename1); + goto out; + } + + allocated2 = bdrv_is_allocated_above(bs2, NULL, sector_num, nb_sectors, + &pnum2); + if (allocated2 < 0) { + ret = 3; + error_report("Sector allocation test failed for %s", filename2); + goto out; + } + nb_sectors = MIN(pnum1, pnum2); + + if (allocated1 == allocated2) { + if (allocated1) { + ret = bdrv_read(bs1, sector_num, buf1, nb_sectors); + if (ret < 0) { + error_report("Error while reading offset %" PRId64 " of %s:" + " %s", sectors_to_bytes(sector_num), filename1, + strerror(-ret)); + ret = 4; + goto out; + } + ret = bdrv_read(bs2, sector_num, buf2, nb_sectors); + if (ret < 0) { + error_report("Error while reading offset %" PRId64 + " of %s: %s", sectors_to_bytes(sector_num), + filename2, strerror(-ret)); + ret = 4; + goto out; + } + ret = compare_sectors(buf1, buf2, nb_sectors, &pnum); + if (ret || pnum != nb_sectors) { + ret = 1; + qprintf(quiet, "Content mismatch at offset %" PRId64 "!\n", + sectors_to_bytes( + ret ? sector_num : sector_num + pnum)); + goto out; + } + } + } else { + if (strict) { + ret = 1; + qprintf(quiet, "Strict mode: Offset %" PRId64 + " allocation mismatch!\n", + sectors_to_bytes(sector_num)); + goto out; + } + + if (allocated1) { + ret = check_empty_sectors(bs1, sector_num, nb_sectors, + filename1, buf1, quiet); + } else { + ret = check_empty_sectors(bs2, sector_num, nb_sectors, + filename2, buf1, quiet); + } + if (ret) { + if (ret < 0) { + ret = 4; + error_report("Error while reading offset %" PRId64 ": %s", + sectors_to_bytes(sector_num), strerror(-ret)); + } + goto out; + } + } + sector_num += nb_sectors; + qemu_progress_print(((float) nb_sectors / progress_base)*100, 100); + } + + if (total_sectors1 != total_sectors2) { + BlockDriverState *bs_over; + int64_t total_sectors_over; + const char *filename_over; + + qprintf(quiet, "Warning: Image size mismatch!\n"); + if (total_sectors1 > total_sectors2) { + total_sectors_over = total_sectors1; + bs_over = bs1; + filename_over = filename1; + } else { + total_sectors_over = total_sectors2; + bs_over = bs2; + filename_over = filename2; + } + + for (;;) { + nb_sectors = sectors_to_process(total_sectors_over, sector_num); + if (nb_sectors <= 0) { + break; + } + ret = bdrv_is_allocated_above(bs_over, NULL, sector_num, + nb_sectors, &pnum); + if (ret < 0) { + ret = 3; + error_report("Sector allocation test failed for %s", + filename_over); + goto out; + + } + nb_sectors = pnum; + if (ret) { + ret = check_empty_sectors(bs_over, sector_num, nb_sectors, + filename_over, buf1, quiet); + if (ret) { + if (ret < 0) { + ret = 4; + error_report("Error while reading offset %" PRId64 + " of %s: %s", sectors_to_bytes(sector_num), + filename_over, strerror(-ret)); + } + goto out; + } + } + sector_num += nb_sectors; + qemu_progress_print(((float) nb_sectors / progress_base)*100, 100); + } + } + + qprintf(quiet, "Images are identical.\n"); + ret = 0; + +out: + bdrv_delete(bs2); + qemu_vfree(buf1); + qemu_vfree(buf2); +out2: + bdrv_delete(bs1); +out3: + qemu_progress_end(); + return ret; +} + static int img_convert(int argc, char **argv) { int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size, cluster_sectors; @@ -677,6 +1126,7 @@ static int img_convert(int argc, char **argv) const char *snapshot_name = NULL; float local_progress = 0; int min_sparse = 8; /* Need at least 4k of zeros for sparse detection */ + bool quiet = false; fmt = NULL; out_fmt = "raw"; @@ -684,7 +1134,7 @@ static int img_convert(int argc, char **argv) out_baseimg = NULL; compress = 0; for(;;) { - c = getopt(argc, argv, "f:O:B:s:hce6o:pS:t:"); + c = getopt(argc, argv, "f:O:B:s:hce6o:pS:t:q"); if (c == -1) { break; } @@ -738,9 +1188,16 @@ static int img_convert(int argc, char **argv) case 't': cache = optarg; break; + case 'q': + quiet = true; + break; } } + if (quiet) { + progress = 0; + } + bs_n = argc - optind - 1; if (bs_n < 1) { help(); @@ -769,7 +1226,8 @@ static int img_convert(int argc, char **argv) total_sectors = 0; for (bs_i = 0; bs_i < bs_n; bs_i++) { - bs[bs_i] = bdrv_new_open(argv[optind + bs_i], fmt, BDRV_O_FLAGS, true); + bs[bs_i] = bdrv_new_open(argv[optind + bs_i], fmt, BDRV_O_FLAGS, true, + quiet); if (!bs[bs_i]) { error_report("Could not open '%s'", argv[optind + bs_i]); ret = -1; @@ -888,7 +1346,7 @@ static int img_convert(int argc, char **argv) return -1; } - out_bs = bdrv_new_open(out_filename, out_fmt, flags, true); + out_bs = bdrv_new_open(out_filename, out_fmt, flags, true, quiet); if (!out_bs) { ret = -1; goto out; @@ -1351,7 +1809,7 @@ static ImageInfoList *collect_image_info_list(const char *filename, g_hash_table_insert(filenames, (gpointer)filename, NULL); bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_NO_BACKING, - false); + false, false); if (!bs) { goto err; } @@ -1388,16 +1846,6 @@ err: return NULL; } -enum { - OPTION_OUTPUT = 256, - OPTION_BACKING_CHAIN = 257, -}; - -typedef enum OutputFormat { - OFORMAT_JSON, - OFORMAT_HUMAN, -} OutputFormat; - static int img_info(int argc, char **argv) { int c; @@ -1487,11 +1935,12 @@ static int img_snapshot(int argc, char **argv) int c, ret = 0, bdrv_oflags; int action = 0; qemu_timeval tv; + bool quiet = false; bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR; /* Parse commandline parameters */ for(;;) { - c = getopt(argc, argv, "la:c:d:h"); + c = getopt(argc, argv, "la:c:d:hq"); if (c == -1) { break; } @@ -1532,6 +1981,9 @@ static int img_snapshot(int argc, char **argv) action = SNAPSHOT_DELETE; snapshot_name = optarg; break; + case 'q': + quiet = true; + break; } } @@ -1541,7 +1993,7 @@ static int img_snapshot(int argc, char **argv) filename = argv[optind++]; /* Open the image */ - bs = bdrv_new_open(filename, NULL, bdrv_oflags, true); + bs = bdrv_new_open(filename, NULL, bdrv_oflags, true, quiet); if (!bs) { return 1; } @@ -1601,6 +2053,7 @@ static int img_rebase(int argc, char **argv) int c, flags, ret; int unsafe = 0; int progress = 0; + bool quiet = false; /* Parse commandline parameters */ fmt = NULL; @@ -1608,7 +2061,7 @@ static int img_rebase(int argc, char **argv) out_baseimg = NULL; out_basefmt = NULL; for(;;) { - c = getopt(argc, argv, "uhf:F:b:pt:"); + c = getopt(argc, argv, "uhf:F:b:pt:q"); if (c == -1) { break; } @@ -1635,9 +2088,16 @@ static int img_rebase(int argc, char **argv) case 't': cache = optarg; break; + case 'q': + quiet = true; + break; } } + if (quiet) { + progress = 0; + } + if ((optind >= argc) || (!unsafe && !out_baseimg)) { help(); } @@ -1659,7 +2119,7 @@ static int img_rebase(int argc, char **argv) * Ignore the old backing file for unsafe rebase in case we want to correct * the reference to a renamed or moved backing file. */ - bs = bdrv_new_open(filename, fmt, flags, true); + bs = bdrv_new_open(filename, fmt, flags, true, quiet); if (!bs) { return 1; } @@ -1696,7 +2156,7 @@ static int img_rebase(int argc, char **argv) bs_old_backing = bdrv_new("old_backing"); bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); - ret = bdrv_open(bs_old_backing, backing_name, BDRV_O_FLAGS, + ret = bdrv_open(bs_old_backing, backing_name, NULL, BDRV_O_FLAGS, old_backing_drv); if (ret) { error_report("Could not open old backing file '%s'", backing_name); @@ -1704,7 +2164,7 @@ static int img_rebase(int argc, char **argv) } if (out_baseimg[0]) { bs_new_backing = bdrv_new("new_backing"); - ret = bdrv_open(bs_new_backing, out_baseimg, BDRV_O_FLAGS, + ret = bdrv_open(bs_new_backing, out_baseimg, NULL, BDRV_O_FLAGS, new_backing_drv); if (ret) { error_report("Could not open new backing file '%s'", @@ -1871,6 +2331,7 @@ static int img_resize(int argc, char **argv) int c, ret, relative; const char *filename, *fmt, *size; int64_t n, total_size; + bool quiet = false; BlockDriverState *bs = NULL; QemuOpts *param; static QemuOptsList resize_options = { @@ -1899,7 +2360,7 @@ static int img_resize(int argc, char **argv) /* Parse getopt arguments */ fmt = NULL; for(;;) { - c = getopt(argc, argv, "f:h"); + c = getopt(argc, argv, "f:hq"); if (c == -1) { break; } @@ -1911,6 +2372,9 @@ static int img_resize(int argc, char **argv) case 'f': fmt = optarg; break; + case 'q': + quiet = true; + break; } } if (optind >= argc) { @@ -1944,7 +2408,7 @@ static int img_resize(int argc, char **argv) n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0); qemu_opts_del(param); - bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, true); + bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, true, quiet); if (!bs) { ret = -1; goto out; @@ -1964,7 +2428,7 @@ static int img_resize(int argc, char **argv) ret = bdrv_truncate(bs, total_size); switch (ret) { case 0: - printf("Image resized.\n"); + qprintf(quiet, "Image resized.\n"); break; case -ENOTSUP: error_report("This image does not support resize"); diff --git a/qemu-img.texi b/qemu-img.texi index 00fca8da86..69f1bda6ae 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -54,6 +54,9 @@ indicates that target image must be compressed (qcow format only) with or without a command shows help and lists the supported formats @item -p display progress bar (convert and rebase commands only) +@item -q +Quiet mode - do not print any output (except errors). There's no progress bar +in case both @var{-q} and @var{-p} options are used. @item -S @var{size} indicates the consecutive number of bytes that must contain only zeros for qemu-img to create a sparse image during conversion. This value is rounded @@ -81,12 +84,25 @@ deletes a snapshot lists all snapshots in the given image @end table +Parameters to compare subcommand: + +@table @option + +@item -f +First image format +@item -F +Second image format +@item -s +Strict mode - fail on on different image size or sector allocation +@end table + Command description: @table @option -@item check [-f @var{fmt}] [-r [leaks | all]] @var{filename} +@item check [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] @var{filename} -Perform a consistency check on the disk image @var{filename}. +Perform a consistency check on the disk image @var{filename}. The command can +output in the format @var{ofmt} which is either @code{human} or @code{json}. If @code{-r} is specified, qemu-img tries to repair any inconsistencies found during the check. @code{-r leaks} repairs only cluster leaks, whereas @@ -114,6 +130,47 @@ it doesn't need to be specified separately in this case. Commit the changes recorded in @var{filename} in its base image. +@item compare [-f @var{fmt}] [-F @var{fmt}] [-p] [-s] [-q] @var{filename1} @var{filename2} + +Check if two images have the same content. You can compare images with +different format or settings. + +The format is probed unless you specify it by @var{-f} (used for +@var{filename1}) and/or @var{-F} (used for @var{filename2}) option. + +By default, images with different size are considered identical if the larger +image contains only unallocated and/or zeroed sectors in the area after the end +of the other image. In addition, if any sector is not allocated in one image +and contains only zero bytes in the second one, it is evaluated as equal. You +can use Strict mode by specifying the @var{-s} option. When compare runs in +Strict mode, it fails in case image size differs or a sector is allocated in +one image and is not allocated in the second one. + +By default, compare prints out a result message. This message displays +information that both images are same or the position of the first different +byte. In addition, result message can report different image size in case +Strict mode is used. + +Compare exits with @code{0} in case the images are equal and with @code{1} +in case the images differ. Other exit codes mean an error occurred during +execution and standard error output should contain an error message. +The following table sumarizes all exit codes of the compare subcommand: + +@table @option + +@item 0 +Images are identical +@item 1 +Images differ +@item 2 +Error on opening an image +@item 3 +Error on checking a sector allocation +@item 4 +Error on reading data + +@end table + @item convert [-c] [-p] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename} diff --git a/qemu-io.c b/qemu-io.c index 61880932b3..79be516953 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -1773,7 +1773,7 @@ static int openfile(char *name, int flags, int growable) } else { bs = bdrv_new("hda"); - if (bdrv_open(bs, name, flags, NULL) < 0) { + if (bdrv_open(bs, name, NULL, flags, NULL) < 0) { fprintf(stderr, "%s: can't open device %s\n", progname, name); bdrv_delete(bs); bs = NULL; @@ -1899,7 +1899,7 @@ int main(int argc, char **argv) { int readonly = 0; int growable = 0; - const char *sopt = "hVc:rsnmgkt:T:"; + const char *sopt = "hVc:d:rsnmgkt:T:"; const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, @@ -1911,13 +1911,14 @@ int main(int argc, char **argv) { "misalign", 0, NULL, 'm' }, { "growable", 0, NULL, 'g' }, { "native-aio", 0, NULL, 'k' }, + { "discard", 1, NULL, 'd' }, { "cache", 1, NULL, 't' }, { "trace", 1, NULL, 'T' }, { NULL, 0, NULL, 0 } }; int c; int opt_index = 0; - int flags = 0; + int flags = BDRV_O_UNMAP; progname = basename(argv[0]); @@ -1929,6 +1930,12 @@ int main(int argc, char **argv) case 'n': flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB; break; + case 'd': + if (bdrv_parse_discard_flags(optarg, &flags) < 0) { + error_report("Invalid discard option: %s", optarg); + exit(1); + } + break; case 'c': add_user_command(optarg); break; diff --git a/qemu-log.c b/qemu-log.c index b655b305ea..797f2af983 100644 --- a/qemu-log.c +++ b/qemu-log.c @@ -20,11 +20,7 @@ #include "qemu-common.h" #include "qemu/log.h" -#ifdef WIN32 -static const char *logfilename = "qemu.log"; -#else -static const char *logfilename = "/tmp/qemu.log"; -#endif +static char *logfilename; FILE *qemu_logfile; int qemu_loglevel; static int log_append = 0; @@ -52,14 +48,19 @@ void qemu_log_mask(int mask, const char *fmt, ...) } /* enable or disable low levels log */ -void qemu_set_log(int log_flags, bool use_own_buffers) +void do_qemu_set_log(int log_flags, bool use_own_buffers) { qemu_loglevel = log_flags; if (qemu_loglevel && !qemu_logfile) { - qemu_logfile = fopen(logfilename, log_append ? "a" : "w"); - if (!qemu_logfile) { - perror(logfilename); - _exit(1); + if (logfilename) { + qemu_logfile = fopen(logfilename, log_append ? "a" : "w"); + if (!qemu_logfile) { + perror(logfilename); + _exit(1); + } + } else { + /* Default to stderr if no log file specified */ + qemu_logfile = stderr; } /* must avoid mmap() usage of glibc by setting a buffer "by hand" */ if (use_own_buffers) { @@ -77,22 +78,19 @@ void qemu_set_log(int log_flags, bool use_own_buffers) } } if (!qemu_loglevel && qemu_logfile) { - fclose(qemu_logfile); - qemu_logfile = NULL; + qemu_log_close(); } } -void cpu_set_log_filename(const char *filename) +void qemu_set_log_filename(const char *filename) { - logfilename = strdup(filename); - if (qemu_logfile) { - fclose(qemu_logfile); - qemu_logfile = NULL; - } - cpu_set_log(qemu_loglevel); + g_free(logfilename); + logfilename = g_strdup(filename); + qemu_log_close(); + qemu_set_log(qemu_loglevel); } -const CPULogItem cpu_log_items[] = { +const QEMULogItem qemu_log_items[] = { { CPU_LOG_TB_OUT_ASM, "out_asm", "show generated host assembly code for each compiled TB" }, { CPU_LOG_TB_IN_ASM, "in_asm", @@ -131,9 +129,9 @@ static int cmp1(const char *s1, int n, const char *s2) } /* takes a comma separated list of log masks. Return 0 if error. */ -int cpu_str_to_log_mask(const char *str) +int qemu_str_to_log_mask(const char *str) { - const CPULogItem *item; + const QEMULogItem *item; int mask; const char *p, *p1; @@ -145,11 +143,11 @@ int cpu_str_to_log_mask(const char *str) p1 = p + strlen(p); } if (cmp1(p,p1-p,"all")) { - for (item = cpu_log_items; item->mask != 0; item++) { + for (item = qemu_log_items; item->mask != 0; item++) { mask |= item->mask; } } else { - for (item = cpu_log_items; item->mask != 0; item++) { + for (item = qemu_log_items; item->mask != 0; item++) { if (cmp1(p, p1 - p, item->name)) { goto found; } @@ -165,3 +163,12 @@ int cpu_str_to_log_mask(const char *str) } return mask; } + +void qemu_print_log_usage(FILE *f) +{ + const QEMULogItem *item; + fprintf(f, "Log items (comma separated):\n"); + for (item = qemu_log_items; item->mask != 0; item++) { + fprintf(f, "%-10s %s\n", item->name, item->help); + } +} diff --git a/qemu-nbd.c b/qemu-nbd.c index 0a6091b6a8..ca722ed425 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -33,9 +33,10 @@ #include #include -#define SOCKET_PATH "/var/lock/qemu-nbd-%s" -#define QEMU_NBD_OPT_CACHE 1 -#define QEMU_NBD_OPT_AIO 2 +#define SOCKET_PATH "/var/lock/qemu-nbd-%s" +#define QEMU_NBD_OPT_CACHE 1 +#define QEMU_NBD_OPT_AIO 2 +#define QEMU_NBD_OPT_DISCARD 3 static NBDExport *exp; static int verbose; @@ -330,6 +331,7 @@ int main(int argc, char **argv) #ifdef CONFIG_LINUX_AIO { "aio", 1, NULL, QEMU_NBD_OPT_AIO }, #endif + { "discard", 1, NULL, QEMU_NBD_OPT_DISCARD }, { "shared", 1, NULL, 'e' }, { "persistent", 0, NULL, 't' }, { "verbose", 0, NULL, 'v' }, @@ -344,6 +346,7 @@ int main(int argc, char **argv) int ret; int fd; bool seen_cache = false; + bool seen_discard = false; #ifdef CONFIG_LINUX_AIO bool seen_aio = false; #endif @@ -389,6 +392,15 @@ int main(int argc, char **argv) } break; #endif + case QEMU_NBD_OPT_DISCARD: + if (seen_discard) { + errx(EXIT_FAILURE, "--discard can only be specified once"); + } + seen_discard = true; + if (bdrv_parse_discard_flags(optarg, &flags) == -1) { + errx(EXIT_FAILURE, "Invalid discard mode `%s'", optarg); + } + break; case 'b': bindto = optarg; break; @@ -545,7 +557,7 @@ int main(int argc, char **argv) bs = bdrv_new("hda"); srcpath = argv[optind]; - if ((ret = bdrv_open(bs, srcpath, flags, NULL)) < 0) { + if ((ret = bdrv_open(bs, srcpath, NULL, flags, NULL)) < 0) { errno = -ret; err(EXIT_FAILURE, "Failed to bdrv_open '%s'", argv[optind]); } diff --git a/qemu-nbd.texi b/qemu-nbd.texi index 6955d90327..5f3f3e3276 100644 --- a/qemu-nbd.texi +++ b/qemu-nbd.texi @@ -29,7 +29,16 @@ Export QEMU disk image using NBD protocol. @item -s, --snapshot use snapshot file @item -n, --nocache - disable host cache +@itemx --cache=@var{cache} + set cache mode to be used with the file. See the documentation of + the emulator's @code{-drive cache=...} option for allowed values. +@item --aio=@var{aio} + choose asynchronous I/O mode between @samp{threads} (the default) + and @samp{native} (Linux only). +@item --discard=@var{discard} + toggles whether @dfn{discard} (also known as @dfn{trim} or @dfn{unmap}) + requests are ignored or passed to the filesystem. The default is no + (@samp{--discard=ignore}). @item -c, --connect=@var{dev} connect @var{filename} to NBD device @var{dev} @item -d, --disconnect diff --git a/qemu-options.hx b/qemu-options.hx index 9df0cde64c..30fb85d619 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -103,6 +103,261 @@ Simulate a multi node NUMA system. If mem and cpus are omitted, resources are split equally. ETEXI +DEF("add-fd", HAS_ARG, QEMU_OPTION_add_fd, + "-add-fd fd=fd,set=set[,opaque=opaque]\n" + " Add 'fd' to fd 'set'\n", QEMU_ARCH_ALL) +STEXI +@item -add-fd fd=@var{fd},set=@var{set}[,opaque=@var{opaque}] +@findex -add-fd + +Add a file descriptor to an fd set. Valid options are: + +@table @option +@item fd=@var{fd} +This option defines the file descriptor of which a duplicate is added to fd set. +The file descriptor cannot be stdin, stdout, or stderr. +@item set=@var{set} +This option defines the ID of the fd set to add the file descriptor to. +@item opaque=@var{opaque} +This option defines a free-form string that can be used to describe @var{fd}. +@end table + +You can open an image using pre-opened file descriptors from an fd set: +@example +qemu-system-i386 +-add-fd fd=3,set=2,opaque="rdwr:/path/to/file" +-add-fd fd=4,set=2,opaque="rdonly:/path/to/file" +-drive file=/dev/fdset/2,index=0,media=disk +@end example +ETEXI + +DEF("set", HAS_ARG, QEMU_OPTION_set, + "-set group.id.arg=value\n" + " set parameter for item of type \n" + " i.e. -set drive.$id.file=/path/to/image\n", QEMU_ARCH_ALL) +STEXI +@item -set @var{group}.@var{id}.@var{arg}=@var{value} +@findex -set +Set parameter @var{arg} for item @var{id} of type @var{group}\n" +ETEXI + +DEF("global", HAS_ARG, QEMU_OPTION_global, + "-global driver.prop=value\n" + " set a global default for a driver property\n", + QEMU_ARCH_ALL) +STEXI +@item -global @var{driver}.@var{prop}=@var{value} +@findex -global +Set default value of @var{driver}'s property @var{prop} to @var{value}, e.g.: + +@example +qemu-system-i386 -global ide-drive.physical_block_size=4096 -drive file=file,if=ide,index=0,media=disk +@end example + +In particular, you can use this to set driver properties for devices which are +created automatically by the machine model. To create a device which is not +created automatically and set properties on it, use -@option{device}. +ETEXI + +DEF("boot", HAS_ARG, QEMU_OPTION_boot, + "-boot [order=drives][,once=drives][,menu=on|off]\n" + " [,splash=sp_name][,splash-time=sp_time][,reboot-timeout=rb_time]\n" + " 'drives': floppy (a), hard disk (c), CD-ROM (d), network (n)\n" + " 'sp_name': the file's name that would be passed to bios as logo picture, if menu=on\n" + " 'sp_time': the period that splash picture last if menu=on, unit is ms\n" + " 'rb_timeout': the timeout before guest reboot when boot failed, unit is ms\n", + QEMU_ARCH_ALL) +STEXI +@item -boot [order=@var{drives}][,once=@var{drives}][,menu=on|off][,splash=@var{sp_name}][,splash-time=@var{sp_time}][,reboot-timeout=@var{rb_timeout}] +@findex -boot +Specify boot order @var{drives} as a string of drive letters. Valid +drive letters depend on the target achitecture. The x86 PC uses: a, b +(floppy 1 and 2), c (first hard disk), d (first CD-ROM), n-p (Etherboot +from network adapter 1-4), hard disk boot is the default. To apply a +particular boot order only on the first startup, specify it via +@option{once}. + +Interactive boot menus/prompts can be enabled via @option{menu=on} as far +as firmware/BIOS supports them. The default is non-interactive boot. + +A splash picture could be passed to bios, enabling user to show it as logo, +when option splash=@var{sp_name} is given and menu=on, If firmware/BIOS +supports them. Currently Seabios for X86 system support it. +limitation: The splash file could be a jpeg file or a BMP file in 24 BPP +format(true color). The resolution should be supported by the SVGA mode, so +the recommended is 320x240, 640x480, 800x640. + +A timeout could be passed to bios, guest will pause for @var{rb_timeout} ms +when boot failed, then reboot. If @var{rb_timeout} is '-1', guest will not +reboot, qemu passes '-1' to bios by default. Currently Seabios for X86 +system support it. + +@example +# try to boot from network first, then from hard disk +qemu-system-i386 -boot order=nc +# boot from CD-ROM first, switch back to default order after reboot +qemu-system-i386 -boot once=d +# boot with a splash picture for 5 seconds. +qemu-system-i386 -boot menu=on,splash=/root/boot.bmp,splash-time=5000 +@end example + +Note: The legacy format '-boot @var{drives}' is still supported but its +use is discouraged as it may be removed from future versions. +ETEXI + +DEF("m", HAS_ARG, QEMU_OPTION_m, + "-m megs set virtual RAM size to megs MB [default=" + stringify(DEFAULT_RAM_SIZE) "]\n", QEMU_ARCH_ALL) +STEXI +@item -m @var{megs} +@findex -m +Set virtual RAM size to @var{megs} megabytes. Default is 128 MiB. Optionally, +a suffix of ``M'' or ``G'' can be used to signify a value in megabytes or +gigabytes respectively. +ETEXI + +DEF("mem-path", HAS_ARG, QEMU_OPTION_mempath, + "-mem-path FILE provide backing storage for guest RAM\n", QEMU_ARCH_ALL) +STEXI +@item -mem-path @var{path} +@findex -mem-path +Allocate guest RAM from a temporarily created file in @var{path}. +ETEXI + +#ifdef MAP_POPULATE +DEF("mem-prealloc", 0, QEMU_OPTION_mem_prealloc, + "-mem-prealloc preallocate guest memory (use with -mem-path)\n", + QEMU_ARCH_ALL) +STEXI +@item -mem-prealloc +@findex -mem-prealloc +Preallocate memory when using -mem-path. +ETEXI +#endif + +DEF("k", HAS_ARG, QEMU_OPTION_k, + "-k language use keyboard layout (for example 'fr' for French)\n", + QEMU_ARCH_ALL) +STEXI +@item -k @var{language} +@findex -k +Use keyboard layout @var{language} (for example @code{fr} for +French). This option is only needed where it is not easy to get raw PC +keycodes (e.g. on Macs, with some X11 servers or with a VNC +display). You don't normally need to use it on PC/Linux or PC/Windows +hosts. + +The available layouts are: +@example +ar de-ch es fo fr-ca hu ja mk no pt-br sv +da en-gb et fr fr-ch is lt nl pl ru th +de en-us fi fr-be hr it lv nl-be pt sl tr +@end example + +The default is @code{en-us}. +ETEXI + + +DEF("audio-help", 0, QEMU_OPTION_audio_help, + "-audio-help print list of audio drivers and their options\n", + QEMU_ARCH_ALL) +STEXI +@item -audio-help +@findex -audio-help +Will show the audio subsystem help: list of drivers, tunable +parameters. +ETEXI + +DEF("soundhw", HAS_ARG, QEMU_OPTION_soundhw, + "-soundhw c1,... enable audio support\n" + " and only specified sound cards (comma separated list)\n" + " use '-soundhw help' to get the list of supported cards\n" + " use '-soundhw all' to enable all of them\n", QEMU_ARCH_ALL) +STEXI +@item -soundhw @var{card1}[,@var{card2},...] or -soundhw all +@findex -soundhw +Enable audio and selected sound hardware. Use 'help' to print all +available sound hardware. + +@example +qemu-system-i386 -soundhw sb16,adlib disk.img +qemu-system-i386 -soundhw es1370 disk.img +qemu-system-i386 -soundhw ac97 disk.img +qemu-system-i386 -soundhw hda disk.img +qemu-system-i386 -soundhw all disk.img +qemu-system-i386 -soundhw help +@end example + +Note that Linux's i810_audio OSS kernel (for AC97) module might +require manually specifying clocking. + +@example +modprobe i810_audio clocking=48000 +@end example +ETEXI + +DEF("balloon", HAS_ARG, QEMU_OPTION_balloon, + "-balloon none disable balloon device\n" + "-balloon virtio[,addr=str]\n" + " enable virtio balloon device (default)\n", QEMU_ARCH_ALL) +STEXI +@item -balloon none +@findex -balloon +Disable balloon device. +@item -balloon virtio[,addr=@var{addr}] +Enable virtio balloon device (default), optionally with PCI address +@var{addr}. +ETEXI + +DEF("device", HAS_ARG, QEMU_OPTION_device, + "-device driver[,prop[=value][,...]]\n" + " add device (based on driver)\n" + " prop=value,... sets driver properties\n" + " use '-device help' to print all possible drivers\n" + " use '-device driver,help' to print all possible properties\n", + QEMU_ARCH_ALL) +STEXI +@item -device @var{driver}[,@var{prop}[=@var{value}][,...]] +@findex -device +Add device @var{driver}. @var{prop}=@var{value} sets driver +properties. Valid properties depend on the driver. To get help on +possible drivers and properties, use @code{-device help} and +@code{-device @var{driver},help}. +ETEXI + +DEF("name", HAS_ARG, QEMU_OPTION_name, + "-name string1[,process=string2]\n" + " set the name of the guest\n" + " string1 sets the window title and string2 the process name (on Linux)\n", + QEMU_ARCH_ALL) +STEXI +@item -name @var{name} +@findex -name +Sets the @var{name} of the guest. +This name will be displayed in the SDL window caption. +The @var{name} will also be used for the VNC server. +Also optionally set the top visible process name in Linux. +ETEXI + +DEF("uuid", HAS_ARG, QEMU_OPTION_uuid, + "-uuid %08x-%04x-%04x-%04x-%012x\n" + " specify machine UUID\n", QEMU_ARCH_ALL) +STEXI +@item -uuid @var{uuid} +@findex -uuid +Set system UUID. +ETEXI + +STEXI +@end table +ETEXI +DEFHEADING() + +DEFHEADING(Block device options:) +STEXI +@table @option +ETEXI + DEF("fda", HAS_ARG, QEMU_OPTION_fda, "-fda/-fdb file use 'file' as floppy disk 0/1 image\n", QEMU_ARCH_ALL) DEF("fdb", HAS_ARG, QEMU_OPTION_fdb, "", QEMU_ARCH_ALL) @@ -185,6 +440,8 @@ These options have the same definition as they have in @option{-hdachs}. @var{cache} is "none", "writeback", "unsafe", "directsync" or "writethrough" and controls how the host cache is used to access block data. @item aio=@var{aio} @var{aio} is "threads", or "native" and selects between pthread based disk I/O and native Linux AIO. +@item discard=@var{discard} +@var{discard} is one of "ignore" (or "off") or "unmap" (or "on") and controls whether @dfn{discard} (also known as @dfn{trim} or @dfn{unmap}) requests are ignored or passed to the filesystem. Some machine types may not support discard requests. @item format=@var{format} Specify which disk @var{format} will be used rather than detecting the format. Can be used to specifiy format=raw to avoid interpreting @@ -293,62 +550,6 @@ qemu-system-i386 -hda a -hdb b @end example ETEXI -DEF("add-fd", HAS_ARG, QEMU_OPTION_add_fd, - "-add-fd fd=fd,set=set[,opaque=opaque]\n" - " Add 'fd' to fd 'set'\n", QEMU_ARCH_ALL) -STEXI -@item -add-fd fd=@var{fd},set=@var{set}[,opaque=@var{opaque}] -@findex -add-fd - -Add a file descriptor to an fd set. Valid options are: - -@table @option -@item fd=@var{fd} -This option defines the file descriptor of which a duplicate is added to fd set. -The file descriptor cannot be stdin, stdout, or stderr. -@item set=@var{set} -This option defines the ID of the fd set to add the file descriptor to. -@item opaque=@var{opaque} -This option defines a free-form string that can be used to describe @var{fd}. -@end table - -You can open an image using pre-opened file descriptors from an fd set: -@example -qemu-system-i386 --add-fd fd=3,set=2,opaque="rdwr:/path/to/file" --add-fd fd=4,set=2,opaque="rdonly:/path/to/file" --drive file=/dev/fdset/2,index=0,media=disk -@end example -ETEXI - -DEF("set", HAS_ARG, QEMU_OPTION_set, - "-set group.id.arg=value\n" - " set parameter for item of type \n" - " i.e. -set drive.$id.file=/path/to/image\n", QEMU_ARCH_ALL) -STEXI -@item -set -@findex -set -TODO -ETEXI - -DEF("global", HAS_ARG, QEMU_OPTION_global, - "-global driver.prop=value\n" - " set a global default for a driver property\n", - QEMU_ARCH_ALL) -STEXI -@item -global @var{driver}.@var{prop}=@var{value} -@findex -global -Set default value of @var{driver}'s property @var{prop} to @var{value}, e.g.: - -@example -qemu-system-i386 -global ide-drive.physical_block_size=4096 -drive file=file,if=ide,index=0,media=disk -@end example - -In particular, you can use this to set driver properties for devices which are -created automatically by the machine model. To create a device which is not -created automatically and set properties on it, use -@option{device}. -ETEXI - DEF("mtdblock", HAS_ARG, QEMU_OPTION_mtdblock, "-mtdblock file use 'file' as on-board Flash memory image\n", QEMU_ARCH_ALL) @@ -374,52 +575,6 @@ STEXI Use @var{file} as a parallel flash image. ETEXI -DEF("boot", HAS_ARG, QEMU_OPTION_boot, - "-boot [order=drives][,once=drives][,menu=on|off]\n" - " [,splash=sp_name][,splash-time=sp_time][,reboot-timeout=rb_time]\n" - " 'drives': floppy (a), hard disk (c), CD-ROM (d), network (n)\n" - " 'sp_name': the file's name that would be passed to bios as logo picture, if menu=on\n" - " 'sp_time': the period that splash picture last if menu=on, unit is ms\n" - " 'rb_timeout': the timeout before guest reboot when boot failed, unit is ms\n", - QEMU_ARCH_ALL) -STEXI -@item -boot [order=@var{drives}][,once=@var{drives}][,menu=on|off][,splash=@var{sp_name}][,splash-time=@var{sp_time}][,reboot-timeout=@var{rb_timeout}] -@findex -boot -Specify boot order @var{drives} as a string of drive letters. Valid -drive letters depend on the target achitecture. The x86 PC uses: a, b -(floppy 1 and 2), c (first hard disk), d (first CD-ROM), n-p (Etherboot -from network adapter 1-4), hard disk boot is the default. To apply a -particular boot order only on the first startup, specify it via -@option{once}. - -Interactive boot menus/prompts can be enabled via @option{menu=on} as far -as firmware/BIOS supports them. The default is non-interactive boot. - -A splash picture could be passed to bios, enabling user to show it as logo, -when option splash=@var{sp_name} is given and menu=on, If firmware/BIOS -supports them. Currently Seabios for X86 system support it. -limitation: The splash file could be a jpeg file or a BMP file in 24 BPP -format(true color). The resolution should be supported by the SVGA mode, so -the recommended is 320x240, 640x480, 800x640. - -A timeout could be passed to bios, guest will pause for @var{rb_timeout} ms -when boot failed, then reboot. If @var{rb_timeout} is '-1', guest will not -reboot, qemu passes '-1' to bios by default. Currently Seabios for X86 -system support it. - -@example -# try to boot from network first, then from hard disk -qemu-system-i386 -boot order=nc -# boot from CD-ROM first, switch back to default order after reboot -qemu-system-i386 -boot once=d -# boot with a splash picture for 5 seconds. -qemu-system-i386 -boot menu=on,splash=/root/boot.bmp,splash-time=5000 -@end example - -Note: The legacy format '-boot @var{drives}' is still supported but its -use is discouraged as it may be removed from future versions. -ETEXI - DEF("snapshot", 0, QEMU_OPTION_snapshot, "-snapshot write to temporary files instead of disk image files\n", QEMU_ARCH_ALL) @@ -431,188 +586,20 @@ the raw disk image you use is not written back. You can however force the write back by pressing @key{C-a s} (@pxref{disk_images}). ETEXI -DEF("m", HAS_ARG, QEMU_OPTION_m, - "-m megs set virtual RAM size to megs MB [default=" - stringify(DEFAULT_RAM_SIZE) "]\n", QEMU_ARCH_ALL) -STEXI -@item -m @var{megs} -@findex -m -Set virtual RAM size to @var{megs} megabytes. Default is 128 MiB. Optionally, -a suffix of ``M'' or ``G'' can be used to signify a value in megabytes or -gigabytes respectively. -ETEXI - -DEF("mem-path", HAS_ARG, QEMU_OPTION_mempath, - "-mem-path FILE provide backing storage for guest RAM\n", QEMU_ARCH_ALL) -STEXI -@item -mem-path @var{path} -Allocate guest RAM from a temporarily created file in @var{path}. -ETEXI - -#ifdef MAP_POPULATE -DEF("mem-prealloc", 0, QEMU_OPTION_mem_prealloc, - "-mem-prealloc preallocate guest memory (use with -mem-path)\n", +DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \ + "-hdachs c,h,s[,t]\n" \ + " force hard disk 0 physical geometry and the optional BIOS\n" \ + " translation (t=none or lba) (usually QEMU can guess them)\n", QEMU_ARCH_ALL) STEXI -@item -mem-prealloc -Preallocate memory when using -mem-path. +@item -hdachs @var{c},@var{h},@var{s},[,@var{t}] +@findex -hdachs +Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <= +@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS +translation mode (@var{t}=none, lba or auto). Usually QEMU can guess +all those parameters. This option is useful for old MS-DOS disk +images. ETEXI -#endif - -DEF("k", HAS_ARG, QEMU_OPTION_k, - "-k language use keyboard layout (for example 'fr' for French)\n", - QEMU_ARCH_ALL) -STEXI -@item -k @var{language} -@findex -k -Use keyboard layout @var{language} (for example @code{fr} for -French). This option is only needed where it is not easy to get raw PC -keycodes (e.g. on Macs, with some X11 servers or with a VNC -display). You don't normally need to use it on PC/Linux or PC/Windows -hosts. - -The available layouts are: -@example -ar de-ch es fo fr-ca hu ja mk no pt-br sv -da en-gb et fr fr-ch is lt nl pl ru th -de en-us fi fr-be hr it lv nl-be pt sl tr -@end example - -The default is @code{en-us}. -ETEXI - - -DEF("audio-help", 0, QEMU_OPTION_audio_help, - "-audio-help print list of audio drivers and their options\n", - QEMU_ARCH_ALL) -STEXI -@item -audio-help -@findex -audio-help -Will show the audio subsystem help: list of drivers, tunable -parameters. -ETEXI - -DEF("soundhw", HAS_ARG, QEMU_OPTION_soundhw, - "-soundhw c1,... enable audio support\n" - " and only specified sound cards (comma separated list)\n" - " use '-soundhw help' to get the list of supported cards\n" - " use '-soundhw all' to enable all of them\n", QEMU_ARCH_ALL) -STEXI -@item -soundhw @var{card1}[,@var{card2},...] or -soundhw all -@findex -soundhw -Enable audio and selected sound hardware. Use 'help' to print all -available sound hardware. - -@example -qemu-system-i386 -soundhw sb16,adlib disk.img -qemu-system-i386 -soundhw es1370 disk.img -qemu-system-i386 -soundhw ac97 disk.img -qemu-system-i386 -soundhw hda disk.img -qemu-system-i386 -soundhw all disk.img -qemu-system-i386 -soundhw help -@end example - -Note that Linux's i810_audio OSS kernel (for AC97) module might -require manually specifying clocking. - -@example -modprobe i810_audio clocking=48000 -@end example -ETEXI - -DEF("balloon", HAS_ARG, QEMU_OPTION_balloon, - "-balloon none disable balloon device\n" - "-balloon virtio[,addr=str]\n" - " enable virtio balloon device (default)\n", QEMU_ARCH_ALL) -STEXI -@item -balloon none -@findex -balloon -Disable balloon device. -@item -balloon virtio[,addr=@var{addr}] -Enable virtio balloon device (default), optionally with PCI address -@var{addr}. -ETEXI - -STEXI -@end table -ETEXI - -DEF("usb", 0, QEMU_OPTION_usb, - "-usb enable the USB driver (will be the default soon)\n", - QEMU_ARCH_ALL) -STEXI -USB options: -@table @option - -@item -usb -@findex -usb -Enable the USB driver (will be the default soon) -ETEXI - -DEF("usbdevice", HAS_ARG, QEMU_OPTION_usbdevice, - "-usbdevice name add the host or guest USB device 'name'\n", - QEMU_ARCH_ALL) -STEXI - -@item -usbdevice @var{devname} -@findex -usbdevice -Add the USB device @var{devname}. @xref{usb_devices}. - -@table @option - -@item mouse -Virtual Mouse. This will override the PS/2 mouse emulation when activated. - -@item tablet -Pointer device that uses absolute coordinates (like a touchscreen). This -means QEMU is able to report the mouse position without having to grab the -mouse. Also overrides the PS/2 mouse emulation when activated. - -@item disk:[format=@var{format}]:@var{file} -Mass storage device based on file. The optional @var{format} argument -will be used rather than detecting the format. Can be used to specifiy -@code{format=raw} to avoid interpreting an untrusted format header. - -@item host:@var{bus}.@var{addr} -Pass through the host device identified by @var{bus}.@var{addr} (Linux only). - -@item host:@var{vendor_id}:@var{product_id} -Pass through the host device identified by @var{vendor_id}:@var{product_id} -(Linux only). - -@item serial:[vendorid=@var{vendor_id}][,productid=@var{product_id}]:@var{dev} -Serial converter to host character device @var{dev}, see @code{-serial} for the -available devices. - -@item braille -Braille device. This will use BrlAPI to display the braille output on a real -or fake device. - -@item net:@var{options} -Network adapter that supports CDC ethernet and RNDIS protocols. - -@end table -ETEXI - -DEF("device", HAS_ARG, QEMU_OPTION_device, - "-device driver[,prop[=value][,...]]\n" - " add device (based on driver)\n" - " prop=value,... sets driver properties\n" - " use '-device help' to print all possible drivers\n" - " use '-device driver,help' to print all possible properties\n", - QEMU_ARCH_ALL) -STEXI -@item -device @var{driver}[,@var{prop}[=@var{value}][,...]] -@findex -device -Add device @var{driver}. @var{prop}=@var{value} sets driver -properties. Valid properties depend on the driver. To get help on -possible drivers and properties, use @code{-device help} and -@code{-device @var{driver},help}. -ETEXI - -DEFHEADING() - -DEFHEADING(File system options:) DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev, "-fsdev fsdriver,id=id[,path=path,][security_model={mapped-xattr|mapped-file|passthrough|none}]\n" @@ -676,10 +663,6 @@ Specifies the tag name to be used by the guest to mount this export point ETEXI -DEFHEADING() - -DEFHEADING(Virtual File system pass-through options:) - DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs, "-virtfs local,path=path,mount_tag=tag,security_model=[mapped-xattr|mapped-file|passthrough|none]\n" " [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd]\n", @@ -741,39 +724,76 @@ STEXI Create synthetic file system image ETEXI +STEXI +@end table +ETEXI DEFHEADING() -DEF("name", HAS_ARG, QEMU_OPTION_name, - "-name string1[,process=string2]\n" - " set the name of the guest\n" - " string1 sets the window title and string2 the process name (on Linux)\n", - QEMU_ARCH_ALL) +DEFHEADING(USB options:) STEXI -@item -name @var{name} -@findex -name -Sets the @var{name} of the guest. -This name will be displayed in the SDL window caption. -The @var{name} will also be used for the VNC server. -Also optionally set the top visible process name in Linux. +@table @option ETEXI -DEF("uuid", HAS_ARG, QEMU_OPTION_uuid, - "-uuid %08x-%04x-%04x-%04x-%012x\n" - " specify machine UUID\n", QEMU_ARCH_ALL) +DEF("usb", 0, QEMU_OPTION_usb, + "-usb enable the USB driver (will be the default soon)\n", + QEMU_ARCH_ALL) STEXI -@item -uuid @var{uuid} -@findex -uuid -Set system UUID. +@item -usb +@findex -usb +Enable the USB driver (will be the default soon) +ETEXI + +DEF("usbdevice", HAS_ARG, QEMU_OPTION_usbdevice, + "-usbdevice name add the host or guest USB device 'name'\n", + QEMU_ARCH_ALL) +STEXI + +@item -usbdevice @var{devname} +@findex -usbdevice +Add the USB device @var{devname}. @xref{usb_devices}. + +@table @option + +@item mouse +Virtual Mouse. This will override the PS/2 mouse emulation when activated. + +@item tablet +Pointer device that uses absolute coordinates (like a touchscreen). This +means QEMU is able to report the mouse position without having to grab the +mouse. Also overrides the PS/2 mouse emulation when activated. + +@item disk:[format=@var{format}]:@var{file} +Mass storage device based on file. The optional @var{format} argument +will be used rather than detecting the format. Can be used to specifiy +@code{format=raw} to avoid interpreting an untrusted format header. + +@item host:@var{bus}.@var{addr} +Pass through the host device identified by @var{bus}.@var{addr} (Linux only). + +@item host:@var{vendor_id}:@var{product_id} +Pass through the host device identified by @var{vendor_id}:@var{product_id} +(Linux only). + +@item serial:[vendorid=@var{vendor_id}][,productid=@var{product_id}]:@var{dev} +Serial converter to host character device @var{dev}, see @code{-serial} for the +available devices. + +@item braille +Braille device. This will use BrlAPI to display the braille output on a real +or fake device. + +@item net:@var{options} +Network adapter that supports CDC ethernet and RNDIS protocols. + +@end table ETEXI STEXI @end table ETEXI - DEFHEADING() DEFHEADING(Display options:) - STEXI @table @option ETEXI @@ -827,7 +847,7 @@ DEF("curses", 0, QEMU_OPTION_curses, QEMU_ARCH_ALL) STEXI @item -curses -@findex curses +@findex -curses Normally, QEMU uses SDL to display the VGA output. With this option, QEMU can display the VGA output when in text mode using a curses/ncurses interface. Nothing is displayed in graphical mode. @@ -998,7 +1018,7 @@ DEF("rotate", HAS_ARG, QEMU_OPTION_rotate, "-rotate rotate graphical output some deg left (only PXA LCD)\n", QEMU_ARCH_ALL) STEXI -@item -rotate +@item -rotate @var{deg} @findex -rotate Rotate graphical output some deg left (only PXA LCD). ETEXI @@ -1096,6 +1116,14 @@ client is specified by the @var{display}. For reverse network connections (@var{host}:@var{d},@code{reverse}), the @var{d} argument is a TCP port number, not a display number. +@item websocket + +Opens an additional TCP listening port dedicated to VNC Websocket connections. +By defintion the Websocket port is 5700+@var{display}. If @var{host} is +specified connections will only be allowed from this host. +As an alternative the Websocket port could be specified by using +@code{websocket}=@var{port}. + @item password Require that password based authentication is used for client connections. @@ -1207,7 +1235,6 @@ ETEXI STEXI @end table ETEXI - ARCHHEADING(, QEMU_ARCH_I386) ARCHHEADING(i386 target only:, QEMU_ARCH_I386) @@ -1286,17 +1313,16 @@ STEXI Load SMBIOS entry from binary file. @item -smbios type=0[,vendor=@var{str}][,version=@var{str}][,date=@var{str}][,release=@var{%d.%d}] -@findex -smbios Specify SMBIOS type 0 fields @item -smbios type=1[,manufacturer=@var{str}][,product=@var{str}] [,version=@var{str}][,serial=@var{str}][,uuid=@var{uuid}][,sku=@var{str}] [,family=@var{str}] Specify SMBIOS type 1 fields ETEXI -DEFHEADING() STEXI @end table ETEXI +DEFHEADING() DEFHEADING(Network options:) STEXI @@ -1330,7 +1356,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, "-net tap[,vlan=n][,name=str],ifname=name\n" " connect the host TAP network interface to VLAN 'n'\n" #else - "-net tap[,vlan=n][,name=str][,fd=h][,ifname=name][,script=file][,downscript=dfile][,helper=helper][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h][,vhostforce=on|off]\n" + "-net tap[,vlan=n][,name=str][,fd=h][,fds=x:y:...:z][,ifname=name][,script=file][,downscript=dfile][,helper=helper][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h][,vhostfds=x:y:...:z][,vhostforce=on|off]\n" " connect the host TAP network interface to VLAN 'n'\n" " use network scripts 'file' (default=" DEFAULT_NETWORK_SCRIPT ")\n" " to configure it and 'dfile' (default=" DEFAULT_NETWORK_DOWN_SCRIPT ")\n" @@ -1339,6 +1365,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, " use network helper 'helper' (default=" DEFAULT_BRIDGE_HELPER ") to\n" " configure it\n" " use 'fd=h' to connect to an already opened TAP interface\n" + " use 'fds=x:y:...:z' to connect to already opened multiqueue capable TAP interfaces\n" " use 'sndbuf=nbytes' to limit the size of the send buffer (the\n" " default is disabled 'sndbuf=0' to enable flow control set 'sndbuf=1048576')\n" " use vnet_hdr=off to avoid enabling the IFF_VNET_HDR tap flag\n" @@ -1347,6 +1374,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, " (only has effect for virtio guests which use MSIX)\n" " use vhostforce=on to force vhost on for non-MSIX virtio guests\n" " use 'vhostfd=h' to connect to an already opened vhost net device\n" + " use 'vhostfds=x:y:...:z to connect to multiple already opened vhost net devices\n" "-net bridge[,vlan=n][,name=str][,br=bridge][,helper=helper]\n" " connects a host TAP network interface to a host bridge device 'br'\n" " (default=" DEFAULT_BRIDGE_INTERFACE ") using the program 'helper'\n" @@ -1380,7 +1408,8 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, #ifdef CONFIG_VDE "vde|" #endif - "socket],id=str[,option][,option][,...]\n", QEMU_ARCH_ALL) + "socket|" + "hubport],id=str[,option][,option][,...]\n", QEMU_ARCH_ALL) STEXI @item -net nic[,vlan=@var{n}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}] @findex -net @@ -1401,6 +1430,7 @@ Not all devices are supported on all targets. Use @code{-net nic,model=help} for a list of available devices for your target. @item -netdev user,id=@var{id}[,@var{option}][,@var{option}][,...] +@findex -netdev @item -net user[,@var{option}][,@var{option}][,...] Use the user mode network stack which requires no administrator privilege to run. Valid options are: @@ -1701,6 +1731,14 @@ vde_switch -F -sock /tmp/myswitch qemu-system-i386 linux.img -net nic -net vde,sock=/tmp/myswitch @end example +@item -netdev hubport,id=@var{id},hubid=@var{hubid} + +Create a hub port on QEMU "vlan" @var{hubid}. + +The hubport netdev lets you connect a NIC to a QEMU "vlan" instead of a single +netdev. @code{-net} and @code{-device} with parameter @option{vlan} create the +required hub automatically. + @item -net dump[,vlan=@var{n}][,file=@var{file}][,len=@var{len}] Dump network traffic on VLAN @var{n} to file @var{file} (@file{qemu-vlan0.pcap} by default). At most @var{len} bytes (64k by default) per packet are stored. The file format is @@ -1710,13 +1748,19 @@ libpcap, so it can be analyzed with tools such as tcpdump or Wireshark. Indicate that no network devices should be configured. It is used to override the default configuration (@option{-net nic -net user}) which is activated if no @option{-net} options are provided. - -@end table ETEXI +STEXI +@end table +ETEXI DEFHEADING() DEFHEADING(Character device options:) +STEXI + +The general form of a character device option is: +@table @option +ETEXI DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev null,id=id[,mux=on|off]\n" @@ -1728,6 +1772,7 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev msmouse,id=id[,mux=on|off]\n" "-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]\n" " [,mux=on|off]\n" + "-chardev ringbuf,id=id[,size=size]\n" "-chardev file,id=id,path=path[,mux=on|off]\n" "-chardev pipe,id=id,path=path[,mux=on|off]\n" #ifdef _WIN32 @@ -1742,9 +1787,11 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, #endif #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) + "-chardev serial,id=id,path=path[,mux=on|off]\n" "-chardev tty,id=id,path=path[,mux=on|off]\n" #endif #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) + "-chardev parallel,id=id,path=path[,mux=on|off]\n" "-chardev parport,id=id,path=path[,mux=on|off]\n" #endif #if defined(CONFIG_SPICE) @@ -1755,10 +1802,6 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, ) STEXI - -The general form of a character device option is: -@table @option - @item -chardev @var{backend} ,id=@var{id} [,mux=on|off] [,@var{options}] @findex -chardev Backend is one of: @@ -1767,6 +1810,7 @@ Backend is one of: @option{udp}, @option{msmouse}, @option{vc}, +@option{ringbuf}, @option{file}, @option{pipe}, @option{console}, @@ -1775,6 +1819,7 @@ Backend is one of: @option{stdio}, @option{braille}, @option{tty}, +@option{parallel}, @option{parport}, @option{spicevmc}. @option{spiceport}. @@ -1874,6 +1919,11 @@ the console, in pixels. @option{cols} and @option{rows} specify that the console be sized to fit a text console with the given dimensions. +@item -chardev ringbuf ,id=@var{id} [,size=@var{size}] + +Create a ring buffer with fixed size @option{size}. +@var{size} must be a power of two, and defaults to @code{64K}). + @item -chardev file ,id=@var{id} ,path=@var{path} Log all traffic received from the guest to a file. @@ -1910,8 +1960,8 @@ take any options. Send traffic from the guest to a serial device on the host. -@option{serial} is -only available on Windows hosts. +On Unix hosts serial will actually accept any tty device, +not only serial lines. @option{path} specifies the name of the serial device to open. @@ -1937,16 +1987,15 @@ Connect to a local BrlAPI server. @option{braille} does not take any options. @item -chardev tty ,id=@var{id} ,path=@var{path} -Connect to a local tty device. - @option{tty} is only available on Linux, Sun, FreeBSD, NetBSD, OpenBSD and -DragonFlyBSD hosts. +DragonFlyBSD hosts. It is an alias for @option{serial}. @option{path} specifies the path to the tty. @option{path} is required. +@item -chardev parallel ,id=@var{id} ,path=@var{path} @item -chardev parport ,id=@var{id} ,path=@var{path} -@option{parport} is only available on Linux, FreeBSD and DragonFlyBSD hosts. +@option{parallel} is only available on Linux, FreeBSD and DragonFlyBSD hosts. Connect to a local parallel port. @@ -1973,14 +2022,15 @@ Connect to a spice virtual machine channel, such as vdiport. Connect to a spice port, allowing a Spice client to handle the traffic identified by a name (preferably a fqdn). - -@end table ETEXI +STEXI +@end table +ETEXI DEFHEADING() -STEXI DEFHEADING(Device URL Syntax:) +STEXI In addition to using normal file images for the emulated storage devices, QEMU can also use networked resources such as iSCSI devices. These are @@ -2058,23 +2108,13 @@ QEMU supports using either local sheepdog devices or remote networked devices. Syntax for specifying a sheepdog device -@table @list -``sheepdog:'' - -``sheepdog::'' - -``sheepdog::'' - -``sheepdog:::'' - -``sheepdog::::'' - -``sheepdog::::'' -@end table +@example +sheepdog[+tcp|+unix]://[host:port]/vdiname[?socket=path][#snapid|#tag] +@end example Example @example -qemu-system-i386 --drive file=sheepdog:192.0.2.1:30000:MyVirtualMachine +qemu-system-i386 --drive file=sheepdog://192.0.2.1:30000/MyVirtualMachine @end example See also @url{http://http://www.osrg.net/sheepdog/}. @@ -2092,14 +2132,20 @@ gluster[+transport]://[server[:port]]/volname/image[?socket=...] Example @example -qemu-system-x86_84 --drive file=gluster://192.0.2.1/testvol/a.img +qemu-system-x86_64 --drive file=gluster://192.0.2.1/testvol/a.img @end example See also @url{http://www.gluster.org}. +ETEXI + +STEXI @end table ETEXI DEFHEADING(Bluetooth(R) options:) +STEXI +@table @option +ETEXI DEF("bt", HAS_ARG, QEMU_OPTION_bt, \ "-bt hci,null dumb bluetooth HCI - doesn't respond to commands\n" \ @@ -2113,8 +2159,6 @@ DEF("bt", HAS_ARG, QEMU_OPTION_bt, \ " emulate a bluetooth device 'dev' in scatternet 'n'\n", QEMU_ARCH_ALL) STEXI -@table @option - @item -bt hci[...] @findex -bt Defines the function of the corresponding Bluetooth HCI. -bt options @@ -2166,11 +2210,87 @@ currently: @item keyboard Virtual wireless keyboard implementing the HIDP bluetooth profile. @end table +ETEXI + +STEXI @end table +ETEXI +DEFHEADING() + +#ifdef CONFIG_TPM +DEFHEADING(TPM device options:) + +DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \ + "-tpmdev passthrough,id=id[,path=path][,cancel-path=path]\n" + " use path to provide path to a character device; default is /dev/tpm0\n" + " use cancel-path to provide path to TPM's cancel sysfs entry; if\n" + " not provided it will be searched for in /sys/class/misc/tpm?/device\n", + QEMU_ARCH_ALL) +STEXI + +The general form of a TPM device option is: +@table @option + +@item -tpmdev @var{backend} ,id=@var{id} [,@var{options}] +@findex -tpmdev +Backend type must be: +@option{passthrough}. + +The specific backend type will determine the applicable options. +The @code{-tpmdev} option requires a @code{-device} option. + +Options to each backend are described below. + +Use 'help' to print all available TPM backend types. +@example +qemu -tpmdev help +@end example + +@item -tpmdev passthrough, id=@var{id}, path=@var{path}, cancel-path=@var{cancel-path} + +(Linux-host only) Enable access to the host's TPM using the passthrough +driver. + +@option{path} specifies the path to the host's TPM device, i.e., on +a Linux host this would be @code{/dev/tpm0}. +@option{path} is optional and by default @code{/dev/tpm0} is used. + +@option{cancel-path} specifies the path to the host TPM device's sysfs +entry allowing for cancellation of an ongoing TPM command. +@option{cancel-path} is optional and by default QEMU will search for the +sysfs entry to use. + +Some notes about using the host's TPM with the passthrough driver: + +The TPM device accessed by the passthrough driver must not be +used by any other application on the host. + +Since the host's firmware (BIOS/UEFI) has already initialized the TPM, +the VM's firmware (BIOS/UEFI) will not be able to initialize the +TPM again and may therefore not show a TPM-specific menu that would +otherwise allow the user to configure the TPM, e.g., allow the user to +enable/disable or activate/deactivate the TPM. +Further, if TPM ownership is released from within a VM then the host's TPM +will get disabled and deactivated. To enable and activate the +TPM again afterwards, the host has to be rebooted and the user is +required to enter the firmware's menu to enable and activate the TPM. +If the TPM is left disabled and/or deactivated most TPM commands will fail. + +To create a passthrough TPM use the following two options: +@example +-tpmdev passthrough,id=tpm0 -device tpm-tis,tpmdev=tpm0 +@end example +Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by +@code{tpmdev=tpm0} in the device option. + +@end table + ETEXI DEFHEADING() +#endif + DEFHEADING(Linux/Multiboot boot specific:) STEXI @@ -2225,11 +2345,9 @@ ETEXI STEXI @end table ETEXI - DEFHEADING() DEFHEADING(Debug/Expert options:) - STEXI @table @option ETEXI @@ -2472,36 +2590,21 @@ Shorthand for -gdb tcp::1234, i.e. open a gdbserver on TCP port 1234 ETEXI DEF("d", HAS_ARG, QEMU_OPTION_d, \ - "-d item1,... output log to /tmp/qemu.log (use '-d help' for a list of log items)\n", + "-d item1,... enable logging of specified items (use '-d help' for a list of log items)\n", QEMU_ARCH_ALL) STEXI -@item -d +@item -d @var{item1}[,...] @findex -d -Output log in /tmp/qemu.log +Enable logging of specified items. Use '-d help' for a list of log items. ETEXI DEF("D", HAS_ARG, QEMU_OPTION_D, \ - "-D logfile output log to logfile (instead of the default /tmp/qemu.log)\n", + "-D logfile output log to logfile (default stderr)\n", QEMU_ARCH_ALL) STEXI @item -D @var{logfile} @findex -D -Output log in @var{logfile} instead of /tmp/qemu.log -ETEXI - -DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \ - "-hdachs c,h,s[,t]\n" \ - " force hard disk 0 physical geometry and the optional BIOS\n" \ - " translation (t=none or lba) (usually QEMU can guess them)\n", - QEMU_ARCH_ALL) -STEXI -@item -hdachs @var{c},@var{h},@var{s},[,@var{t}] -@findex -hdachs -Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <= -@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS -translation mode (@var{t}=none, lba or auto). Usually QEMU can guess -all those parameters. This option is useful for old MS-DOS disk -images. +Output log in @var{logfile} instead of to stderr ETEXI DEF("L", HAS_ARG, QEMU_OPTION_L, \ @@ -2692,6 +2795,7 @@ DEF("watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action, \ QEMU_ARCH_ALL) STEXI @item -watchdog-action @var{action} +@findex -watchdog-action The @var{action} controls what QEMU will do when the watchdog timer expires. @@ -2838,7 +2942,7 @@ DEF("sandbox", HAS_ARG, QEMU_OPTION_sandbox, \ "-sandbox Enable seccomp mode 2 system call filter (default 'off').\n", QEMU_ARCH_ALL) STEXI -@item -sandbox +@item -sandbox @var{arg} @findex -sandbox Enable Seccomp mode 2 system call filter. 'on' will enable syscall filtering and 'off' will disable it. The default is 'off'. @@ -2911,13 +3015,9 @@ the @var{simple} tracing backend. @end table ETEXI -DEF("qtest", HAS_ARG, QEMU_OPTION_qtest, - "-qtest CHR specify tracing options\n", - QEMU_ARCH_ALL) - -DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log, - "-qtest-log LOG specify tracing options\n", - QEMU_ARCH_ALL) +HXCOMM Internal use +DEF("qtest", HAS_ARG, QEMU_OPTION_qtest, "", QEMU_ARCH_ALL) +DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log, "", QEMU_ARCH_ALL) #ifdef __linux__ DEF("enable-fips", 0, QEMU_OPTION_enablefips, @@ -2953,6 +3053,14 @@ DEF("object", HAS_ARG, QEMU_OPTION_object, " property must be set. These objects are placed in the\n" " '/objects' path.\n", QEMU_ARCH_ALL) +STEXI +@item -object @var{typename}[,@var{prop1}=@var{value1},...] +@findex -object +Create an new object of type @var{typename} setting properties +in the order they are specified. Note that the 'id' +property must be set. These objects are placed in the +'/objects' path. +ETEXI HXCOMM This is the last statement. Insert new options before this line! STEXI diff --git a/qemu-tool.c b/qemu-tool.c deleted file mode 100644 index 1a474c45bc..0000000000 --- a/qemu-tool.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Compatibility for qemu-img/qemu-nbd - * - * Copyright IBM, Corp. 2008 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "qemu-common.h" -#include "monitor/monitor.h" -#include "qemu/timer.h" -#include "qemu/log.h" -#include "migration/migration.h" -#include "qemu/main-loop.h" -#include "sysemu/sysemu.h" -#include "qemu/sockets.h" -#include "slirp/libslirp.h" - -#include - -struct QEMUBH -{ - QEMUBHFunc *cb; - void *opaque; -}; - -const char *qemu_get_vm_name(void) -{ - return NULL; -} - -Monitor *cur_mon; - -void vm_stop(RunState state) -{ - abort(); -} - -int monitor_cur_is_qmp(void) -{ - return 0; -} - -void monitor_set_error(Monitor *mon, QError *qerror) -{ -} - -void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) -{ -} - -void monitor_printf(Monitor *mon, const char *fmt, ...) -{ -} - -void monitor_print_filename(Monitor *mon, const char *filename) -{ -} - -void monitor_protocol_event(MonitorEvent event, QObject *data) -{ -} - -int64_t cpu_get_clock(void) -{ - return get_clock_realtime(); -} - -int64_t cpu_get_icount(void) -{ - abort(); -} - -void qemu_mutex_lock_iothread(void) -{ -} - -void qemu_mutex_unlock_iothread(void) -{ -} - -int use_icount; - -void qemu_clock_warp(QEMUClock *clock) -{ -} - -void slirp_update_timeout(uint32_t *timeout) -{ -} - -void slirp_select_fill(int *pnfds, fd_set *readfds, - fd_set *writefds, fd_set *xfds) -{ -} - -void slirp_select_poll(fd_set *readfds, fd_set *writefds, - fd_set *xfds, int select_error) -{ -} - -void migrate_add_blocker(Error *reason) -{ -} - -void migrate_del_blocker(Error *reason) -{ -} diff --git a/qemu-user.c b/qemu-user.c deleted file mode 100644 index f8b450c03d..0000000000 --- a/qemu-user.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Stubs for QEMU user emulation - * - * Copyright (c) 2012 SUSE LINUX Products GmbH - * - * 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) 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 - * - */ - -#include "qemu-common.h" -#include "monitor/monitor.h" - -Monitor *cur_mon; - -int monitor_cur_is_qmp(void) -{ - return 0; -} - -void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) -{ -} - -void monitor_set_error(Monitor *mon, QError *qerror) -{ -} diff --git a/qga/channel-posix.c b/qga/channel-posix.c index d4fd628907..e65dda3822 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "qemu/osdep.h" #include "qemu/sockets.h" #include "qga/channel.h" @@ -45,6 +46,7 @@ static gboolean ga_channel_listen_accept(GIOChannel *channel, ret = ga_channel_client_add(c, client_fd); if (ret) { g_warning("error setting up connection"); + close(client_fd); goto out; } accepted = true; @@ -139,19 +141,21 @@ static gboolean ga_channel_open(GAChannel *c, const gchar *path, GAChannelMethod ); if (fd == -1) { g_critical("error opening channel: %s", strerror(errno)); - exit(EXIT_FAILURE); + return false; } #ifdef CONFIG_SOLARIS ret = ioctl(fd, I_SETSIG, S_OUTPUT | S_INPUT | S_HIPRI); if (ret == -1) { g_critical("error setting event mask for channel: %s", strerror(errno)); - exit(EXIT_FAILURE); + close(fd); + return false; } #endif ret = ga_channel_client_add(c, fd); if (ret) { g_critical("error adding channel to main loop"); + close(fd); return false; } break; @@ -161,7 +165,7 @@ static gboolean ga_channel_open(GAChannel *c, const gchar *path, GAChannelMethod int fd = qemu_open(path, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fd == -1) { g_critical("error opening channel: %s", strerror(errno)); - exit(EXIT_FAILURE); + return false; } tcgetattr(fd, &tio); /* set up serial port for non-canonical, dumb byte streaming */ @@ -181,7 +185,9 @@ static gboolean ga_channel_open(GAChannel *c, const gchar *path, GAChannelMethod tcsetattr(fd, TCSANOW, &tio); ret = ga_channel_client_add(c, fd); if (ret) { - g_error("error adding channel to main loop"); + g_critical("error adding channel to main loop"); + close(fd); + return false; } break; } diff --git a/qga/channel-win32.c b/qga/channel-win32.c index 16bf44a376..7ed98d72fb 100644 --- a/qga/channel-win32.c +++ b/qga/channel-win32.c @@ -287,7 +287,7 @@ GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size) static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method, const gchar *path) { - if (!method == GA_CHANNEL_VIRTIO_SERIAL) { + if (method != GA_CHANNEL_VIRTIO_SERIAL) { g_critical("unsupported communication method"); return false; } diff --git a/qga/commands-posix.c b/qga/commands-posix.c index a657201e7a..d7da850615 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -15,6 +15,10 @@ #include #include #include +#include +#include +#include +#include #include "qga/guest-agent-core.h" #include "qga-qmp-commands.h" #include "qapi/qmp/qerror.h" @@ -46,10 +50,29 @@ extern char **environ; #endif #endif +static void ga_wait_child(pid_t pid, int *status, Error **err) +{ + pid_t rpid; + + *status = 0; + + do { + rpid = waitpid(pid, status, 0); + } while (rpid == -1 && errno == EINTR); + + if (rpid == -1) { + error_setg_errno(err, errno, "failed to wait for child (pid: %d)", pid); + return; + } + + g_assert(rpid == pid); +} + void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) { const char *shutdown_flag; - pid_t rpid, pid; + Error *local_err = NULL; + pid_t pid; int status; slog("guest-shutdown called, mode: %s", mode); @@ -60,8 +83,8 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) } else if (strcmp(mode, "reboot") == 0) { shutdown_flag = "-r"; } else { - error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode", - "halt|powerdown|reboot"); + error_setg(err, + "mode is invalid (valid values are: halt|powerdown|reboot"); return; } @@ -77,18 +100,98 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) "hypervisor initiated shutdown", (char*)NULL, environ); _exit(EXIT_FAILURE); } else if (pid < 0) { - goto exit_err; - } - - do { - rpid = waitpid(pid, &status, 0); - } while (rpid == -1 && errno == EINTR); - if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) { + error_setg_errno(err, errno, "failed to create child process"); return; } -exit_err: - error_set(err, QERR_UNDEFINED_ERROR); + ga_wait_child(pid, &status, &local_err); + if (error_is_set(&local_err)) { + error_propagate(err, local_err); + return; + } + + if (!WIFEXITED(status)) { + error_setg(err, "child process has terminated abnormally"); + return; + } + + if (WEXITSTATUS(status)) { + error_setg(err, "child process has failed to shutdown"); + return; + } + + /* succeded */ +} + +int64_t qmp_guest_get_time(Error **errp) +{ + int ret; + qemu_timeval tq; + int64_t time_ns; + + ret = qemu_gettimeofday(&tq); + if (ret < 0) { + error_setg_errno(errp, errno, "Failed to get time"); + return -1; + } + + time_ns = tq.tv_sec * 1000000000LL + tq.tv_usec * 1000; + return time_ns; +} + +void qmp_guest_set_time(int64_t time_ns, Error **errp) +{ + int ret; + int status; + pid_t pid; + Error *local_err = NULL; + struct timeval tv; + + /* year-2038 will overflow in case time_t is 32bit */ + if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) { + error_setg(errp, "Time %" PRId64 " is too large", time_ns); + return; + } + + tv.tv_sec = time_ns / 1000000000; + tv.tv_usec = (time_ns % 1000000000) / 1000; + + ret = settimeofday(&tv, NULL); + if (ret < 0) { + error_setg_errno(errp, errno, "Failed to set time to guest"); + return; + } + + /* Set the Hardware Clock to the current System Time. */ + pid = fork(); + if (pid == 0) { + setsid(); + reopen_fd_to_null(0); + reopen_fd_to_null(1); + reopen_fd_to_null(2); + + execle("/sbin/hwclock", "hwclock", "-w", NULL, environ); + _exit(EXIT_FAILURE); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + return; + } + + ga_wait_child(pid, &status, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + return; + } + + if (!WIFEXITED(status)) { + error_setg(errp, "child process has terminated abnormally"); + return; + } + + if (WEXITSTATUS(status)) { + error_setg(errp, "hwclock failed to set hardware clock to system time"); + return; + } } typedef struct GuestFileHandle { @@ -101,17 +204,25 @@ static struct { QTAILQ_HEAD(, GuestFileHandle) filehandles; } guest_file_state; -static void guest_file_handle_add(FILE *fh) +static int64_t guest_file_handle_add(FILE *fh, Error **errp) { GuestFileHandle *gfh; + int64_t handle; + + handle = ga_get_fd_handle(ga_state, errp); + if (error_is_set(errp)) { + return 0; + } gfh = g_malloc0(sizeof(GuestFileHandle)); - gfh->id = fileno(fh); + gfh->id = handle; gfh->fh = fh; QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); + + return handle; } -static GuestFileHandle *guest_file_handle_find(int64_t id) +static GuestFileHandle *guest_file_handle_find(int64_t id, Error **err) { GuestFileHandle *gfh; @@ -122,6 +233,7 @@ static GuestFileHandle *guest_file_handle_find(int64_t id) } } + error_setg(err, "handle '%" PRId64 "' has not been found", id); return NULL; } @@ -129,7 +241,7 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, E { FILE *fh; int fd; - int64_t ret = -1; + int64_t ret = -1, handle; if (!has_mode) { mode = "r"; @@ -137,7 +249,8 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, E slog("guest-file-open called, filepath: %s, mode: %s", path, mode); fh = fopen(path, mode); if (!fh) { - error_set(err, QERR_OPEN_FILE_FAILED, path); + error_setg_errno(err, errno, "failed to open file '%s' (mode: '%s')", + path, mode); return -1; } @@ -148,30 +261,35 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, E ret = fcntl(fd, F_GETFL); ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK); if (ret == -1) { - error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed"); + error_setg_errno(err, errno, "failed to make file '%s' non-blocking", + path); fclose(fh); return -1; } - guest_file_handle_add(fh); - slog("guest-file-open, handle: %d", fd); - return fd; + handle = guest_file_handle_add(fh, err); + if (error_is_set(err)) { + fclose(fh); + return -1; + } + + slog("guest-file-open, handle: %d", handle); + return handle; } void qmp_guest_file_close(int64_t handle, Error **err) { - GuestFileHandle *gfh = guest_file_handle_find(handle); + GuestFileHandle *gfh = guest_file_handle_find(handle, err); int ret; slog("guest-file-close called, handle: %ld", handle); if (!gfh) { - error_set(err, QERR_FD_NOT_FOUND, "handle"); return; } ret = fclose(gfh->fh); - if (ret == -1) { - error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed"); + if (ret == EOF) { + error_setg_errno(err, errno, "failed to close handle"); return; } @@ -182,21 +300,21 @@ void qmp_guest_file_close(int64_t handle, Error **err) struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, int64_t count, Error **err) { - GuestFileHandle *gfh = guest_file_handle_find(handle); + GuestFileHandle *gfh = guest_file_handle_find(handle, err); GuestFileRead *read_data = NULL; guchar *buf; FILE *fh; size_t read_count; if (!gfh) { - error_set(err, QERR_FD_NOT_FOUND, "handle"); return NULL; } if (!has_count) { count = QGA_READ_COUNT_DEFAULT; } else if (count < 0) { - error_set(err, QERR_INVALID_PARAMETER, "count"); + error_setg(err, "value '%" PRId64 "' is invalid for argument count", + count); return NULL; } @@ -204,8 +322,8 @@ struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, buf = g_malloc0(count+1); read_count = fread(buf, 1, count, fh); if (ferror(fh)) { + error_setg_errno(err, errno, "failed to read file"); slog("guest-file-read failed, handle: %ld", handle); - error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed"); } else { buf[read_count] = 0; read_data = g_malloc0(sizeof(GuestFileRead)); @@ -228,11 +346,10 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, guchar *buf; gsize buf_len; int write_count; - GuestFileHandle *gfh = guest_file_handle_find(handle); + GuestFileHandle *gfh = guest_file_handle_find(handle, err); FILE *fh; if (!gfh) { - error_set(err, QERR_FD_NOT_FOUND, "handle"); return NULL; } @@ -242,15 +359,16 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, if (!has_count) { count = buf_len; } else if (count < 0 || count > buf_len) { + error_setg(err, "value '%" PRId64 "' is invalid for argument count", + count); g_free(buf); - error_set(err, QERR_INVALID_PARAMETER, "count"); return NULL; } write_count = fwrite(buf, 1, count, fh); if (ferror(fh)) { + error_setg_errno(err, errno, "failed to write to file"); slog("guest-file-write failed, handle: %ld", handle); - error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error"); } else { write_data = g_malloc0(sizeof(GuestFileWrite)); write_data->count = write_count; @@ -265,20 +383,19 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, int64_t whence, Error **err) { - GuestFileHandle *gfh = guest_file_handle_find(handle); + GuestFileHandle *gfh = guest_file_handle_find(handle, err); GuestFileSeek *seek_data = NULL; FILE *fh; int ret; if (!gfh) { - error_set(err, QERR_FD_NOT_FOUND, "handle"); return NULL; } fh = gfh->fh; ret = fseek(fh, offset, whence); if (ret == -1) { - error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); + error_setg_errno(err, errno, "failed to seek file"); } else { seek_data = g_malloc0(sizeof(GuestFileRead)); seek_data->position = ftell(fh); @@ -291,19 +408,18 @@ struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, void qmp_guest_file_flush(int64_t handle, Error **err) { - GuestFileHandle *gfh = guest_file_handle_find(handle); + GuestFileHandle *gfh = guest_file_handle_find(handle, err); FILE *fh; int ret; if (!gfh) { - error_set(err, QERR_FD_NOT_FOUND, "handle"); return; } fh = gfh->fh; ret = fflush(fh); if (ret == EOF) { - error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); + error_setg_errno(err, errno, "failed to flush file"); } } @@ -343,7 +459,7 @@ static void free_fs_mount_list(FsMountList *mounts) /* * Walk the mount table and build a list of local file systems */ -static int build_fs_mount_list(FsMountList *mounts) +static void build_fs_mount_list(FsMountList *mounts, Error **err) { struct mntent *ment; FsMount *mount; @@ -352,8 +468,8 @@ static int build_fs_mount_list(FsMountList *mounts) fp = setmntent(mtab, "r"); if (!fp) { - g_warning("fsfreeze: unable to read mtab"); - return -1; + error_setg(err, "failed to open mtab file: '%s'", mtab); + return; } while ((ment = getmntent(fp))) { @@ -377,13 +493,71 @@ static int build_fs_mount_list(FsMountList *mounts) } endmntent(fp); - - return 0; } #endif #if defined(CONFIG_FSFREEZE) +typedef enum { + FSFREEZE_HOOK_THAW = 0, + FSFREEZE_HOOK_FREEZE, +} FsfreezeHookArg; + +const char *fsfreeze_hook_arg_string[] = { + "thaw", + "freeze", +}; + +static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **err) +{ + int status; + pid_t pid; + const char *hook; + const char *arg_str = fsfreeze_hook_arg_string[arg]; + Error *local_err = NULL; + + hook = ga_fsfreeze_hook(ga_state); + if (!hook) { + return; + } + if (access(hook, X_OK) != 0) { + error_setg_errno(err, errno, "can't access fsfreeze hook '%s'", hook); + return; + } + + slog("executing fsfreeze hook with arg '%s'", arg_str); + pid = fork(); + if (pid == 0) { + setsid(); + reopen_fd_to_null(0); + reopen_fd_to_null(1); + reopen_fd_to_null(2); + + execle(hook, hook, arg_str, NULL, environ); + _exit(EXIT_FAILURE); + } else if (pid < 0) { + error_setg_errno(err, errno, "failed to create child process"); + return; + } + + ga_wait_child(pid, &status, &local_err); + if (error_is_set(&local_err)) { + error_propagate(err, local_err); + return; + } + + if (!WIFEXITED(status)) { + error_setg(err, "fsfreeze hook has terminated abnormally"); + return; + } + + status = WEXITSTATUS(status); + if (status) { + error_setg(err, "fsfreeze hook has failed with status %d", status); + return; + } +} + /* * Return status of freeze/thaw */ @@ -405,15 +579,22 @@ int64_t qmp_guest_fsfreeze_freeze(Error **err) int ret = 0, i = 0; FsMountList mounts; struct FsMount *mount; + Error *local_err = NULL; int fd; - char err_msg[512]; slog("guest-fsfreeze called"); + execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err); + if (error_is_set(&local_err)) { + error_propagate(err, local_err); + return -1; + } + QTAILQ_INIT(&mounts); - ret = build_fs_mount_list(&mounts); - if (ret < 0) { - return ret; + build_fs_mount_list(&mounts, &local_err); + if (error_is_set(&local_err)) { + error_propagate(err, local_err); + return -1; } /* cannot risk guest agent blocking itself on a write in this state */ @@ -422,9 +603,7 @@ int64_t qmp_guest_fsfreeze_freeze(Error **err) QTAILQ_FOREACH(mount, &mounts, next) { fd = qemu_open(mount->dirname, O_RDONLY); if (fd == -1) { - sprintf(err_msg, "failed to open %s, %s", mount->dirname, - strerror(errno)); - error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); + error_setg_errno(err, errno, "failed to open %s", mount->dirname); goto error; } @@ -440,9 +619,8 @@ int64_t qmp_guest_fsfreeze_freeze(Error **err) ret = ioctl(fd, FIFREEZE); if (ret == -1) { if (errno != EOPNOTSUPP) { - sprintf(err_msg, "failed to freeze %s, %s", - mount->dirname, strerror(errno)); - error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); + error_setg_errno(err, errno, "failed to freeze %s", + mount->dirname); close(fd); goto error; } @@ -470,12 +648,12 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err) FsMountList mounts; FsMount *mount; int fd, i = 0, logged; + Error *local_err = NULL; QTAILQ_INIT(&mounts); - ret = build_fs_mount_list(&mounts); - if (ret) { - error_set(err, QERR_QGA_COMMAND_FAILED, - "failed to enumerate filesystems"); + build_fs_mount_list(&mounts, &local_err); + if (error_is_set(&local_err)) { + error_propagate(err, local_err); return 0; } @@ -513,18 +691,22 @@ int64_t qmp_guest_fsfreeze_thaw(Error **err) ga_unset_frozen(ga_state); free_fs_mount_list(&mounts); + + execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, err); + return i; } static void guest_fsfreeze_cleanup(void) { - int64_t ret; Error *err = NULL; if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) { - ret = qmp_guest_fsfreeze_thaw(&err); - if (ret < 0 || err) { - slog("failed to clean up frozen filesystems"); + qmp_guest_fsfreeze_thaw(&err); + if (err) { + slog("failed to clean up frozen filesystems: %s", + error_get_pretty(err)); + error_free(err); } } } @@ -540,7 +722,7 @@ void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err) FsMountList mounts; struct FsMount *mount; int fd; - char err_msg[512]; + Error *local_err = NULL; struct fstrim_range r = { .start = 0, .len = -1, @@ -550,17 +732,16 @@ void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err) slog("guest-fstrim called"); QTAILQ_INIT(&mounts); - ret = build_fs_mount_list(&mounts); - if (ret < 0) { + build_fs_mount_list(&mounts, &local_err); + if (error_is_set(&local_err)) { + error_propagate(err, local_err); return; } QTAILQ_FOREACH(mount, &mounts, next) { fd = qemu_open(mount->dirname, O_RDONLY); if (fd == -1) { - sprintf(err_msg, "failed to open %s, %s", mount->dirname, - strerror(errno)); - error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); + error_setg_errno(err, errno, "failed to open %s", mount->dirname); goto error; } @@ -573,9 +754,8 @@ void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **err) ret = ioctl(fd, FITRIM, &r); if (ret == -1) { if (errno != ENOTTY && errno != EOPNOTSUPP) { - sprintf(err_msg, "failed to trim %s, %s", - mount->dirname, strerror(errno)); - error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); + error_setg_errno(err, errno, "failed to trim %s", + mount->dirname); close(fd); goto error; } @@ -596,8 +776,9 @@ error: static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg, const char *sysfile_str, Error **err) { + Error *local_err = NULL; char *pmutils_path; - pid_t pid, rpid; + pid_t pid; int status; pmutils_path = g_find_program_in_path(pmutils_bin); @@ -642,38 +823,46 @@ static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg, } _exit(SUSPEND_NOT_SUPPORTED); + } else if (pid < 0) { + error_setg_errno(err, errno, "failed to create child process"); + goto out; } + ga_wait_child(pid, &status, &local_err); + if (error_is_set(&local_err)) { + error_propagate(err, local_err); + goto out; + } + + if (!WIFEXITED(status)) { + error_setg(err, "child process has terminated abnormally"); + goto out; + } + + switch (WEXITSTATUS(status)) { + case SUSPEND_SUPPORTED: + goto out; + case SUSPEND_NOT_SUPPORTED: + error_setg(err, + "the requested suspend mode is not supported by the guest"); + goto out; + default: + error_setg(err, + "the helper program '%s' returned an unexpected exit status" + " code (%d)", pmutils_path, WEXITSTATUS(status)); + goto out; + } + +out: g_free(pmutils_path); - - if (pid < 0) { - goto undef_err; - } - - do { - rpid = waitpid(pid, &status, 0); - } while (rpid == -1 && errno == EINTR); - if (rpid == pid && WIFEXITED(status)) { - switch (WEXITSTATUS(status)) { - case SUSPEND_SUPPORTED: - return; - case SUSPEND_NOT_SUPPORTED: - error_set(err, QERR_UNSUPPORTED); - return; - default: - goto undef_err; - } - } - -undef_err: - error_set(err, QERR_UNDEFINED_ERROR); } static void guest_suspend(const char *pmutils_bin, const char *sysfile_str, Error **err) { + Error *local_err = NULL; char *pmutils_path; - pid_t rpid, pid; + pid_t pid; int status; pmutils_path = g_find_program_in_path(pmutils_bin); @@ -711,23 +900,29 @@ static void guest_suspend(const char *pmutils_bin, const char *sysfile_str, } _exit(EXIT_SUCCESS); + } else if (pid < 0) { + error_setg_errno(err, errno, "failed to create child process"); + goto out; } + ga_wait_child(pid, &status, &local_err); + if (error_is_set(&local_err)) { + error_propagate(err, local_err); + goto out; + } + + if (!WIFEXITED(status)) { + error_setg(err, "child process has terminated abnormally"); + goto out; + } + + if (WEXITSTATUS(status)) { + error_setg(err, "child process has failed to suspend"); + goto out; + } + +out: g_free(pmutils_path); - - if (pid < 0) { - goto exit_err; - } - - do { - rpid = waitpid(pid, &status, 0); - } while (rpid == -1 && errno == EINTR); - if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) { - return; - } - -exit_err: - error_set(err, QERR_UNDEFINED_ERROR); } void qmp_guest_suspend_disk(Error **err) @@ -780,12 +975,9 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) { GuestNetworkInterfaceList *head = NULL, *cur_item = NULL; struct ifaddrs *ifap, *ifa; - char err_msg[512]; if (getifaddrs(&ifap) < 0) { - snprintf(err_msg, sizeof(err_msg), - "getifaddrs failed: %s", strerror(errno)); - error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); + error_setg_errno(errp, errno, "getifaddrs failed"); goto error; } @@ -821,53 +1013,43 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) /* we haven't obtained HW address yet */ sock = socket(PF_INET, SOCK_STREAM, 0); if (sock == -1) { - snprintf(err_msg, sizeof(err_msg), - "failed to create socket: %s", strerror(errno)); - error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); + error_setg_errno(errp, errno, "failed to create socket"); goto error; } memset(&ifr, 0, sizeof(ifr)); pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->value->name); if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) { - snprintf(err_msg, sizeof(err_msg), - "failed to get MAC address of %s: %s", - ifa->ifa_name, - strerror(errno)); - error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); + error_setg_errno(errp, errno, + "failed to get MAC address of %s", + ifa->ifa_name); + close(sock); goto error; } + close(sock); mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data; - if (asprintf(&info->value->hardware_address, - "%02x:%02x:%02x:%02x:%02x:%02x", - (int) mac_addr[0], (int) mac_addr[1], - (int) mac_addr[2], (int) mac_addr[3], - (int) mac_addr[4], (int) mac_addr[5]) == -1) { - snprintf(err_msg, sizeof(err_msg), - "failed to format MAC: %s", strerror(errno)); - error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); - goto error; - } + info->value->hardware_address = + g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x", + (int) mac_addr[0], (int) mac_addr[1], + (int) mac_addr[2], (int) mac_addr[3], + (int) mac_addr[4], (int) mac_addr[5]); info->value->has_hardware_address = true; - close(sock); } if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) { /* interface with IPv4 address */ - address_item = g_malloc0(sizeof(*address_item)); - address_item->value = g_malloc0(sizeof(*address_item->value)); p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) { - snprintf(err_msg, sizeof(err_msg), - "inet_ntop failed : %s", strerror(errno)); - error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); + error_setg_errno(errp, errno, "inet_ntop failed"); goto error; } + address_item = g_malloc0(sizeof(*address_item)); + address_item->value = g_malloc0(sizeof(*address_item->value)); address_item->value->ip_address = g_strdup(addr4); address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4; @@ -880,16 +1062,14 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) } else if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) { /* interface with IPv6 address */ - address_item = g_malloc0(sizeof(*address_item)); - address_item->value = g_malloc0(sizeof(*address_item->value)); p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) { - snprintf(err_msg, sizeof(err_msg), - "inet_ntop failed : %s", strerror(errno)); - error_set(errp, QERR_QGA_COMMAND_FAILED, err_msg); + error_setg_errno(errp, errno, "inet_ntop failed"); goto error; } + address_item = g_malloc0(sizeof(*address_item)); + address_item->value = g_malloc0(sizeof(*address_item->value)); address_item->value->ip_address = g_strdup(addr6); address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6; @@ -935,6 +1115,162 @@ error: return NULL; } +#define SYSCONF_EXACT(name, err) sysconf_exact((name), #name, (err)) + +static long sysconf_exact(int name, const char *name_str, Error **err) +{ + long ret; + + errno = 0; + ret = sysconf(name); + if (ret == -1) { + if (errno == 0) { + error_setg(err, "sysconf(%s): value indefinite", name_str); + } else { + error_setg_errno(err, errno, "sysconf(%s)", name_str); + } + } + return ret; +} + +/* Transfer online/offline status between @vcpu and the guest system. + * + * On input either @errp or *@errp must be NULL. + * + * In system-to-@vcpu direction, the following @vcpu fields are accessed: + * - R: vcpu->logical_id + * - W: vcpu->online + * - W: vcpu->can_offline + * + * In @vcpu-to-system direction, the following @vcpu fields are accessed: + * - R: vcpu->logical_id + * - R: vcpu->online + * + * Written members remain unmodified on error. + */ +static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu, + Error **errp) +{ + char *dirpath; + int dirfd; + + dirpath = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/", + vcpu->logical_id); + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd == -1) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + } else { + static const char fn[] = "online"; + int fd; + int res; + + fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR); + if (fd == -1) { + if (errno != ENOENT) { + error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn); + } else if (sys2vcpu) { + vcpu->online = true; + vcpu->can_offline = false; + } else if (!vcpu->online) { + error_setg(errp, "logical processor #%" PRId64 " can't be " + "offlined", vcpu->logical_id); + } /* otherwise pretend successful re-onlining */ + } else { + unsigned char status; + + res = pread(fd, &status, 1, 0); + if (res == -1) { + error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn); + } else if (res == 0) { + error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath, + fn); + } else if (sys2vcpu) { + vcpu->online = (status != '0'); + vcpu->can_offline = true; + } else if (vcpu->online != (status != '0')) { + status = '0' + vcpu->online; + if (pwrite(fd, &status, 1, 0) == -1) { + error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath, + fn); + } + } /* otherwise pretend successful re-(on|off)-lining */ + + res = close(fd); + g_assert(res == 0); + } + + res = close(dirfd); + g_assert(res == 0); + } + + g_free(dirpath); +} + +GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) +{ + int64_t current; + GuestLogicalProcessorList *head, **link; + long sc_max; + Error *local_err = NULL; + + current = 0; + head = NULL; + link = &head; + sc_max = SYSCONF_EXACT(_SC_NPROCESSORS_CONF, &local_err); + + while (local_err == NULL && current < sc_max) { + GuestLogicalProcessor *vcpu; + GuestLogicalProcessorList *entry; + + vcpu = g_malloc0(sizeof *vcpu); + vcpu->logical_id = current++; + vcpu->has_can_offline = true; /* lolspeak ftw */ + transfer_vcpu(vcpu, true, &local_err); + + entry = g_malloc0(sizeof *entry); + entry->value = vcpu; + + *link = entry; + link = &entry->next; + } + + if (local_err == NULL) { + /* there's no guest with zero VCPUs */ + g_assert(head != NULL); + return head; + } + + qapi_free_GuestLogicalProcessorList(head); + error_propagate(errp, local_err); + return NULL; +} + +int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) +{ + int64_t processed; + Error *local_err = NULL; + + processed = 0; + while (vcpus != NULL) { + transfer_vcpu(vcpus->value, false, &local_err); + if (local_err != NULL) { + break; + } + ++processed; + vcpus = vcpus->next; + } + + if (local_err != NULL) { + if (processed == 0) { + error_propagate(errp, local_err); + } else { + error_free(local_err); + } + } + + return processed; +} + #else /* defined(__linux__) */ void qmp_guest_suspend_disk(Error **err) @@ -958,6 +1294,18 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) return NULL; } +GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return -1; +} + #endif #if !defined(CONFIG_FSFREEZE) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 7e8ecb3b40..b19be9db48 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -278,6 +278,29 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **err) return NULL; } +int64_t qmp_guest_get_time(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return -1; +} + +void qmp_guest_set_time(int64_t time_ns, Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); +} + +GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return -1; +} + /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { diff --git a/qga/commands.c b/qga/commands.c index 7ffb35e4af..528b082fa8 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -61,7 +61,7 @@ struct GuestAgentInfo *qmp_guest_info(Error **err) while (*cmd_list) { cmd_info = g_malloc0(sizeof(GuestAgentCommandInfo)); - cmd_info->name = strdup(*cmd_list); + cmd_info->name = g_strdup(*cmd_list); cmd_info->enabled = qmp_command_is_enabled(cmd_info->name); cmd_info_list = g_malloc0(sizeof(GuestAgentCommandInfoList)); diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h index 8934163375..624a559d94 100644 --- a/qga/guest-agent-core.h +++ b/qga/guest-agent-core.h @@ -34,6 +34,8 @@ void ga_set_response_delimited(GAState *s); bool ga_is_frozen(GAState *s); void ga_set_frozen(GAState *s); void ga_unset_frozen(GAState *s); +const char *ga_fsfreeze_hook(GAState *s); +int64_t ga_get_fd_handle(GAState *s, Error **errp); #ifndef _WIN32 void reopen_fd_to_null(int fd); diff --git a/qga/main.c b/qga/main.c index ba5fa1c778..74ef7885b2 100644 --- a/qga/main.c +++ b/qga/main.c @@ -15,6 +15,7 @@ #include #include #include +#include #ifndef _WIN32 #include #include @@ -30,10 +31,17 @@ #include "qapi/qmp/qerror.h" #include "qapi/qmp/dispatch.h" #include "qga/channel.h" +#include "qemu/bswap.h" #ifdef _WIN32 #include "qga/service-win32.h" #include #endif +#ifdef __linux__ +#include +#ifdef FIFREEZE +#define CONFIG_FSFREEZE +#endif +#endif #ifndef _WIN32 #define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0" @@ -42,8 +50,16 @@ #endif #define QGA_STATEDIR_DEFAULT CONFIG_QEMU_LOCALSTATEDIR "/run" #define QGA_PIDFILE_DEFAULT QGA_STATEDIR_DEFAULT "/qemu-ga.pid" +#ifdef CONFIG_FSFREEZE +#define QGA_FSFREEZE_HOOK_DEFAULT CONFIG_QEMU_CONFDIR "/fsfreeze-hook" +#endif #define QGA_SENTINEL_BYTE 0xFF +typedef struct GAPersistentState { +#define QGA_PSTATE_DEFAULT_FD_COUNTER 1000 + int64_t fd_counter; +} GAPersistentState; + struct GAState { JSONMessageParser parser; GMainLoop *main_loop; @@ -64,6 +80,11 @@ struct GAState { const char *log_filepath; const char *pid_filepath; } deferred_options; +#ifdef CONFIG_FSFREEZE + const char *fsfreeze_hook; +#endif + const gchar *pstate_filepath; + GAPersistentState pstate; }; struct GAState *ga_state; @@ -73,6 +94,7 @@ static const char *ga_freeze_whitelist[] = { "guest-ping", "guest-info", "guest-sync", + "guest-sync-delimited", "guest-fsfreeze-status", "guest-fsfreeze-thaw", NULL @@ -153,6 +175,16 @@ static void usage(const char *cmd) " %s)\n" " -l, --logfile set logfile path, logs to stderr by default\n" " -f, --pidfile specify pidfile (default is %s)\n" +#ifdef CONFIG_FSFREEZE +" -F, --fsfreeze-hook\n" +" enable fsfreeze hook. Accepts an optional argument that\n" +" specifies script to run on freeze/thaw. Script will be\n" +" called with 'freeze'/'thaw' arguments accordingly.\n" +" (default is %s)\n" +" If using -F with an argument, do not follow -F with a\n" +" space.\n" +" (for example: -F/var/run/fsfreezehook.sh)\n" +#endif " -t, --statedir specify dir to store state information (absolute paths\n" " only, default is %s)\n" " -v, --verbose log extra debugging information\n" @@ -167,6 +199,9 @@ static void usage(const char *cmd) "\n" "Report bugs to \n" , cmd, QEMU_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_PIDFILE_DEFAULT, +#ifdef CONFIG_FSFREEZE + QGA_FSFREEZE_HOOK_DEFAULT, +#endif QGA_STATEDIR_DEFAULT); } @@ -236,13 +271,26 @@ void ga_set_response_delimited(GAState *s) s->delimit_response = true; } +static FILE *ga_open_logfile(const char *logfile) +{ + FILE *f; + + f = fopen(logfile, "a"); + if (!f) { + return NULL; + } + + qemu_set_cloexec(fileno(f)); + return f; +} + #ifndef _WIN32 static bool ga_open_pidfile(const char *pidfile) { int pidfd; char pidstr[32]; - pidfd = open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR); + pidfd = qemu_open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR); if (pidfd == -1 || lockf(pidfd, F_TLOCK, 0)) { g_critical("Cannot lock pid file, %s", strerror(errno)); if (pidfd != -1) { @@ -251,7 +299,7 @@ static bool ga_open_pidfile(const char *pidfile) return false; } - if (ftruncate(pidfd, 0) || lseek(pidfd, 0, SEEK_SET)) { + if (ftruncate(pidfd, 0)) { g_critical("Failed to truncate pid file"); goto fail; } @@ -261,10 +309,12 @@ static bool ga_open_pidfile(const char *pidfile) goto fail; } + /* keep pidfile open & locked forever */ return true; fail: unlink(pidfile); + close(pidfd); return false; } #else /* _WIN32 */ @@ -377,7 +427,7 @@ void ga_unset_frozen(GAState *s) * in a frozen state at start up, do it now */ if (s->deferred_options.log_filepath) { - s->log_file = fopen(s->deferred_options.log_filepath, "a"); + s->log_file = ga_open_logfile(s->deferred_options.log_filepath); if (!s->log_file) { s->log_file = stderr; } @@ -401,6 +451,13 @@ void ga_unset_frozen(GAState *s) } } +#ifdef CONFIG_FSFREEZE +const char *ga_fsfreeze_hook(GAState *s) +{ + return s->fsfreeze_hook; +} +#endif + static void become_daemon(const char *pidfile) { #ifndef _WIN32 @@ -573,6 +630,7 @@ static gboolean channel_event_cb(GIOCondition condition, gpointer data) if (!s->virtio) { return false; } + /* fall through */ case G_IO_STATUS_AGAIN: /* virtio causes us to spin here when no process is attached to * host-side chardev. sleep a bit to mitigate this @@ -676,12 +734,180 @@ VOID WINAPI service_main(DWORD argc, TCHAR *argv[]) } #endif +static void set_persistent_state_defaults(GAPersistentState *pstate) +{ + g_assert(pstate); + pstate->fd_counter = QGA_PSTATE_DEFAULT_FD_COUNTER; +} + +static void persistent_state_from_keyfile(GAPersistentState *pstate, + GKeyFile *keyfile) +{ + g_assert(pstate); + g_assert(keyfile); + /* if any fields are missing, either because the file was tampered with + * by agents of chaos, or because the field wasn't present at the time the + * file was created, the best we can ever do is start over with the default + * values. so load them now, and ignore any errors in accessing key-value + * pairs + */ + set_persistent_state_defaults(pstate); + + if (g_key_file_has_key(keyfile, "global", "fd_counter", NULL)) { + pstate->fd_counter = + g_key_file_get_integer(keyfile, "global", "fd_counter", NULL); + } +} + +static void persistent_state_to_keyfile(const GAPersistentState *pstate, + GKeyFile *keyfile) +{ + g_assert(pstate); + g_assert(keyfile); + + g_key_file_set_integer(keyfile, "global", "fd_counter", pstate->fd_counter); +} + +static gboolean write_persistent_state(const GAPersistentState *pstate, + const gchar *path) +{ + GKeyFile *keyfile = g_key_file_new(); + GError *gerr = NULL; + gboolean ret = true; + gchar *data = NULL; + gsize data_len; + + g_assert(pstate); + + persistent_state_to_keyfile(pstate, keyfile); + data = g_key_file_to_data(keyfile, &data_len, &gerr); + if (gerr) { + g_critical("failed to convert persistent state to string: %s", + gerr->message); + ret = false; + goto out; + } + + g_file_set_contents(path, data, data_len, &gerr); + if (gerr) { + g_critical("failed to write persistent state to %s: %s", + path, gerr->message); + ret = false; + goto out; + } + +out: + if (gerr) { + g_error_free(gerr); + } + if (keyfile) { + g_key_file_free(keyfile); + } + g_free(data); + return ret; +} + +static gboolean read_persistent_state(GAPersistentState *pstate, + const gchar *path, gboolean frozen) +{ + GKeyFile *keyfile = NULL; + GError *gerr = NULL; + struct stat st; + gboolean ret = true; + + g_assert(pstate); + + if (stat(path, &st) == -1) { + /* it's okay if state file doesn't exist, but any other error + * indicates a permissions issue or some other misconfiguration + * that we likely won't be able to recover from. + */ + if (errno != ENOENT) { + g_critical("unable to access state file at path %s: %s", + path, strerror(errno)); + ret = false; + goto out; + } + + /* file doesn't exist. initialize state to default values and + * attempt to save now. (we could wait till later when we have + * modified state we need to commit, but if there's a problem, + * such as a missing parent directory, we want to catch it now) + * + * there is a potential scenario where someone either managed to + * update the agent from a version that didn't use a key store + * while qemu-ga thought the filesystem was frozen, or + * deleted the key store prior to issuing a fsfreeze, prior + * to restarting the agent. in this case we go ahead and defer + * initial creation till we actually have modified state to + * write, otherwise fail to recover from freeze. + */ + set_persistent_state_defaults(pstate); + if (!frozen) { + ret = write_persistent_state(pstate, path); + if (!ret) { + g_critical("unable to create state file at path %s", path); + ret = false; + goto out; + } + } + ret = true; + goto out; + } + + keyfile = g_key_file_new(); + g_key_file_load_from_file(keyfile, path, 0, &gerr); + if (gerr) { + g_critical("error loading persistent state from path: %s, %s", + path, gerr->message); + ret = false; + goto out; + } + + persistent_state_from_keyfile(pstate, keyfile); + +out: + if (keyfile) { + g_key_file_free(keyfile); + } + if (gerr) { + g_error_free(gerr); + } + + return ret; +} + +int64_t ga_get_fd_handle(GAState *s, Error **errp) +{ + int64_t handle; + + g_assert(s->pstate_filepath); + /* we blacklist commands and avoid operations that potentially require + * writing to disk when we're in a frozen state. this includes opening + * new files, so we should never get here in that situation + */ + g_assert(!ga_is_frozen(s)); + + handle = s->pstate.fd_counter++; + if (s->pstate.fd_counter < 0) { + s->pstate.fd_counter = 0; + } + if (!write_persistent_state(&s->pstate, s->pstate_filepath)) { + error_setg(errp, "failed to commit persistent state to disk"); + } + + return handle; +} + int main(int argc, char **argv) { - const char *sopt = "hVvdm:p:l:f:b:s:t:"; + const char *sopt = "hVvdm:p:l:f:F::b:s:t:"; const char *method = NULL, *path = NULL; const char *log_filepath = NULL; const char *pid_filepath = QGA_PIDFILE_DEFAULT; +#ifdef CONFIG_FSFREEZE + const char *fsfreeze_hook = NULL; +#endif const char *state_dir = QGA_STATEDIR_DEFAULT; #ifdef _WIN32 const char *service = NULL; @@ -691,6 +917,9 @@ int main(int argc, char **argv) { "version", 0, NULL, 'V' }, { "logfile", 1, NULL, 'l' }, { "pidfile", 1, NULL, 'f' }, +#ifdef CONFIG_FSFREEZE + { "fsfreeze-hook", 2, NULL, 'F' }, +#endif { "verbose", 0, NULL, 'v' }, { "method", 1, NULL, 'm' }, { "path", 1, NULL, 'p' }, @@ -723,6 +952,11 @@ int main(int argc, char **argv) case 'f': pid_filepath = optarg; break; +#ifdef CONFIG_FSFREEZE + case 'F': + fsfreeze_hook = optarg ? optarg : QGA_FSFREEZE_HOOK_DEFAULT; + break; +#endif case 't': state_dir = optarg; break; @@ -786,12 +1020,17 @@ int main(int argc, char **argv) s = g_malloc0(sizeof(GAState)); s->log_level = log_level; s->log_file = stderr; +#ifdef CONFIG_FSFREEZE + s->fsfreeze_hook = fsfreeze_hook; +#endif g_log_set_default_handler(ga_log, s); g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR); ga_enable_logging(s); s->state_filepath_isfrozen = g_strdup_printf("%s/qga.state.isfrozen", state_dir); + s->pstate_filepath = g_strdup_printf("%s/qga.state", state_dir); s->frozen = false; + #ifndef _WIN32 /* check if a previous instance of qemu-ga exited with filesystems' state * marked as frozen. this could be a stale value (a non-qemu-ga process @@ -838,7 +1077,7 @@ int main(int argc, char **argv) become_daemon(pid_filepath); } if (log_filepath) { - FILE *log_file = fopen(log_filepath, "a"); + FILE *log_file = ga_open_logfile(log_filepath); if (!log_file) { g_critical("unable to open specified log file: %s", strerror(errno)); @@ -848,6 +1087,14 @@ int main(int argc, char **argv) } } + /* load persistent state from disk */ + if (!read_persistent_state(&s->pstate, + s->pstate_filepath, + ga_is_frozen(s))) { + g_critical("failed to load persistent state"); + goto out_bad; + } + if (blacklist) { s->blacklist = blacklist; do { diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index ed0eb698c6..dac4e6f95f 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -31,7 +31,7 @@ # # Since: 1.1 # ## -{ 'command': 'guest-sync-delimited' +{ 'command': 'guest-sync-delimited', 'data': { 'id': 'int' }, 'returns': 'int' } @@ -69,7 +69,7 @@ # # Since: 0.15.0 ## -{ 'command': 'guest-sync' +{ 'command': 'guest-sync', 'data': { 'id': 'int' }, 'returns': 'int' } @@ -82,6 +82,45 @@ ## { 'command': 'guest-ping' } +## +# @guest-get-time: +# +# Get the information about guest time relative to the Epoch +# of 1970-01-01 in UTC. +# +# Returns: Time in nanoseconds. +# +# Since 1.5 +## +{ 'command': 'guest-get-time', + 'returns': 'int' } + +## +# @guest-set-time: +# +# Set guest time. +# +# When a guest is paused or migrated to a file then loaded +# from that file, the guest OS has no idea that there +# was a big gap in the time. Depending on how long the +# gap was, NTP might not be able to resynchronize the +# guest. +# +# This command tries to set guest time to the given value, +# then sets the Hardware Clock to the current System Time. +# This will make it easier for a guest to resynchronize +# without waiting for NTP. +# +# @time: time of nanoseconds, relative to the Epoch of +# 1970-01-01 in UTC. +# +# Returns: Nothing on success. +# +# Since: 1.5 +## +{ 'command': 'guest-set-time', + 'data': { 'time': 'int' } } + ## # @GuestAgentCommandInfo: # @@ -515,3 +554,75 @@ ## { 'command': 'guest-network-get-interfaces', 'returns': ['GuestNetworkInterface'] } + +## +# @GuestLogicalProcessor: +# +# @logical-id: Arbitrary guest-specific unique identifier of the VCPU. +# +# @online: Whether the VCPU is enabled. +# +# @can-offline: Whether offlining the VCPU is possible. This member is always +# filled in by the guest agent when the structure is returned, +# and always ignored on input (hence it can be omitted then). +# +# Since: 1.5 +## +{ 'type': 'GuestLogicalProcessor', + 'data': {'logical-id': 'int', + 'online': 'bool', + '*can-offline': 'bool'} } + +## +# @guest-get-vcpus: +# +# Retrieve the list of the guest's logical processors. +# +# This is a read-only operation. +# +# Returns: The list of all VCPUs the guest knows about. Each VCPU is put on the +# list exactly once, but their order is unspecified. +# +# Since: 1.5 +## +{ 'command': 'guest-get-vcpus', + 'returns': ['GuestLogicalProcessor'] } + +## +# @guest-set-vcpus: +# +# Attempt to reconfigure (currently: enable/disable) logical processors inside +# the guest. +# +# The input list is processed node by node in order. In each node @logical-id +# is used to look up the guest VCPU, for which @online specifies the requested +# state. The set of distinct @logical-id's is only required to be a subset of +# the guest-supported identifiers. There's no restriction on list length or on +# repeating the same @logical-id (with possibly different @online field). +# Preferably the input list should describe a modified subset of +# @guest-get-vcpus' return value. +# +# Returns: The length of the initial sublist that has been successfully +# processed. The guest agent maximizes this value. Possible cases: +# +# 0: if the @vcpus list was empty on input. Guest state +# has not been changed. Otherwise, +# +# Error: processing the first node of @vcpus failed for the +# reason returned. Guest state has not been changed. +# Otherwise, +# +# < length(@vcpus): more than zero initial nodes have been processed, +# but not the entire @vcpus list. Guest state has +# changed accordingly. To retrieve the error +# (assuming it persists), repeat the call with the +# successfully processed initial sublist removed. +# Otherwise, +# +# length(@vcpus): call successful. +# +# Since: 1.5 +## +{ 'command': 'guest-set-vcpus', + 'data': {'vcpus': ['GuestLogicalProcessor'] }, + 'returns': 'int' } diff --git a/qga/service-win32.c b/qga/service-win32.c index 09054565d3..843398a6c6 100644 --- a/qga/service-win32.c +++ b/qga/service-win32.c @@ -29,7 +29,7 @@ static int printf_win_error(const char *text) MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *)&message, 0, NULL); - n = printf("%s. (Error: %d) %s", text, err, message); + n = printf("%s. (Error: %d) %s", text, (int)err, message); LocalFree(message); return n; diff --git a/qmp-commands.hx b/qmp-commands.hx index 5c692d0cb5..b370060848 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -463,6 +463,73 @@ Example: Note: inject-nmi fails when the guest doesn't support injecting. Currently, only x86 guests do. +EQMP + + { + .name = "ringbuf-write", + .args_type = "device:s,data:s,format:s?", + .mhandler.cmd_new = qmp_marshal_input_ringbuf_write, + }, + +SQMP +ringbuf-write +------------- + +Write to a ring buffer character device. + +Arguments: + +- "device": ring buffer character device name (json-string) +- "data": data to write (json-string) +- "format": data format (json-string, optional) + - Possible values: "utf8" (default), "base64" + Bug: invalid base64 is currently not rejected. + Whitespace *is* invalid. + +Example: + +-> { "execute": "ringbuf-write", + "arguments": { "device": "foo", + "data": "abcdefgh", + "format": "utf8" } } +<- { "return": {} } + +EQMP + + { + .name = "ringbuf-read", + .args_type = "device:s,size:i,format:s?", + .mhandler.cmd_new = qmp_marshal_input_ringbuf_read, + }, + +SQMP +ringbuf-read +------------- + +Read from a ring buffer character device. + +Arguments: + +- "device": ring buffer character device name (json-string) +- "size": how many bytes to read at most (json-int) + - Number of data bytes, not number of characters in encoded data +- "format": data format (json-string, optional) + - Possible values: "utf8" (default), "base64" + - Naturally, format "utf8" works only when the ring buffer + contains valid UTF-8 text. Invalid UTF-8 sequences get + replaced. Bug: replacement doesn't work. Bug: can screw + up on encountering NUL characters, after the ring buffer + lost data, and when reading stops because the size limit + is reached. + +Example: + +-> { "execute": "ringbuf-read", + "arguments": { "device": "foo", + "size": 1000, + "format": "utf8" } } +<- {"return": "abcdefgh"} + EQMP { @@ -755,7 +822,7 @@ Example: -> { "execute": "netdev_add", "arguments": { "type": "user", "id": "netdev1" } } <- { "return": {} } -Note: The supported device options are the same ones supported by the '-net' +Note: The supported device options are the same ones supported by the '-netdev' command-line argument, which are listed in the '-help' output or QEMU's manual @@ -938,7 +1005,8 @@ EQMP { .name = "drive-mirror", .args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?," - "on-source-error:s?,on-target-error:s?", + "on-source-error:s?,on-target-error:s?," + "granularity:i?,buf-size:i?", .mhandler.cmd_new = qmp_marshal_input_drive_mirror, }, @@ -962,6 +1030,9 @@ Arguments: file/device (NewImageMode, optional, default 'absolute-paths') - "speed": maximum speed of the streaming job, in bytes per second (json-int) +- "granularity": granularity of the dirty bitmap, in bytes (json-int, optional) +- "buf_size": maximum amount of data in flight from source to target, in bytes + (json-int, default 10M) - "sync": what parts of the disk image should be copied to the destination; possibilities include "full" for all the disk, "top" for only the sectors allocated in the topmost image, or "none" to only replicate new I/O @@ -971,6 +1042,10 @@ Arguments: - "on-target-error": the action to take on an error on the target (BlockdevOnError, default 'report') +The default value of the granularity is the image cluster size clamped +between 4096 and 65536, if the image format defines one. If the format +does not define a cluster size, the default value of the granularity +is 65536. Example: @@ -1585,7 +1660,7 @@ Each json-object contain the following: - Possible values: "unknown" - "removable": true if the device is removable, false otherwise (json-bool) - "locked": true if the device is locked, false otherwise (json-bool) -- "tray-open": only present if removable, true if the device has a tray, +- "tray_open": only present if removable, true if the device has a tray, and it is open (json-bool) - "inserted": only present if the device is inserted, it is a json-object containing the following: @@ -2527,10 +2602,8 @@ Arguments: Example: -> { "execute": "query-migrate-capabilities" } -<- { "return": { - "capabilities" : [ { "capability" : "xbzrle", "state" : false } ] - } - } +<- { "return": [ { "state": false, "capability": "xbzrle" } ] } + EQMP { @@ -2549,13 +2622,6 @@ Make an asynchronous request for balloon info. When the request completes a json-object will be returned containing the following data: - "actual": current balloon value in bytes (json-int) -- "mem_swapped_in": Amount of memory swapped in bytes (json-int, optional) -- "mem_swapped_out": Amount of memory swapped out in bytes (json-int, optional) -- "major_page_faults": Number of major faults (json-int, optional) -- "minor_page_faults": Number of minor faults (json-int, optional) -- "free_mem": Total amount of free and unused memory in - bytes (json-int, optional) -- "total_mem": Total amount of available memory in bytes (json-int, optional) Example: @@ -2563,12 +2629,6 @@ Example: <- { "return":{ "actual":1073741824, - "mem_swapped_in":0, - "mem_swapped_out":0, - "major_page_faults":142, - "minor_page_faults":239245, - "free_mem":1014185984, - "total_mem":1044668416 } } @@ -2654,3 +2714,82 @@ EQMP .args_type = "", .mhandler.cmd_new = qmp_marshal_input_query_target, }, + + { + .name = "query-tpm", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_tpm, + }, + + { + .name = "query-tpm-models", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_tpm_models, + }, + + { + .name = "query-tpm-types", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_tpm_types, + }, + + { + .name = "chardev-add", + .args_type = "id:s,backend:q", + .mhandler.cmd_new = qmp_marshal_input_chardev_add, + }, + +SQMP +chardev-add +---------------- + +Add a chardev. + +Arguments: + +- "id": the chardev's ID, must be unique (json-string) +- "backend": chardev backend type + parameters + +Examples: + +-> { "execute" : "chardev-add", + "arguments" : { "id" : "foo", + "backend" : { "type" : "null", "data" : {} } } } +<- { "return": {} } + +-> { "execute" : "chardev-add", + "arguments" : { "id" : "bar", + "backend" : { "type" : "file", + "data" : { "out" : "/tmp/bar.log" } } } } +<- { "return": {} } + +-> { "execute" : "chardev-add", + "arguments" : { "id" : "baz", + "backend" : { "type" : "pty", "data" : {} } } } +<- { "return": { "pty" : "/dev/pty/42" } } + +EQMP + + { + .name = "chardev-remove", + .args_type = "id:s", + .mhandler.cmd_new = qmp_marshal_input_chardev_remove, + }, + + +SQMP +chardev-remove +-------------- + +Remove a chardev. + +Arguments: + +- "id": the chardev's ID, must exist and not be in use (json-string) + +Example: + +-> { "execute": "chardev-remove", "arguments": { "id" : "foo" } } +<- { "return": {} } + +EQMP diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs new file mode 100644 index 0000000000..c9ff59c6cc --- /dev/null +++ b/qobject/Makefile.objs @@ -0,0 +1,3 @@ +util-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o +util-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o +util-obj-y += qerror.o diff --git a/json-lexer.c b/qobject/json-lexer.c similarity index 100% rename from json-lexer.c rename to qobject/json-lexer.c diff --git a/json-parser.c b/qobject/json-parser.c similarity index 100% rename from json-parser.c rename to qobject/json-parser.c diff --git a/json-streamer.c b/qobject/json-streamer.c similarity index 100% rename from json-streamer.c rename to qobject/json-streamer.c diff --git a/qbool.c b/qobject/qbool.c similarity index 100% rename from qbool.c rename to qobject/qbool.c diff --git a/qdict.c b/qobject/qdict.c similarity index 95% rename from qdict.c rename to qobject/qdict.c index 7543ccc10f..ed381f9a50 100644 --- a/qdict.c +++ b/qobject/qdict.c @@ -400,6 +400,28 @@ const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry) return ret; } +/** + * qdict_clone_shallow(): Clones a given QDict. Its entries are not copied, but + * another reference is added. + */ +QDict *qdict_clone_shallow(const QDict *src) +{ + QDict *dest; + QDictEntry *entry; + int i; + + dest = qdict_new(); + + for (i = 0; i < QDICT_BUCKET_MAX; i++) { + QLIST_FOREACH(entry, &src->table[i], next) { + qobject_incref(entry->value); + qdict_put_obj(dest, entry->key, entry->value); + } + } + + return dest; +} + /** * qentry_destroy(): Free all the memory allocated by a QDictEntry */ diff --git a/qerror.c b/qobject/qerror.c similarity index 100% rename from qerror.c rename to qobject/qerror.c diff --git a/qfloat.c b/qobject/qfloat.c similarity index 100% rename from qfloat.c rename to qobject/qfloat.c diff --git a/qint.c b/qobject/qint.c similarity index 100% rename from qint.c rename to qobject/qint.c diff --git a/qjson.c b/qobject/qjson.c similarity index 100% rename from qjson.c rename to qobject/qjson.c diff --git a/qlist.c b/qobject/qlist.c similarity index 100% rename from qlist.c rename to qobject/qlist.c diff --git a/qstring.c b/qobject/qstring.c similarity index 100% rename from qstring.c rename to qobject/qstring.c diff --git a/qom/Makefile.objs b/qom/Makefile.objs index 5ef060a401..6a93ac7398 100644 --- a/qom/Makefile.objs +++ b/qom/Makefile.objs @@ -1,4 +1,2 @@ -qom-obj-y = object.o container.o qom-qobject.o -qom-obj-twice-y = cpu.o -common-obj-y = $(qom-obj-twice-y) -user-obj-y = $(qom-obj-twice-y) +common-obj-y = object.o container.o qom-qobject.o +common-obj-y += cpu.o diff --git a/qom/container.c b/qom/container.c index 5270a5ee9c..62b1648add 100644 --- a/qom/container.c +++ b/qom/container.c @@ -14,7 +14,7 @@ #include "qemu/module.h" #include -static TypeInfo container_info = { +static const TypeInfo container_info = { .name = "container", .instance_size = sizeof(Object), .parent = TYPE_OBJECT, diff --git a/qom/cpu.c b/qom/cpu.c index d4d436f80a..e242dcbeb4 100644 --- a/qom/cpu.c +++ b/qom/cpu.c @@ -21,6 +21,11 @@ #include "qom/cpu.h" #include "qemu-common.h" +void cpu_reset_interrupt(CPUState *cpu, int mask) +{ + cpu->interrupt_request &= ~mask; +} + void cpu_reset(CPUState *cpu) { CPUClass *klass = CPU_GET_CLASS(cpu); @@ -31,19 +36,43 @@ void cpu_reset(CPUState *cpu) } static void cpu_common_reset(CPUState *cpu) +{ + cpu->exit_request = 0; + cpu->interrupt_request = 0; + cpu->current_tb = NULL; + cpu->halted = 0; +} + +ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) +{ + CPUClass *cc = CPU_CLASS(object_class_by_name(typename)); + + return cc->class_by_name(cpu_model); +} + +static ObjectClass *cpu_common_class_by_name(const char *cpu_model) +{ + return NULL; +} + +static void cpu_common_realizefn(DeviceState *dev, Error **errp) { } static void cpu_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); CPUClass *k = CPU_CLASS(klass); + k->class_by_name = cpu_common_class_by_name; k->reset = cpu_common_reset; + dc->realize = cpu_common_realizefn; + dc->no_user = 1; } -static TypeInfo cpu_type_info = { +static const TypeInfo cpu_type_info = { .name = TYPE_CPU, - .parent = TYPE_OBJECT, + .parent = TYPE_DEVICE, .instance_size = sizeof(CPUState), .abstract = true, .class_size = sizeof(CPUClass), diff --git a/qom/object.c b/qom/object.c index 351b88c817..3d638ff273 100644 --- a/qom/object.c +++ b/qom/object.c @@ -245,6 +245,7 @@ static void type_initialize(TypeImpl *ti) g_assert(parent->class_size <= ti->class_size); memcpy(ti->class, parent->class, parent->class_size); + ti->class->interfaces = NULL; for (e = parent->class->interfaces; e; e = e->next) { ObjectClass *iface = e->data; @@ -361,12 +362,14 @@ static void object_property_del_child(Object *obj, Object *child, Error **errp) void object_unparent(Object *obj) { + object_ref(obj); if (obj->parent) { object_property_del_child(obj->parent, obj, NULL); } if (obj->class->unparent) { (obj->class->unparent)(obj); } + object_unref(obj); } static void object_deinit(Object *obj, TypeImpl *type) @@ -415,13 +418,6 @@ Object *object_new(const char *typename) return object_new_with_type(ti); } -void object_delete(Object *obj) -{ - object_unparent(obj); - g_assert(obj->ref == 1); - object_unref(obj); -} - Object *object_dynamic_cast(Object *obj, const char *typename) { if (obj && object_class_dynamic_cast(object_get_class(obj), typename)) { @@ -453,7 +449,8 @@ ObjectClass *object_class_dynamic_cast(ObjectClass *class, TypeImpl *type = class->type; ObjectClass *ret = NULL; - if (type->num_interfaces && type_is_ancestor(target_type, type_interface)) { + if (type->class->interfaces && + type_is_ancestor(target_type, type_interface)) { int found = 0; GSList *i; @@ -501,6 +498,11 @@ ObjectClass *object_get_class(Object *obj) return obj->class; } +bool object_class_is_abstract(ObjectClass *klass) +{ + return klass->type->abstract; +} + const char *object_class_get_name(ObjectClass *klass) { return klass->type->name; @@ -1017,7 +1019,7 @@ gchar *object_get_canonical_path(Object *obj) return newpath; } -Object *object_resolve_path_component(Object *parent, gchar *part) +Object *object_resolve_path_component(Object *parent, const gchar *part) { ObjectProperty *prop = object_property_find(parent, part, NULL); if (prop == NULL) { diff --git a/qtest.c b/qtest.c index c9b58ceb8b..5e0e9ec791 100644 --- a/qtest.c +++ b/qtest.c @@ -24,7 +24,7 @@ const char *qtest_chrdev; const char *qtest_log; -int qtest_allowed = 0; +bool qtest_allowed; static DeviceState *irq_intercept_dev; static FILE *qtest_log_fp; @@ -87,6 +87,30 @@ static bool qtest_opened; * > inl ADDR * < OK VALUE * + * > writeb ADDR VALUE + * < OK + * + * > writew ADDR VALUE + * < OK + * + * > writel ADDR VALUE + * < OK + * + * > writeq ADDR VALUE + * < OK + * + * > readb ADDR + * < OK VALUE + * + * > readw ADDR + * < OK VALUE + * + * > readl ADDR + * < OK VALUE + * + * > readq ADDR + * < OK VALUE + * * > read ADDR SIZE * < OK DATA * @@ -277,13 +301,70 @@ static void qtest_process_command(CharDriverState *chr, gchar **words) } qtest_send_prefix(chr); qtest_send(chr, "OK 0x%04x\n", value); + } else if (strcmp(words[0], "writeb") == 0 || + strcmp(words[0], "writew") == 0 || + strcmp(words[0], "writel") == 0 || + strcmp(words[0], "writeq") == 0) { + uint64_t addr; + uint64_t value; + + g_assert(words[1] && words[2]); + addr = strtoull(words[1], NULL, 0); + value = strtoull(words[2], NULL, 0); + + if (words[0][5] == 'b') { + uint8_t data = value; + cpu_physical_memory_write(addr, &data, 1); + } else if (words[0][5] == 'w') { + uint16_t data = value; + tswap16s(&data); + cpu_physical_memory_write(addr, &data, 2); + } else if (words[0][5] == 'l') { + uint32_t data = value; + tswap32s(&data); + cpu_physical_memory_write(addr, &data, 4); + } else if (words[0][5] == 'q') { + uint64_t data = value; + tswap64s(&data); + cpu_physical_memory_write(addr, &data, 8); + } + qtest_send_prefix(chr); + qtest_send(chr, "OK\n"); + } else if (strcmp(words[0], "readb") == 0 || + strcmp(words[0], "readw") == 0 || + strcmp(words[0], "readl") == 0 || + strcmp(words[0], "readq") == 0) { + uint64_t addr; + uint64_t value = UINT64_C(-1); + + g_assert(words[1]); + addr = strtoull(words[1], NULL, 0); + + if (words[0][4] == 'b') { + uint8_t data; + cpu_physical_memory_read(addr, &data, 1); + value = data; + } else if (words[0][4] == 'w') { + uint16_t data; + cpu_physical_memory_read(addr, &data, 2); + value = tswap16(data); + } else if (words[0][4] == 'l') { + uint32_t data; + cpu_physical_memory_read(addr, &data, 4); + value = tswap32(data); + } else if (words[0][4] == 'q') { + cpu_physical_memory_read(addr, &value, 8); + tswap64s(&value); + } + qtest_send_prefix(chr); + qtest_send(chr, "OK 0x%016" PRIx64 "\n", value); } else if (strcmp(words[0], "read") == 0) { uint64_t addr, len, i; uint8_t *data; g_assert(words[1] && words[2]); - addr = strtoul(words[1], NULL, 0); - len = strtoul(words[2], NULL, 0); + addr = strtoull(words[1], NULL, 0); + len = strtoull(words[2], NULL, 0); data = g_malloc(len); cpu_physical_memory_read(addr, data, len); @@ -302,8 +383,8 @@ static void qtest_process_command(CharDriverState *chr, gchar **words) size_t data_len; g_assert(words[1] && words[2] && words[3]); - addr = strtoul(words[1], NULL, 0); - len = strtoul(words[2], NULL, 0); + addr = strtoull(words[1], NULL, 0); + len = strtoull(words[2], NULL, 0); data_len = strlen(words[3]); if (data_len < 3) { diff --git a/readline.c b/readline.c index 5fc9643c2b..d6e04d4796 100644 --- a/readline.c +++ b/readline.c @@ -247,14 +247,14 @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline) } if (idx == READLINE_MAX_CMDS) { /* Need to get one free slot */ - free(rs->history[0]); - memcpy(rs->history, &rs->history[1], - (READLINE_MAX_CMDS - 1) * sizeof(char *)); + g_free(rs->history[0]); + memmove(rs->history, &rs->history[1], + (READLINE_MAX_CMDS - 1) * sizeof(char *)); rs->history[READLINE_MAX_CMDS - 1] = NULL; idx = READLINE_MAX_CMDS - 1; } if (new_entry == NULL) - new_entry = strdup(cmdline); + new_entry = g_strdup(cmdline); rs->history[idx] = new_entry; rs->hist_entry = -1; } diff --git a/roms/seabios b/roms/seabios index e8a76b0f22..88cb66ea54 160000 --- a/roms/seabios +++ b/roms/seabios @@ -1 +1 @@ -Subproject commit e8a76b0f225bba5ba9d63ab227e0a37b3beb1059 +Subproject commit 88cb66ea542906ffff8a80ef397b9e3adbb33116 diff --git a/rules.mak b/rules.mak index 8448b94cdf..edc2552f08 100644 --- a/rules.mak +++ b/rules.mak @@ -21,20 +21,35 @@ QEMU_CFLAGS += -I$(/dev/null 2>&1 || cp $< $@ -%.h-timestamp: %.mak - $(call quiet-command, sh $(SRC_PATH)/scripts/create_config < $< > $@, " GEN $(TARGET_DIR)$*.h") - @cmp $@ $*.h >/dev/null 2>&1 || cp $@ $*.h +config-%.h-timestamp: config-%.mak + $(call quiet-command, sh $(SRC_PATH)/scripts/create_config < $< > $@, " GEN $(TARGET_DIR)config-$*.h") + +.PHONY: clean-timestamp +clean-timestamp: + rm -f *.timestamp +clean: clean-timestamp # will delete the target of a rule if commands exit with a nonzero exit status .DELETE_ON_ERROR: diff --git a/savevm.c b/savevm.c index bcdb92ee81..35c8d1e445 100644 --- a/savevm.c +++ b/savevm.c @@ -21,54 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include -#include -#include -#include -#include -#include -/* Needed early for CONFIG_BSD etc. */ #include "config-host.h" - -#ifndef _WIN32 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_BSD -#include -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) -#include -#else -#include -#endif -#ifdef __linux__ -#include -#include -#include -#endif -#endif -#endif - -#ifdef _WIN32 -#include -#include -#include -#include -#define getopt_long_only getopt_long -#define memalign(align, size) malloc(size) -#endif - #include "qemu-common.h" #include "hw/hw.h" #include "hw/qdev.h" @@ -80,7 +34,6 @@ #include "migration/migration.h" #include "qemu/sockets.h" #include "qemu/queue.h" -#include "qemu/timer.h" #include "sysemu/cpus.h" #include "exec/memory.h" #include "qmp-commands.h" @@ -128,7 +81,7 @@ static void qemu_announce_self_iter(NICState *nic, void *opaque) len = announce_self_create(buf, nic->conf->macaddr.a); - qemu_send_packet_raw(&nic->nc, buf, len); + qemu_send_packet_raw(qemu_get_queue(nic), buf, len); } @@ -166,8 +119,11 @@ struct QEMUFile { void *opaque; int is_write; - int64_t buf_offset; /* start of buffer when writing, end of buffer - when reading */ + int64_t bytes_xfer; + int64_t xfer_limit; + + int64_t pos; /* start of buffer when writing, end of buffer + when reading */ int buf_index; int buf_size; /* 0 when writing */ uint8_t buf[IO_BUF_SIZE]; @@ -187,6 +143,34 @@ typedef struct QEMUFileSocket QEMUFile *file; } QEMUFileSocket; +typedef struct { + Coroutine *co; + int fd; +} FDYieldUntilData; + +static void fd_coroutine_enter(void *opaque) +{ + FDYieldUntilData *data = opaque; + qemu_set_fd_handler(data->fd, NULL, NULL, NULL); + qemu_coroutine_enter(data->co, NULL); +} + +/** + * Yield until a file descriptor becomes readable + * + * Note that this function clobbers the handlers for the file descriptor. + */ +static void coroutine_fn yield_until_fd_readable(int fd) +{ + FDYieldUntilData data; + + assert(qemu_in_coroutine()); + data.co = qemu_coroutine_self(); + data.fd = fd; + qemu_set_fd_handler(fd, fd_coroutine_enter, NULL, &data); + qemu_coroutine_yield(); +} + static int socket_get_fd(void *opaque) { QEMUFileSocket *s = opaque; @@ -205,8 +189,7 @@ static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) break; } if (socket_error() == EAGAIN) { - assert(qemu_in_coroutine()); - qemu_coroutine_yield(); + yield_until_fd_readable(s->fd); } else if (socket_error() != EINTR) { break; } @@ -218,6 +201,18 @@ static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) return len; } +static int socket_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size) +{ + QEMUFileSocket *s = opaque; + ssize_t len; + + len = qemu_send_full(s->fd, buf, size, 0); + if (len < size) { + len = -socket_error(); + } + return len; +} + static int socket_close(void *opaque) { QEMUFileSocket *s = opaque; @@ -252,8 +247,7 @@ static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) break; } if (errno == EAGAIN) { - assert(qemu_in_coroutine()); - qemu_coroutine_yield(); + yield_until_fd_readable(fileno(fp)); } else if (errno != EINTR) { break; } @@ -268,6 +262,9 @@ static int stdio_pclose(void *opaque) ret = pclose(s->stdio_file); if (ret == -1) { ret = -errno; + } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) { + /* close succeeded, but non-zero exit code: */ + ret = -EIO; /* fake errno value */ } g_free(s); return ret; @@ -277,6 +274,24 @@ static int stdio_fclose(void *opaque) { QEMUFileStdio *s = opaque; int ret = 0; + + if (s->file->ops->put_buffer) { + int fd = fileno(s->stdio_file); + struct stat st; + + ret = fstat(fd, &st); + if (ret == 0 && S_ISREG(st.st_mode)) { + /* + * If the file handle is a regular file make sure the + * data is flushed to disk before signaling success. + */ + ret = fsync(fd); + if (ret != 0) { + ret = -errno; + return ret; + } + } + } if (fclose(s->stdio_file) == EOF) { ret = -errno; } @@ -296,11 +311,17 @@ static const QEMUFileOps stdio_pipe_write_ops = { .close = stdio_pclose }; -QEMUFile *qemu_popen(FILE *stdio_file, const char *mode) +QEMUFile *qemu_popen_cmd(const char *command, const char *mode) { + FILE *stdio_file; QEMUFileStdio *s; - if (stdio_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { + stdio_file = popen(command, mode); + if (stdio_file == NULL) { + return NULL; + } + + if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { fprintf(stderr, "qemu_popen: Argument validity check failed\n"); return NULL; } @@ -317,18 +338,6 @@ QEMUFile *qemu_popen(FILE *stdio_file, const char *mode) return s->file; } -QEMUFile *qemu_popen_cmd(const char *command, const char *mode) -{ - FILE *popen_file; - - popen_file = popen(command, mode); - if(popen_file == NULL) { - return NULL; - } - - return qemu_popen(popen_file, mode); -} - static const QEMUFileOps stdio_file_read_ops = { .get_fd = stdio_get_fd, .get_buffer = stdio_get_buffer, @@ -375,12 +384,30 @@ static const QEMUFileOps socket_read_ops = { .close = socket_close }; -QEMUFile *qemu_fopen_socket(int fd) +static const QEMUFileOps socket_write_ops = { + .get_fd = socket_get_fd, + .put_buffer = socket_put_buffer, + .close = socket_close +}; + +QEMUFile *qemu_fopen_socket(int fd, const char *mode) { QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket)); + if (mode == NULL || + (mode[0] != 'r' && mode[0] != 'w') || + mode[1] != 'b' || mode[2] != 0) { + fprintf(stderr, "qemu_fopen: Argument validity check failed\n"); + return NULL; + } + s->fd = fd; - s->file = qemu_fopen_ops(s, &socket_read_ops); + if (mode[0] == 'w') { + socket_set_block(s->fd); + s->file = qemu_fopen_ops(s, &socket_write_ops); + } else { + s->file = qemu_fopen_ops(s, &socket_read_ops); + } return s->file; } @@ -455,7 +482,6 @@ QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops) f->opaque = opaque; f->ops = ops; f->is_write = 0; - return f; } @@ -466,27 +492,31 @@ int qemu_file_get_error(QEMUFile *f) static void qemu_file_set_error(QEMUFile *f, int ret) { - f->last_error = ret; + if (f->last_error == 0) { + f->last_error = ret; + } } /** Flushes QEMUFile buffer * */ -static int qemu_fflush(QEMUFile *f) +static void qemu_fflush(QEMUFile *f) { int ret = 0; - if (!f->ops->put_buffer) - return 0; - + if (!f->ops->put_buffer) { + return; + } if (f->is_write && f->buf_index > 0) { - ret = f->ops->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index); + ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index); if (ret >= 0) { - f->buf_offset += f->buf_index; + f->pos += f->buf_index; } f->buf_index = 0; } - return ret; + if (ret < 0) { + qemu_file_set_error(f, ret); + } } static void qemu_fill_buffer(QEMUFile *f) @@ -507,11 +537,11 @@ static void qemu_fill_buffer(QEMUFile *f) f->buf_index = 0; f->buf_size = pending; - len = f->ops->get_buffer(f->opaque, f->buf + pending, f->buf_offset, + len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos, IO_BUF_SIZE - pending); if (len > 0) { f->buf_size += len; - f->buf_offset += len; + f->pos += len; } else if (len == 0) { qemu_file_set_error(f, -EIO); } else if (len != -EAGAIN) @@ -537,7 +567,8 @@ int qemu_get_fd(QEMUFile *f) int qemu_fclose(QEMUFile *f) { int ret; - ret = qemu_fflush(f); + qemu_fflush(f); + ret = qemu_file_get_error(f); if (f->ops->close) { int ret2 = f->ops->close(f->opaque); @@ -576,12 +607,12 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) memcpy(f->buf + f->buf_index, buf, l); f->is_write = 1; f->buf_index += l; + f->bytes_xfer += l; buf += l; size -= l; if (f->buf_index >= IO_BUF_SIZE) { - int ret = qemu_fflush(f); - if (ret < 0) { - qemu_file_set_error(f, ret); + qemu_fflush(f); + if (qemu_file_get_error(f)) { break; } } @@ -603,10 +634,7 @@ void qemu_put_byte(QEMUFile *f, int v) f->buf[f->buf_index++] = v; f->is_write = 1; if (f->buf_index >= IO_BUF_SIZE) { - int ret = qemu_fflush(f); - if (ret < 0) { - qemu_file_set_error(f, ret); - } + qemu_fflush(f); } } @@ -692,35 +720,36 @@ int qemu_get_byte(QEMUFile *f) return result; } -static int64_t qemu_ftell(QEMUFile *f) +int64_t qemu_ftell(QEMUFile *f) { - return f->buf_offset - f->buf_size + f->buf_index; + qemu_fflush(f); + return f->pos; } int qemu_file_rate_limit(QEMUFile *f) { - if (f->ops->rate_limit) - return f->ops->rate_limit(f->opaque); - + if (qemu_file_get_error(f)) { + return 1; + } + if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) { + return 1; + } return 0; } int64_t qemu_file_get_rate_limit(QEMUFile *f) { - if (f->ops->get_rate_limit) - return f->ops->get_rate_limit(f->opaque); - - return 0; + return f->xfer_limit; } -int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate) +void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit) { - /* any failed or completed migration keeps its state to allow probing of - * migration data, but has no associated file anymore */ - if (f && f->ops->set_rate_limit) - return f->ops->set_rate_limit(f->opaque, new_rate); + f->xfer_limit = limit; +} - return 0; +void qemu_file_reset_rate_limit(QEMUFile *f) +{ + f->bytes_xfer = 0; } void qemu_put_be16(QEMUFile *f, unsigned int v) @@ -1394,13 +1423,6 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, return 0; } -int vmstate_register(DeviceState *dev, int instance_id, - const VMStateDescription *vmsd, void *opaque) -{ - return vmstate_register_with_alias_id(dev, instance_id, vmsd, - opaque, -1, 0); -} - void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, void *opaque) { @@ -1594,8 +1616,8 @@ bool qemu_savevm_state_blocked(Error **errp) return false; } -int qemu_savevm_state_begin(QEMUFile *f, - const MigrationParams *params) +void qemu_savevm_state_begin(QEMUFile *f, + const MigrationParams *params) { SaveStateEntry *se; int ret; @@ -1635,17 +1657,10 @@ int qemu_savevm_state_begin(QEMUFile *f, ret = se->ops->save_live_setup(f, se->opaque); if (ret < 0) { - qemu_savevm_state_cancel(f); - return ret; + qemu_file_set_error(f, ret); + break; } } - ret = qemu_file_get_error(f); - if (ret != 0) { - qemu_savevm_state_cancel(f); - } - - return ret; - } /* @@ -1679,6 +1694,9 @@ int qemu_savevm_state_iterate(QEMUFile *f) ret = se->ops->save_live_iterate(f, se->opaque); trace_savevm_section_end(se->section_id); + if (ret < 0) { + qemu_file_set_error(f, ret); + } if (ret <= 0) { /* Do not proceed to the next vmstate before this one reported completion of the current stage. This serializes the migration @@ -1687,17 +1705,10 @@ int qemu_savevm_state_iterate(QEMUFile *f) break; } } - if (ret != 0) { - return ret; - } - ret = qemu_file_get_error(f); - if (ret != 0) { - qemu_savevm_state_cancel(f); - } return ret; } -int qemu_savevm_state_complete(QEMUFile *f) +void qemu_savevm_state_complete(QEMUFile *f) { SaveStateEntry *se; int ret; @@ -1721,7 +1732,8 @@ int qemu_savevm_state_complete(QEMUFile *f) ret = se->ops->save_live_complete(f, se->opaque); trace_savevm_section_end(se->section_id); if (ret < 0) { - return ret; + qemu_file_set_error(f, ret); + return; } } @@ -1749,8 +1761,7 @@ int qemu_savevm_state_complete(QEMUFile *f) } qemu_put_byte(f, QEMU_VM_EOF); - - return qemu_file_get_error(f); + qemu_fflush(f); } uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size) @@ -1772,7 +1783,7 @@ uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size) return ret; } -void qemu_savevm_state_cancel(QEMUFile *f) +void qemu_savevm_state_cancel(void) { SaveStateEntry *se; @@ -1792,27 +1803,27 @@ static int qemu_savevm_state(QEMUFile *f) }; if (qemu_savevm_state_blocked(NULL)) { - ret = -EINVAL; - goto out; + return -EINVAL; } - ret = qemu_savevm_state_begin(f, ¶ms); - if (ret < 0) - goto out; + qemu_mutex_unlock_iothread(); + qemu_savevm_state_begin(f, ¶ms); + qemu_mutex_lock_iothread(); - do { - ret = qemu_savevm_state_iterate(f); - if (ret < 0) - goto out; - } while (ret == 0); + while (qemu_file_get_error(f) == 0) { + if (qemu_savevm_state_iterate(f) > 0) { + break; + } + } - ret = qemu_savevm_state_complete(f); - -out: + ret = qemu_file_get_error(f); if (ret == 0) { + qemu_savevm_state_complete(f); ret = qemu_file_get_error(f); } - + if (ret != 0) { + qemu_savevm_state_cancel(); + } return ret; } @@ -2131,13 +2142,8 @@ void do_savevm(Monitor *mon, const QDict *qdict) QEMUFile *f; int saved_vm_running; uint64_t vm_state_size; -#ifdef _WIN32 - struct _timeb tb; - struct tm *ptm; -#else - struct timeval tv; + qemu_timeval tv; struct tm tm; -#endif const char *name = qdict_get_try_str(qdict, "name"); /* Verify if there is a device that doesn't support snapshots and is writable */ @@ -2167,15 +2173,9 @@ void do_savevm(Monitor *mon, const QDict *qdict) memset(sn, 0, sizeof(*sn)); /* fill auxiliary fields */ -#ifdef _WIN32 - _ftime(&tb); - sn->date_sec = tb.time; - sn->date_nsec = tb.millitm * 1000000; -#else - gettimeofday(&tv, NULL); + qemu_gettimeofday(&tv); sn->date_sec = tv.tv_sec; sn->date_nsec = tv.tv_usec * 1000; -#endif sn->vm_clock_nsec = qemu_get_clock_ns(vm_clock); if (name) { @@ -2187,15 +2187,9 @@ void do_savevm(Monitor *mon, const QDict *qdict) pstrcpy(sn->name, sizeof(sn->name), name); } } else { -#ifdef _WIN32 - time_t t = tb.time; - ptm = localtime(&t); - strftime(sn->name, sizeof(sn->name), "vm-%Y%m%d%H%M%S", ptm); -#else /* cast below needed for OpenBSD where tv_sec is still 'long' */ localtime_r((const time_t *)&tv.tv_sec, &tm); strftime(sn->name, sizeof(sn->name), "vm-%Y%m%d%H%M%S", &tm); -#endif } /* Delete old snapshots of the same name */ @@ -2371,7 +2365,7 @@ void do_delvm(Monitor *mon, const QDict *qdict) } } -void do_info_snapshots(Monitor *mon) +void do_info_snapshots(Monitor *mon, const QDict *qdict) { BlockDriverState *bs, *bs1; QEMUSnapshotInfo *sn_tab, *sn, s, *sn_info = &s; @@ -2450,162 +2444,3 @@ void vmstate_register_ram_global(MemoryRegion *mr) { vmstate_register_ram(mr, NULL); } - -/* - page = zrun nzrun - | zrun nzrun page - - zrun = length - - nzrun = length byte... - - length = uleb128 encoded integer - */ -int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen, - uint8_t *dst, int dlen) -{ - uint32_t zrun_len = 0, nzrun_len = 0; - int d = 0, i = 0; - long res, xor; - uint8_t *nzrun_start = NULL; - - g_assert(!(((uintptr_t)old_buf | (uintptr_t)new_buf | slen) % - sizeof(long))); - - while (i < slen) { - /* overflow */ - if (d + 2 > dlen) { - return -1; - } - - /* not aligned to sizeof(long) */ - res = (slen - i) % sizeof(long); - while (res && old_buf[i] == new_buf[i]) { - zrun_len++; - i++; - res--; - } - - /* word at a time for speed */ - if (!res) { - while (i < slen && - (*(long *)(old_buf + i)) == (*(long *)(new_buf + i))) { - i += sizeof(long); - zrun_len += sizeof(long); - } - - /* go over the rest */ - while (i < slen && old_buf[i] == new_buf[i]) { - zrun_len++; - i++; - } - } - - /* buffer unchanged */ - if (zrun_len == slen) { - return 0; - } - - /* skip last zero run */ - if (i == slen) { - return d; - } - - d += uleb128_encode_small(dst + d, zrun_len); - - zrun_len = 0; - nzrun_start = new_buf + i; - - /* overflow */ - if (d + 2 > dlen) { - return -1; - } - /* not aligned to sizeof(long) */ - res = (slen - i) % sizeof(long); - while (res && old_buf[i] != new_buf[i]) { - i++; - nzrun_len++; - res--; - } - - /* word at a time for speed, use of 32-bit long okay */ - if (!res) { - /* truncation to 32-bit long okay */ - long mask = (long)0x0101010101010101ULL; - while (i < slen) { - xor = *(long *)(old_buf + i) ^ *(long *)(new_buf + i); - if ((xor - mask) & ~xor & (mask << 7)) { - /* found the end of an nzrun within the current long */ - while (old_buf[i] != new_buf[i]) { - nzrun_len++; - i++; - } - break; - } else { - i += sizeof(long); - nzrun_len += sizeof(long); - } - } - } - - d += uleb128_encode_small(dst + d, nzrun_len); - /* overflow */ - if (d + nzrun_len > dlen) { - return -1; - } - memcpy(dst + d, nzrun_start, nzrun_len); - d += nzrun_len; - nzrun_len = 0; - } - - return d; -} - -int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen) -{ - int i = 0, d = 0; - int ret; - uint32_t count = 0; - - while (i < slen) { - - /* zrun */ - if ((slen - i) < 2) { - return -1; - } - - ret = uleb128_decode_small(src + i, &count); - if (ret < 0 || (i && !count)) { - return -1; - } - i += ret; - d += count; - - /* overflow */ - if (d > dlen) { - return -1; - } - - /* nzrun */ - if ((slen - i) < 2) { - return -1; - } - - ret = uleb128_decode_small(src + i, &count); - if (ret < 0 || !count) { - return -1; - } - i += ret; - - /* overflow */ - if (d + count > dlen || i + count > slen) { - return -1; - } - - memcpy(dst + d, src + i, count); - d += count; - i += count; - } - - return d; -} diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index cbe6440ba3..0b23f7795a 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -147,6 +147,7 @@ controls = [ 5: 'Enable VPID', 6: 'WBINVD exiting', 7: 'Unrestricted guest', + 9: 'Virtual interrupt delivery', 10: 'PAUSE-loop exiting', 11: 'RDRAND exiting', 12: 'Enable INVPCID', diff --git a/scripts/make_device_config.sh b/scripts/make_device_config.sh index 5d14885dfc..7242707819 100644 --- a/scripts/make_device_config.sh +++ b/scripts/make_device_config.sh @@ -3,7 +3,7 @@ # files from include directives. dest=$1.tmp -dep=$1.d +dep=`dirname $1`-`basename $1`.d src=$2 src_dir=`dirname $src` all_includes= @@ -18,7 +18,7 @@ process_includes () { f=$src while [ -n "$f" ] ; do - f=`tr -d '\r' < $f | awk '/^include / {printf "'$src_dir'/%s", $2}'` + f=`cat $f | tr -d '\r' | awk '/^include / {printf "'$src_dir'/%s ", $2}'` [ $? = 0 ] || exit 1 all_includes="$all_includes $f" done diff --git a/scripts/qemu-guest-agent/fsfreeze-hook b/scripts/qemu-guest-agent/fsfreeze-hook new file mode 100755 index 0000000000..c27b29f282 --- /dev/null +++ b/scripts/qemu-guest-agent/fsfreeze-hook @@ -0,0 +1,33 @@ +#!/bin/sh + +# This script is executed when a guest agent receives fsfreeze-freeze and +# fsfreeze-thaw command, if it is specified in --fsfreeze-hook (-F) +# option of qemu-ga or placed in default path (/etc/qemu/fsfreeze-hook). +# When the agent receives fsfreeze-freeze request, this script is issued with +# "freeze" argument before the filesystem is frozen. And for fsfreeze-thaw +# request, it is issued with "thaw" argument after filesystem is thawed. + +LOGFILE=/var/log/qga-fsfreeze-hook.log +FSFREEZE_D=$(dirname -- "$0")/fsfreeze-hook.d + +# Check whether file $1 is a backup or rpm-generated file and should be ignored +is_ignored_file() { + case "$1" in + *~ | *.bak | *.orig | *.rpmnew | *.rpmorig | *.rpmsave | *.sample) + return 0 ;; + esac + return 1 +} + +# Iterate executables in directory "fsfreeze-hook.d" with the specified args +[ ! -d "$FSFREEZE_D" ] && exit 0 +for file in "$FSFREEZE_D"/* ; do + is_ignored_file "$file" && continue + [ -x "$file" ] || continue + printf "$(date): execute $file $@\n" >>$LOGFILE + "$file" "$@" >>$LOGFILE 2>&1 + STATUS=$? + printf "$(date): $file finished with status=$STATUS\n" >>$LOGFILE +done + +exit 0 diff --git a/scripts/qemu-guest-agent/fsfreeze-hook.d/mysql-flush.sh.sample b/scripts/qemu-guest-agent/fsfreeze-hook.d/mysql-flush.sh.sample new file mode 100755 index 0000000000..2b4fa3aeb1 --- /dev/null +++ b/scripts/qemu-guest-agent/fsfreeze-hook.d/mysql-flush.sh.sample @@ -0,0 +1,56 @@ +#!/bin/sh + +# Flush MySQL tables to the disk before the filesystem is frozen. +# At the same time, this keeps a read lock in order to avoid write accesses +# from the other clients until the filesystem is thawed. + +MYSQL="/usr/bin/mysql" +MYSQL_OPTS="-uroot" #"-prootpassword" +FIFO=/var/run/mysql-flush.fifo + +# Check mysql is installed and the server running +[ -x "$MYSQL" ] && "$MYSQL" $MYSQL_OPTS < /dev/null || exit 0 + +flush_and_wait() { + printf "FLUSH TABLES WITH READ LOCK \\G\n" + trap 'printf "$(date): $0 is killed\n">&2' HUP INT QUIT ALRM TERM + read < $FIFO + printf "UNLOCK TABLES \\G\n" + rm -f $FIFO +} + +case "$1" in + freeze) + mkfifo $FIFO || exit 1 + flush_and_wait | "$MYSQL" $MYSQL_OPTS & + # wait until every block is flushed + while [ "$(echo 'SHOW STATUS LIKE "Key_blocks_not_flushed"' |\ + "$MYSQL" $MYSQL_OPTS | tail -1 | cut -f 2)" -gt 0 ]; do + sleep 1 + done + # for InnoDB, wait until every log is flushed + INNODB_STATUS=$(mktemp /tmp/mysql-flush.XXXXXX) + [ $? -ne 0 ] && exit 2 + trap "rm -f $INNODB_STATUS; exit 1" HUP INT QUIT ALRM TERM + while :; do + printf "SHOW ENGINE INNODB STATUS \\G" |\ + "$MYSQL" $MYSQL_OPTS > $INNODB_STATUS + LOG_CURRENT=$(grep 'Log sequence number' $INNODB_STATUS |\ + tr -s ' ' | cut -d' ' -f4) + LOG_FLUSHED=$(grep 'Log flushed up to' $INNODB_STATUS |\ + tr -s ' ' | cut -d' ' -f5) + [ "$LOG_CURRENT" = "$LOG_FLUSHED" ] && break + sleep 1 + done + rm -f $INNODB_STATUS + ;; + + thaw) + [ ! -p $FIFO ] && exit 1 + echo > $FIFO + ;; + + *) + exit 1 + ;; +esac diff --git a/scripts/tracetool/backend/dtrace.py b/scripts/tracetool/backend/dtrace.py index 23c43e2772..ad5eb3b0ab 100644 --- a/scripts/tracetool/backend/dtrace.py +++ b/scripts/tracetool/backend/dtrace.py @@ -37,7 +37,7 @@ def c(events): def h(events): - out('#include "trace-dtrace.h"', + out('#include "trace/generated-tracers-dtrace.h"', '') for e in events: diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py index 6ffb3c2f4e..9a58de1331 100644 --- a/scripts/tracetool/format/h.py +++ b/scripts/tracetool/format/h.py @@ -19,8 +19,8 @@ from tracetool import out def begin(events): out('/* This file is autogenerated by tracetool, do not edit. */', '', - '#ifndef TRACE_H', - '#define TRACE_H', + '#ifndef TRACE__GENERATED_TRACERS_H', + '#define TRACE__GENERATED_TRACERS_H', '', '#include "qemu-common.h"') @@ -32,7 +32,7 @@ def end(events): enabled = 1 out('#define TRACE_%s_ENABLED %d' % (e.name.upper(), enabled)) out('', - '#endif /* TRACE_H */') + '#endif /* TRACE__GENERATED_TRACERS_H */') def nop(events): for e in events: diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 4c7b566fdf..120a694313 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -54,6 +54,9 @@ for arch in $ARCHLIST; do if [ $arch = x86 ]; then cp "$tmpdir/include/asm/hyperv.h" "$output/linux-headers/asm-x86" fi + if [ $arch = powerpc ]; then + cp "$tmpdir/include/asm/epapr_hcalls.h" "$output/linux-headers/asm-powerpc/" + fi done rm -rf "$output/linux-headers/linux" diff --git a/slirp/libslirp.h b/slirp/libslirp.h index 49609c2ad7..ceabff81b2 100644 --- a/slirp/libslirp.h +++ b/slirp/libslirp.h @@ -17,11 +17,9 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork, void slirp_cleanup(Slirp *slirp); void slirp_update_timeout(uint32_t *timeout); -void slirp_select_fill(int *pnfds, - fd_set *readfds, fd_set *writefds, fd_set *xfds); +void slirp_pollfds_fill(GArray *pollfds); -void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, - int select_error); +void slirp_pollfds_poll(GArray *pollfds, int select_error); void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len); diff --git a/slirp/main.h b/slirp/main.h index 66e4f9252f..f2e58cfe2d 100644 --- a/slirp/main.h +++ b/slirp/main.h @@ -31,7 +31,6 @@ extern int ctty_closed; extern char *slirp_tty; extern char *exec_shell; extern u_int curtime; -extern fd_set *global_readfds, *global_writefds, *global_xfds; extern struct in_addr loopback_addr; extern unsigned long loopback_mask; extern char *username; diff --git a/slirp/slirp.c b/slirp/slirp.c index e93b578832..bd9b7cb644 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -39,9 +39,6 @@ static const uint8_t special_ethaddr[ETH_ALEN] = { static const uint8_t zero_ethaddr[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; -/* XXX: suppress those select globals */ -fd_set *global_readfds, *global_writefds, *global_xfds; - u_int curtime; static u_int time_fasttimo, last_slowtimo; static int do_slowtimo; @@ -225,12 +222,8 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork, pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname), vhostname); } - if (tftp_path) { - slirp->tftp_prefix = g_strdup(tftp_path); - } - if (bootfile) { - slirp->bootp_filename = g_strdup(bootfile); - } + slirp->tftp_prefix = g_strdup(tftp_path); + slirp->bootp_filename = g_strdup(bootfile); slirp->vdhcp_startaddr = vdhcp_start; slirp->vnameserver_addr = vnameserver; @@ -265,7 +258,6 @@ void slirp_cleanup(Slirp *slirp) #define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) #define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) -#define UPD_NFDS(x) if (nfds < (x)) nfds = (x) void slirp_update_timeout(uint32_t *timeout) { @@ -274,156 +266,179 @@ void slirp_update_timeout(uint32_t *timeout) } } -void slirp_select_fill(int *pnfds, - fd_set *readfds, fd_set *writefds, fd_set *xfds) +void slirp_pollfds_fill(GArray *pollfds) { Slirp *slirp; struct socket *so, *so_next; - int nfds; if (QTAILQ_EMPTY(&slirp_instances)) { return; } - /* fail safe */ - global_readfds = NULL; - global_writefds = NULL; - global_xfds = NULL; + /* + * First, TCP sockets + */ + do_slowtimo = 0; - nfds = *pnfds; - /* - * First, TCP sockets - */ - do_slowtimo = 0; + QTAILQ_FOREACH(slirp, &slirp_instances, entry) { + /* + * *_slowtimo needs calling if there are IP fragments + * in the fragment queue, or there are TCP connections active + */ + do_slowtimo |= ((slirp->tcb.so_next != &slirp->tcb) || + (&slirp->ipq.ip_link != slirp->ipq.ip_link.next)); - QTAILQ_FOREACH(slirp, &slirp_instances, entry) { - /* - * *_slowtimo needs calling if there are IP fragments - * in the fragment queue, or there are TCP connections active - */ - do_slowtimo |= ((slirp->tcb.so_next != &slirp->tcb) || - (&slirp->ipq.ip_link != slirp->ipq.ip_link.next)); + for (so = slirp->tcb.so_next; so != &slirp->tcb; + so = so_next) { + int events = 0; - for (so = slirp->tcb.so_next; so != &slirp->tcb; - so = so_next) { - so_next = so->so_next; + so_next = so->so_next; - /* - * See if we need a tcp_fasttimo - */ - if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK) - time_fasttimo = curtime; /* Flag when we want a fasttimo */ + so->pollfds_idx = -1; - /* - * NOFDREF can include still connecting to local-host, - * newly socreated() sockets etc. Don't want to select these. - */ - if (so->so_state & SS_NOFDREF || so->s == -1) - continue; + /* + * See if we need a tcp_fasttimo + */ + if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK) { + time_fasttimo = curtime; /* Flag when we want a fasttimo */ + } - /* - * Set for reading sockets which are accepting - */ - if (so->so_state & SS_FACCEPTCONN) { - FD_SET(so->s, readfds); - UPD_NFDS(so->s); - continue; - } + /* + * NOFDREF can include still connecting to local-host, + * newly socreated() sockets etc. Don't want to select these. + */ + if (so->so_state & SS_NOFDREF || so->s == -1) { + continue; + } - /* - * Set for writing sockets which are connecting - */ - if (so->so_state & SS_ISFCONNECTING) { - FD_SET(so->s, writefds); - UPD_NFDS(so->s); - continue; - } + /* + * Set for reading sockets which are accepting + */ + if (so->so_state & SS_FACCEPTCONN) { + GPollFD pfd = { + .fd = so->s, + .events = G_IO_IN | G_IO_HUP | G_IO_ERR, + }; + so->pollfds_idx = pollfds->len; + g_array_append_val(pollfds, pfd); + continue; + } - /* - * Set for writing if we are connected, can send more, and - * we have something to send - */ - if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) { - FD_SET(so->s, writefds); - UPD_NFDS(so->s); - } + /* + * Set for writing sockets which are connecting + */ + if (so->so_state & SS_ISFCONNECTING) { + GPollFD pfd = { + .fd = so->s, + .events = G_IO_OUT | G_IO_ERR, + }; + so->pollfds_idx = pollfds->len; + g_array_append_val(pollfds, pfd); + continue; + } - /* - * Set for reading (and urgent data) if we are connected, can - * receive more, and we have room for it XXX /2 ? - */ - if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) { - FD_SET(so->s, readfds); - FD_SET(so->s, xfds); - UPD_NFDS(so->s); - } - } + /* + * Set for writing if we are connected, can send more, and + * we have something to send + */ + if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) { + events |= G_IO_OUT | G_IO_ERR; + } - /* - * UDP sockets - */ - for (so = slirp->udb.so_next; so != &slirp->udb; - so = so_next) { - so_next = so->so_next; + /* + * Set for reading (and urgent data) if we are connected, can + * receive more, and we have room for it XXX /2 ? + */ + if (CONN_CANFRCV(so) && + (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) { + events |= G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI; + } - /* - * See if it's timed out - */ - if (so->so_expire) { - if (so->so_expire <= curtime) { - udp_detach(so); - continue; - } else - do_slowtimo = 1; /* Let socket expire */ - } + if (events) { + GPollFD pfd = { + .fd = so->s, + .events = events, + }; + so->pollfds_idx = pollfds->len; + g_array_append_val(pollfds, pfd); + } + } - /* - * When UDP packets are received from over the - * link, they're sendto()'d straight away, so - * no need for setting for writing - * Limit the number of packets queued by this session - * to 4. Note that even though we try and limit this - * to 4 packets, the session could have more queued - * if the packets needed to be fragmented - * (XXX <= 4 ?) - */ - if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) { - FD_SET(so->s, readfds); - UPD_NFDS(so->s); - } - } + /* + * UDP sockets + */ + for (so = slirp->udb.so_next; so != &slirp->udb; + so = so_next) { + so_next = so->so_next; - /* - * ICMP sockets - */ - for (so = slirp->icmp.so_next; so != &slirp->icmp; - so = so_next) { - so_next = so->so_next; + so->pollfds_idx = -1; - /* - * See if it's timed out - */ - if (so->so_expire) { - if (so->so_expire <= curtime) { - icmp_detach(so); - continue; - } else { - do_slowtimo = 1; /* Let socket expire */ - } - } - - if (so->so_state & SS_ISFCONNECTED) { - FD_SET(so->s, readfds); - UPD_NFDS(so->s); - } + /* + * See if it's timed out + */ + if (so->so_expire) { + if (so->so_expire <= curtime) { + udp_detach(so); + continue; + } else { + do_slowtimo = 1; /* Let socket expire */ } - } + } - *pnfds = nfds; + /* + * When UDP packets are received from over the + * link, they're sendto()'d straight away, so + * no need for setting for writing + * Limit the number of packets queued by this session + * to 4. Note that even though we try and limit this + * to 4 packets, the session could have more queued + * if the packets needed to be fragmented + * (XXX <= 4 ?) + */ + if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) { + GPollFD pfd = { + .fd = so->s, + .events = G_IO_IN | G_IO_HUP | G_IO_ERR, + }; + so->pollfds_idx = pollfds->len; + g_array_append_val(pollfds, pfd); + } + } + + /* + * ICMP sockets + */ + for (so = slirp->icmp.so_next; so != &slirp->icmp; + so = so_next) { + so_next = so->so_next; + + so->pollfds_idx = -1; + + /* + * See if it's timed out + */ + if (so->so_expire) { + if (so->so_expire <= curtime) { + icmp_detach(so); + continue; + } else { + do_slowtimo = 1; /* Let socket expire */ + } + } + + if (so->so_state & SS_ISFCONNECTED) { + GPollFD pfd = { + .fd = so->s, + .events = G_IO_IN | G_IO_HUP | G_IO_ERR, + }; + so->pollfds_idx = pollfds->len; + g_array_append_val(pollfds, pfd); + } + } + } } -void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, - int select_error) +void slirp_pollfds_poll(GArray *pollfds, int select_error) { Slirp *slirp; struct socket *so, *so_next; @@ -433,184 +448,202 @@ void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, return; } - global_readfds = readfds; - global_writefds = writefds; - global_xfds = xfds; - curtime = qemu_get_clock_ms(rt_clock); QTAILQ_FOREACH(slirp, &slirp_instances, entry) { - /* - * See if anything has timed out - */ - if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) { - tcp_fasttimo(slirp); - time_fasttimo = 0; - } - if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) { - ip_slowtimo(slirp); - tcp_slowtimo(slirp); - last_slowtimo = curtime; - } + /* + * See if anything has timed out + */ + if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) { + tcp_fasttimo(slirp); + time_fasttimo = 0; + } + if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) { + ip_slowtimo(slirp); + tcp_slowtimo(slirp); + last_slowtimo = curtime; + } - /* - * Check sockets - */ - if (!select_error) { - /* - * Check TCP sockets - */ - for (so = slirp->tcb.so_next; so != &slirp->tcb; - so = so_next) { - so_next = so->so_next; + /* + * Check sockets + */ + if (!select_error) { + /* + * Check TCP sockets + */ + for (so = slirp->tcb.so_next; so != &slirp->tcb; + so = so_next) { + int revents; - /* - * FD_ISSET is meaningless on these sockets - * (and they can crash the program) - */ - if (so->so_state & SS_NOFDREF || so->s == -1) - continue; + so_next = so->so_next; - /* - * Check for URG data - * This will soread as well, so no need to - * test for readfds below if this succeeds - */ - if (FD_ISSET(so->s, xfds)) - sorecvoob(so); - /* - * Check sockets for reading - */ - else if (FD_ISSET(so->s, readfds)) { - /* - * Check for incoming connections - */ - if (so->so_state & SS_FACCEPTCONN) { - tcp_connect(so); - continue; - } /* else */ - ret = soread(so); + revents = 0; + if (so->pollfds_idx != -1) { + revents = g_array_index(pollfds, GPollFD, + so->pollfds_idx).revents; + } - /* Output it if we read something */ - if (ret > 0) - tcp_output(sototcpcb(so)); - } - - /* - * Check sockets for writing - */ - if (FD_ISSET(so->s, writefds)) { - /* - * Check for non-blocking, still-connecting sockets - */ - if (so->so_state & SS_ISFCONNECTING) { - /* Connected */ - so->so_state &= ~SS_ISFCONNECTING; - - ret = send(so->s, (const void *) &ret, 0, 0); - if (ret < 0) { - /* XXXXX Must fix, zero bytes is a NOP */ - if (errno == EAGAIN || errno == EWOULDBLOCK || - errno == EINPROGRESS || errno == ENOTCONN) - continue; - - /* else failed */ - so->so_state &= SS_PERSISTENT_MASK; - so->so_state |= SS_NOFDREF; - } - /* else so->so_state &= ~SS_ISFCONNECTING; */ - - /* - * Continue tcp_input - */ - tcp_input((struct mbuf *)NULL, sizeof(struct ip), so); - /* continue; */ - } else - ret = sowrite(so); - /* - * XXXXX If we wrote something (a lot), there - * could be a need for a window update. - * In the worst case, the remote will send - * a window probe to get things going again - */ - } - - /* - * Probe a still-connecting, non-blocking socket - * to check if it's still alive - */ -#ifdef PROBE_CONN - if (so->so_state & SS_ISFCONNECTING) { - ret = qemu_recv(so->s, &ret, 0,0); - - if (ret < 0) { - /* XXX */ - if (errno == EAGAIN || errno == EWOULDBLOCK || - errno == EINPROGRESS || errno == ENOTCONN) - continue; /* Still connecting, continue */ - - /* else failed */ - so->so_state &= SS_PERSISTENT_MASK; - so->so_state |= SS_NOFDREF; - - /* tcp_input will take care of it */ - } else { - ret = send(so->s, &ret, 0,0); - if (ret < 0) { - /* XXX */ - if (errno == EAGAIN || errno == EWOULDBLOCK || - errno == EINPROGRESS || errno == ENOTCONN) - continue; - /* else failed */ - so->so_state &= SS_PERSISTENT_MASK; - so->so_state |= SS_NOFDREF; - } else - so->so_state &= ~SS_ISFCONNECTING; - - } - tcp_input((struct mbuf *)NULL, sizeof(struct ip),so); - } /* SS_ISFCONNECTING */ -#endif - } - - /* - * Now UDP sockets. - * Incoming packets are sent straight away, they're not buffered. - * Incoming UDP data isn't buffered either. - */ - for (so = slirp->udb.so_next; so != &slirp->udb; - so = so_next) { - so_next = so->so_next; - - if (so->s != -1 && FD_ISSET(so->s, readfds)) { - sorecvfrom(so); - } - } + if (so->so_state & SS_NOFDREF || so->s == -1) { + continue; + } /* - * Check incoming ICMP relies. + * Check for URG data + * This will soread as well, so no need to + * test for G_IO_IN below if this succeeds */ - for (so = slirp->icmp.so_next; so != &slirp->icmp; - so = so_next) { - so_next = so->so_next; + if (revents & G_IO_PRI) { + sorecvoob(so); + } + /* + * Check sockets for reading + */ + else if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) { + /* + * Check for incoming connections + */ + if (so->so_state & SS_FACCEPTCONN) { + tcp_connect(so); + continue; + } /* else */ + ret = soread(so); - if (so->s != -1 && FD_ISSET(so->s, readfds)) { - icmp_receive(so); + /* Output it if we read something */ + if (ret > 0) { + tcp_output(sototcpcb(so)); } } - } + + /* + * Check sockets for writing + */ + if (!(so->so_state & SS_NOFDREF) && + (revents & (G_IO_OUT | G_IO_ERR))) { + /* + * Check for non-blocking, still-connecting sockets + */ + if (so->so_state & SS_ISFCONNECTING) { + /* Connected */ + so->so_state &= ~SS_ISFCONNECTING; + + ret = send(so->s, (const void *) &ret, 0, 0); + if (ret < 0) { + /* XXXXX Must fix, zero bytes is a NOP */ + if (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINPROGRESS || errno == ENOTCONN) { + continue; + } + + /* else failed */ + so->so_state &= SS_PERSISTENT_MASK; + so->so_state |= SS_NOFDREF; + } + /* else so->so_state &= ~SS_ISFCONNECTING; */ + + /* + * Continue tcp_input + */ + tcp_input((struct mbuf *)NULL, sizeof(struct ip), so); + /* continue; */ + } else { + ret = sowrite(so); + } + /* + * XXXXX If we wrote something (a lot), there + * could be a need for a window update. + * In the worst case, the remote will send + * a window probe to get things going again + */ + } + + /* + * Probe a still-connecting, non-blocking socket + * to check if it's still alive + */ +#ifdef PROBE_CONN + if (so->so_state & SS_ISFCONNECTING) { + ret = qemu_recv(so->s, &ret, 0, 0); + + if (ret < 0) { + /* XXX */ + if (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINPROGRESS || errno == ENOTCONN) { + continue; /* Still connecting, continue */ + } + + /* else failed */ + so->so_state &= SS_PERSISTENT_MASK; + so->so_state |= SS_NOFDREF; + + /* tcp_input will take care of it */ + } else { + ret = send(so->s, &ret, 0, 0); + if (ret < 0) { + /* XXX */ + if (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINPROGRESS || errno == ENOTCONN) { + continue; + } + /* else failed */ + so->so_state &= SS_PERSISTENT_MASK; + so->so_state |= SS_NOFDREF; + } else { + so->so_state &= ~SS_ISFCONNECTING; + } + + } + tcp_input((struct mbuf *)NULL, sizeof(struct ip), so); + } /* SS_ISFCONNECTING */ +#endif + } + + /* + * Now UDP sockets. + * Incoming packets are sent straight away, they're not buffered. + * Incoming UDP data isn't buffered either. + */ + for (so = slirp->udb.so_next; so != &slirp->udb; + so = so_next) { + int revents; + + so_next = so->so_next; + + revents = 0; + if (so->pollfds_idx != -1) { + revents = g_array_index(pollfds, GPollFD, + so->pollfds_idx).revents; + } + + if (so->s != -1 && + (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) { + sorecvfrom(so); + } + } + + /* + * Check incoming ICMP relies. + */ + for (so = slirp->icmp.so_next; so != &slirp->icmp; + so = so_next) { + int revents; + + so_next = so->so_next; + + revents = 0; + if (so->pollfds_idx != -1) { + revents = g_array_index(pollfds, GPollFD, + so->pollfds_idx).revents; + } + + if (so->s != -1 && + (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) { + icmp_receive(so); + } + } + } if_start(slirp); } - - /* clear global file descriptor sets. - * these reside on the stack in vl.c - * so they're unusable if we're not in - * slirp_select_fill or slirp_select_poll. - */ - global_readfds = NULL; - global_writefds = NULL; - global_xfds = NULL; } static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) @@ -831,12 +864,12 @@ int slirp_add_exec(Slirp *slirp, int do_pty, const void *args, ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags) { - if (so->s == -1 && so->extra) { - qemu_chr_fe_write(so->extra, buf, len); - return len; - } + if (so->s == -1 && so->extra) { + qemu_chr_fe_write(so->extra, buf, len); + return len; + } - return send(so->s, buf, len, flags); + return send(so->s, buf, len, flags); } static struct socket * @@ -856,18 +889,20 @@ slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, int guest_port) size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port) { - struct iovec iov[2]; - struct socket *so; + struct iovec iov[2]; + struct socket *so; - so = slirp_find_ctl_socket(slirp, guest_addr, guest_port); + so = slirp_find_ctl_socket(slirp, guest_addr, guest_port); - if (!so || so->so_state & SS_NOFDREF) - return 0; + if (!so || so->so_state & SS_NOFDREF) { + return 0; + } - if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen/2)) - return 0; + if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen/2)) { + return 0; + } - return sopreprbuf(so, iov, NULL); + return sopreprbuf(so, iov, NULL); } void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port, diff --git a/slirp/slirp.h b/slirp/slirp.h index dfc3e3a2b8..fe0e65d0ee 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -215,7 +215,6 @@ struct Slirp { char client_hostname[33]; int restricted; - struct timeval tt; struct ex_list *exec_list; /* mbuf states */ diff --git a/slirp/socket.c b/slirp/socket.c index 77b0c98197..bb639aed9c 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -51,6 +51,7 @@ socreate(Slirp *slirp) so->so_state = SS_NOFDREF; so->s = -1; so->slirp = slirp; + so->pollfds_idx = -1; } return(so); } @@ -680,9 +681,6 @@ sofcantrcvmore(struct socket *so) { if ((so->so_state & SS_NOFDREF) == 0) { shutdown(so->s,0); - if(global_writefds) { - FD_CLR(so->s,global_writefds); - } } so->so_state &= ~(SS_ISFCONNECTING); if (so->so_state & SS_FCANTSENDMORE) { @@ -698,12 +696,6 @@ sofcantsendmore(struct socket *so) { if ((so->so_state & SS_NOFDREF) == 0) { shutdown(so->s,1); /* send FIN to fhost */ - if (global_readfds) { - FD_CLR(so->s,global_readfds); - } - if (global_xfds) { - FD_CLR(so->s,global_xfds); - } } so->so_state &= ~(SS_ISFCONNECTING); if (so->so_state & SS_FCANTRCVMORE) { diff --git a/slirp/socket.h b/slirp/socket.h index 857b0da311..57e0407ebc 100644 --- a/slirp/socket.h +++ b/slirp/socket.h @@ -20,6 +20,8 @@ struct socket { int s; /* The actual socket */ + int pollfds_idx; /* GPollFD GArray index */ + Slirp *slirp; /* managing slirp instance */ /* XXX union these with not-yet-used sbuf params */ diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c index 1542e43619..7b7ad60aea 100644 --- a/slirp/tcp_subr.c +++ b/slirp/tcp_subr.c @@ -384,83 +384,86 @@ int tcp_fconnect(struct socket *so) * the time it gets to accept(), so... We simply accept * here and SYN the local-host. */ -void -tcp_connect(struct socket *inso) +void tcp_connect(struct socket *inso) { - Slirp *slirp = inso->slirp; - struct socket *so; - struct sockaddr_in addr; - socklen_t addrlen = sizeof(struct sockaddr_in); - struct tcpcb *tp; - int s, opt; + Slirp *slirp = inso->slirp; + struct socket *so; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(struct sockaddr_in); + struct tcpcb *tp; + int s, opt; - DEBUG_CALL("tcp_connect"); - DEBUG_ARG("inso = %lx", (long)inso); + DEBUG_CALL("tcp_connect"); + DEBUG_ARG("inso = %lx", (long)inso); - /* - * If it's an SS_ACCEPTONCE socket, no need to socreate() - * another socket, just use the accept() socket. - */ - if (inso->so_state & SS_FACCEPTONCE) { - /* FACCEPTONCE already have a tcpcb */ - so = inso; - } else { - if ((so = socreate(slirp)) == NULL) { - /* If it failed, get rid of the pending connection */ - closesocket(accept(inso->s,(struct sockaddr *)&addr,&addrlen)); - return; - } - if (tcp_attach(so) < 0) { - free(so); /* NOT sofree */ - return; - } - so->so_laddr = inso->so_laddr; - so->so_lport = inso->so_lport; - } - - (void) tcp_mss(sototcpcb(so), 0); - - if ((s = accept(inso->s,(struct sockaddr *)&addr,&addrlen)) < 0) { - tcp_close(sototcpcb(so)); /* This will sofree() as well */ - return; - } - socket_set_nonblock(s); - opt = 1; - setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)); - opt = 1; - setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int)); - opt = 1; - setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&opt,sizeof(int)); - - so->so_fport = addr.sin_port; - so->so_faddr = addr.sin_addr; - /* Translate connections from localhost to the real hostname */ - if (so->so_faddr.s_addr == 0 || - (so->so_faddr.s_addr & loopback_mask) == - (loopback_addr.s_addr & loopback_mask)) { - so->so_faddr = slirp->vhost_addr; + /* + * If it's an SS_ACCEPTONCE socket, no need to socreate() + * another socket, just use the accept() socket. + */ + if (inso->so_state & SS_FACCEPTONCE) { + /* FACCEPTONCE already have a tcpcb */ + so = inso; + } else { + so = socreate(slirp); + if (so == NULL) { + /* If it failed, get rid of the pending connection */ + closesocket(accept(inso->s, (struct sockaddr *)&addr, &addrlen)); + return; } + if (tcp_attach(so) < 0) { + free(so); /* NOT sofree */ + return; + } + so->so_laddr = inso->so_laddr; + so->so_lport = inso->so_lport; + } - /* Close the accept() socket, set right state */ - if (inso->so_state & SS_FACCEPTONCE) { - closesocket(so->s); /* If we only accept once, close the accept() socket */ - so->so_state = SS_NOFDREF; /* Don't select it yet, even though we have an FD */ - /* if it's not FACCEPTONCE, it's already NOFDREF */ - } - so->s = s; - so->so_state |= SS_INCOMING; + tcp_mss(sototcpcb(so), 0); - so->so_iptos = tcp_tos(so); - tp = sototcpcb(so); + s = accept(inso->s, (struct sockaddr *)&addr, &addrlen); + if (s < 0) { + tcp_close(sototcpcb(so)); /* This will sofree() as well */ + return; + } + socket_set_nonblock(s); + opt = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int)); + opt = 1; + setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&opt, sizeof(int)); + socket_set_nodelay(s); - tcp_template(tp); + so->so_fport = addr.sin_port; + so->so_faddr = addr.sin_addr; + /* Translate connections from localhost to the real hostname */ + if (so->so_faddr.s_addr == 0 || + (so->so_faddr.s_addr & loopback_mask) == + (loopback_addr.s_addr & loopback_mask)) { + so->so_faddr = slirp->vhost_addr; + } - tp->t_state = TCPS_SYN_SENT; - tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; - tp->iss = slirp->tcp_iss; - slirp->tcp_iss += TCP_ISSINCR/2; - tcp_sendseqinit(tp); - tcp_output(tp); + /* Close the accept() socket, set right state */ + if (inso->so_state & SS_FACCEPTONCE) { + /* If we only accept once, close the accept() socket */ + closesocket(so->s); + + /* Don't select it yet, even though we have an FD */ + /* if it's not FACCEPTONCE, it's already NOFDREF */ + so->so_state = SS_NOFDREF; + } + so->s = s; + so->so_state |= SS_INCOMING; + + so->so_iptos = tcp_tos(so); + tp = sototcpcb(so); + + tcp_template(tp); + + tp->t_state = TCPS_SYN_SENT; + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; + tp->iss = slirp->tcp_iss; + slirp->tcp_iss += TCP_ISSINCR/2; + tcp_sendseqinit(tp); + tcp_output(tp); } /* diff --git a/spice-qemu-char.c b/spice-qemu-char.c index a4d7de8c4f..8a9236d0a8 100644 --- a/spice-qemu-char.c +++ b/spice-qemu-char.c @@ -8,14 +8,6 @@ #include "qemu/osdep.h" -#define dprintf(_scd, _level, _fmt, ...) \ - do { \ - static unsigned __dprintf_counter = 0; \ - if (_scd->debug >= _level) { \ - fprintf(stderr, "scd: %3d: " _fmt, ++__dprintf_counter, ## __VA_ARGS__);\ - } \ - } while (0) - typedef struct SpiceCharDriver { CharDriverState* chr; SpiceCharDeviceInstance sin; @@ -24,7 +16,6 @@ typedef struct SpiceCharDriver { uint8_t *buffer; uint8_t *datapos; ssize_t bufsize, datalen; - uint32_t debug; QLIST_ENTRY(SpiceCharDriver) next; } SpiceCharDriver; @@ -49,7 +40,6 @@ static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) p += last_out; } - dprintf(scd, 3, "%s: %zu/%zd\n", __func__, out, len + out); trace_spice_vmc_write(out, len + out); return out; } @@ -59,7 +49,6 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); int bytes = MIN(len, scd->datalen); - dprintf(scd, 2, "%s: %p %d/%d/%zd\n", __func__, scd->datapos, len, bytes, scd->datalen); if (bytes > 0) { memcpy(buf, scd->datapos, bytes); scd->datapos += bytes; @@ -84,11 +73,9 @@ static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event) chr_event = CHR_EVENT_BREAK; break; default: - dprintf(scd, 2, "%s: unknown %d\n", __func__, event); return; } - dprintf(scd, 2, "%s: %d\n", __func__, event); trace_spice_vmc_event(chr_event); qemu_chr_be_event(scd->chr, chr_event); } @@ -141,7 +128,6 @@ static void vmc_register_interface(SpiceCharDriver *scd) if (scd->active) { return; } - dprintf(scd, 1, "%s\n", __func__); scd->sin.base.sif = &vmc_interface.base; qemu_spice_add_interface(&scd->sin.base); scd->active = true; @@ -153,7 +139,6 @@ static void vmc_unregister_interface(SpiceCharDriver *scd) if (!scd->active) { return; } - dprintf(scd, 1, "%s\n", __func__); spice_server_remove_interface(&scd->sin.base); scd->active = false; trace_spice_vmc_unregister_interface(scd); @@ -164,7 +149,6 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { SpiceCharDriver *s = chr->opaque; - dprintf(s, 2, "%s: %d\n", __func__, len); vmc_register_interface(s); assert(s->datalen == 0); if (s->bufsize < len) { @@ -182,9 +166,13 @@ static void spice_chr_close(struct CharDriverState *chr) { SpiceCharDriver *s = chr->opaque; - printf("%s\n", __func__); vmc_unregister_interface(s); QLIST_REMOVE(s, next); + + g_free((char *)s->sin.subtype); +#if SPICE_SERVER_VERSION >= 0x000c02 + g_free((char *)s->sin.portname); +#endif g_free(s); } @@ -217,18 +205,16 @@ static void print_allowed_subtypes(void) fprintf(stderr, "\n"); } -static CharDriverState *chr_open(QemuOpts *opts, const char *subtype) +static CharDriverState *chr_open(const char *subtype) { CharDriverState *chr; SpiceCharDriver *s; - uint32_t debug = qemu_opt_get_number(opts, "debug", 0); chr = g_malloc0(sizeof(CharDriverState)); s = g_malloc0(sizeof(SpiceCharDriver)); s->chr = chr; - s->debug = debug; s->active = false; - s->sin.subtype = subtype; + s->sin.subtype = g_strdup(subtype); chr->opaque = s; chr->chr_write = spice_chr_write; chr->chr_close = spice_chr_close; @@ -240,35 +226,32 @@ static CharDriverState *chr_open(QemuOpts *opts, const char *subtype) return chr; } -CharDriverState *qemu_chr_open_spice(QemuOpts *opts) +CharDriverState *qemu_chr_open_spice_vmc(const char *type) { CharDriverState *chr; - const char *name = qemu_opt_get(opts, "name"); const char **psubtype = spice_server_char_device_recognized_subtypes(); - const char *subtype = NULL; - if (name == NULL) { + if (type == NULL) { fprintf(stderr, "spice-qemu-char: missing name parameter\n"); print_allowed_subtypes(); return NULL; } - for(;*psubtype != NULL; ++psubtype) { - if (strcmp(name, *psubtype) == 0) { - subtype = *psubtype; + for (; *psubtype != NULL; ++psubtype) { + if (strcmp(type, *psubtype) == 0) { break; } } - if (subtype == NULL) { - fprintf(stderr, "spice-qemu-char: unsupported name: %s\n", name); + if (*psubtype == NULL) { + fprintf(stderr, "spice-qemu-char: unsupported type: %s\n", type); print_allowed_subtypes(); return NULL; } - chr = chr_open(opts, subtype); + chr = chr_open(type); #if SPICE_SERVER_VERSION < 0x000901 /* See comment in vmc_state() */ - if (strcmp(subtype, "vdagent") == 0) { + if (strcmp(type, "vdagent") == 0) { qemu_chr_generic_open(chr); } #endif @@ -277,20 +260,19 @@ CharDriverState *qemu_chr_open_spice(QemuOpts *opts) } #if SPICE_SERVER_VERSION >= 0x000c02 -CharDriverState *qemu_chr_open_spice_port(QemuOpts *opts) +CharDriverState *qemu_chr_open_spice_port(const char *name) { CharDriverState *chr; SpiceCharDriver *s; - const char *name = qemu_opt_get(opts, "name"); if (name == NULL) { fprintf(stderr, "spice-qemu-char: missing name parameter\n"); return NULL; } - chr = chr_open(opts, "port"); + chr = chr_open("port"); s = chr->opaque; - s->sin.portname = name; + s->sin.portname = g_strdup(name); return chr; } @@ -307,3 +289,39 @@ void qemu_spice_register_ports(void) } } #endif + +static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + const char *name = qemu_opt_get(opts, "name"); + + if (name == NULL) { + error_setg(errp, "chardev: spice channel: no name given"); + return; + } + backend->spicevmc = g_new0(ChardevSpiceChannel, 1); + backend->spicevmc->type = g_strdup(name); +} + +static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + const char *name = qemu_opt_get(opts, "name"); + + if (name == NULL) { + error_setg(errp, "chardev: spice port: no name given"); + return; + } + backend->spiceport = g_new0(ChardevSpicePort, 1); + backend->spiceport->fqdn = g_strdup(name); +} + +static void register_types(void) +{ + register_char_driver_qapi("spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC, + qemu_chr_parse_spice_vmc); + register_char_driver_qapi("spiceport", CHARDEV_BACKEND_KIND_SPICEPORT, + qemu_chr_parse_spice_port); +} + +type_init(register_types); diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 035b29a1f3..9c55b34354 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -1,8 +1,25 @@ stub-obj-y += arch-query-cpu-def.o +stub-obj-y += clock-warp.o +stub-obj-y += cpu-get-clock.o +stub-obj-y += cpu-get-icount.o stub-obj-y += fdset-add-fd.o stub-obj-y += fdset-find-fd.o stub-obj-y += fdset-get-fd.o stub-obj-y += fdset-remove-fd.o stub-obj-y += get-fd.o +stub-obj-y += get-vm-name.o +stub-obj-y += iothread-lock.o +stub-obj-y += migr-blocker.o +stub-obj-y += mon-is-qmp.o +stub-obj-y += mon-printf.o +stub-obj-y += mon-print-filename.o +stub-obj-y += mon-protocol-event.o +stub-obj-y += mon-set-error.o +stub-obj-y += pci-drive-hot-add.o +stub-obj-y += reset.o stub-obj-y += set-fd-handler.o +stub-obj-y += slirp.o +stub-obj-y += sysbus.o +stub-obj-y += vm-stop.o +stub-obj-y += vmstate.o stub-obj-$(CONFIG_WIN32) += fd-register.o diff --git a/stubs/clock-warp.c b/stubs/clock-warp.c new file mode 100644 index 0000000000..b64c462e73 --- /dev/null +++ b/stubs/clock-warp.c @@ -0,0 +1,7 @@ +#include "qemu-common.h" +#include "qemu/timer.h" + +void qemu_clock_warp(QEMUClock *clock) +{ +} + diff --git a/stubs/cpu-get-clock.c b/stubs/cpu-get-clock.c new file mode 100644 index 0000000000..5b34c976d9 --- /dev/null +++ b/stubs/cpu-get-clock.c @@ -0,0 +1,7 @@ +#include "qemu-common.h" +#include "qemu/timer.h" + +int64_t cpu_get_clock(void) +{ + return get_clock_realtime(); +} diff --git a/stubs/cpu-get-icount.c b/stubs/cpu-get-icount.c new file mode 100644 index 0000000000..d68585965f --- /dev/null +++ b/stubs/cpu-get-icount.c @@ -0,0 +1,9 @@ +#include "qemu-common.h" +#include "qemu/timer.h" + +int use_icount; + +int64_t cpu_get_icount(void) +{ + abort(); +} diff --git a/stubs/get-vm-name.c b/stubs/get-vm-name.c new file mode 100644 index 0000000000..e5f619ffab --- /dev/null +++ b/stubs/get-vm-name.c @@ -0,0 +1,7 @@ +#include "qemu-common.h" + +const char *qemu_get_vm_name(void) +{ + return NULL; +} + diff --git a/stubs/iothread-lock.c b/stubs/iothread-lock.c new file mode 100644 index 0000000000..5d8aca1b37 --- /dev/null +++ b/stubs/iothread-lock.c @@ -0,0 +1,10 @@ +#include "qemu-common.h" +#include "qemu/main-loop.h" + +void qemu_mutex_lock_iothread(void) +{ +} + +void qemu_mutex_unlock_iothread(void) +{ +} diff --git a/stubs/migr-blocker.c b/stubs/migr-blocker.c new file mode 100644 index 0000000000..300df6e205 --- /dev/null +++ b/stubs/migr-blocker.c @@ -0,0 +1,10 @@ +#include "qemu-common.h" +#include "migration/migration.h" + +void migrate_add_blocker(Error *reason) +{ +} + +void migrate_del_blocker(Error *reason) +{ +} diff --git a/stubs/mon-is-qmp.c b/stubs/mon-is-qmp.c new file mode 100644 index 0000000000..1f0a8fd98a --- /dev/null +++ b/stubs/mon-is-qmp.c @@ -0,0 +1,7 @@ +#include "qemu-common.h" +#include "monitor/monitor.h" + +int monitor_cur_is_qmp(void) +{ + return 0; +} diff --git a/stubs/mon-print-filename.c b/stubs/mon-print-filename.c new file mode 100644 index 0000000000..9c939641ff --- /dev/null +++ b/stubs/mon-print-filename.c @@ -0,0 +1,6 @@ +#include "qemu-common.h" +#include "monitor/monitor.h" + +void monitor_print_filename(Monitor *mon, const char *filename) +{ +} diff --git a/stubs/mon-printf.c b/stubs/mon-printf.c new file mode 100644 index 0000000000..0ce2ca6925 --- /dev/null +++ b/stubs/mon-printf.c @@ -0,0 +1,10 @@ +#include "qemu-common.h" +#include "monitor/monitor.h" + +void monitor_printf(Monitor *mon, const char *fmt, ...) +{ +} + +void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) +{ +} diff --git a/stubs/mon-protocol-event.c b/stubs/mon-protocol-event.c new file mode 100644 index 0000000000..0946e94724 --- /dev/null +++ b/stubs/mon-protocol-event.c @@ -0,0 +1,6 @@ +#include "qemu-common.h" +#include "monitor/monitor.h" + +void monitor_protocol_event(MonitorEvent event, QObject *data) +{ +} diff --git a/stubs/mon-set-error.c b/stubs/mon-set-error.c new file mode 100644 index 0000000000..d0411f97fa --- /dev/null +++ b/stubs/mon-set-error.c @@ -0,0 +1,8 @@ +#include "qemu-common.h" +#include "monitor/monitor.h" + +Monitor *cur_mon; + +void monitor_set_error(Monitor *mon, QError *qerror) +{ +} diff --git a/stubs/pci-drive-hot-add.c b/stubs/pci-drive-hot-add.c new file mode 100644 index 0000000000..1d98145802 --- /dev/null +++ b/stubs/pci-drive-hot-add.c @@ -0,0 +1,10 @@ +#include +#include +#include + +int pci_drive_hot_add(Monitor *mon, const QDict *qdict, DriveInfo *dinfo) +{ + /* On non-x86 we don't do PCI hotplug */ + monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); + return -1; +} diff --git a/stubs/reset.c b/stubs/reset.c new file mode 100644 index 0000000000..ad287251ed --- /dev/null +++ b/stubs/reset.c @@ -0,0 +1,13 @@ +#include "hw/hw.h" + +/* Stub functions for binaries that never call qemu_devices_reset(), + * and don't need to keep track of the reset handler list. + */ + +void qemu_register_reset(QEMUResetHandler *func, void *opaque) +{ +} + +void qemu_unregister_reset(QEMUResetHandler *func, void *opaque) +{ +} diff --git a/stubs/slirp.c b/stubs/slirp.c new file mode 100644 index 0000000000..f1fc833f7a --- /dev/null +++ b/stubs/slirp.c @@ -0,0 +1,15 @@ +#include "qemu-common.h" +#include "slirp/slirp.h" + +void slirp_update_timeout(uint32_t *timeout) +{ +} + +void slirp_pollfds_fill(GArray *pollfds) +{ +} + +void slirp_pollfds_poll(GArray *pollfds, int select_error) +{ +} + diff --git a/stubs/sysbus.c b/stubs/sysbus.c new file mode 100644 index 0000000000..e13496582b --- /dev/null +++ b/stubs/sysbus.c @@ -0,0 +1,6 @@ +#include "hw/qdev-core.h" + +BusState *sysbus_get_default(void) +{ + return NULL; +} diff --git a/stubs/vm-stop.c b/stubs/vm-stop.c new file mode 100644 index 0000000000..45689354f6 --- /dev/null +++ b/stubs/vm-stop.c @@ -0,0 +1,7 @@ +#include "qemu-common.h" +#include "sysemu/sysemu.h" + +void vm_stop(RunState state) +{ + abort(); +} diff --git a/stubs/vmstate.c b/stubs/vmstate.c new file mode 100644 index 0000000000..778bc3fc69 --- /dev/null +++ b/stubs/vmstate.c @@ -0,0 +1,19 @@ +#include "qemu-common.h" +#include "migration/vmstate.h" + +const VMStateDescription vmstate_dummy = {}; + +int vmstate_register_with_alias_id(DeviceState *dev, + int instance_id, + const VMStateDescription *vmsd, + void *base, int alias_id, + int required_for_version) +{ + return 0; +} + +void vmstate_unregister(DeviceState *dev, + const VMStateDescription *vmsd, + void *opaque) +{ +} diff --git a/target-alpha/cpu-qom.h b/target-alpha/cpu-qom.h index f2414f7e4f..32ee286db3 100644 --- a/target-alpha/cpu-qom.h +++ b/target-alpha/cpu-qom.h @@ -34,6 +34,7 @@ /** * AlphaCPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. * * An Alpha CPU model. @@ -43,6 +44,7 @@ typedef struct AlphaCPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); } AlphaCPUClass; @@ -58,6 +60,9 @@ typedef struct AlphaCPU { /*< public >*/ CPUAlphaState env; + + /* This alarm doesn't exist in real hardware; we wish it did. */ + struct QEMUTimer *alarm_timer; } AlphaCPU; static inline AlphaCPU *alpha_env_get_cpu(CPUAlphaState *env) @@ -67,5 +72,8 @@ static inline AlphaCPU *alpha_env_get_cpu(CPUAlphaState *env) #define ENV_GET_CPU(e) CPU(alpha_env_get_cpu(e)) +#define ENV_OFFSET offsetof(AlphaCPU, env) + +void alpha_cpu_do_interrupt(CPUState *cpu); #endif diff --git a/target-alpha/cpu.c b/target-alpha/cpu.c index 11a19ebc87..cad1716601 100644 --- a/target-alpha/cpu.c +++ b/target-alpha/cpu.c @@ -23,14 +23,226 @@ #include "qemu-common.h" -static void alpha_cpu_initfn(Object *obj) +static void alpha_cpu_realizefn(DeviceState *dev, Error **errp) +{ + AlphaCPU *cpu = ALPHA_CPU(dev); + AlphaCPUClass *acc = ALPHA_CPU_GET_CLASS(dev); + + qemu_init_vcpu(&cpu->env); + + acc->parent_realize(dev, errp); +} + +/* Sort alphabetically by type name. */ +static gint alpha_cpu_list_compare(gconstpointer a, gconstpointer b) +{ + ObjectClass *class_a = (ObjectClass *)a; + ObjectClass *class_b = (ObjectClass *)b; + const char *name_a, *name_b; + + name_a = object_class_get_name(class_a); + name_b = object_class_get_name(class_b); + return strcmp(name_a, name_b); +} + +static void alpha_cpu_list_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CPUListState *s = user_data; + + (*s->cpu_fprintf)(s->file, " %s\n", + object_class_get_name(oc)); +} + +void alpha_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ + CPUListState s = { + .file = f, + .cpu_fprintf = cpu_fprintf, + }; + GSList *list; + + list = object_class_get_list(TYPE_ALPHA_CPU, false); + list = g_slist_sort(list, alpha_cpu_list_compare); + (*cpu_fprintf)(f, "Available CPUs:\n"); + g_slist_foreach(list, alpha_cpu_list_entry, &s); + g_slist_free(list); +} + +/* Models */ + +#define TYPE(model) model "-" TYPE_ALPHA_CPU + +typedef struct AlphaCPUAlias { + const char *alias; + const char *typename; +} AlphaCPUAlias; + +static const AlphaCPUAlias alpha_cpu_aliases[] = { + { "21064", TYPE("ev4") }, + { "21164", TYPE("ev5") }, + { "21164a", TYPE("ev56") }, + { "21164pc", TYPE("pca56") }, + { "21264", TYPE("ev6") }, + { "21264a", TYPE("ev67") }, +}; + +static ObjectClass *alpha_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc = NULL; + char *typename; + int i; + + if (cpu_model == NULL) { + return NULL; + } + + oc = object_class_by_name(cpu_model); + if (oc != NULL && object_class_dynamic_cast(oc, TYPE_ALPHA_CPU) != NULL && + !object_class_is_abstract(oc)) { + return oc; + } + + for (i = 0; i < ARRAY_SIZE(alpha_cpu_aliases); i++) { + if (strcmp(cpu_model, alpha_cpu_aliases[i].alias) == 0) { + oc = object_class_by_name(alpha_cpu_aliases[i].typename); + assert(oc != NULL && !object_class_is_abstract(oc)); + return oc; + } + } + + typename = g_strdup_printf("%s-" TYPE_ALPHA_CPU, cpu_model); + oc = object_class_by_name(typename); + g_free(typename); + if (oc != NULL && object_class_is_abstract(oc)) { + oc = NULL; + } + return oc; +} + +AlphaCPU *cpu_alpha_init(const char *cpu_model) +{ + AlphaCPU *cpu; + CPUAlphaState *env; + ObjectClass *cpu_class; + + cpu_class = alpha_cpu_class_by_name(cpu_model); + if (cpu_class == NULL) { + /* Default to ev67; no reason not to emulate insns by default. */ + cpu_class = object_class_by_name(TYPE("ev67")); + } + cpu = ALPHA_CPU(object_new(object_class_get_name(cpu_class))); + env = &cpu->env; + + env->cpu_model_str = cpu_model; + + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + + return cpu; +} + +static void ev4_cpu_initfn(Object *obj) { AlphaCPU *cpu = ALPHA_CPU(obj); CPUAlphaState *env = &cpu->env; + env->implver = IMPLVER_2106x; +} + +static const TypeInfo ev4_cpu_type_info = { + .name = TYPE("ev4"), + .parent = TYPE_ALPHA_CPU, + .instance_init = ev4_cpu_initfn, +}; + +static void ev5_cpu_initfn(Object *obj) +{ + AlphaCPU *cpu = ALPHA_CPU(obj); + CPUAlphaState *env = &cpu->env; + + env->implver = IMPLVER_21164; +} + +static const TypeInfo ev5_cpu_type_info = { + .name = TYPE("ev5"), + .parent = TYPE_ALPHA_CPU, + .instance_init = ev5_cpu_initfn, +}; + +static void ev56_cpu_initfn(Object *obj) +{ + AlphaCPU *cpu = ALPHA_CPU(obj); + CPUAlphaState *env = &cpu->env; + + env->amask |= AMASK_BWX; +} + +static const TypeInfo ev56_cpu_type_info = { + .name = TYPE("ev56"), + .parent = TYPE("ev5"), + .instance_init = ev56_cpu_initfn, +}; + +static void pca56_cpu_initfn(Object *obj) +{ + AlphaCPU *cpu = ALPHA_CPU(obj); + CPUAlphaState *env = &cpu->env; + + env->amask |= AMASK_MVI; +} + +static const TypeInfo pca56_cpu_type_info = { + .name = TYPE("pca56"), + .parent = TYPE("ev56"), + .instance_init = pca56_cpu_initfn, +}; + +static void ev6_cpu_initfn(Object *obj) +{ + AlphaCPU *cpu = ALPHA_CPU(obj); + CPUAlphaState *env = &cpu->env; + + env->implver = IMPLVER_21264; + env->amask = AMASK_BWX | AMASK_FIX | AMASK_MVI | AMASK_TRAP; +} + +static const TypeInfo ev6_cpu_type_info = { + .name = TYPE("ev6"), + .parent = TYPE_ALPHA_CPU, + .instance_init = ev6_cpu_initfn, +}; + +static void ev67_cpu_initfn(Object *obj) +{ + AlphaCPU *cpu = ALPHA_CPU(obj); + CPUAlphaState *env = &cpu->env; + + env->amask |= AMASK_CIX | AMASK_PREFETCH; +} + +static const TypeInfo ev67_cpu_type_info = { + .name = TYPE("ev67"), + .parent = TYPE("ev6"), + .instance_init = ev67_cpu_initfn, +}; + +static const TypeInfo ev68_cpu_type_info = { + .name = TYPE("ev68"), + .parent = TYPE("ev67"), +}; + +static void alpha_cpu_initfn(Object *obj) +{ + CPUState *cs = CPU(obj); + AlphaCPU *cpu = ALPHA_CPU(obj); + CPUAlphaState *env = &cpu->env; + + cs->env_ptr = env; cpu_exec_init(env); tlb_flush(env, 1); + alpha_translate_init(); + #if defined(CONFIG_USER_ONLY) env->ps = PS_USER_MODE; cpu_alpha_store_fpcr(env, (FPCR_INVD | FPCR_DZED | FPCR_OVFD @@ -41,18 +253,39 @@ static void alpha_cpu_initfn(Object *obj) env->fen = 1; } +static void alpha_cpu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + AlphaCPUClass *acc = ALPHA_CPU_CLASS(oc); + + acc->parent_realize = dc->realize; + dc->realize = alpha_cpu_realizefn; + + cc->class_by_name = alpha_cpu_class_by_name; + cc->do_interrupt = alpha_cpu_do_interrupt; +} + static const TypeInfo alpha_cpu_type_info = { .name = TYPE_ALPHA_CPU, .parent = TYPE_CPU, .instance_size = sizeof(AlphaCPU), .instance_init = alpha_cpu_initfn, - .abstract = false, + .abstract = true, .class_size = sizeof(AlphaCPUClass), + .class_init = alpha_cpu_class_init, }; static void alpha_cpu_register_types(void) { type_register_static(&alpha_cpu_type_info); + type_register_static(&ev4_cpu_type_info); + type_register_static(&ev5_cpu_type_info); + type_register_static(&ev56_cpu_type_info); + type_register_static(&pca56_cpu_type_info); + type_register_static(&ev6_cpu_type_info); + type_register_static(&ev67_cpu_type_info); + type_register_static(&ev68_cpu_type_info); } type_init(alpha_cpu_register_types) diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h index b75c856f97..2156a1e5fd 100644 --- a/target-alpha/cpu.h +++ b/target-alpha/cpu.h @@ -277,7 +277,6 @@ struct CPUAlphaState { #endif /* This alarm doesn't exist in real hardware; we wish it did. */ - struct QEMUTimer *alarm_timer; uint64_t alarm_expire; /* Those resources are used only in QEMU core */ @@ -290,7 +289,7 @@ struct CPUAlphaState { int implver; }; -#define cpu_init cpu_alpha_init +#define cpu_list alpha_cpu_list #define cpu_exec cpu_alpha_exec #define cpu_gen_code cpu_alpha_gen_code #define cpu_signal_handler cpu_alpha_signal_handler @@ -427,7 +426,20 @@ enum { IR_ZERO = 31, }; -CPUAlphaState * cpu_alpha_init (const char *cpu_model); +void alpha_translate_init(void); + +AlphaCPU *cpu_alpha_init(const char *cpu_model); + +static inline CPUAlphaState *cpu_init(const char *cpu_model) +{ + AlphaCPU *cpu = cpu_alpha_init(cpu_model); + if (cpu == NULL) { + return NULL; + } + return &cpu->env; +} + +void alpha_cpu_list(FILE *f, fprintf_function cpu_fprintf); int cpu_alpha_exec(CPUAlphaState *s); /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero @@ -437,7 +449,6 @@ int cpu_alpha_signal_handler(int host_signum, void *pinfo, int cpu_alpha_handle_mmu_fault (CPUAlphaState *env, uint64_t address, int rw, int mmu_idx); #define cpu_handle_mmu_fault cpu_alpha_handle_mmu_fault -void do_interrupt (CPUAlphaState *env); void do_restore_state(CPUAlphaState *, uintptr_t retaddr); void QEMU_NORETURN dynamic_excp(CPUAlphaState *, uintptr_t, int, int); void QEMU_NORETURN arith_excp(CPUAlphaState *, uintptr_t, int, uint64_t); @@ -505,8 +516,6 @@ static inline void cpu_set_tls(CPUAlphaState *env, target_ulong newtls) static inline bool cpu_has_work(CPUState *cpu) { - CPUAlphaState *env = &ALPHA_CPU(cpu)->env; - /* Here we are checking to see if the CPU should wake up from HALT. We will have gotten into this state only for WTINT from PALmode. */ /* ??? I'm not sure how the IPL state works with WTINT to keep a CPU @@ -514,7 +523,7 @@ static inline bool cpu_has_work(CPUState *cpu) assume that if a CPU really wants to stay asleep, it will mask interrupts at the chipset level, which will prevent these bits from being set in the first place. */ - return env->interrupt_request & (CPU_INTERRUPT_HARD + return cpu->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_TIMER | CPU_INTERRUPT_SMP | CPU_INTERRUPT_MCHK); diff --git a/target-alpha/helper.c b/target-alpha/helper.c index 22c9c6ec8c..5741ec25ea 100644 --- a/target-alpha/helper.c +++ b/target-alpha/helper.c @@ -345,8 +345,10 @@ int cpu_alpha_handle_mmu_fault(CPUAlphaState *env, target_ulong addr, int rw, } #endif /* USER_ONLY */ -void do_interrupt (CPUAlphaState *env) +void alpha_cpu_do_interrupt(CPUState *cs) { + AlphaCPU *cpu = ALPHA_CPU(cs); + CPUAlphaState *env = &cpu->env; int i = env->exception_index; if (qemu_loglevel_mask(CPU_LOG_INT)) { diff --git a/target-alpha/helper.h b/target-alpha/helper.h index eac3041b87..3321fde916 100644 --- a/target-alpha/helper.h +++ b/target-alpha/helper.h @@ -9,7 +9,6 @@ DEF_HELPER_FLAGS_3(subqv, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(sublv, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(mullv, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(mulqv, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_2(umulh, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_1(ctpop, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_FLAGS_1(ctlz, TCG_CALL_NO_RWG_SE, i64, i64) diff --git a/target-alpha/int_helper.c b/target-alpha/int_helper.c index c9b42b6ed4..51ccd41bd2 100644 --- a/target-alpha/int_helper.c +++ b/target-alpha/int_helper.c @@ -22,13 +22,6 @@ #include "qemu/host-utils.h" -uint64_t helper_umulh(uint64_t op1, uint64_t op2) -{ - uint64_t tl, th; - mulu64(&tl, &th, op1, op2); - return th; -} - uint64_t helper_ctpop(uint64_t arg) { return ctpop64(arg); diff --git a/target-alpha/sys_helper.c b/target-alpha/sys_helper.c index 434a63a97d..339501af90 100644 --- a/target-alpha/sys_helper.c +++ b/target-alpha/sys_helper.c @@ -77,11 +77,13 @@ uint64_t helper_get_time(void) void helper_set_alarm(CPUAlphaState *env, uint64_t expire) { + AlphaCPU *cpu = alpha_env_get_cpu(env); + if (expire) { env->alarm_expire = expire; - qemu_mod_timer(env->alarm_timer, expire); + qemu_mod_timer(cpu->alarm_timer, expire); } else { - qemu_del_timer(env->alarm_timer); + qemu_del_timer(cpu->alarm_timer); } } #endif /* CONFIG_USER_ONLY */ diff --git a/target-alpha/translate.c b/target-alpha/translate.c index 3afc3c63f9..4db16db462 100644 --- a/target-alpha/translate.c +++ b/target-alpha/translate.c @@ -90,7 +90,7 @@ static char cpu_reg_names[10*4+21*5 + 10*5+21*6]; #include "exec/gen-icount.h" -static void alpha_translate_init(void) +void alpha_translate_init(void) { int i; char *p; @@ -1390,7 +1390,6 @@ static inline void glue(gen_, name)(int ra, int rb, int rc, int islit,\ tcg_temp_free(tmp1); \ } \ } -ARITH3(umulh) ARITH3(cmpbge) ARITH3(minub8) ARITH3(minsb8) @@ -1579,7 +1578,7 @@ static ExitStatus gen_call_pal(DisasContext *ctx, int palcode) case 0x3C: /* WHAMI */ tcg_gen_ld32s_i64(cpu_ir[IR_V0], cpu_env, - offsetof(CPUAlphaState, cpu_index)); + -offsetof(AlphaCPU, env) + offsetof(CPUState, cpu_index)); break; default: @@ -1687,7 +1686,8 @@ static ExitStatus gen_mtpr(DisasContext *ctx, int rb, int regno) case 253: /* WAIT */ tmp = tcg_const_i64(1); - tcg_gen_st32_i64(tmp, cpu_env, offsetof(CPUAlphaState, halted)); + tcg_gen_st32_i64(tmp, cpu_env, -offsetof(AlphaCPU, env) + + offsetof(CPUState, halted)); return gen_excp(ctx, EXCP_HLT, 0); case 252: @@ -2426,7 +2426,24 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) break; case 0x30: /* UMULH */ - gen_umulh(ra, rb, rc, islit, lit); + { + TCGv low; + if (unlikely(rc == 31)){ + break; + } + if (ra == 31) { + tcg_gen_movi_i64(cpu_ir[rc], 0); + break; + } + low = tcg_temp_new(); + if (islit) { + tcg_gen_movi_tl(low, lit); + tcg_gen_mulu2_i64(low, cpu_ir[rc], cpu_ir[ra], low); + } else { + tcg_gen_mulu2_i64(low, cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); + } + tcg_temp_free(low); + } break; case 0x40: /* MULL/V */ @@ -3395,7 +3412,7 @@ static inline void gen_intermediate_code_internal(CPUAlphaState *env, if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_icount_start(); + gen_tb_start(); do { if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { QTAILQ_FOREACH(bp, &env->breakpoints, entry) { @@ -3462,7 +3479,7 @@ static inline void gen_intermediate_code_internal(CPUAlphaState *env, abort(); } - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; if (search_pc) { j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; @@ -3493,62 +3510,6 @@ void gen_intermediate_code_pc (CPUAlphaState *env, struct TranslationBlock *tb) gen_intermediate_code_internal(env, tb, 1); } -struct cpu_def_t { - const char *name; - int implver, amask; -}; - -static const struct cpu_def_t cpu_defs[] = { - { "ev4", IMPLVER_2106x, 0 }, - { "ev5", IMPLVER_21164, 0 }, - { "ev56", IMPLVER_21164, AMASK_BWX }, - { "pca56", IMPLVER_21164, AMASK_BWX | AMASK_MVI }, - { "ev6", IMPLVER_21264, AMASK_BWX | AMASK_FIX | AMASK_MVI | AMASK_TRAP }, - { "ev67", IMPLVER_21264, (AMASK_BWX | AMASK_FIX | AMASK_CIX - | AMASK_MVI | AMASK_TRAP | AMASK_PREFETCH), }, - { "ev68", IMPLVER_21264, (AMASK_BWX | AMASK_FIX | AMASK_CIX - | AMASK_MVI | AMASK_TRAP | AMASK_PREFETCH), }, - { "21064", IMPLVER_2106x, 0 }, - { "21164", IMPLVER_21164, 0 }, - { "21164a", IMPLVER_21164, AMASK_BWX }, - { "21164pc", IMPLVER_21164, AMASK_BWX | AMASK_MVI }, - { "21264", IMPLVER_21264, AMASK_BWX | AMASK_FIX | AMASK_MVI | AMASK_TRAP }, - { "21264a", IMPLVER_21264, (AMASK_BWX | AMASK_FIX | AMASK_CIX - | AMASK_MVI | AMASK_TRAP | AMASK_PREFETCH), } -}; - -CPUAlphaState * cpu_alpha_init (const char *cpu_model) -{ - AlphaCPU *cpu; - CPUAlphaState *env; - int implver, amask, i, max; - - cpu = ALPHA_CPU(object_new(TYPE_ALPHA_CPU)); - env = &cpu->env; - - alpha_translate_init(); - - /* Default to ev67; no reason not to emulate insns by default. */ - implver = IMPLVER_21264; - amask = (AMASK_BWX | AMASK_FIX | AMASK_CIX | AMASK_MVI - | AMASK_TRAP | AMASK_PREFETCH); - - max = ARRAY_SIZE(cpu_defs); - for (i = 0; i < max; i++) { - if (strcmp (cpu_model, cpu_defs[i].name) == 0) { - implver = cpu_defs[i].implver; - amask = cpu_defs[i].amask; - break; - } - } - env->implver = implver; - env->amask = amask; - env->cpu_model_str = cpu_model; - - qemu_init_vcpu(env); - return env; -} - void restore_state_to_opc(CPUAlphaState *env, TranslationBlock *tb, int pc_pos) { env->pc = tcg_ctx.gen_opc_pc[pc_pos]; diff --git a/target-arm/Makefile.objs b/target-arm/Makefile.objs index b6f1a9ebf6..d89b57c114 100644 --- a/target-arm/Makefile.objs +++ b/target-arm/Makefile.objs @@ -1,4 +1,5 @@ obj-y += arm-semi.o obj-$(CONFIG_SOFTMMU) += machine.o +obj-$(CONFIG_KVM) += kvm.o obj-y += translate.o op_helper.o helper.o cpu.o obj-y += neon_helper.o iwmmxt_helper.o diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index 0f455c40ff..25895509be 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -33,6 +33,7 @@ /** * ARMCPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. * * An ARM CPU model. @@ -42,6 +43,7 @@ typedef struct ARMCPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); } ARMCPUClass; @@ -107,7 +109,11 @@ static inline ARMCPU *arm_env_get_cpu(CPUARMState *env) #define ENV_GET_CPU(e) CPU(arm_env_get_cpu(e)) -void arm_cpu_realize(ARMCPU *cpu); +#define ENV_OFFSET offsetof(ARMCPU, env) + void register_cp_regs_for_features(ARMCPU *cpu); +void arm_cpu_do_interrupt(CPUState *cpu); +void arm_v7m_cpu_do_interrupt(CPUState *cpu); + #endif diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 17875ed0f0..a1e90939d6 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -64,7 +64,7 @@ static void arm_cpu_reset(CPUState *s) CPUARMState *env = &cpu->env; if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); + qemu_log("CPU Reset (CPU %d)\n", s->cpu_index); log_cpu_state(env, 0); } @@ -134,11 +134,19 @@ static inline void set_feature(CPUARMState *env, int feature) static void arm_cpu_initfn(Object *obj) { + CPUState *cs = CPU(obj); ARMCPU *cpu = ARM_CPU(obj); + static bool inited; + cs->env_ptr = &cpu->env; cpu_exec_init(&cpu->env); cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); + + if (tcg_enabled() && !inited) { + inited = true; + arm_translate_init(); + } } static void arm_cpu_finalizefn(Object *obj) @@ -147,15 +155,12 @@ static void arm_cpu_finalizefn(Object *obj) g_hash_table_destroy(cpu->cp_regs); } -void arm_cpu_realize(ARMCPU *cpu) +static void arm_cpu_realizefn(DeviceState *dev, Error **errp) { - /* This function is called by cpu_arm_init() because it - * needs to do common actions based on feature bits, etc - * that have been set by the subclass init functions. - * When we have QOM realize support it should become - * a true realize function instead. - */ + ARMCPU *cpu = ARM_CPU(dev); + ARMCPUClass *acc = ARM_CPU_GET_CLASS(dev); CPUARMState *env = &cpu->env; + /* Some features automatically imply others: */ if (arm_feature(env, ARM_FEATURE_V7)) { set_feature(env, ARM_FEATURE_VAPA); @@ -197,10 +202,35 @@ void arm_cpu_realize(ARMCPU *cpu) } register_cp_regs_for_features(cpu); + arm_cpu_register_gdb_regs_for_features(cpu); + + cpu_reset(CPU(cpu)); + qemu_init_vcpu(env); + + acc->parent_realize(dev, errp); } /* CPU models */ +static ObjectClass *arm_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + + if (!cpu_model) { + return NULL; + } + + typename = g_strdup_printf("%s-" TYPE_ARM_CPU, cpu_model); + oc = object_class_by_name(typename); + g_free(typename); + if (!oc || !object_class_dynamic_cast(oc, TYPE_ARM_CPU) || + object_class_is_abstract(oc)) { + return NULL; + } + return oc; +} + static void arm926_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -382,6 +412,15 @@ static void cortex_m3_initfn(Object *obj) cpu->midr = 0x410fc231; } +static void arm_v7m_class_init(ObjectClass *oc, void *data) +{ +#ifndef CONFIG_USER_ONLY + CPUClass *cc = CPU_CLASS(oc); + + cc->do_interrupt = arm_v7m_cpu_do_interrupt; +#endif +} + static const ARMCPRegInfo cortexa8_cp_reginfo[] = { { .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -722,6 +761,7 @@ static void arm_any_initfn(Object *obj) typedef struct ARMCPUInfo { const char *name; void (*initfn)(Object *obj); + void (*class_init)(ObjectClass *oc, void *data); } ARMCPUInfo; static const ARMCPUInfo arm_cpus[] = { @@ -736,7 +776,8 @@ static const ARMCPUInfo arm_cpus[] = { { .name = "arm1136", .initfn = arm1136_initfn }, { .name = "arm1176", .initfn = arm1176_initfn }, { .name = "arm11mpcore", .initfn = arm11mpcore_initfn }, - { .name = "cortex-m3", .initfn = cortex_m3_initfn }, + { .name = "cortex-m3", .initfn = cortex_m3_initfn, + .class_init = arm_v7m_class_init }, { .name = "cortex-a8", .initfn = cortex_a8_initfn }, { .name = "cortex-a9", .initfn = cortex_a9_initfn }, { .name = "cortex-a15", .initfn = cortex_a15_initfn }, @@ -763,22 +804,31 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) { ARMCPUClass *acc = ARM_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(acc); + DeviceClass *dc = DEVICE_CLASS(oc); + + acc->parent_realize = dc->realize; + dc->realize = arm_cpu_realizefn; acc->parent_reset = cc->reset; cc->reset = arm_cpu_reset; + + cc->class_by_name = arm_cpu_class_by_name; + cc->do_interrupt = arm_cpu_do_interrupt; } static void cpu_register(const ARMCPUInfo *info) { TypeInfo type_info = { - .name = info->name, .parent = TYPE_ARM_CPU, .instance_size = sizeof(ARMCPU), .instance_init = info->initfn, .class_size = sizeof(ARMCPUClass), + .class_init = info->class_init, }; - type_register_static(&type_info); + type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name); + type_register(&type_info); + g_free((void *)type_info.name); } static const TypeInfo arm_cpu_type_info = { diff --git a/target-arm/cpu.h b/target-arm/cpu.h index ffddfcbc0d..2b97221209 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -234,8 +234,9 @@ typedef struct CPUARMState { ARMCPU *cpu_arm_init(const char *cpu_model); void arm_translate_init(void); +void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu); int cpu_arm_exec(CPUARMState *s); -void do_interrupt(CPUARMState *); +int bank_number(int mode); void switch_mode(CPUARMState *, int); uint32_t do_arm_semihosting(CPUARMState *env); @@ -720,9 +721,7 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, static inline bool cpu_has_work(CPUState *cpu) { - CPUARMState *env = &ARM_CPU(cpu)->env; - - return env->interrupt_request & + return cpu->interrupt_request & (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXITTB); } diff --git a/target-arm/helper.c b/target-arm/helper.c index e343fac853..fd055e89f2 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -764,7 +764,7 @@ static int omap_wfi_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { /* Wait-for-interrupt (deprecated) */ - cpu_interrupt(env, CPU_INTERRUPT_HALT); + cpu_interrupt(CPU(arm_env_get_cpu(env)), CPU_INTERRUPT_HALT); return 0; } @@ -902,7 +902,8 @@ static const ARMCPRegInfo strongarm_cp_reginfo[] = { static int mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value) { - uint32_t mpidr = env->cpu_index; + CPUState *cs = CPU(arm_env_get_cpu(env)); + uint32_t mpidr = cs->cpu_index; /* We don't support setting cluster ID ([8..11]) * so these bits always RAZ. */ @@ -1261,22 +1262,26 @@ ARMCPU *cpu_arm_init(const char *cpu_model) { ARMCPU *cpu; CPUARMState *env; - static int inited = 0; + ObjectClass *oc; - if (!object_class_by_name(cpu_model)) { + oc = cpu_class_by_name(TYPE_ARM_CPU, cpu_model); + if (!oc) { return NULL; } - cpu = ARM_CPU(object_new(cpu_model)); + cpu = ARM_CPU(object_new(object_class_get_name(oc))); env = &cpu->env; env->cpu_model_str = cpu_model; - arm_cpu_realize(cpu); - if (tcg_enabled() && !inited) { - inited = 1; - arm_translate_init(); - } + /* TODO this should be set centrally, once possible */ + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + + return cpu; +} + +void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) +{ + CPUARMState *env = &cpu->env; - cpu_reset(CPU(cpu)); if (arm_feature(env, ARM_FEATURE_NEON)) { gdb_register_coprocessor(env, vfp_gdb_get_reg, vfp_gdb_set_reg, 51, "arm-neon.xml", 0); @@ -1287,15 +1292,8 @@ ARMCPU *cpu_arm_init(const char *cpu_model) gdb_register_coprocessor(env, vfp_gdb_get_reg, vfp_gdb_set_reg, 19, "arm-vfp.xml", 0); } - qemu_init_vcpu(env); - return cpu; } -typedef struct ARMCPUListState { - fprintf_function cpu_fprintf; - FILE *file; -} ARMCPUListState; - /* Sort alphabetically by type name, except for "any". */ static gint arm_cpu_list_compare(gconstpointer a, gconstpointer b) { @@ -1305,9 +1303,9 @@ static gint arm_cpu_list_compare(gconstpointer a, gconstpointer b) name_a = object_class_get_name(class_a); name_b = object_class_get_name(class_b); - if (strcmp(name_a, "any") == 0) { + if (strcmp(name_a, "any-" TYPE_ARM_CPU) == 0) { return 1; - } else if (strcmp(name_b, "any") == 0) { + } else if (strcmp(name_b, "any-" TYPE_ARM_CPU) == 0) { return -1; } else { return strcmp(name_a, name_b); @@ -1317,15 +1315,20 @@ static gint arm_cpu_list_compare(gconstpointer a, gconstpointer b) static void arm_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; - ARMCPUListState *s = user_data; + CPUListState *s = user_data; + const char *typename; + char *name; + typename = object_class_get_name(oc); + name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_ARM_CPU)); (*s->cpu_fprintf)(s->file, " %s\n", - object_class_get_name(oc)); + name); + g_free(name); } void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf) { - ARMCPUListState s = { + CPUListState s = { .file = f, .cpu_fprintf = cpu_fprintf, }; @@ -1564,8 +1567,11 @@ uint32_t HELPER(rbit)(uint32_t x) #if defined(CONFIG_USER_ONLY) -void do_interrupt (CPUARMState *env) +void arm_cpu_do_interrupt(CPUState *cs) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + env->exception_index = -1; } @@ -1614,7 +1620,7 @@ uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode) #else /* Map CPU modes onto saved register banks. */ -static inline int bank_number(CPUARMState *env, int mode) +int bank_number(int mode) { switch (mode) { case ARM_CPU_MODE_USR: @@ -1631,8 +1637,7 @@ static inline int bank_number(CPUARMState *env, int mode) case ARM_CPU_MODE_FIQ: return 5; } - cpu_abort(env, "Bad mode %x\n", mode); - return -1; + hw_error("bank number requested for bad CPSR mode value 0x%x\n", mode); } void switch_mode(CPUARMState *env, int mode) @@ -1652,12 +1657,12 @@ void switch_mode(CPUARMState *env, int mode) memcpy (env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t)); } - i = bank_number(env, old_mode); + i = bank_number(old_mode); env->banked_r13[i] = env->regs[13]; env->banked_r14[i] = env->regs[14]; env->banked_spsr[i] = env->spsr; - i = bank_number(env, mode); + i = bank_number(mode); env->regs[13] = env->banked_r13[i]; env->regs[14] = env->banked_r14[i]; env->spsr = env->banked_spsr[i]; @@ -1720,8 +1725,10 @@ static void do_v7m_exception_exit(CPUARMState *env) pointer. */ } -static void do_interrupt_v7m(CPUARMState *env) +void arm_v7m_cpu_do_interrupt(CPUState *cs) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; uint32_t xpsr = xpsr_read(env); uint32_t lr; uint32_t addr; @@ -1741,7 +1748,7 @@ static void do_interrupt_v7m(CPUARMState *env) armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE); return; case EXCP_SWI: - env->regs[15] += 2; + /* The PC already points to the next instruction. */ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC); return; case EXCP_PREFETCH_ABORT: @@ -1797,17 +1804,17 @@ static void do_interrupt_v7m(CPUARMState *env) } /* Handle a CPU exception. */ -void do_interrupt(CPUARMState *env) +void arm_cpu_do_interrupt(CPUState *cs) { + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; uint32_t addr; uint32_t mask; int new_mode; uint32_t offset; - if (IS_M(env)) { - do_interrupt_v7m(env); - return; - } + assert(!IS_M(env)); + /* TODO: Vectored interrupt controller. */ switch (env->exception_index) { case EXCP_UDEF: @@ -1905,7 +1912,7 @@ void do_interrupt(CPUARMState *env) } env->regs[14] = env->regs[15] + offset; env->regs[15] = addr; - env->interrupt_request |= CPU_INTERRUPT_EXITTB; + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } /* Check section/page access permissions. @@ -2527,7 +2534,7 @@ void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val) if ((env->uncached_cpsr & CPSR_M) == mode) { env->regs[13] = val; } else { - env->banked_r13[bank_number(env, mode)] = val; + env->banked_r13[bank_number(mode)] = val; } } @@ -2536,7 +2543,7 @@ uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode) if ((env->uncached_cpsr & CPSR_M) == mode) { return env->regs[13]; } else { - return env->banked_r13[bank_number(env, mode)]; + return env->banked_r13[bank_number(mode)]; } } @@ -2890,11 +2897,6 @@ uint32_t HELPER(sel_flags)(uint32_t flags, uint32_t a, uint32_t b) return (a & mask) | (b & ~mask); } -uint32_t HELPER(logicq_cc)(uint64_t val) -{ - return (val >> 32) | (val != 0); -} - /* VFP support. We follow the convention used for VFP instructions: Single precision routines have a "s" suffix, double precision a "d" suffix. */ diff --git a/target-arm/helper.h b/target-arm/helper.h index 8544f82a94..63ae13acff 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -46,8 +46,6 @@ DEF_HELPER_3(usat16, i32, env, i32, i32) DEF_HELPER_FLAGS_2(usad8, TCG_CALL_NO_RWG_SE, i32, i32, i32) -DEF_HELPER_1(logicq_cc, i32, i64) - DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) DEF_HELPER_2(exception, void, env, i32) @@ -142,9 +140,6 @@ DEF_HELPER_2(recpe_u32, i32, i32, env) DEF_HELPER_2(rsqrte_u32, i32, i32, env) DEF_HELPER_5(neon_tbl, i32, env, i32, i32, i32, i32) -DEF_HELPER_3(adc_cc, i32, env, i32, i32) -DEF_HELPER_3(sbc_cc, i32, env, i32, i32) - DEF_HELPER_3(shl_cc, i32, env, i32, i32) DEF_HELPER_3(shr_cc, i32, env, i32, i32) DEF_HELPER_3(sar_cc, i32, env, i32, i32) diff --git a/target-arm/kvm.c b/target-arm/kvm.c new file mode 100644 index 0000000000..82e2e084c3 --- /dev/null +++ b/target-arm/kvm.c @@ -0,0 +1,493 @@ +/* + * ARM implementation of KVM hooks + * + * Copyright Christoffer Dall 2009-2010 + * + * 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 +#include +#include +#include + +#include + +#include "qemu-common.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "kvm_arm.h" +#include "cpu.h" +#include "hw/arm-misc.h" + +const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_LAST_INFO +}; + +int kvm_arch_init(KVMState *s) +{ + /* For ARM interrupt delivery is always asynchronous, + * whether we are using an in-kernel VGIC or not. + */ + kvm_async_interrupts_allowed = true; + return 0; +} + +unsigned long kvm_arch_vcpu_id(CPUState *cpu) +{ + return cpu->cpu_index; +} + +int kvm_arch_init_vcpu(CPUState *cs) +{ + struct kvm_vcpu_init init; + int ret; + uint64_t v; + struct kvm_one_reg r; + + init.target = KVM_ARM_TARGET_CORTEX_A15; + memset(init.features, 0, sizeof(init.features)); + ret = kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init); + if (ret) { + return ret; + } + /* Query the kernel to make sure it supports 32 VFP + * registers: QEMU's "cortex-a15" CPU is always a + * VFP-D32 core. The simplest way to do this is just + * to attempt to read register d31. + */ + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP | 31; + r.addr = (uintptr_t)(&v); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + if (ret == ENOENT) { + return EINVAL; + } + return ret; +} + +/* We track all the KVM devices which need their memory addresses + * passing to the kernel in a list of these structures. + * When board init is complete we run through the list and + * tell the kernel the base addresses of the memory regions. + * We use a MemoryListener to track mapping and unmapping of + * the regions during board creation, so the board models don't + * need to do anything special for the KVM case. + */ +typedef struct KVMDevice { + struct kvm_arm_device_addr kda; + MemoryRegion *mr; + QSLIST_ENTRY(KVMDevice) entries; +} KVMDevice; + +static QSLIST_HEAD(kvm_devices_head, KVMDevice) kvm_devices_head; + +static void kvm_arm_devlistener_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + KVMDevice *kd; + + QSLIST_FOREACH(kd, &kvm_devices_head, entries) { + if (section->mr == kd->mr) { + kd->kda.addr = section->offset_within_address_space; + } + } +} + +static void kvm_arm_devlistener_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + KVMDevice *kd; + + QSLIST_FOREACH(kd, &kvm_devices_head, entries) { + if (section->mr == kd->mr) { + kd->kda.addr = -1; + } + } +} + +static MemoryListener devlistener = { + .region_add = kvm_arm_devlistener_add, + .region_del = kvm_arm_devlistener_del, +}; + +static void kvm_arm_machine_init_done(Notifier *notifier, void *data) +{ + KVMDevice *kd, *tkd; + + memory_listener_unregister(&devlistener); + QSLIST_FOREACH_SAFE(kd, &kvm_devices_head, entries, tkd) { + if (kd->kda.addr != -1) { + if (kvm_vm_ioctl(kvm_state, KVM_ARM_SET_DEVICE_ADDR, + &kd->kda) < 0) { + fprintf(stderr, "KVM_ARM_SET_DEVICE_ADDRESS failed: %s\n", + strerror(errno)); + abort(); + } + } + g_free(kd); + } +} + +static Notifier notify = { + .notify = kvm_arm_machine_init_done, +}; + +void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid) +{ + KVMDevice *kd; + + if (!kvm_irqchip_in_kernel()) { + return; + } + + if (QSLIST_EMPTY(&kvm_devices_head)) { + memory_listener_register(&devlistener, NULL); + qemu_add_machine_init_done_notifier(¬ify); + } + kd = g_new0(KVMDevice, 1); + kd->mr = mr; + kd->kda.id = devid; + kd->kda.addr = -1; + QSLIST_INSERT_HEAD(&kvm_devices_head, kd, entries); +} + +typedef struct Reg { + uint64_t id; + int offset; +} Reg; + +#define COREREG(KERNELNAME, QEMUFIELD) \ + { \ + KVM_REG_ARM | KVM_REG_SIZE_U32 | \ + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(KERNELNAME), \ + offsetof(CPUARMState, QEMUFIELD) \ + } + +#define CP15REG(CRN, CRM, OPC1, OPC2, QEMUFIELD) \ + { \ + KVM_REG_ARM | KVM_REG_SIZE_U32 | \ + (15 << KVM_REG_ARM_COPROC_SHIFT) | \ + ((CRN) << KVM_REG_ARM_32_CRN_SHIFT) | \ + ((CRM) << KVM_REG_ARM_CRM_SHIFT) | \ + ((OPC1) << KVM_REG_ARM_OPC1_SHIFT) | \ + ((OPC2) << KVM_REG_ARM_32_OPC2_SHIFT), \ + offsetof(CPUARMState, QEMUFIELD) \ + } + +#define VFPSYSREG(R) \ + { \ + KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | \ + KVM_REG_ARM_VFP_##R, \ + offsetof(CPUARMState, vfp.xregs[ARM_VFP_##R]) \ + } + +static const Reg regs[] = { + /* R0_usr .. R14_usr */ + COREREG(usr_regs.uregs[0], regs[0]), + COREREG(usr_regs.uregs[1], regs[1]), + COREREG(usr_regs.uregs[2], regs[2]), + COREREG(usr_regs.uregs[3], regs[3]), + COREREG(usr_regs.uregs[4], regs[4]), + COREREG(usr_regs.uregs[5], regs[5]), + COREREG(usr_regs.uregs[6], regs[6]), + COREREG(usr_regs.uregs[7], regs[7]), + COREREG(usr_regs.uregs[8], usr_regs[0]), + COREREG(usr_regs.uregs[9], usr_regs[1]), + COREREG(usr_regs.uregs[10], usr_regs[2]), + COREREG(usr_regs.uregs[11], usr_regs[3]), + COREREG(usr_regs.uregs[12], usr_regs[4]), + COREREG(usr_regs.uregs[13], banked_r13[0]), + COREREG(usr_regs.uregs[14], banked_r14[0]), + /* R13, R14, SPSR for SVC, ABT, UND, IRQ banks */ + COREREG(svc_regs[0], banked_r13[1]), + COREREG(svc_regs[1], banked_r14[1]), + COREREG(svc_regs[2], banked_spsr[1]), + COREREG(abt_regs[0], banked_r13[2]), + COREREG(abt_regs[1], banked_r14[2]), + COREREG(abt_regs[2], banked_spsr[2]), + COREREG(und_regs[0], banked_r13[3]), + COREREG(und_regs[1], banked_r14[3]), + COREREG(und_regs[2], banked_spsr[3]), + COREREG(irq_regs[0], banked_r13[4]), + COREREG(irq_regs[1], banked_r14[4]), + COREREG(irq_regs[2], banked_spsr[4]), + /* R8_fiq .. R14_fiq and SPSR_fiq */ + COREREG(fiq_regs[0], fiq_regs[0]), + COREREG(fiq_regs[1], fiq_regs[1]), + COREREG(fiq_regs[2], fiq_regs[2]), + COREREG(fiq_regs[3], fiq_regs[3]), + COREREG(fiq_regs[4], fiq_regs[4]), + COREREG(fiq_regs[5], banked_r13[5]), + COREREG(fiq_regs[6], banked_r14[5]), + COREREG(fiq_regs[7], banked_spsr[5]), + /* R15 */ + COREREG(usr_regs.uregs[15], regs[15]), + /* A non-comprehensive set of cp15 registers. + * TODO: drive this from the cp_regs hashtable instead. + */ + CP15REG(1, 0, 0, 0, cp15.c1_sys), /* SCTLR */ + CP15REG(2, 0, 0, 2, cp15.c2_control), /* TTBCR */ + CP15REG(3, 0, 0, 0, cp15.c3), /* DACR */ + /* VFP system registers */ + VFPSYSREG(FPSID), + VFPSYSREG(MVFR1), + VFPSYSREG(MVFR0), + VFPSYSREG(FPEXC), + VFPSYSREG(FPINST), + VFPSYSREG(FPINST2), +}; + +int kvm_arch_put_registers(CPUState *cs, int level) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + struct kvm_one_reg r; + int mode, bn; + int ret, i; + uint32_t cpsr, fpscr; + uint64_t ttbr; + + /* Make sure the banked regs are properly set */ + mode = env->uncached_cpsr & CPSR_M; + bn = bank_number(mode); + if (mode == ARM_CPU_MODE_FIQ) { + memcpy(env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t)); + } else { + memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t)); + } + env->banked_r13[bn] = env->regs[13]; + env->banked_r14[bn] = env->regs[14]; + env->banked_spsr[bn] = env->spsr; + + /* Now we can safely copy stuff down to the kernel */ + for (i = 0; i < ARRAY_SIZE(regs); i++) { + r.id = regs[i].id; + r.addr = (uintptr_t)(env) + regs[i].offset; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); + if (ret) { + return ret; + } + } + + /* Special cases which aren't a single CPUARMState field */ + cpsr = cpsr_read(env); + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr); + r.addr = (uintptr_t)(&cpsr); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); + if (ret) { + return ret; + } + + /* TTBR0: cp15 crm=2 opc1=0 */ + ttbr = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0; + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT) | + (2 << KVM_REG_ARM_CRM_SHIFT) | (0 << KVM_REG_ARM_OPC1_SHIFT); + r.addr = (uintptr_t)(&ttbr); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); + if (ret) { + return ret; + } + + /* TTBR1: cp15 crm=2 opc1=1 */ + ttbr = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1; + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT) | + (2 << KVM_REG_ARM_CRM_SHIFT) | (1 << KVM_REG_ARM_OPC1_SHIFT); + r.addr = (uintptr_t)(&ttbr); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); + if (ret) { + return ret; + } + + /* VFP registers */ + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP; + for (i = 0; i < 32; i++) { + r.addr = (uintptr_t)(&env->vfp.regs[i]); + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); + if (ret) { + return ret; + } + r.id++; + } + + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | + KVM_REG_ARM_VFP_FPSCR; + fpscr = vfp_get_fpscr(env); + r.addr = (uintptr_t)&fpscr; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &r); + + return ret; +} + +int kvm_arch_get_registers(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + struct kvm_one_reg r; + int mode, bn; + int ret, i; + uint32_t cpsr, fpscr; + uint64_t ttbr; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + r.id = regs[i].id; + r.addr = (uintptr_t)(env) + regs[i].offset; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + if (ret) { + return ret; + } + } + + /* Special cases which aren't a single CPUARMState field */ + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | + KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(usr_regs.ARM_cpsr); + r.addr = (uintptr_t)(&cpsr); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + if (ret) { + return ret; + } + cpsr_write(env, cpsr, 0xffffffff); + + /* TTBR0: cp15 crm=2 opc1=0 */ + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT) | + (2 << KVM_REG_ARM_CRM_SHIFT) | (0 << KVM_REG_ARM_OPC1_SHIFT); + r.addr = (uintptr_t)(&ttbr); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + if (ret) { + return ret; + } + env->cp15.c2_base0_hi = ttbr >> 32; + env->cp15.c2_base0 = ttbr; + + /* TTBR1: cp15 crm=2 opc1=1 */ + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | (15 << KVM_REG_ARM_COPROC_SHIFT) | + (2 << KVM_REG_ARM_CRM_SHIFT) | (1 << KVM_REG_ARM_OPC1_SHIFT); + r.addr = (uintptr_t)(&ttbr); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + if (ret) { + return ret; + } + env->cp15.c2_base1_hi = ttbr >> 32; + env->cp15.c2_base1 = ttbr; + + /* Make sure the current mode regs are properly set */ + mode = env->uncached_cpsr & CPSR_M; + bn = bank_number(mode); + if (mode == ARM_CPU_MODE_FIQ) { + memcpy(env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t)); + } else { + memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t)); + } + env->regs[13] = env->banked_r13[bn]; + env->regs[14] = env->banked_r14[bn]; + env->spsr = env->banked_spsr[bn]; + + /* The main GET_ONE_REG loop above set c2_control, but we need to + * update some extra cached precomputed values too. + * When this is driven from the cp_regs hashtable then this ugliness + * can disappear because we'll use the access function which sets + * these values automatically. + */ + env->cp15.c2_mask = ~(0xffffffffu >> env->cp15.c2_control); + env->cp15.c2_base_mask = ~(0x3fffu >> env->cp15.c2_control); + + /* VFP registers */ + r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP; + for (i = 0; i < 32; i++) { + r.addr = (uintptr_t)(&env->vfp.regs[i]); + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + if (ret) { + return ret; + } + r.id++; + } + + r.id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | + KVM_REG_ARM_VFP_FPSCR; + r.addr = (uintptr_t)&fpscr; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &r); + if (ret) { + return ret; + } + vfp_set_fpscr(env, fpscr); + + return 0; +} + +void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) +{ +} + +void kvm_arch_post_run(CPUState *cs, struct kvm_run *run) +{ +} + +int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) +{ + return 0; +} + +void kvm_arch_reset_vcpu(CPUState *cs) +{ +} + +bool kvm_arch_stop_on_emulation_error(CPUState *cs) +{ + return true; +} + +int kvm_arch_process_async_events(CPUState *cs) +{ + return 0; +} + +int kvm_arch_on_sigbus_vcpu(CPUState *cs, int code, void *addr) +{ + return 1; +} + +int kvm_arch_on_sigbus(int code, void *addr) +{ + return 1; +} + +void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg) +{ + qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); +} + +int kvm_arch_insert_sw_breakpoint(CPUState *cs, + struct kvm_sw_breakpoint *bp) +{ + qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); + return -EINVAL; +} + +int kvm_arch_insert_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); + return -EINVAL; +} + +int kvm_arch_remove_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); + return -EINVAL; +} + +int kvm_arch_remove_sw_breakpoint(CPUState *cs, + struct kvm_sw_breakpoint *bp) +{ + qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); + return -EINVAL; +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ + qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__); +} diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h new file mode 100644 index 0000000000..b1c54ffb5d --- /dev/null +++ b/target-arm/kvm_arm.h @@ -0,0 +1,32 @@ +/* + * QEMU KVM support -- ARM specific functions. + * + * Copyright (c) 2012 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_KVM_ARM_H +#define QEMU_KVM_ARM_H + +#include "sysemu/kvm.h" +#include "exec/memory.h" + +/** + * kvm_arm_register_device: + * @mr: memory region for this device + * @devid: the KVM device ID + * + * Remember the memory region @mr, and when it is mapped by the + * machine model, tell the kernel that base address using the + * KVM_SET_DEVICE_ADDRESS ioctl. @devid should be the ID of + * the device as defined by KVM_SET_DEVICE_ADDRESS. + * The machine model may map and unmap the device multiple times; + * the kernel will only be told the final address at the point + * where machine init is complete. + */ +void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid); + +#endif diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 99610d7734..a918e5b27a 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -218,8 +218,10 @@ uint32_t HELPER(usat16)(CPUARMState *env, uint32_t x, uint32_t shift) void HELPER(wfi)(CPUARMState *env) { + CPUState *cs = CPU(arm_env_get_cpu(env)); + env->exception_index = EXCP_HLT; - env->halted = 1; + cs->halted = 1; cpu_loop_exit(env); } @@ -315,36 +317,6 @@ uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip) The only way to do that in TCG is a conditional branch, which clobbers all our temporaries. For now implement these as helper functions. */ -uint32_t HELPER(adc_cc)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t result; - if (!env->CF) { - result = a + b; - env->CF = result < a; - } else { - result = a + b + 1; - env->CF = result <= a; - } - env->VF = (a ^ b ^ -1) & (a ^ result); - env->NF = env->ZF = result; - return result; -} - -uint32_t HELPER(sbc_cc)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t result; - if (!env->CF) { - result = a - b - 1; - env->CF = a > b; - } else { - result = a - b; - env->CF = a >= b; - } - env->VF = (a ^ b) & (a ^ result); - env->NF = env->ZF = result; - return result; -} - /* Similarly for variable shift instructions. */ uint32_t HELPER(shl_cc)(CPUARMState *env, uint32_t x, uint32_t i) diff --git a/target-arm/translate.c b/target-arm/translate.c index 724e00f7cf..35a21be931 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -305,35 +305,41 @@ static TCGv_i64 gen_subq_msw(TCGv_i64 a, TCGv b) return a; } -/* FIXME: Most targets have native widening multiplication. - It would be good to use that instead of a full wide multiply. */ /* 32x32->64 multiply. Marks inputs as dead. */ static TCGv_i64 gen_mulu_i64_i32(TCGv a, TCGv b) { - TCGv_i64 tmp1 = tcg_temp_new_i64(); - TCGv_i64 tmp2 = tcg_temp_new_i64(); + TCGv lo = tcg_temp_new_i32(); + TCGv hi = tcg_temp_new_i32(); + TCGv_i64 ret; - tcg_gen_extu_i32_i64(tmp1, a); + tcg_gen_mulu2_i32(lo, hi, a, b); tcg_temp_free_i32(a); - tcg_gen_extu_i32_i64(tmp2, b); tcg_temp_free_i32(b); - tcg_gen_mul_i64(tmp1, tmp1, tmp2); - tcg_temp_free_i64(tmp2); - return tmp1; + + ret = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(ret, lo, hi); + tcg_temp_free(lo); + tcg_temp_free(hi); + + return ret; } static TCGv_i64 gen_muls_i64_i32(TCGv a, TCGv b) { - TCGv_i64 tmp1 = tcg_temp_new_i64(); - TCGv_i64 tmp2 = tcg_temp_new_i64(); + TCGv lo = tcg_temp_new_i32(); + TCGv hi = tcg_temp_new_i32(); + TCGv_i64 ret; - tcg_gen_ext_i32_i64(tmp1, a); + tcg_gen_muls2_i32(lo, hi, a, b); tcg_temp_free_i32(a); - tcg_gen_ext_i32_i64(tmp2, b); tcg_temp_free_i32(b); - tcg_gen_mul_i64(tmp1, tmp1, tmp2); - tcg_temp_free_i64(tmp2); - return tmp1; + + ret = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(ret, lo, hi); + tcg_temp_free(lo); + tcg_temp_free(hi); + + return ret; } /* Swap low and high halfwords. */ @@ -404,12 +410,39 @@ static void gen_sub_carry(TCGv dest, TCGv t0, TCGv t1) /* dest = T0 + T1. Compute C, N, V and Z flags */ static void gen_add_CC(TCGv dest, TCGv t0, TCGv t1) { - TCGv tmp; - tcg_gen_add_i32(cpu_NF, t0, t1); + TCGv tmp = tcg_temp_new_i32(); + tcg_gen_movi_i32(tmp, 0); + tcg_gen_add2_i32(cpu_NF, cpu_CF, t0, tmp, t1, tmp); + tcg_gen_mov_i32(cpu_ZF, cpu_NF); + tcg_gen_xor_i32(cpu_VF, cpu_NF, t0); + tcg_gen_xor_i32(tmp, t0, t1); + tcg_gen_andc_i32(cpu_VF, cpu_VF, tmp); + tcg_temp_free_i32(tmp); + tcg_gen_mov_i32(dest, cpu_NF); +} + +/* dest = T0 + T1 + CF. Compute C, N, V and Z flags */ +static void gen_adc_CC(TCGv dest, TCGv t0, TCGv t1) +{ + TCGv tmp = tcg_temp_new_i32(); + if (TCG_TARGET_HAS_add2_i32) { + tcg_gen_movi_i32(tmp, 0); + tcg_gen_add2_i32(cpu_NF, cpu_CF, t0, tmp, cpu_CF, tmp); + tcg_gen_add2_i32(cpu_NF, cpu_CF, cpu_NF, cpu_CF, t1, tmp); + } else { + TCGv_i64 q0 = tcg_temp_new_i64(); + TCGv_i64 q1 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(q0, t0); + tcg_gen_extu_i32_i64(q1, t1); + tcg_gen_add_i64(q0, q0, q1); + tcg_gen_extu_i32_i64(q1, cpu_CF); + tcg_gen_add_i64(q0, q0, q1); + tcg_gen_extr_i64_i32(cpu_NF, cpu_CF, q0); + tcg_temp_free_i64(q0); + tcg_temp_free_i64(q1); + } tcg_gen_mov_i32(cpu_ZF, cpu_NF); - tcg_gen_setcond_i32(TCG_COND_LTU, cpu_CF, cpu_NF, t0); tcg_gen_xor_i32(cpu_VF, cpu_NF, t0); - tmp = tcg_temp_new_i32(); tcg_gen_xor_i32(tmp, t0, t1); tcg_gen_andc_i32(cpu_VF, cpu_VF, tmp); tcg_temp_free_i32(tmp); @@ -431,6 +464,15 @@ static void gen_sub_CC(TCGv dest, TCGv t0, TCGv t1) tcg_gen_mov_i32(dest, cpu_NF); } +/* dest = T0 + ~T1 + CF. Compute C, N, V and Z flags */ +static void gen_sbc_CC(TCGv dest, TCGv t0, TCGv t1) +{ + TCGv tmp = tcg_temp_new_i32(); + tcg_gen_not_i32(tmp, t1); + gen_adc_CC(dest, t0, tmp); + tcg_temp_free(tmp); +} + #define GEN_SHIFT(name) \ static void gen_##name(TCGv dest, TCGv t0, TCGv t1) \ { \ @@ -2737,7 +2779,6 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) } } else { /* arm->vfp */ - tmp = load_reg(s, rd); if (insn & (1 << 21)) { rn >>= 1; /* system register */ @@ -2748,6 +2789,7 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) /* Writes are ignored. */ break; case ARM_VFP_FPSCR: + tmp = load_reg(s, rd); gen_helper_vfp_set_fpscr(cpu_env, tmp); tcg_temp_free_i32(tmp); gen_lookup_tb(s); @@ -2757,18 +2799,21 @@ static int disas_vfp_insn(CPUARMState * env, DisasContext *s, uint32_t insn) return 1; /* TODO: VFP subarchitecture support. * For now, keep the EN bit only */ + tmp = load_reg(s, rd); tcg_gen_andi_i32(tmp, tmp, 1 << 30); store_cpu_field(tmp, vfp.xregs[rn]); gen_lookup_tb(s); break; case ARM_VFP_FPINST: case ARM_VFP_FPINST2: + tmp = load_reg(s, rd); store_cpu_field(tmp, vfp.xregs[rn]); break; default: return 1; } } else { + tmp = load_reg(s, rd); gen_vfp_msr(tmp); gen_mov_vreg_F0(0, rn); } @@ -6424,13 +6469,11 @@ static void gen_addq(DisasContext *s, TCGv_i64 val, int rlow, int rhigh) tcg_temp_free_i64(tmp); } -/* Set N and Z flags from a 64-bit value. */ -static void gen_logicq_cc(TCGv_i64 val) +/* Set N and Z flags from hi|lo. */ +static void gen_logicq_cc(TCGv lo, TCGv hi) { - TCGv tmp = tcg_temp_new_i32(); - gen_helper_logicq_cc(tmp, val); - gen_logic_CC(tmp); - tcg_temp_free_i32(tmp); + tcg_gen_mov_i32(cpu_NF, hi); + tcg_gen_or_i32(cpu_ZF, lo, hi); } /* Load/Store exclusive instructions are implemented by remembering @@ -6558,6 +6601,70 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, } #endif +/* gen_srs: + * @env: CPUARMState + * @s: DisasContext + * @mode: mode field from insn (which stack to store to) + * @amode: addressing mode (DA/IA/DB/IB), encoded as per P,U bits in ARM insn + * @writeback: true if writeback bit set + * + * Generate code for the SRS (Store Return State) insn. + */ +static void gen_srs(DisasContext *s, + uint32_t mode, uint32_t amode, bool writeback) +{ + int32_t offset; + TCGv_i32 addr = tcg_temp_new_i32(); + TCGv_i32 tmp = tcg_const_i32(mode); + gen_helper_get_r13_banked(addr, cpu_env, tmp); + tcg_temp_free_i32(tmp); + switch (amode) { + case 0: /* DA */ + offset = -4; + break; + case 1: /* IA */ + offset = 0; + break; + case 2: /* DB */ + offset = -8; + break; + case 3: /* IB */ + offset = 4; + break; + default: + abort(); + } + tcg_gen_addi_i32(addr, addr, offset); + tmp = load_reg(s, 14); + gen_st32(tmp, addr, 0); + tmp = load_cpu_field(spsr); + tcg_gen_addi_i32(addr, addr, 4); + gen_st32(tmp, addr, 0); + if (writeback) { + switch (amode) { + case 0: + offset = -8; + break; + case 1: + offset = 4; + break; + case 2: + offset = -4; + break; + case 3: + offset = 0; + break; + default: + abort(); + } + tcg_gen_addi_i32(addr, addr, offset); + tmp = tcg_const_i32(mode); + gen_helper_set_r13_banked(cpu_env, tmp, addr); + tcg_temp_free_i32(tmp); + } + tcg_temp_free_i32(addr); +} + static void disas_arm_insn(CPUARMState * env, DisasContext *s) { unsigned int cond, insn, val, op1, i, shift, rm, rs, rn, rd, sh; @@ -6650,49 +6757,11 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) } } else if ((insn & 0x0e5fffe0) == 0x084d0500) { /* srs */ - int32_t offset; - if (IS_USER(s)) + if (IS_USER(s)) { goto illegal_op; + } ARCH(6); - op1 = (insn & 0x1f); - addr = tcg_temp_new_i32(); - tmp = tcg_const_i32(op1); - gen_helper_get_r13_banked(addr, cpu_env, tmp); - tcg_temp_free_i32(tmp); - i = (insn >> 23) & 3; - switch (i) { - case 0: offset = -4; break; /* DA */ - case 1: offset = 0; break; /* IA */ - case 2: offset = -8; break; /* DB */ - case 3: offset = 4; break; /* IB */ - default: abort(); - } - if (offset) - tcg_gen_addi_i32(addr, addr, offset); - tmp = load_reg(s, 14); - gen_st32(tmp, addr, 0); - tmp = load_cpu_field(spsr); - tcg_gen_addi_i32(addr, addr, 4); - gen_st32(tmp, addr, 0); - if (insn & (1 << 21)) { - /* Base writeback. */ - switch (i) { - case 0: offset = -8; break; - case 1: offset = 4; break; - case 2: offset = -4; break; - case 3: offset = 0; break; - default: abort(); - } - if (offset) - tcg_gen_addi_i32(addr, addr, offset); - tmp = tcg_const_i32(op1); - gen_helper_set_r13_banked(cpu_env, tmp, addr); - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(addr); - } else { - tcg_temp_free_i32(addr); - } - return; + gen_srs(s, (insn & 0x1f), (insn >> 23) & 3, insn & (1 << 21)); } else if ((insn & 0x0e50ffe0) == 0x08100a00) { /* rfe */ int32_t offset; @@ -7067,7 +7136,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) break; case 0x05: if (set_cc) { - gen_helper_adc_cc(tmp, cpu_env, tmp, tmp2); + gen_adc_CC(tmp, tmp, tmp2); } else { gen_add_carry(tmp, tmp, tmp2); } @@ -7075,7 +7144,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) break; case 0x06: if (set_cc) { - gen_helper_sbc_cc(tmp, cpu_env, tmp, tmp2); + gen_sbc_CC(tmp, tmp, tmp2); } else { gen_sub_carry(tmp, tmp, tmp2); } @@ -7083,7 +7152,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) break; case 0x07: if (set_cc) { - gen_helper_sbc_cc(tmp, cpu_env, tmp2, tmp); + gen_sbc_CC(tmp, tmp2, tmp); } else { gen_sub_carry(tmp, tmp2, tmp); } @@ -7210,18 +7279,22 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) tmp = load_reg(s, rs); tmp2 = load_reg(s, rm); if (insn & (1 << 22)) { - tmp64 = gen_muls_i64_i32(tmp, tmp2); + tcg_gen_muls2_i32(tmp, tmp2, tmp, tmp2); } else { - tmp64 = gen_mulu_i64_i32(tmp, tmp2); + tcg_gen_mulu2_i32(tmp, tmp2, tmp, tmp2); } if (insn & (1 << 21)) { /* mult accumulate */ - gen_addq(s, tmp64, rn, rd); + TCGv al = load_reg(s, rn); + TCGv ah = load_reg(s, rd); + tcg_gen_add2_i32(tmp, tmp2, tmp, tmp2, al, ah); + tcg_temp_free(al); + tcg_temp_free(ah); } if (insn & (1 << 20)) { - gen_logicq_cc(tmp64); + gen_logicq_cc(tmp, tmp2); } - gen_storeq_reg(s, rn, rd, tmp64); - tcg_temp_free_i64(tmp64); + store_reg(s, rn, tmp); + store_reg(s, rd, tmp2); break; default: goto illegal_op; @@ -7904,15 +7977,16 @@ gen_thumb2_data_op(DisasContext *s, int op, int conds, uint32_t shifter_out, TCG break; case 10: /* adc */ if (conds) - gen_helper_adc_cc(t0, cpu_env, t0, t1); + gen_adc_CC(t0, t0, t1); else gen_adc(t0, t1); break; case 11: /* sbc */ - if (conds) - gen_helper_sbc_cc(t0, cpu_env, t0, t1); - else + if (conds) { + gen_sbc_CC(t0, t0, t1); + } else { gen_sub_carry(t0, t0, t1); + } break; case 13: /* sub */ if (conds) @@ -8106,9 +8180,10 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw } else { /* Load/store multiple, RFE, SRS. */ if (((insn >> 23) & 1) == ((insn >> 24) & 1)) { - /* Not available in user mode. */ - if (IS_USER(s)) + /* RFE, SRS: not available in user mode or on M profile */ + if (IS_USER(s) || IS_M(env)) { goto illegal_op; + } if (insn & (1 << 20)) { /* rfe */ addr = load_reg(s, rn); @@ -8132,32 +8207,8 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw gen_rfe(s, tmp, tmp2); } else { /* srs */ - op = (insn & 0x1f); - addr = tcg_temp_new_i32(); - tmp = tcg_const_i32(op); - gen_helper_get_r13_banked(addr, cpu_env, tmp); - tcg_temp_free_i32(tmp); - if ((insn & (1 << 24)) == 0) { - tcg_gen_addi_i32(addr, addr, -8); - } - tmp = load_reg(s, 14); - gen_st32(tmp, addr, 0); - tcg_gen_addi_i32(addr, addr, 4); - tmp = tcg_temp_new_i32(); - gen_helper_cpsr_read(tmp, cpu_env); - gen_st32(tmp, addr, 0); - if (insn & (1 << 21)) { - if ((insn & (1 << 24)) == 0) { - tcg_gen_addi_i32(addr, addr, -4); - } else { - tcg_gen_addi_i32(addr, addr, 4); - } - tmp = tcg_const_i32(op); - gen_helper_set_r13_banked(cpu_env, tmp, addr); - tcg_temp_free_i32(tmp); - } else { - tcg_temp_free_i32(addr); - } + gen_srs(s, (insn & 0x1f), (insn & (1 << 24)) ? 1 : 2, + insn & (1 << 21)); } } else { int i, loaded_base = 0; @@ -9222,16 +9273,18 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) } break; case 0x5: /* adc */ - if (s->condexec_mask) + if (s->condexec_mask) { gen_adc(tmp, tmp2); - else - gen_helper_adc_cc(tmp, cpu_env, tmp, tmp2); + } else { + gen_adc_CC(tmp, tmp, tmp2); + } break; case 0x6: /* sbc */ - if (s->condexec_mask) + if (s->condexec_mask) { gen_sub_carry(tmp, tmp, tmp2); - else - gen_helper_sbc_cc(tmp, cpu_env, tmp, tmp2); + } else { + gen_sbc_CC(tmp, tmp, tmp2); + } break; case 0x7: /* ror */ if (s->condexec_mask) { @@ -9758,7 +9811,7 @@ static inline void gen_intermediate_code_internal(CPUARMState *env, if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_icount_start(); + gen_tb_start(); tcg_clear_temp_count(); @@ -9961,7 +10014,7 @@ static inline void gen_intermediate_code_internal(CPUARMState *env, } done_generating: - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; #ifdef DEBUG_DISAS diff --git a/target-cris/cpu-qom.h b/target-cris/cpu-qom.h index 41ab9b2fa5..deea1d804b 100644 --- a/target-cris/cpu-qom.h +++ b/target-cris/cpu-qom.h @@ -33,7 +33,9 @@ /** * CRISCPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. + * @vr: Version Register value. * * A CRIS CPU model. */ @@ -42,7 +44,10 @@ typedef struct CRISCPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); + + uint32_t vr; } CRISCPUClass; /** @@ -66,5 +71,8 @@ static inline CRISCPU *cris_env_get_cpu(CPUCRISState *env) #define ENV_GET_CPU(e) CPU(cris_env_get_cpu(e)) +#define ENV_OFFSET offsetof(CRISCPU, env) + +void cris_cpu_do_interrupt(CPUState *cpu); #endif diff --git a/target-cris/cpu.c b/target-cris/cpu.c index c596609bd4..95cbf399d9 100644 --- a/target-cris/cpu.c +++ b/target-cris/cpu.c @@ -35,7 +35,7 @@ static void cris_cpu_reset(CPUState *s) uint32_t vr; if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); + qemu_log("CPU Reset (CPU %d)\n", s->cpu_index); log_cpu_state(env, 0); } @@ -55,21 +55,195 @@ static void cris_cpu_reset(CPUState *s) #endif } +static ObjectClass *cris_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + + if (cpu_model == NULL) { + return NULL; + } + + typename = g_strdup_printf("%s-" TYPE_CRIS_CPU, cpu_model); + oc = object_class_by_name(typename); + g_free(typename); + if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_CRIS_CPU) || + object_class_is_abstract(oc))) { + oc = NULL; + } + return oc; +} + +CRISCPU *cpu_cris_init(const char *cpu_model) +{ + CRISCPU *cpu; + ObjectClass *oc; + + oc = cris_cpu_class_by_name(cpu_model); + if (oc == NULL) { + return NULL; + } + cpu = CRIS_CPU(object_new(object_class_get_name(oc))); + + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + + return cpu; +} + +/* Sort alphabetically by VR. */ +static gint cris_cpu_list_compare(gconstpointer a, gconstpointer b) +{ + CRISCPUClass *ccc_a = CRIS_CPU_CLASS(a); + CRISCPUClass *ccc_b = CRIS_CPU_CLASS(b); + + /* */ + if (ccc_a->vr > ccc_b->vr) { + return 1; + } else if (ccc_a->vr < ccc_b->vr) { + return -1; + } else { + return 0; + } +} + +static void cris_cpu_list_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CPUListState *s = user_data; + const char *typename = object_class_get_name(oc); + char *name; + + name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_CRIS_CPU)); + (*s->cpu_fprintf)(s->file, " %s\n", name); + g_free(name); +} + +void cris_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ + CPUListState s = { + .file = f, + .cpu_fprintf = cpu_fprintf, + }; + GSList *list; + + list = object_class_get_list(TYPE_CRIS_CPU, false); + list = g_slist_sort(list, cris_cpu_list_compare); + (*cpu_fprintf)(f, "Available CPUs:\n"); + g_slist_foreach(list, cris_cpu_list_entry, &s); + g_slist_free(list); +} + +static void cris_cpu_realizefn(DeviceState *dev, Error **errp) +{ + CRISCPU *cpu = CRIS_CPU(dev); + CRISCPUClass *ccc = CRIS_CPU_GET_CLASS(dev); + + cpu_reset(CPU(cpu)); + qemu_init_vcpu(&cpu->env); + + ccc->parent_realize(dev, errp); +} + static void cris_cpu_initfn(Object *obj) { + CPUState *cs = CPU(obj); CRISCPU *cpu = CRIS_CPU(obj); + CRISCPUClass *ccc = CRIS_CPU_GET_CLASS(obj); CPUCRISState *env = &cpu->env; + static bool tcg_initialized; + cs->env_ptr = env; cpu_exec_init(env); + + env->pregs[PR_VR] = ccc->vr; + + if (tcg_enabled() && !tcg_initialized) { + tcg_initialized = true; + if (env->pregs[PR_VR] < 32) { + cris_initialize_crisv10_tcg(); + } else { + cris_initialize_tcg(); + } + } } +static void crisv8_cpu_class_init(ObjectClass *oc, void *data) +{ + CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); + + ccc->vr = 8; +} + +static void crisv9_cpu_class_init(ObjectClass *oc, void *data) +{ + CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); + + ccc->vr = 9; +} + +static void crisv10_cpu_class_init(ObjectClass *oc, void *data) +{ + CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); + + ccc->vr = 10; +} + +static void crisv11_cpu_class_init(ObjectClass *oc, void *data) +{ + CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); + + ccc->vr = 11; +} + +static void crisv32_cpu_class_init(ObjectClass *oc, void *data) +{ + CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); + + ccc->vr = 32; +} + +#define TYPE(model) model "-" TYPE_CRIS_CPU + +static const TypeInfo cris_cpu_model_type_infos[] = { + { + .name = TYPE("crisv8"), + .parent = TYPE_CRIS_CPU, + .class_init = crisv8_cpu_class_init, + }, { + .name = TYPE("crisv9"), + .parent = TYPE_CRIS_CPU, + .class_init = crisv9_cpu_class_init, + }, { + .name = TYPE("crisv10"), + .parent = TYPE_CRIS_CPU, + .class_init = crisv10_cpu_class_init, + }, { + .name = TYPE("crisv11"), + .parent = TYPE_CRIS_CPU, + .class_init = crisv11_cpu_class_init, + }, { + .name = TYPE("crisv32"), + .parent = TYPE_CRIS_CPU, + .class_init = crisv32_cpu_class_init, + } +}; + +#undef TYPE + static void cris_cpu_class_init(ObjectClass *oc, void *data) { + DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); + ccc->parent_realize = dc->realize; + dc->realize = cris_cpu_realizefn; + ccc->parent_reset = cc->reset; cc->reset = cris_cpu_reset; + + cc->class_by_name = cris_cpu_class_by_name; + cc->do_interrupt = cris_cpu_do_interrupt; } static const TypeInfo cris_cpu_type_info = { @@ -77,14 +251,19 @@ static const TypeInfo cris_cpu_type_info = { .parent = TYPE_CPU, .instance_size = sizeof(CRISCPU), .instance_init = cris_cpu_initfn, - .abstract = false, + .abstract = true, .class_size = sizeof(CRISCPUClass), .class_init = cris_cpu_class_init, }; static void cris_cpu_register_types(void) { + int i; + type_register_static(&cris_cpu_type_info); + for (i = 0; i < ARRAY_SIZE(cris_cpu_model_type_infos); i++) { + type_register_static(&cris_cpu_model_type_infos[i]); + } } type_init(cris_cpu_register_types) diff --git a/target-cris/cpu.h b/target-cris/cpu.h index 63e6234f11..dbd7d36870 100644 --- a/target-cris/cpu.h +++ b/target-cris/cpu.h @@ -175,14 +175,15 @@ typedef struct CPUCRISState { CRISCPU *cpu_cris_init(const char *cpu_model); int cpu_cris_exec(CPUCRISState *s); -void cpu_cris_close(CPUCRISState *s); -void do_interrupt(CPUCRISState *env); /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero is returned if the signal was handled by the virtual CPU. */ int cpu_cris_signal_handler(int host_signum, void *pinfo, void *puc); +void cris_initialize_tcg(void); +void cris_initialize_crisv10_tcg(void); + enum { CC_OP_DYNAMIC, /* Use env->cc_op */ CC_OP_FLAGS, @@ -287,9 +288,7 @@ void cris_cpu_list(FILE *f, fprintf_function cpu_fprintf); static inline bool cpu_has_work(CPUState *cpu) { - CPUCRISState *env = &CRIS_CPU(cpu)->env; - - return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); + return cpu->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } #include "exec/exec-all.h" diff --git a/target-cris/helper.c b/target-cris/helper.c index 8407a6d880..e1ef7bcc0b 100644 --- a/target-cris/helper.c +++ b/target-cris/helper.c @@ -28,7 +28,7 @@ #ifdef CRIS_HELPER_DEBUG #define D(x) x -#define D_LOG(...) qemu_log(__VA__ARGS__) +#define D_LOG(...) qemu_log(__VA_ARGS__) #else #define D(x) #define D_LOG(...) do { } while (0) @@ -36,19 +36,22 @@ #if defined(CONFIG_USER_ONLY) -void do_interrupt (CPUCRISState *env) +void cris_cpu_do_interrupt(CPUState *cs) { - env->exception_index = -1; - env->pregs[PR_ERP] = env->pc; + CRISCPU *cpu = CRIS_CPU(cs); + CPUCRISState *env = &cpu->env; + + env->exception_index = -1; + env->pregs[PR_ERP] = env->pc; } int cpu_cris_handle_mmu_fault(CPUCRISState * env, target_ulong address, int rw, int mmu_idx) { - env->exception_index = 0xaa; - env->pregs[PR_EDA] = address; - cpu_dump_state(env, stderr, fprintf, 0); - return 1; + env->exception_index = 0xaa; + env->pregs[PR_EDA] = address; + cpu_dump_state(env, stderr, fprintf, 0); + return 1; } #else /* !CONFIG_USER_ONLY */ @@ -56,211 +59,214 @@ int cpu_cris_handle_mmu_fault(CPUCRISState * env, target_ulong address, int rw, static void cris_shift_ccs(CPUCRISState *env) { - uint32_t ccs; - /* Apply the ccs shift. */ - ccs = env->pregs[PR_CCS]; - ccs = ((ccs & 0xc0000000) | ((ccs << 12) >> 2)) & ~0x3ff; - env->pregs[PR_CCS] = ccs; + uint32_t ccs; + /* Apply the ccs shift. */ + ccs = env->pregs[PR_CCS]; + ccs = ((ccs & 0xc0000000) | ((ccs << 12) >> 2)) & ~0x3ff; + env->pregs[PR_CCS] = ccs; } -int cpu_cris_handle_mmu_fault (CPUCRISState *env, target_ulong address, int rw, - int mmu_idx) +int cpu_cris_handle_mmu_fault(CPUCRISState *env, target_ulong address, int rw, + int mmu_idx) { - struct cris_mmu_result res; - int prot, miss; - int r = -1; - target_ulong phy; + D(CPUState *cpu = CPU(cris_env_get_cpu(env))); + struct cris_mmu_result res; + int prot, miss; + int r = -1; + target_ulong phy; - D(printf ("%s addr=%x pc=%x rw=%x\n", __func__, address, env->pc, rw)); - miss = cris_mmu_translate(&res, env, address & TARGET_PAGE_MASK, - rw, mmu_idx, 0); - if (miss) - { - if (env->exception_index == EXCP_BUSFAULT) - cpu_abort(env, - "CRIS: Illegal recursive bus fault." - "addr=%x rw=%d\n", - address, rw); + D(printf("%s addr=%x pc=%x rw=%x\n", __func__, address, env->pc, rw)); + miss = cris_mmu_translate(&res, env, address & TARGET_PAGE_MASK, + rw, mmu_idx, 0); + if (miss) { + if (env->exception_index == EXCP_BUSFAULT) { + cpu_abort(env, + "CRIS: Illegal recursive bus fault." + "addr=%x rw=%d\n", + address, rw); + } - env->pregs[PR_EDA] = address; - env->exception_index = EXCP_BUSFAULT; - env->fault_vector = res.bf_vec; - r = 1; - } - else - { - /* - * Mask off the cache selection bit. The ETRAX busses do not - * see the top bit. - */ - phy = res.phy & ~0x80000000; - prot = res.prot; - tlb_set_page(env, address & TARGET_PAGE_MASK, phy, - prot, mmu_idx, TARGET_PAGE_SIZE); - r = 0; - } - if (r > 0) - D_LOG("%s returns %d irqreq=%x addr=%x phy=%x vec=%x pc=%x\n", - __func__, r, env->interrupt_request, address, res.phy, - res.bf_vec, env->pc); - return r; + env->pregs[PR_EDA] = address; + env->exception_index = EXCP_BUSFAULT; + env->fault_vector = res.bf_vec; + r = 1; + } else { + /* + * Mask off the cache selection bit. The ETRAX busses do not + * see the top bit. + */ + phy = res.phy & ~0x80000000; + prot = res.prot; + tlb_set_page(env, address & TARGET_PAGE_MASK, phy, + prot, mmu_idx, TARGET_PAGE_SIZE); + r = 0; + } + if (r > 0) { + D_LOG("%s returns %d irqreq=%x addr=%x phy=%x vec=%x pc=%x\n", + __func__, r, cpu->interrupt_request, address, res.phy, + res.bf_vec, env->pc); + } + return r; } static void do_interruptv10(CPUCRISState *env) { - int ex_vec = -1; + D(CPUState *cs = CPU(cris_env_get_cpu(env))); + int ex_vec = -1; - D_LOG( "exception index=%d interrupt_req=%d\n", - env->exception_index, - env->interrupt_request); + D_LOG("exception index=%d interrupt_req=%d\n", + env->exception_index, + cs->interrupt_request); - assert(!(env->pregs[PR_CCS] & PFIX_FLAG)); - switch (env->exception_index) - { - case EXCP_BREAK: - /* These exceptions are genereated by the core itself. - ERP should point to the insn following the brk. */ - ex_vec = env->trap_vector; - env->pregs[PRV10_BRP] = env->pc; - break; + assert(!(env->pregs[PR_CCS] & PFIX_FLAG)); + switch (env->exception_index) { + case EXCP_BREAK: + /* These exceptions are genereated by the core itself. + ERP should point to the insn following the brk. */ + ex_vec = env->trap_vector; + env->pregs[PRV10_BRP] = env->pc; + break; - case EXCP_NMI: - /* NMI is hardwired to vector zero. */ - ex_vec = 0; - env->pregs[PR_CCS] &= ~M_FLAG_V10; - env->pregs[PRV10_BRP] = env->pc; - break; + case EXCP_NMI: + /* NMI is hardwired to vector zero. */ + ex_vec = 0; + env->pregs[PR_CCS] &= ~M_FLAG_V10; + env->pregs[PRV10_BRP] = env->pc; + break; - case EXCP_BUSFAULT: - cpu_abort(env, "Unhandled busfault"); - break; + case EXCP_BUSFAULT: + cpu_abort(env, "Unhandled busfault"); + break; - default: - /* The interrupt controller gives us the vector. */ - ex_vec = env->interrupt_vector; - /* Normal interrupts are taken between - TB's. env->pc is valid here. */ - env->pregs[PR_ERP] = env->pc; - break; - } + default: + /* The interrupt controller gives us the vector. */ + ex_vec = env->interrupt_vector; + /* Normal interrupts are taken between + TB's. env->pc is valid here. */ + env->pregs[PR_ERP] = env->pc; + break; + } - if (env->pregs[PR_CCS] & U_FLAG) { - /* Swap stack pointers. */ - env->pregs[PR_USP] = env->regs[R_SP]; - env->regs[R_SP] = env->ksp; - } + if (env->pregs[PR_CCS] & U_FLAG) { + /* Swap stack pointers. */ + env->pregs[PR_USP] = env->regs[R_SP]; + env->regs[R_SP] = env->ksp; + } - /* Now that we are in kernel mode, load the handlers address. */ - env->pc = cpu_ldl_code(env, env->pregs[PR_EBP] + ex_vec * 4); - env->locked_irq = 1; - env->pregs[PR_CCS] |= F_FLAG_V10; /* set F. */ + /* Now that we are in kernel mode, load the handlers address. */ + env->pc = cpu_ldl_code(env, env->pregs[PR_EBP] + ex_vec * 4); + env->locked_irq = 1; + env->pregs[PR_CCS] |= F_FLAG_V10; /* set F. */ - qemu_log_mask(CPU_LOG_INT, "%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", - __func__, env->pc, ex_vec, - env->pregs[PR_CCS], - env->pregs[PR_PID], - env->pregs[PR_ERP]); + qemu_log_mask(CPU_LOG_INT, "%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", + __func__, env->pc, ex_vec, + env->pregs[PR_CCS], + env->pregs[PR_PID], + env->pregs[PR_ERP]); } -void do_interrupt(CPUCRISState *env) +void cris_cpu_do_interrupt(CPUState *cs) { - int ex_vec = -1; + CRISCPU *cpu = CRIS_CPU(cs); + CPUCRISState *env = &cpu->env; + int ex_vec = -1; - if (env->pregs[PR_VR] < 32) - return do_interruptv10(env); + if (env->pregs[PR_VR] < 32) { + return do_interruptv10(env); + } - D_LOG( "exception index=%d interrupt_req=%d\n", - env->exception_index, - env->interrupt_request); + D_LOG("exception index=%d interrupt_req=%d\n", + env->exception_index, + cs->interrupt_request); - switch (env->exception_index) - { - case EXCP_BREAK: - /* These exceptions are genereated by the core itself. - ERP should point to the insn following the brk. */ - ex_vec = env->trap_vector; - env->pregs[PR_ERP] = env->pc; - break; + switch (env->exception_index) { + case EXCP_BREAK: + /* These exceptions are genereated by the core itself. + ERP should point to the insn following the brk. */ + ex_vec = env->trap_vector; + env->pregs[PR_ERP] = env->pc; + break; - case EXCP_NMI: - /* NMI is hardwired to vector zero. */ - ex_vec = 0; - env->pregs[PR_CCS] &= ~M_FLAG_V32; - env->pregs[PR_NRP] = env->pc; - break; + case EXCP_NMI: + /* NMI is hardwired to vector zero. */ + ex_vec = 0; + env->pregs[PR_CCS] &= ~M_FLAG_V32; + env->pregs[PR_NRP] = env->pc; + break; - case EXCP_BUSFAULT: - ex_vec = env->fault_vector; - env->pregs[PR_ERP] = env->pc; - break; + case EXCP_BUSFAULT: + ex_vec = env->fault_vector; + env->pregs[PR_ERP] = env->pc; + break; - default: - /* The interrupt controller gives us the vector. */ - ex_vec = env->interrupt_vector; - /* Normal interrupts are taken between - TB's. env->pc is valid here. */ - env->pregs[PR_ERP] = env->pc; - break; - } + default: + /* The interrupt controller gives us the vector. */ + ex_vec = env->interrupt_vector; + /* Normal interrupts are taken between + TB's. env->pc is valid here. */ + env->pregs[PR_ERP] = env->pc; + break; + } - /* Fill in the IDX field. */ - env->pregs[PR_EXS] = (ex_vec & 0xff) << 8; + /* Fill in the IDX field. */ + env->pregs[PR_EXS] = (ex_vec & 0xff) << 8; - if (env->dslot) { - D_LOG("excp isr=%x PC=%x ds=%d SP=%x" - " ERP=%x pid=%x ccs=%x cc=%d %x\n", - ex_vec, env->pc, env->dslot, - env->regs[R_SP], - env->pregs[PR_ERP], env->pregs[PR_PID], - env->pregs[PR_CCS], - env->cc_op, env->cc_mask); - /* We loose the btarget, btaken state here so rexec the - branch. */ - env->pregs[PR_ERP] -= env->dslot; - /* Exception starts with dslot cleared. */ - env->dslot = 0; - } + if (env->dslot) { + D_LOG("excp isr=%x PC=%x ds=%d SP=%x" + " ERP=%x pid=%x ccs=%x cc=%d %x\n", + ex_vec, env->pc, env->dslot, + env->regs[R_SP], + env->pregs[PR_ERP], env->pregs[PR_PID], + env->pregs[PR_CCS], + env->cc_op, env->cc_mask); + /* We loose the btarget, btaken state here so rexec the + branch. */ + env->pregs[PR_ERP] -= env->dslot; + /* Exception starts with dslot cleared. */ + env->dslot = 0; + } - if (env->pregs[PR_CCS] & U_FLAG) { - /* Swap stack pointers. */ - env->pregs[PR_USP] = env->regs[R_SP]; - env->regs[R_SP] = env->ksp; - } + if (env->pregs[PR_CCS] & U_FLAG) { + /* Swap stack pointers. */ + env->pregs[PR_USP] = env->regs[R_SP]; + env->regs[R_SP] = env->ksp; + } - /* Apply the CRIS CCS shift. Clears U if set. */ - cris_shift_ccs(env); + /* Apply the CRIS CCS shift. Clears U if set. */ + cris_shift_ccs(env); - /* Now that we are in kernel mode, load the handlers address. - This load may not fault, real hw leaves that behaviour as - undefined. */ - env->pc = cpu_ldl_code(env, env->pregs[PR_EBP] + ex_vec * 4); + /* Now that we are in kernel mode, load the handlers address. + This load may not fault, real hw leaves that behaviour as + undefined. */ + env->pc = cpu_ldl_code(env, env->pregs[PR_EBP] + ex_vec * 4); - /* Clear the excption_index to avoid spurios hw_aborts for recursive - bus faults. */ - env->exception_index = -1; + /* Clear the excption_index to avoid spurios hw_aborts for recursive + bus faults. */ + env->exception_index = -1; - D_LOG("%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", - __func__, env->pc, ex_vec, - env->pregs[PR_CCS], - env->pregs[PR_PID], - env->pregs[PR_ERP]); + D_LOG("%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", + __func__, env->pc, ex_vec, + env->pregs[PR_CCS], + env->pregs[PR_PID], + env->pregs[PR_ERP]); } hwaddr cpu_get_phys_page_debug(CPUCRISState * env, target_ulong addr) { - uint32_t phy = addr; - struct cris_mmu_result res; - int miss; + uint32_t phy = addr; + struct cris_mmu_result res; + int miss; - miss = cris_mmu_translate(&res, env, addr, 0, 0, 1); - /* If D TLB misses, try I TLB. */ - if (miss) { - miss = cris_mmu_translate(&res, env, addr, 2, 0, 1); - } + miss = cris_mmu_translate(&res, env, addr, 0, 0, 1); + /* If D TLB misses, try I TLB. */ + if (miss) { + miss = cris_mmu_translate(&res, env, addr, 2, 0, 1); + } - if (!miss) - phy = res.phy; - D(fprintf(stderr, "%s %x -> %x\n", __func__, addr, phy)); - return phy; + if (!miss) { + phy = res.phy; + } + D(fprintf(stderr, "%s %x -> %x\n", __func__, addr, phy)); + return phy; } #endif diff --git a/target-cris/op_helper.c b/target-cris/op_helper.c index 79bff38663..b580513848 100644 --- a/target-cris/op_helper.c +++ b/target-cris/op_helper.c @@ -28,7 +28,7 @@ #ifdef CRIS_OP_HELPER_DEBUG #define D(x) x -#define D_LOG(...) qemu_log(__VA__ARGS__) +#define D_LOG(...) qemu_log(__VA_ARGS__) #else #define D(x) #define D_LOG(...) do { } while (0) @@ -60,7 +60,7 @@ void tlb_fill(CPUCRISState *env, target_ulong addr, int is_write, int mmu_idx, int ret; D_LOG("%s pc=%x tpc=%x ra=%p\n", __func__, - env->pc, env->debug1, (void *)retaddr); + env->pc, env->pregs[PR_EDA], (void *)retaddr); ret = cpu_cris_handle_mmu_fault(env, addr, is_write, mmu_idx); if (unlikely(ret)) { if (retaddr) { diff --git a/target-cris/translate.c b/target-cris/translate.c index 09e6011ea4..dbcb811b7b 100644 --- a/target-cris/translate.c +++ b/target-cris/translate.c @@ -340,46 +340,6 @@ static void t_gen_asr(TCGv d, TCGv a, TCGv b) tcg_temp_free(t_31); } -/* 64-bit signed mul, lower result in d and upper in d2. */ -static void t_gen_muls(TCGv d, TCGv d2, TCGv a, TCGv b) -{ - TCGv_i64 t0, t1; - - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - - tcg_gen_ext_i32_i64(t0, a); - tcg_gen_ext_i32_i64(t1, b); - tcg_gen_mul_i64(t0, t0, t1); - - tcg_gen_trunc_i64_i32(d, t0); - tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_trunc_i64_i32(d2, t0); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); -} - -/* 64-bit unsigned muls, lower result in d and upper in d2. */ -static void t_gen_mulu(TCGv d, TCGv d2, TCGv a, TCGv b) -{ - TCGv_i64 t0, t1; - - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - - tcg_gen_extu_i32_i64(t0, a); - tcg_gen_extu_i32_i64(t1, b); - tcg_gen_mul_i64(t0, t0, t1); - - tcg_gen_trunc_i64_i32(d, t0); - tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_trunc_i64_i32(d2, t0); - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); -} - static void t_gen_cris_dstep(TCGv d, TCGv a, TCGv b) { int l1; @@ -832,10 +792,10 @@ static void cris_alu_op_exec(DisasContext *dc, int op, gen_helper_lz(dst, b); break; case CC_OP_MULS: - t_gen_muls(dst, cpu_PR[PR_MOF], a, b); + tcg_gen_muls2_tl(dst, cpu_PR[PR_MOF], a, b); break; case CC_OP_MULU: - t_gen_mulu(dst, cpu_PR[PR_MOF], a, b); + tcg_gen_mulu2_tl(dst, cpu_PR[PR_MOF], a, b); break; case CC_OP_DSTEP: t_gen_cris_dstep(dst, a, b); @@ -2928,7 +2888,8 @@ static int dec_rfe_etc(CPUCRISState *env, DisasContext *dc) cris_cc_mask(dc, 0); if (dc->op2 == 15) { - t_gen_mov_env_TN(halted, tcg_const_tl(1)); + tcg_gen_st_i32(tcg_const_i32(1), cpu_env, + -offsetof(CRISCPU, env) + offsetof(CPUState, halted)); tcg_gen_movi_tl(env_pc, dc->pc + 2); t_gen_raise_exception(EXCP_HLT); return 2; @@ -3215,8 +3176,6 @@ gen_intermediate_code_internal(CPUCRISState *env, TranslationBlock *tb, int num_insns; int max_insns; - qemu_log_try_set_file(stderr); - if (env->pregs[PR_VR] == 32) { dc->decoder = crisv32_decoder; dc->clear_locked_irq = 0; @@ -3292,7 +3251,7 @@ gen_intermediate_code_internal(CPUCRISState *env, TranslationBlock *tb, max_insns = CF_COUNT_MASK; } - gen_icount_start(); + gen_tb_start(); do { check_breakpoint(env, dc); @@ -3433,7 +3392,7 @@ gen_intermediate_code_internal(CPUCRISState *env, TranslationBlock *tb, break; } } - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; if (search_pc) { j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; @@ -3513,69 +3472,13 @@ void cpu_dump_state (CPUCRISState *env, FILE *f, fprintf_function cpu_fprintf, } -struct +void cris_initialize_tcg(void) { - uint32_t vr; - const char *name; -} cris_cores[] = { - {8, "crisv8"}, - {9, "crisv9"}, - {10, "crisv10"}, - {11, "crisv11"}, - {32, "crisv32"}, -}; - -void cris_cpu_list(FILE *f, fprintf_function cpu_fprintf) -{ - unsigned int i; - - (*cpu_fprintf)(f, "Available CPUs:\n"); - for (i = 0; i < ARRAY_SIZE(cris_cores); i++) { - (*cpu_fprintf)(f, " %s\n", cris_cores[i].name); - } -} - -static uint32_t vr_by_name(const char *name) -{ - unsigned int i; - for (i = 0; i < ARRAY_SIZE(cris_cores); i++) { - if (strcmp(name, cris_cores[i].name) == 0) { - return cris_cores[i].vr; - } - } - return 32; -} - -CRISCPU *cpu_cris_init(const char *cpu_model) -{ - CRISCPU *cpu; - CPUCRISState *env; - static int tcg_initialized = 0; int i; - cpu = CRIS_CPU(object_new(TYPE_CRIS_CPU)); - env = &cpu->env; - - env->pregs[PR_VR] = vr_by_name(cpu_model); - - cpu_reset(CPU(cpu)); - qemu_init_vcpu(env); - - if (tcg_initialized) { - return cpu; - } - - tcg_initialized = 1; - #define GEN_HELPER 2 #include "helper.h" - if (env->pregs[PR_VR] < 32) { - cpu_crisv10_init(env); - return cpu; - } - - cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); cc_x = tcg_global_mem_new(TCG_AREG0, offsetof(CPUCRISState, cc_x), "cc_x"); @@ -3615,8 +3518,6 @@ CRISCPU *cpu_cris_init(const char *cpu_model) offsetof(CPUCRISState, pregs[i]), pregnames[i]); } - - return cpu; } void restore_state_to_opc(CPUCRISState *env, TranslationBlock *tb, int pc_pos) diff --git a/target-cris/translate_v10.c b/target-cris/translate_v10.c index d2cca892e0..d6ef084483 100644 --- a/target-cris/translate_v10.c +++ b/target-cris/translate_v10.c @@ -1257,7 +1257,7 @@ static unsigned int crisv10_decoder(CPUCRISState *env, DisasContext *dc) return insn_len; } -static CPUCRISState *cpu_crisv10_init (CPUCRISState *env) +void cris_initialize_crisv10_tcg(void) { int i; @@ -1300,7 +1300,4 @@ static CPUCRISState *cpu_crisv10_init (CPUCRISState *env) offsetof(CPUCRISState, pregs[i]), pregnames_v10[i]); } - - return env; } - diff --git a/target-i386/arch_memory_mapping.c b/target-i386/arch_memory_mapping.c index c6c7874474..844893f44d 100644 --- a/target-i386/arch_memory_mapping.c +++ b/target-i386/arch_memory_mapping.c @@ -115,7 +115,7 @@ static void walk_pde2(MemoryMappingList *list, hwaddr pde_start_addr, int32_t a20_mask, bool pse) { - hwaddr pde_addr, pte_start_addr, start_paddr; + hwaddr pde_addr, pte_start_addr, start_paddr, high_paddr; uint32_t pde; target_ulong line_addr, start_vaddr; int i; @@ -130,8 +130,13 @@ static void walk_pde2(MemoryMappingList *list, line_addr = (((unsigned int)i & 0x3ff) << 22); if ((pde & PG_PSE_MASK) && pse) { - /* 4 MB page */ - start_paddr = (pde & ~0x3fffff) | ((pde & 0x1fe000) << 19); + /* + * 4 MB page: + * bits 39:32 are bits 20:13 of the PDE + * bit3 31:22 are bits 31:22 of the PDE + */ + high_paddr = ((hwaddr)(pde & 0x1fe000) << 19); + start_paddr = (pde & ~0x3fffff) | high_paddr; if (cpu_physical_memory_is_io(start_paddr)) { /* I/O region */ continue; diff --git a/target-i386/cc_helper.c b/target-i386/cc_helper.c index 9422003f24..9daa1a06b8 100644 --- a/target-i386/cc_helper.c +++ b/target-i386/cc_helper.c @@ -75,243 +75,247 @@ const uint8_t parity_table[256] = { #endif -static int compute_all_eflags(CPUX86State *env) +static target_ulong compute_all_adcx(target_ulong dst, target_ulong src1, + target_ulong src2) { - return CC_SRC; + return (src1 & ~CC_C) | (dst * CC_C); } -static int compute_c_eflags(CPUX86State *env) +static target_ulong compute_all_adox(target_ulong dst, target_ulong src1, + target_ulong src2) { - return CC_SRC & CC_C; + return (src1 & ~CC_O) | (src2 * CC_O); } -uint32_t helper_cc_compute_all(CPUX86State *env, int op) +static target_ulong compute_all_adcox(target_ulong dst, target_ulong src1, + target_ulong src2) +{ + return (src1 & ~(CC_C | CC_O)) | (dst * CC_C) | (src2 * CC_O); +} + +target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, + target_ulong src2, int op) { switch (op) { default: /* should never happen */ return 0; case CC_OP_EFLAGS: - return compute_all_eflags(env); + return src1; + case CC_OP_CLR: + return CC_Z; case CC_OP_MULB: - return compute_all_mulb(env); + return compute_all_mulb(dst, src1); case CC_OP_MULW: - return compute_all_mulw(env); + return compute_all_mulw(dst, src1); case CC_OP_MULL: - return compute_all_mull(env); + return compute_all_mull(dst, src1); case CC_OP_ADDB: - return compute_all_addb(env); + return compute_all_addb(dst, src1); case CC_OP_ADDW: - return compute_all_addw(env); + return compute_all_addw(dst, src1); case CC_OP_ADDL: - return compute_all_addl(env); + return compute_all_addl(dst, src1); case CC_OP_ADCB: - return compute_all_adcb(env); + return compute_all_adcb(dst, src1, src2); case CC_OP_ADCW: - return compute_all_adcw(env); + return compute_all_adcw(dst, src1, src2); case CC_OP_ADCL: - return compute_all_adcl(env); + return compute_all_adcl(dst, src1, src2); case CC_OP_SUBB: - return compute_all_subb(env); + return compute_all_subb(dst, src1); case CC_OP_SUBW: - return compute_all_subw(env); + return compute_all_subw(dst, src1); case CC_OP_SUBL: - return compute_all_subl(env); + return compute_all_subl(dst, src1); case CC_OP_SBBB: - return compute_all_sbbb(env); + return compute_all_sbbb(dst, src1, src2); case CC_OP_SBBW: - return compute_all_sbbw(env); + return compute_all_sbbw(dst, src1, src2); case CC_OP_SBBL: - return compute_all_sbbl(env); + return compute_all_sbbl(dst, src1, src2); case CC_OP_LOGICB: - return compute_all_logicb(env); + return compute_all_logicb(dst, src1); case CC_OP_LOGICW: - return compute_all_logicw(env); + return compute_all_logicw(dst, src1); case CC_OP_LOGICL: - return compute_all_logicl(env); + return compute_all_logicl(dst, src1); case CC_OP_INCB: - return compute_all_incb(env); + return compute_all_incb(dst, src1); case CC_OP_INCW: - return compute_all_incw(env); + return compute_all_incw(dst, src1); case CC_OP_INCL: - return compute_all_incl(env); + return compute_all_incl(dst, src1); case CC_OP_DECB: - return compute_all_decb(env); + return compute_all_decb(dst, src1); case CC_OP_DECW: - return compute_all_decw(env); + return compute_all_decw(dst, src1); case CC_OP_DECL: - return compute_all_decl(env); + return compute_all_decl(dst, src1); case CC_OP_SHLB: - return compute_all_shlb(env); + return compute_all_shlb(dst, src1); case CC_OP_SHLW: - return compute_all_shlw(env); + return compute_all_shlw(dst, src1); case CC_OP_SHLL: - return compute_all_shll(env); + return compute_all_shll(dst, src1); case CC_OP_SARB: - return compute_all_sarb(env); + return compute_all_sarb(dst, src1); case CC_OP_SARW: - return compute_all_sarw(env); + return compute_all_sarw(dst, src1); case CC_OP_SARL: - return compute_all_sarl(env); + return compute_all_sarl(dst, src1); + + case CC_OP_BMILGB: + return compute_all_bmilgb(dst, src1); + case CC_OP_BMILGW: + return compute_all_bmilgw(dst, src1); + case CC_OP_BMILGL: + return compute_all_bmilgl(dst, src1); + + case CC_OP_ADCX: + return compute_all_adcx(dst, src1, src2); + case CC_OP_ADOX: + return compute_all_adox(dst, src1, src2); + case CC_OP_ADCOX: + return compute_all_adcox(dst, src1, src2); #ifdef TARGET_X86_64 case CC_OP_MULQ: - return compute_all_mulq(env); - + return compute_all_mulq(dst, src1); case CC_OP_ADDQ: - return compute_all_addq(env); - + return compute_all_addq(dst, src1); case CC_OP_ADCQ: - return compute_all_adcq(env); - + return compute_all_adcq(dst, src1, src2); case CC_OP_SUBQ: - return compute_all_subq(env); - + return compute_all_subq(dst, src1); case CC_OP_SBBQ: - return compute_all_sbbq(env); - + return compute_all_sbbq(dst, src1, src2); case CC_OP_LOGICQ: - return compute_all_logicq(env); - + return compute_all_logicq(dst, src1); case CC_OP_INCQ: - return compute_all_incq(env); - + return compute_all_incq(dst, src1); case CC_OP_DECQ: - return compute_all_decq(env); - + return compute_all_decq(dst, src1); case CC_OP_SHLQ: - return compute_all_shlq(env); - + return compute_all_shlq(dst, src1); case CC_OP_SARQ: - return compute_all_sarq(env); + return compute_all_sarq(dst, src1); + case CC_OP_BMILGQ: + return compute_all_bmilgq(dst, src1); #endif } } uint32_t cpu_cc_compute_all(CPUX86State *env, int op) { - return helper_cc_compute_all(env, op); + return helper_cc_compute_all(CC_DST, CC_SRC, CC_SRC2, op); } -uint32_t helper_cc_compute_c(CPUX86State *env, int op) +target_ulong helper_cc_compute_c(target_ulong dst, target_ulong src1, + target_ulong src2, int op) { switch (op) { default: /* should never happen */ + case CC_OP_LOGICB: + case CC_OP_LOGICW: + case CC_OP_LOGICL: + case CC_OP_LOGICQ: + case CC_OP_CLR: return 0; case CC_OP_EFLAGS: - return compute_c_eflags(env); - - case CC_OP_MULB: - return compute_c_mull(env); - case CC_OP_MULW: - return compute_c_mull(env); - case CC_OP_MULL: - return compute_c_mull(env); - - case CC_OP_ADDB: - return compute_c_addb(env); - case CC_OP_ADDW: - return compute_c_addw(env); - case CC_OP_ADDL: - return compute_c_addl(env); - - case CC_OP_ADCB: - return compute_c_adcb(env); - case CC_OP_ADCW: - return compute_c_adcw(env); - case CC_OP_ADCL: - return compute_c_adcl(env); - - case CC_OP_SUBB: - return compute_c_subb(env); - case CC_OP_SUBW: - return compute_c_subw(env); - case CC_OP_SUBL: - return compute_c_subl(env); - - case CC_OP_SBBB: - return compute_c_sbbb(env); - case CC_OP_SBBW: - return compute_c_sbbw(env); - case CC_OP_SBBL: - return compute_c_sbbl(env); - - case CC_OP_LOGICB: - return compute_c_logicb(); - case CC_OP_LOGICW: - return compute_c_logicw(); - case CC_OP_LOGICL: - return compute_c_logicl(); + case CC_OP_SARB: + case CC_OP_SARW: + case CC_OP_SARL: + case CC_OP_SARQ: + case CC_OP_ADOX: + return src1 & 1; case CC_OP_INCB: - return compute_c_incl(env); case CC_OP_INCW: - return compute_c_incl(env); case CC_OP_INCL: - return compute_c_incl(env); - + case CC_OP_INCQ: case CC_OP_DECB: - return compute_c_incl(env); case CC_OP_DECW: - return compute_c_incl(env); case CC_OP_DECL: - return compute_c_incl(env); + case CC_OP_DECQ: + return src1; + + case CC_OP_MULB: + case CC_OP_MULW: + case CC_OP_MULL: + case CC_OP_MULQ: + return src1 != 0; + + case CC_OP_ADCX: + case CC_OP_ADCOX: + return dst; + + case CC_OP_ADDB: + return compute_c_addb(dst, src1); + case CC_OP_ADDW: + return compute_c_addw(dst, src1); + case CC_OP_ADDL: + return compute_c_addl(dst, src1); + + case CC_OP_ADCB: + return compute_c_adcb(dst, src1, src2); + case CC_OP_ADCW: + return compute_c_adcw(dst, src1, src2); + case CC_OP_ADCL: + return compute_c_adcl(dst, src1, src2); + + case CC_OP_SUBB: + return compute_c_subb(dst, src1); + case CC_OP_SUBW: + return compute_c_subw(dst, src1); + case CC_OP_SUBL: + return compute_c_subl(dst, src1); + + case CC_OP_SBBB: + return compute_c_sbbb(dst, src1, src2); + case CC_OP_SBBW: + return compute_c_sbbw(dst, src1, src2); + case CC_OP_SBBL: + return compute_c_sbbl(dst, src1, src2); case CC_OP_SHLB: - return compute_c_shlb(env); + return compute_c_shlb(dst, src1); case CC_OP_SHLW: - return compute_c_shlw(env); + return compute_c_shlw(dst, src1); case CC_OP_SHLL: - return compute_c_shll(env); + return compute_c_shll(dst, src1); - case CC_OP_SARB: - return compute_c_sarl(env); - case CC_OP_SARW: - return compute_c_sarl(env); - case CC_OP_SARL: - return compute_c_sarl(env); + case CC_OP_BMILGB: + return compute_c_bmilgb(dst, src1); + case CC_OP_BMILGW: + return compute_c_bmilgw(dst, src1); + case CC_OP_BMILGL: + return compute_c_bmilgl(dst, src1); #ifdef TARGET_X86_64 - case CC_OP_MULQ: - return compute_c_mull(env); - case CC_OP_ADDQ: - return compute_c_addq(env); - + return compute_c_addq(dst, src1); case CC_OP_ADCQ: - return compute_c_adcq(env); - + return compute_c_adcq(dst, src1, src2); case CC_OP_SUBQ: - return compute_c_subq(env); - + return compute_c_subq(dst, src1); case CC_OP_SBBQ: - return compute_c_sbbq(env); - - case CC_OP_LOGICQ: - return compute_c_logicq(); - - case CC_OP_INCQ: - return compute_c_incl(env); - - case CC_OP_DECQ: - return compute_c_incl(env); - + return compute_c_sbbq(dst, src1, src2); case CC_OP_SHLQ: - return compute_c_shlq(env); - - case CC_OP_SARQ: - return compute_c_sarl(env); + return compute_c_shlq(dst, src1); + case CC_OP_BMILGQ: + return compute_c_bmilgq(dst, src1); #endif } } @@ -326,7 +330,7 @@ target_ulong helper_read_eflags(CPUX86State *env) { uint32_t eflags; - eflags = helper_cc_compute_all(env, CC_OP); + eflags = cpu_cc_compute_all(env, CC_OP); eflags |= (DF & DF_MASK); eflags |= env->eflags & ~(VM_MASK | RF_MASK); return eflags; diff --git a/target-i386/cc_helper_template.h b/target-i386/cc_helper_template.h index 1f94e11dcf..607311f195 100644 --- a/target-i386/cc_helper_template.h +++ b/target-i386/cc_helper_template.h @@ -18,258 +18,223 @@ */ #define DATA_BITS (1 << (3 + SHIFT)) -#define SIGN_MASK (((target_ulong)1) << (DATA_BITS - 1)) #if DATA_BITS == 8 #define SUFFIX b #define DATA_TYPE uint8_t -#define DATA_MASK 0xff #elif DATA_BITS == 16 #define SUFFIX w #define DATA_TYPE uint16_t -#define DATA_MASK 0xffff #elif DATA_BITS == 32 #define SUFFIX l #define DATA_TYPE uint32_t -#define DATA_MASK 0xffffffff #elif DATA_BITS == 64 #define SUFFIX q #define DATA_TYPE uint64_t -#define DATA_MASK 0xffffffffffffffffULL #else #error unhandled operand size #endif +#define SIGN_MASK (((DATA_TYPE)1) << (DATA_BITS - 1)) + /* dynamic flags computation */ -static int glue(compute_all_add, SUFFIX)(CPUX86State *env) +static int glue(compute_all_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { int cf, pf, af, zf, sf, of; - target_long src1, src2; + DATA_TYPE src2 = dst - src1; - src1 = CC_SRC; - src2 = CC_DST - CC_SRC; - cf = (DATA_TYPE)CC_DST < (DATA_TYPE)src1; - pf = parity_table[(uint8_t)CC_DST]; - af = (CC_DST ^ src1 ^ src2) & 0x10; - zf = ((DATA_TYPE)CC_DST == 0) << 6; - sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; - of = lshift((src1 ^ src2 ^ -1) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O; + cf = dst < src1; + pf = parity_table[(uint8_t)dst]; + af = (dst ^ src1 ^ src2) & CC_A; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; return cf | pf | af | zf | sf | of; } -static int glue(compute_c_add, SUFFIX)(CPUX86State *env) +static int glue(compute_c_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { - int cf; - target_long src1; - - src1 = CC_SRC; - cf = (DATA_TYPE)CC_DST < (DATA_TYPE)src1; - return cf; + return dst < src1; } -static int glue(compute_all_adc, SUFFIX)(CPUX86State *env) +static int glue(compute_all_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, + DATA_TYPE src3) { int cf, pf, af, zf, sf, of; - target_long src1, src2; + DATA_TYPE src2 = dst - src1 - src3; - src1 = CC_SRC; - src2 = CC_DST - CC_SRC - 1; - cf = (DATA_TYPE)CC_DST <= (DATA_TYPE)src1; - pf = parity_table[(uint8_t)CC_DST]; - af = (CC_DST ^ src1 ^ src2) & 0x10; - zf = ((DATA_TYPE)CC_DST == 0) << 6; - sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; - of = lshift((src1 ^ src2 ^ -1) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O; + cf = (src3 ? dst <= src1 : dst < src1); + pf = parity_table[(uint8_t)dst]; + af = (dst ^ src1 ^ src2) & 0x10; + zf = (dst == 0) << 6; + sf = lshift(dst, 8 - DATA_BITS) & 0x80; + of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; return cf | pf | af | zf | sf | of; } -static int glue(compute_c_adc, SUFFIX)(CPUX86State *env) +static int glue(compute_c_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, + DATA_TYPE src3) { - int cf; - target_long src1; - - src1 = CC_SRC; - cf = (DATA_TYPE)CC_DST <= (DATA_TYPE)src1; - return cf; + return src3 ? dst <= src1 : dst < src1; } -static int glue(compute_all_sub, SUFFIX)(CPUX86State *env) +static int glue(compute_all_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) { int cf, pf, af, zf, sf, of; - target_long src1, src2; + DATA_TYPE src1 = dst + src2; - src1 = CC_DST + CC_SRC; - src2 = CC_SRC; - cf = (DATA_TYPE)src1 < (DATA_TYPE)src2; - pf = parity_table[(uint8_t)CC_DST]; - af = (CC_DST ^ src1 ^ src2) & 0x10; - zf = ((DATA_TYPE)CC_DST == 0) << 6; - sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; - of = lshift((src1 ^ src2) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O; + cf = src1 < src2; + pf = parity_table[(uint8_t)dst]; + af = (dst ^ src1 ^ src2) & CC_A; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; return cf | pf | af | zf | sf | of; } -static int glue(compute_c_sub, SUFFIX)(CPUX86State *env) +static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) { - int cf; - target_long src1, src2; + DATA_TYPE src1 = dst + src2; - src1 = CC_DST + CC_SRC; - src2 = CC_SRC; - cf = (DATA_TYPE)src1 < (DATA_TYPE)src2; - return cf; + return src1 < src2; } -static int glue(compute_all_sbb, SUFFIX)(CPUX86State *env) +static int glue(compute_all_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, + DATA_TYPE src3) { int cf, pf, af, zf, sf, of; - target_long src1, src2; + DATA_TYPE src1 = dst + src2 + src3; - src1 = CC_DST + CC_SRC + 1; - src2 = CC_SRC; - cf = (DATA_TYPE)src1 <= (DATA_TYPE)src2; - pf = parity_table[(uint8_t)CC_DST]; - af = (CC_DST ^ src1 ^ src2) & 0x10; - zf = ((DATA_TYPE)CC_DST == 0) << 6; - sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; - of = lshift((src1 ^ src2) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O; + cf = (src3 ? src1 <= src2 : src1 < src2); + pf = parity_table[(uint8_t)dst]; + af = (dst ^ src1 ^ src2) & 0x10; + zf = (dst == 0) << 6; + sf = lshift(dst, 8 - DATA_BITS) & 0x80; + of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; return cf | pf | af | zf | sf | of; } -static int glue(compute_c_sbb, SUFFIX)(CPUX86State *env) +static int glue(compute_c_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, + DATA_TYPE src3) { - int cf; - target_long src1, src2; + DATA_TYPE src1 = dst + src2 + src3; - src1 = CC_DST + CC_SRC + 1; - src2 = CC_SRC; - cf = (DATA_TYPE)src1 <= (DATA_TYPE)src2; - return cf; + return (src3 ? src1 <= src2 : src1 < src2); } -static int glue(compute_all_logic, SUFFIX)(CPUX86State *env) +static int glue(compute_all_logic, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { int cf, pf, af, zf, sf, of; cf = 0; - pf = parity_table[(uint8_t)CC_DST]; + pf = parity_table[(uint8_t)dst]; af = 0; - zf = ((DATA_TYPE)CC_DST == 0) << 6; - sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; of = 0; return cf | pf | af | zf | sf | of; } -static int glue(compute_c_logic, SUFFIX)(void) -{ - return 0; -} - -static int glue(compute_all_inc, SUFFIX)(CPUX86State *env) +static int glue(compute_all_inc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { int cf, pf, af, zf, sf, of; - target_long src1, src2; + DATA_TYPE src2; - src1 = CC_DST - 1; + cf = src1; + src1 = dst - 1; src2 = 1; - cf = CC_SRC; - pf = parity_table[(uint8_t)CC_DST]; - af = (CC_DST ^ src1 ^ src2) & 0x10; - zf = ((DATA_TYPE)CC_DST == 0) << 6; - sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; - of = ((CC_DST & DATA_MASK) == SIGN_MASK) << 11; + pf = parity_table[(uint8_t)dst]; + af = (dst ^ src1 ^ src2) & CC_A; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = (dst == SIGN_MASK) * CC_O; return cf | pf | af | zf | sf | of; } -#if DATA_BITS == 32 -static int glue(compute_c_inc, SUFFIX)(CPUX86State *env) -{ - return CC_SRC; -} -#endif - -static int glue(compute_all_dec, SUFFIX)(CPUX86State *env) +static int glue(compute_all_dec, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { int cf, pf, af, zf, sf, of; - target_long src1, src2; + DATA_TYPE src2; - src1 = CC_DST + 1; + cf = src1; + src1 = dst + 1; src2 = 1; - cf = CC_SRC; - pf = parity_table[(uint8_t)CC_DST]; - af = (CC_DST ^ src1 ^ src2) & 0x10; - zf = ((DATA_TYPE)CC_DST == 0) << 6; - sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; - of = ((CC_DST & DATA_MASK) == ((target_ulong)SIGN_MASK - 1)) << 11; + pf = parity_table[(uint8_t)dst]; + af = (dst ^ src1 ^ src2) & CC_A; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = (dst == SIGN_MASK - 1) * CC_O; return cf | pf | af | zf | sf | of; } -static int glue(compute_all_shl, SUFFIX)(CPUX86State *env) +static int glue(compute_all_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { int cf, pf, af, zf, sf, of; - cf = (CC_SRC >> (DATA_BITS - 1)) & CC_C; - pf = parity_table[(uint8_t)CC_DST]; + cf = (src1 >> (DATA_BITS - 1)) & CC_C; + pf = parity_table[(uint8_t)dst]; af = 0; /* undefined */ - zf = ((DATA_TYPE)CC_DST == 0) << 6; - sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; - /* of is defined if shift count == 1 */ - of = lshift(CC_SRC ^ CC_DST, 12 - DATA_BITS) & CC_O; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + /* of is defined iff shift count == 1 */ + of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; return cf | pf | af | zf | sf | of; } -static int glue(compute_c_shl, SUFFIX)(CPUX86State *env) +static int glue(compute_c_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { - return (CC_SRC >> (DATA_BITS - 1)) & CC_C; + return (src1 >> (DATA_BITS - 1)) & CC_C; } -#if DATA_BITS == 32 -static int glue(compute_c_sar, SUFFIX)(CPUX86State *env) -{ - return CC_SRC & 1; -} -#endif - -static int glue(compute_all_sar, SUFFIX)(CPUX86State *env) +static int glue(compute_all_sar, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { int cf, pf, af, zf, sf, of; - cf = CC_SRC & 1; - pf = parity_table[(uint8_t)CC_DST]; + cf = src1 & 1; + pf = parity_table[(uint8_t)dst]; af = 0; /* undefined */ - zf = ((DATA_TYPE)CC_DST == 0) << 6; - sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; - /* of is defined if shift count == 1 */ - of = lshift(CC_SRC ^ CC_DST, 12 - DATA_BITS) & CC_O; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + /* of is defined iff shift count == 1 */ + of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; return cf | pf | af | zf | sf | of; } -#if DATA_BITS == 32 -static int glue(compute_c_mul, SUFFIX)(CPUX86State *env) -{ - int cf; - - cf = (CC_SRC != 0); - return cf; -} -#endif - /* NOTE: we compute the flags like the P4. On olders CPUs, only OF and - CF are modified and it is slower to do that. */ -static int glue(compute_all_mul, SUFFIX)(CPUX86State *env) + CF are modified and it is slower to do that. Note as well that we + don't truncate SRC1 for computing carry to DATA_TYPE. */ +static int glue(compute_all_mul, SUFFIX)(DATA_TYPE dst, target_long src1) { int cf, pf, af, zf, sf, of; - cf = (CC_SRC != 0); - pf = parity_table[(uint8_t)CC_DST]; + cf = (src1 != 0); + pf = parity_table[(uint8_t)dst]; af = 0; /* undefined */ - zf = ((DATA_TYPE)CC_DST == 0) << 6; - sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80; - of = cf << 11; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = cf * CC_O; return cf | pf | af | zf | sf | of; } +static int glue(compute_all_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + int cf, pf, af, zf, sf, of; + + cf = (src1 == 0); + pf = 0; /* undefined */ + af = 0; /* undefined */ + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = 0; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + return src1 == 0; +} + #undef DATA_BITS #undef SIGN_MASK #undef DATA_TYPE diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h index 332916a185..08f9eb67b2 100644 --- a/target-i386/cpu-qom.h +++ b/target-i386/cpu-qom.h @@ -39,6 +39,7 @@ /** * X86CPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. * * An x86 CPU model or family. @@ -48,6 +49,7 @@ typedef struct X86CPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); } X86CPUClass; @@ -72,8 +74,16 @@ static inline X86CPU *x86_env_get_cpu(CPUX86State *env) #define ENV_GET_CPU(e) CPU(x86_env_get_cpu(e)) -/* TODO Drop once ObjectClass::realize is available */ -void x86_cpu_realize(Object *obj, Error **errp); +#define ENV_OFFSET offsetof(X86CPU, env) +#ifndef CONFIG_USER_ONLY +extern const struct VMStateDescription vmstate_x86_cpu; +#endif + +/** + * x86_cpu_do_interrupt: + * @cpu: vCPU the interrupt is to be handled by. + */ +void x86_cpu_do_interrupt(CPUState *cpu); #endif diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 1837f5af04..a0640db9e3 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -23,6 +23,8 @@ #include "cpu.h" #include "sysemu/kvm.h" +#include "sysemu/cpus.h" +#include "topology.h" #include "qemu/option.h" #include "qemu/config-file.h" @@ -45,6 +47,18 @@ #include "hw/apic_internal.h" #endif +static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, + uint32_t vendor2, uint32_t vendor3) +{ + int i; + for (i = 0; i < 4; i++) { + dst[i] = vendor1 >> (8 * i); + dst[i + 4] = vendor2 >> (8 * i); + dst[i + 8] = vendor3 >> (8 * i); + } + dst[CPUID_VENDOR_SZ] = '\0'; +} + /* feature flags taken from "Intel Processor Identification and the CPUID * Instruction" and AMD's "CPUID Specification". In cases of disagreement * between feature naming conventions, aliases may be added. @@ -95,6 +109,17 @@ static const char *ext3_feature_name[] = { NULL, NULL, NULL, NULL, }; +static const char *ext4_feature_name[] = { + NULL, NULL, "xstore", "xstore-en", + NULL, NULL, "xcrypt", "xcrypt-en", + "ace2", "ace2-en", "phe", "phe-en", + "pmm", "pmm-en", NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, +}; + static const char *kvm_feature_name[] = { "kvmclock", "kvm_nopiodelay", "kvm_mmu", "kvmclock", "kvm_asyncpf", "kvm_steal_time", "kvm_pv_eoi", NULL, @@ -124,36 +149,88 @@ static const char *cpuid_7_0_ebx_feature_name[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; +typedef struct FeatureWordInfo { + const char **feat_names; + uint32_t cpuid_eax; /* Input EAX for CPUID */ + int cpuid_reg; /* R_* register constant */ +} FeatureWordInfo; + +static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + [FEAT_1_EDX] = { + .feat_names = feature_name, + .cpuid_eax = 1, .cpuid_reg = R_EDX, + }, + [FEAT_1_ECX] = { + .feat_names = ext_feature_name, + .cpuid_eax = 1, .cpuid_reg = R_ECX, + }, + [FEAT_8000_0001_EDX] = { + .feat_names = ext2_feature_name, + .cpuid_eax = 0x80000001, .cpuid_reg = R_EDX, + }, + [FEAT_8000_0001_ECX] = { + .feat_names = ext3_feature_name, + .cpuid_eax = 0x80000001, .cpuid_reg = R_ECX, + }, + [FEAT_C000_0001_EDX] = { + .feat_names = ext4_feature_name, + .cpuid_eax = 0xC0000001, .cpuid_reg = R_EDX, + }, + [FEAT_KVM] = { + .feat_names = kvm_feature_name, + .cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EAX, + }, + [FEAT_SVM] = { + .feat_names = svm_feature_name, + .cpuid_eax = 0x8000000A, .cpuid_reg = R_EDX, + }, + [FEAT_7_0_EBX] = { + .feat_names = cpuid_7_0_ebx_feature_name, + .cpuid_eax = 7, .cpuid_reg = R_EBX, + }, +}; + +const char *get_register_name_32(unsigned int reg) +{ + static const char *reg_names[CPU_NB_REGS32] = { + [R_EAX] = "EAX", + [R_ECX] = "ECX", + [R_EDX] = "EDX", + [R_EBX] = "EBX", + [R_ESP] = "ESP", + [R_EBP] = "EBP", + [R_ESI] = "ESI", + [R_EDI] = "EDI", + }; + + if (reg > CPU_NB_REGS32) { + return NULL; + } + return reg_names[reg]; +} + /* collects per-function cpuid data */ typedef struct model_features_t { uint32_t *guest_feat; uint32_t *host_feat; - uint32_t check_feat; - const char **flag_names; - uint32_t cpuid; - } model_features_t; + FeatureWord feat_word; +} model_features_t; int check_cpuid = 0; int enforce_cpuid = 0; -#if defined(CONFIG_KVM) static uint32_t kvm_default_features = (1 << KVM_FEATURE_CLOCKSOURCE) | (1 << KVM_FEATURE_NOP_IO_DELAY) | - (1 << KVM_FEATURE_MMU_OP) | (1 << KVM_FEATURE_CLOCKSOURCE2) | (1 << KVM_FEATURE_ASYNC_PF) | (1 << KVM_FEATURE_STEAL_TIME) | + (1 << KVM_FEATURE_PV_EOI) | (1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT); -static const uint32_t kvm_pv_eoi_features = (0x1 << KVM_FEATURE_PV_EOI); -#else -static uint32_t kvm_default_features = 0; -static const uint32_t kvm_pv_eoi_features = 0; -#endif -void enable_kvm_pv_eoi(void) +void disable_kvm_pv_eoi(void) { - kvm_default_features |= kvm_pv_eoi_features; + kvm_default_features &= ~(1UL << KVM_FEATURE_PV_EOI); } void host_cpuid(uint32_t function, uint32_t count, @@ -253,39 +330,34 @@ static bool lookup_feature(uint32_t *pval, const char *s, const char *e, return found; } -static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features, - uint32_t *ext_features, - uint32_t *ext2_features, - uint32_t *ext3_features, - uint32_t *kvm_features, - uint32_t *svm_features, - uint32_t *cpuid_7_0_ebx_features) +static void add_flagname_to_bitmaps(const char *flagname, + FeatureWordArray words) { - if (!lookup_feature(features, flagname, NULL, feature_name) && - !lookup_feature(ext_features, flagname, NULL, ext_feature_name) && - !lookup_feature(ext2_features, flagname, NULL, ext2_feature_name) && - !lookup_feature(ext3_features, flagname, NULL, ext3_feature_name) && - !lookup_feature(kvm_features, flagname, NULL, kvm_feature_name) && - !lookup_feature(svm_features, flagname, NULL, svm_feature_name) && - !lookup_feature(cpuid_7_0_ebx_features, flagname, NULL, - cpuid_7_0_ebx_feature_name)) - fprintf(stderr, "CPU feature %s not found\n", flagname); + FeatureWord w; + for (w = 0; w < FEATURE_WORDS; w++) { + FeatureWordInfo *wi = &feature_word_info[w]; + if (wi->feat_names && + lookup_feature(&words[w], flagname, NULL, wi->feat_names)) { + break; + } + } + if (w == FEATURE_WORDS) { + fprintf(stderr, "CPU feature %s not found\n", flagname); + } } typedef struct x86_def_t { - struct x86_def_t *next; const char *name; uint32_t level; - uint32_t vendor1, vendor2, vendor3; + /* vendor is zero-terminated, 12 character ASCII string */ + char vendor[CPUID_VENDOR_SZ + 1]; int family; int model; int stepping; - int tsc_khz; uint32_t features, ext_features, ext2_features, ext3_features; uint32_t kvm_features, svm_features; uint32_t xlevel; char model_id[48]; - int vendor_override; /* Store the results of Centaur's CPUID instructions */ uint32_t ext4_features; uint32_t xlevel2; @@ -317,10 +389,15 @@ typedef struct x86_def_t { CPUID_VME, CPUID_DTS, CPUID_SS, CPUID_HT, CPUID_TM, CPUID_PBE */ #define TCG_EXT_FEATURES (CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | \ CPUID_EXT_SSSE3 | CPUID_EXT_CX16 | CPUID_EXT_POPCNT | \ - CPUID_EXT_HYPERVISOR) + CPUID_EXT_MOVBE | CPUID_EXT_HYPERVISOR) /* missing: - CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_EST, - CPUID_EXT_TM2, CPUID_EXT_XTPR, CPUID_EXT_PDCM, CPUID_EXT_XSAVE */ + CPUID_EXT_PCLMULQDQ, CPUID_EXT_DTES64, CPUID_EXT_DSCPL, + CPUID_EXT_VMX, CPUID_EXT_SMX, CPUID_EXT_EST, CPUID_EXT_TM2, + CPUID_EXT_CID, CPUID_EXT_FMA, CPUID_EXT_XTPR, CPUID_EXT_PDCM, + CPUID_EXT_PCID, CPUID_EXT_DCA, CPUID_EXT_SSE41, CPUID_EXT_SSE42, + CPUID_EXT_X2APIC, CPUID_EXT_TSC_DEADLINE_TIMER, CPUID_EXT_AES, + CPUID_EXT_XSAVE, CPUID_EXT_OSXSAVE, CPUID_EXT_AVX, + CPUID_EXT_F16C, CPUID_EXT_RDRAND */ #define TCG_EXT2_FEATURES ((TCG_FEATURES & CPUID_EXT2_AMD_ALIASES) | \ CPUID_EXT2_NX | CPUID_EXT2_MMXEXT | CPUID_EXT2_RDTSCP | \ CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT) @@ -329,21 +406,20 @@ typedef struct x86_def_t { #define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \ CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A) #define TCG_SVM_FEATURES 0 -#define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP) +#define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP \ + CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX) + /* missing: + CPUID_7_0_EBX_FSGSBASE, CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2, + CPUID_7_0_EBX_ERMS, CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM, + CPUID_7_0_EBX_RDSEED */ -/* maintains list of cpu model definitions - */ -static x86_def_t *x86_defs = {NULL}; - -/* built-in cpu model definitions (deprecated) +/* built-in CPU model definitions */ static x86_def_t builtin_x86_defs[] = { { .name = "qemu64", .level = 4, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 6, .model = 2, .stepping = 3, @@ -360,9 +436,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "phenom", .level = 5, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 16, .model = 2, .stepping = 3, @@ -388,6 +462,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "core2duo", .level = 10, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 15, .stepping = 11, @@ -406,9 +481,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "kvm64", .level = 5, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 15, .model = 6, .stepping = 1, @@ -432,6 +505,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "qemu32", .level = 4, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 3, .stepping = 3, @@ -442,6 +516,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "kvm32", .level = 5, + .vendor = CPUID_VENDOR_INTEL, .family = 15, .model = 6, .stepping = 1, @@ -456,6 +531,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "coreduo", .level = 10, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 14, .stepping = 8, @@ -471,6 +547,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "486", .level = 1, + .vendor = CPUID_VENDOR_INTEL, .family = 4, .model = 0, .stepping = 0, @@ -480,6 +557,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "pentium", .level = 1, + .vendor = CPUID_VENDOR_INTEL, .family = 5, .model = 4, .stepping = 3, @@ -489,6 +567,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "pentium2", .level = 2, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 5, .stepping = 2, @@ -498,6 +577,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "pentium3", .level = 2, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 7, .stepping = 3, @@ -507,9 +587,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "athlon", .level = 2, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 6, .model = 2, .stepping = 3, @@ -523,6 +601,7 @@ static x86_def_t builtin_x86_defs[] = { .name = "n270", /* original is on level 10 */ .level = 5, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 28, .stepping = 2, @@ -541,9 +620,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Conroe", .level = 2, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 2, .stepping = 3, @@ -561,9 +638,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Penryn", .level = 2, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 2, .stepping = 3, @@ -582,9 +657,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Nehalem", .level = 2, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 2, .stepping = 3, @@ -603,9 +676,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Westmere", .level = 11, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 44, .stepping = 1, @@ -625,9 +696,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "SandyBridge", .level = 0xd, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 42, .stepping = 1, @@ -650,9 +719,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Haswell", .level = 0xd, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, + .vendor = CPUID_VENDOR_INTEL, .family = 6, .model = 60, .stepping = 1, @@ -680,9 +747,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Opteron_G1", .level = 5, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 15, .model = 6, .stepping = 1, @@ -704,9 +769,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Opteron_G2", .level = 5, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 15, .model = 6, .stepping = 1, @@ -730,9 +793,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Opteron_G3", .level = 5, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 15, .model = 6, .stepping = 1, @@ -758,9 +819,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Opteron_G4", .level = 0xd, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 21, .model = 1, .stepping = 2, @@ -790,9 +849,7 @@ static x86_def_t builtin_x86_defs[] = { { .name = "Opteron_G5", .level = 0xd, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, + .vendor = CPUID_VENDOR_AMD, .family = 21, .model = 2, .stepping = 0, @@ -853,9 +910,7 @@ static void kvm_cpu_fill_host(x86_def_t *x86_cpu_def) x86_cpu_def->name = "host"; host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx); - x86_cpu_def->vendor1 = ebx; - x86_cpu_def->vendor2 = edx; - x86_cpu_def->vendor3 = ecx; + x86_cpu_vendor_words2str(x86_cpu_def->vendor, ebx, edx, ecx); host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx); x86_cpu_def->family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF); @@ -880,12 +935,9 @@ static void kvm_cpu_fill_host(x86_def_t *x86_cpu_def) kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_ECX); cpu_x86_fill_model_id(x86_cpu_def->model_id); - x86_cpu_def->vendor_override = 0; /* Call Centaur's CPUID instruction. */ - if (x86_cpu_def->vendor1 == CPUID_VENDOR_VIA_1 && - x86_cpu_def->vendor2 == CPUID_VENDOR_VIA_2 && - x86_cpu_def->vendor3 == CPUID_VENDOR_VIA_3) { + if (!strcmp(x86_cpu_def->vendor, CPUID_VENDOR_VIA)) { host_cpuid(0xC0000000, 0, &eax, &ebx, &ecx, &edx); eax = kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX); if (eax >= 0xC0000001) { @@ -897,62 +949,78 @@ static void kvm_cpu_fill_host(x86_def_t *x86_cpu_def) } } - /* - * Every SVM feature requires emulation support in KVM - so we can't just - * read the host features here. KVM might even support SVM features not - * available on the host hardware. Just set all bits and mask out the - * unsupported ones later. - */ - x86_cpu_def->svm_features = -1; + /* Other KVM-specific feature fields: */ + x86_cpu_def->svm_features = + kvm_arch_get_supported_cpuid(s, 0x8000000A, 0, R_EDX); + x86_cpu_def->kvm_features = + kvm_arch_get_supported_cpuid(s, KVM_CPUID_FEATURES, 0, R_EAX); + #endif /* CONFIG_KVM */ } -static int unavailable_host_feature(struct model_features_t *f, uint32_t mask) +static int unavailable_host_feature(FeatureWordInfo *f, uint32_t mask) { int i; for (i = 0; i < 32; ++i) if (1 << i & mask) { - fprintf(stderr, "warning: host cpuid %04x_%04x lacks requested" - " flag '%s' [0x%08x]\n", - f->cpuid >> 16, f->cpuid & 0xffff, - f->flag_names[i] ? f->flag_names[i] : "[reserved]", mask); + const char *reg = get_register_name_32(f->cpuid_reg); + assert(reg); + fprintf(stderr, "warning: host doesn't support requested feature: " + "CPUID.%02XH:%s%s%s [bit %d]\n", + f->cpuid_eax, reg, + f->feat_names[i] ? "." : "", + f->feat_names[i] ? f->feat_names[i] : "", i); break; } return 0; } -/* best effort attempt to inform user requested cpu flags aren't making - * their way to the guest. Note: ft[].check_feat ideally should be - * specified via a guest_def field to suppress report of extraneous flags. +/* Check if all requested cpu flags are making their way to the guest + * + * Returns 0 if all flags are supported by the host, non-zero otherwise. * * This function may be called only if KVM is enabled. */ -static int kvm_check_features_against_host(x86_def_t *guest_def) +static int kvm_check_features_against_host(X86CPU *cpu) { + CPUX86State *env = &cpu->env; x86_def_t host_def; uint32_t mask; int rv, i; struct model_features_t ft[] = { - {&guest_def->features, &host_def.features, - ~0, feature_name, 0x00000000}, - {&guest_def->ext_features, &host_def.ext_features, - ~CPUID_EXT_HYPERVISOR, ext_feature_name, 0x00000001}, - {&guest_def->ext2_features, &host_def.ext2_features, - ~PPRO_FEATURES, ext2_feature_name, 0x80000000}, - {&guest_def->ext3_features, &host_def.ext3_features, - ~CPUID_EXT3_SVM, ext3_feature_name, 0x80000001}}; + {&env->cpuid_features, &host_def.features, + FEAT_1_EDX }, + {&env->cpuid_ext_features, &host_def.ext_features, + FEAT_1_ECX }, + {&env->cpuid_ext2_features, &host_def.ext2_features, + FEAT_8000_0001_EDX }, + {&env->cpuid_ext3_features, &host_def.ext3_features, + FEAT_8000_0001_ECX }, + {&env->cpuid_ext4_features, &host_def.ext4_features, + FEAT_C000_0001_EDX }, + {&env->cpuid_7_0_ebx_features, &host_def.cpuid_7_0_ebx_features, + FEAT_7_0_EBX }, + {&env->cpuid_svm_features, &host_def.svm_features, + FEAT_SVM }, + {&env->cpuid_kvm_features, &host_def.kvm_features, + FEAT_KVM }, + }; assert(kvm_enabled()); kvm_cpu_fill_host(&host_def); - for (rv = 0, i = 0; i < ARRAY_SIZE(ft); ++i) - for (mask = 1; mask; mask <<= 1) - if (ft[i].check_feat & mask && *ft[i].guest_feat & mask && + for (rv = 0, i = 0; i < ARRAY_SIZE(ft); ++i) { + FeatureWord w = ft[i].feat_word; + FeatureWordInfo *wi = &feature_word_info[w]; + for (mask = 1; mask; mask <<= 1) { + if (*ft[i].guest_feat & mask && !(*ft[i].host_feat & mask)) { - unavailable_host_feature(&ft[i], mask); - rv = 1; - } + unavailable_host_feature(wi, mask); + rv = 1; + } + } + } return rv; } @@ -1105,15 +1173,10 @@ static char *x86_cpuid_get_vendor(Object *obj, Error **errp) X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; char *value; - int i; value = (char *)g_malloc(CPUID_VENDOR_SZ + 1); - for (i = 0; i < 4; i++) { - value[i ] = env->cpuid_vendor1 >> (8 * i); - value[i + 4] = env->cpuid_vendor2 >> (8 * i); - value[i + 8] = env->cpuid_vendor3 >> (8 * i); - } - value[CPUID_VENDOR_SZ] = '\0'; + x86_cpu_vendor_words2str(value, env->cpuid_vendor1, env->cpuid_vendor2, + env->cpuid_vendor3); return value; } @@ -1138,7 +1201,6 @@ static void x86_cpuid_set_vendor(Object *obj, const char *value, env->cpuid_vendor2 |= ((uint8_t)value[i + 4]) << (8 * i); env->cpuid_vendor3 |= ((uint8_t)value[i + 8]) << (8 * i); } - env->cpuid_vendor_override = 1; } static char *x86_cpuid_get_model_id(Object *obj, Error **errp) @@ -1212,143 +1274,113 @@ static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, void *opaque, static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *name) { x86_def_t *def; + int i; - for (def = x86_defs; def; def = def->next) { - if (name && !strcmp(name, def->name)) { - break; + if (name == NULL) { + return -1; + } + if (kvm_enabled() && strcmp(name, "host") == 0) { + kvm_cpu_fill_host(x86_cpu_def); + return 0; + } + + for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) { + def = &builtin_x86_defs[i]; + if (strcmp(name, def->name) == 0) { + memcpy(x86_cpu_def, def, sizeof(*def)); + /* sysenter isn't supported in compatibility mode on AMD, + * syscall isn't supported in compatibility mode on Intel. + * Normally we advertise the actual CPU vendor, but you can + * override this using the 'vendor' property if you want to use + * KVM's sysenter/syscall emulation in compatibility mode and + * when doing cross vendor migration + */ + if (kvm_enabled()) { + uint32_t ebx = 0, ecx = 0, edx = 0; + host_cpuid(0, 0, NULL, &ebx, &ecx, &edx); + x86_cpu_vendor_words2str(x86_cpu_def->vendor, ebx, edx, ecx); + } + return 0; } } - if (kvm_enabled() && name && strcmp(name, "host") == 0) { - kvm_cpu_fill_host(x86_cpu_def); - } else if (!def) { - return -1; - } else { - memcpy(x86_cpu_def, def, sizeof(*def)); - } - return 0; + return -1; } /* Parse "+feature,-feature,feature=foo" CPU feature string */ -static int cpu_x86_parse_featurestr(x86_def_t *x86_cpu_def, char *features) +static void cpu_x86_parse_featurestr(X86CPU *cpu, char *features, Error **errp) { - unsigned int i; char *featurestr; /* Single 'key=value" string being parsed */ /* Features to be added */ - uint32_t plus_features = 0, plus_ext_features = 0; - uint32_t plus_ext2_features = 0, plus_ext3_features = 0; - uint32_t plus_kvm_features = kvm_default_features, plus_svm_features = 0; - uint32_t plus_7_0_ebx_features = 0; + FeatureWordArray plus_features = { 0 }; /* Features to be removed */ - uint32_t minus_features = 0, minus_ext_features = 0; - uint32_t minus_ext2_features = 0, minus_ext3_features = 0; - uint32_t minus_kvm_features = 0, minus_svm_features = 0; - uint32_t minus_7_0_ebx_features = 0; + FeatureWordArray minus_features = { 0 }; uint32_t numvalue; - - add_flagname_to_bitmaps("hypervisor", &plus_features, - &plus_ext_features, &plus_ext2_features, &plus_ext3_features, - &plus_kvm_features, &plus_svm_features, &plus_7_0_ebx_features); + CPUX86State *env = &cpu->env; featurestr = features ? strtok(features, ",") : NULL; while (featurestr) { char *val; if (featurestr[0] == '+') { - add_flagname_to_bitmaps(featurestr + 1, &plus_features, - &plus_ext_features, &plus_ext2_features, - &plus_ext3_features, &plus_kvm_features, - &plus_svm_features, &plus_7_0_ebx_features); + add_flagname_to_bitmaps(featurestr + 1, plus_features); } else if (featurestr[0] == '-') { - add_flagname_to_bitmaps(featurestr + 1, &minus_features, - &minus_ext_features, &minus_ext2_features, - &minus_ext3_features, &minus_kvm_features, - &minus_svm_features, &minus_7_0_ebx_features); + add_flagname_to_bitmaps(featurestr + 1, minus_features); } else if ((val = strchr(featurestr, '='))) { *val = 0; val++; if (!strcmp(featurestr, "family")) { - char *err; - numvalue = strtoul(val, &err, 0); - if (!*val || *err || numvalue > 0xff + 0xf) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; - } - x86_cpu_def->family = numvalue; + object_property_parse(OBJECT(cpu), val, featurestr, errp); } else if (!strcmp(featurestr, "model")) { - char *err; - numvalue = strtoul(val, &err, 0); - if (!*val || *err || numvalue > 0xff) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; - } - x86_cpu_def->model = numvalue; + object_property_parse(OBJECT(cpu), val, featurestr, errp); } else if (!strcmp(featurestr, "stepping")) { - char *err; - numvalue = strtoul(val, &err, 0); - if (!*val || *err || numvalue > 0xf) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; - } - x86_cpu_def->stepping = numvalue ; + object_property_parse(OBJECT(cpu), val, featurestr, errp); } else if (!strcmp(featurestr, "level")) { - char *err; - numvalue = strtoul(val, &err, 0); - if (!*val || *err) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; - } - x86_cpu_def->level = numvalue; + object_property_parse(OBJECT(cpu), val, featurestr, errp); } else if (!strcmp(featurestr, "xlevel")) { char *err; + char num[32]; + numvalue = strtoul(val, &err, 0); if (!*val || *err) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; + error_setg(errp, "bad numerical value %s", val); + goto out; } if (numvalue < 0x80000000) { + fprintf(stderr, "xlevel value shall always be >= 0x80000000" + ", fixup will be removed in future versions\n"); numvalue += 0x80000000; } - x86_cpu_def->xlevel = numvalue; + snprintf(num, sizeof(num), "%" PRIu32, numvalue); + object_property_parse(OBJECT(cpu), num, featurestr, errp); } else if (!strcmp(featurestr, "vendor")) { - if (strlen(val) != 12) { - fprintf(stderr, "vendor string must be 12 chars long\n"); - goto error; - } - x86_cpu_def->vendor1 = 0; - x86_cpu_def->vendor2 = 0; - x86_cpu_def->vendor3 = 0; - for(i = 0; i < 4; i++) { - x86_cpu_def->vendor1 |= ((uint8_t)val[i ]) << (8 * i); - x86_cpu_def->vendor2 |= ((uint8_t)val[i + 4]) << (8 * i); - x86_cpu_def->vendor3 |= ((uint8_t)val[i + 8]) << (8 * i); - } - x86_cpu_def->vendor_override = 1; + object_property_parse(OBJECT(cpu), val, featurestr, errp); } else if (!strcmp(featurestr, "model_id")) { - pstrcpy(x86_cpu_def->model_id, sizeof(x86_cpu_def->model_id), - val); + object_property_parse(OBJECT(cpu), val, "model-id", errp); } else if (!strcmp(featurestr, "tsc_freq")) { int64_t tsc_freq; char *err; + char num[32]; tsc_freq = strtosz_suffix_unit(val, &err, STRTOSZ_DEFSUFFIX_B, 1000); if (tsc_freq < 0 || *err) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; + error_setg(errp, "bad numerical value %s", val); + goto out; } - x86_cpu_def->tsc_khz = tsc_freq / 1000; + snprintf(num, sizeof(num), "%" PRId64, tsc_freq); + object_property_parse(OBJECT(cpu), num, "tsc-frequency", errp); } else if (!strcmp(featurestr, "hv_spinlocks")) { char *err; numvalue = strtoul(val, &err, 0); if (!*val || *err) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; + error_setg(errp, "bad numerical value %s", val); + goto out; } hyperv_set_spinlock_retries(numvalue); } else { - fprintf(stderr, "unrecognized feature %s\n", featurestr); - goto error; + error_setg(errp, "unrecognized feature %s", featurestr); + goto out; } } else if (!strcmp(featurestr, "check")) { check_cpuid = 1; @@ -1359,33 +1391,34 @@ static int cpu_x86_parse_featurestr(x86_def_t *x86_cpu_def, char *features) } else if (!strcmp(featurestr, "hv_vapic")) { hyperv_enable_vapic_recommended(true); } else { - fprintf(stderr, "feature string `%s' not in format (+feature|-feature|feature=xyz)\n", featurestr); - goto error; + error_setg(errp, "feature string `%s' not in format (+feature|" + "-feature|feature=xyz)", featurestr); + goto out; + } + if (error_is_set(errp)) { + goto out; } featurestr = strtok(NULL, ","); } - x86_cpu_def->features |= plus_features; - x86_cpu_def->ext_features |= plus_ext_features; - x86_cpu_def->ext2_features |= plus_ext2_features; - x86_cpu_def->ext3_features |= plus_ext3_features; - x86_cpu_def->kvm_features |= plus_kvm_features; - x86_cpu_def->svm_features |= plus_svm_features; - x86_cpu_def->cpuid_7_0_ebx_features |= plus_7_0_ebx_features; - x86_cpu_def->features &= ~minus_features; - x86_cpu_def->ext_features &= ~minus_ext_features; - x86_cpu_def->ext2_features &= ~minus_ext2_features; - x86_cpu_def->ext3_features &= ~minus_ext3_features; - x86_cpu_def->kvm_features &= ~minus_kvm_features; - x86_cpu_def->svm_features &= ~minus_svm_features; - x86_cpu_def->cpuid_7_0_ebx_features &= ~minus_7_0_ebx_features; - if (check_cpuid && kvm_enabled()) { - if (kvm_check_features_against_host(x86_cpu_def) && enforce_cpuid) - goto error; - } - return 0; + env->cpuid_features |= plus_features[FEAT_1_EDX]; + env->cpuid_ext_features |= plus_features[FEAT_1_ECX]; + env->cpuid_ext2_features |= plus_features[FEAT_8000_0001_EDX]; + env->cpuid_ext3_features |= plus_features[FEAT_8000_0001_ECX]; + env->cpuid_ext4_features |= plus_features[FEAT_C000_0001_EDX]; + env->cpuid_kvm_features |= plus_features[FEAT_KVM]; + env->cpuid_svm_features |= plus_features[FEAT_SVM]; + env->cpuid_7_0_ebx_features |= plus_features[FEAT_7_0_EBX]; + env->cpuid_features &= ~minus_features[FEAT_1_EDX]; + env->cpuid_ext_features &= ~minus_features[FEAT_1_ECX]; + env->cpuid_ext2_features &= ~minus_features[FEAT_8000_0001_EDX]; + env->cpuid_ext3_features &= ~minus_features[FEAT_8000_0001_ECX]; + env->cpuid_ext4_features &= ~minus_features[FEAT_C000_0001_EDX]; + env->cpuid_kvm_features &= ~minus_features[FEAT_KVM]; + env->cpuid_svm_features &= ~minus_features[FEAT_SVM]; + env->cpuid_7_0_ebx_features &= ~minus_features[FEAT_7_0_EBX]; -error: - return -1; +out: + return; } /* generate a composite string into buf of all cpuid names in featureset @@ -1423,8 +1456,10 @@ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf) { x86_def_t *def; char buf[256]; + int i; - for (def = x86_defs; def; def = def->next) { + for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) { + def = &builtin_x86_defs[i]; snprintf(buf, sizeof(buf), "%s", def->name); (*cpu_fprintf)(f, "x86 %16s %-48s\n", buf, def->model_id); } @@ -1446,11 +1481,13 @@ CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) { CpuDefinitionInfoList *cpu_list = NULL; x86_def_t *def; + int i; - for (def = x86_defs; def; def = def->next) { + for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) { CpuDefinitionInfoList *entry; CpuDefinitionInfo *info; + def = &builtin_x86_defs[i]; info = g_malloc0(sizeof(*info)); info->name = g_strdup(def->name); @@ -1489,94 +1526,88 @@ static void filter_features_for_kvm(X86CPU *cpu) } #endif -int cpu_x86_register(X86CPU *cpu, const char *cpu_model) +static void cpu_x86_register(X86CPU *cpu, const char *name, Error **errp) { CPUX86State *env = &cpu->env; x86_def_t def1, *def = &def1; - Error *error = NULL; - char *name, *features; - gchar **model_pieces; memset(def, 0, sizeof(*def)); - model_pieces = g_strsplit(cpu_model, ",", 2); - if (!model_pieces[0]) { - goto error; - } - name = model_pieces[0]; - features = model_pieces[1]; - if (cpu_x86_find_by_name(def, name) < 0) { - goto error; + error_setg(errp, "Unable to find CPU definition: %s", name); + return; } - if (cpu_x86_parse_featurestr(def, features) < 0) { - goto error; + if (kvm_enabled()) { + def->kvm_features |= kvm_default_features; } - if (def->vendor1) { - env->cpuid_vendor1 = def->vendor1; - env->cpuid_vendor2 = def->vendor2; - env->cpuid_vendor3 = def->vendor3; - } else { - env->cpuid_vendor1 = CPUID_VENDOR_INTEL_1; - env->cpuid_vendor2 = CPUID_VENDOR_INTEL_2; - env->cpuid_vendor3 = CPUID_VENDOR_INTEL_3; - } - env->cpuid_vendor_override = def->vendor_override; - object_property_set_int(OBJECT(cpu), def->level, "level", &error); - object_property_set_int(OBJECT(cpu), def->family, "family", &error); - object_property_set_int(OBJECT(cpu), def->model, "model", &error); - object_property_set_int(OBJECT(cpu), def->stepping, "stepping", &error); + def->ext_features |= CPUID_EXT_HYPERVISOR; + + object_property_set_str(OBJECT(cpu), def->vendor, "vendor", errp); + object_property_set_int(OBJECT(cpu), def->level, "level", errp); + object_property_set_int(OBJECT(cpu), def->family, "family", errp); + object_property_set_int(OBJECT(cpu), def->model, "model", errp); + object_property_set_int(OBJECT(cpu), def->stepping, "stepping", errp); env->cpuid_features = def->features; env->cpuid_ext_features = def->ext_features; env->cpuid_ext2_features = def->ext2_features; env->cpuid_ext3_features = def->ext3_features; - object_property_set_int(OBJECT(cpu), def->xlevel, "xlevel", &error); + object_property_set_int(OBJECT(cpu), def->xlevel, "xlevel", errp); env->cpuid_kvm_features = def->kvm_features; env->cpuid_svm_features = def->svm_features; env->cpuid_ext4_features = def->ext4_features; env->cpuid_7_0_ebx_features = def->cpuid_7_0_ebx_features; env->cpuid_xlevel2 = def->xlevel2; - object_property_set_int(OBJECT(cpu), (int64_t)def->tsc_khz * 1000, - "tsc-frequency", &error); - /* On AMD CPUs, some CPUID[8000_0001].EDX bits must match the bits on - * CPUID[1].EDX. - */ - if (env->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && - env->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && - env->cpuid_vendor3 == CPUID_VENDOR_AMD_3) { - env->cpuid_ext2_features &= ~CPUID_EXT2_AMD_ALIASES; - env->cpuid_ext2_features |= (def->features & CPUID_EXT2_AMD_ALIASES); + object_property_set_str(OBJECT(cpu), def->model_id, "model-id", errp); +} + +X86CPU *cpu_x86_init(const char *cpu_model) +{ + X86CPU *cpu = NULL; + CPUX86State *env; + gchar **model_pieces; + char *name, *features; + Error *error = NULL; + + model_pieces = g_strsplit(cpu_model, ",", 2); + if (!model_pieces[0]) { + error_setg(&error, "Invalid/empty CPU model name"); + goto out; + } + name = model_pieces[0]; + features = model_pieces[1]; + + cpu = X86_CPU(object_new(TYPE_X86_CPU)); + env = &cpu->env; + env->cpu_model_str = cpu_model; + + cpu_x86_register(cpu, name, &error); + if (error) { + goto out; } - if (!kvm_enabled()) { - env->cpuid_features &= TCG_FEATURES; - env->cpuid_ext_features &= TCG_EXT_FEATURES; - env->cpuid_ext2_features &= (TCG_EXT2_FEATURES -#ifdef TARGET_X86_64 - | CPUID_EXT2_SYSCALL | CPUID_EXT2_LM -#endif - ); - env->cpuid_ext3_features &= TCG_EXT3_FEATURES; - env->cpuid_svm_features &= TCG_SVM_FEATURES; - } else { -#ifdef CONFIG_KVM - filter_features_for_kvm(cpu); -#endif + cpu_x86_parse_featurestr(cpu, features, &error); + if (error) { + goto out; } - object_property_set_str(OBJECT(cpu), def->model_id, "model-id", &error); + + object_property_set_bool(OBJECT(cpu), true, "realized", &error); + if (error) { + goto out; + } + +out: + g_strfreev(model_pieces); if (error) { fprintf(stderr, "%s\n", error_get_pretty(error)); error_free(error); - goto error; + if (cpu != NULL) { + object_unref(OBJECT(cpu)); + cpu = NULL; + } } - - g_strfreev(model_pieces); - return 0; -error: - g_strfreev(model_pieces); - return -1; + return cpu; } #if !defined(CONFIG_USER_ONLY) @@ -1597,7 +1628,6 @@ void x86_cpudef_setup(void) for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); ++i) { x86_def_t *def = &builtin_x86_defs[i]; - def->next = x86_defs; /* Look for specific "cpudef" models that */ /* have the QEMU version in .model_id */ @@ -1610,8 +1640,6 @@ void x86_cpudef_setup(void) break; } } - - x86_defs = def; } } @@ -1621,22 +1649,15 @@ static void get_cpuid_vendor(CPUX86State *env, uint32_t *ebx, *ebx = env->cpuid_vendor1; *edx = env->cpuid_vendor2; *ecx = env->cpuid_vendor3; - - /* sysenter isn't supported on compatibility mode on AMD, syscall - * isn't supported in compatibility mode on Intel. - * Normally we advertise the actual cpu vendor, but you can override - * this if you want to use KVM's sysenter/syscall emulation - * in compatibility mode and when doing cross vendor migration - */ - if (kvm_enabled() && ! env->cpuid_vendor_override) { - host_cpuid(0, 0, NULL, ebx, ecx, edx); - } } void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { + X86CPU *cpu = x86_env_get_cpu(env); + CPUState *cs = CPU(cpu); + /* test if maximum index reached */ if (index & 0x80000000) { if (index > env->cpuid_xlevel) { @@ -1648,7 +1669,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, index = env->cpuid_xlevel; } } else { - index = env->cpuid_xlevel; + /* Intel documentation states that invalid EAX input will + * return the same information as EAX=cpuid_level + * (Intel SDM Vol. 2A - Instruction Set Reference - CPUID) + */ + index = env->cpuid_level; } } } else { @@ -1666,8 +1691,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ebx = (env->cpuid_apic_id << 24) | 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ *ecx = env->cpuid_ext_features; *edx = env->cpuid_features; - if (env->nr_cores * env->nr_threads > 1) { - *ebx |= (env->nr_cores * env->nr_threads) << 16; + if (cs->nr_cores * cs->nr_threads > 1) { + *ebx |= (cs->nr_cores * cs->nr_threads) << 16; *edx |= 1 << 28; /* HTT bit */ } break; @@ -1680,8 +1705,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 4: /* cache info: needed for Core compatibility */ - if (env->nr_cores > 1) { - *eax = (env->nr_cores - 1) << 26; + if (cs->nr_cores > 1) { + *eax = (cs->nr_cores - 1) << 26; } else { *eax = 0; } @@ -1700,8 +1725,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 2: /* L2 cache info */ *eax |= 0x0000143; - if (env->nr_threads > 1) { - *eax |= (env->nr_threads - 1) << 14; + if (cs->nr_threads > 1) { + *eax |= (cs->nr_threads - 1) << 14; } *ebx = 0x3c0003f; *ecx = 0x0000fff; @@ -1753,7 +1778,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, case 0xA: /* Architectural Performance Monitoring Leaf */ if (kvm_enabled()) { - KVMState *s = env->kvm_state; + KVMState *s = cs->kvm_state; *eax = kvm_arch_get_supported_cpuid(s, 0xA, count, R_EAX); *ebx = kvm_arch_get_supported_cpuid(s, 0xA, count, R_EBX); @@ -1776,7 +1801,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; } if (kvm_enabled()) { - KVMState *s = env->kvm_state; + KVMState *s = cs->kvm_state; *eax = kvm_arch_get_supported_cpuid(s, 0xd, count, R_EAX); *ebx = kvm_arch_get_supported_cpuid(s, 0xd, count, R_EBX); @@ -1805,7 +1830,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * discards multiple thread information if it is set. * So dont set it here for Intel to make Linux guests happy. */ - if (env->nr_cores * env->nr_threads > 1) { + if (cs->nr_cores * cs->nr_threads > 1) { uint32_t tebx, tecx, tedx; get_cpuid_vendor(env, &tebx, &tecx, &tedx); if (tebx != CPUID_VENDOR_INTEL_1 || @@ -1853,8 +1878,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ebx = 0; *ecx = 0; *edx = 0; - if (env->nr_cores * env->nr_threads > 1) { - *ecx |= (env->nr_cores * env->nr_threads) - 1; + if (cs->nr_cores * cs->nr_threads > 1) { + *ecx |= (cs->nr_cores * cs->nr_threads) - 1; } break; case 0x8000000A: @@ -1911,7 +1936,7 @@ static void x86_cpu_reset(CPUState *s) int i; if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); + qemu_log("CPU Reset (CPU %d)\n", s->cpu_index); log_cpu_state(env, CPU_DUMP_FPU | CPU_DUMP_CCOP); } @@ -1985,11 +2010,11 @@ static void x86_cpu_reset(CPUState *s) #if !defined(CONFIG_USER_ONLY) /* We hard-wire the BSP to the first CPU. */ - if (env->cpu_index == 0) { + if (s->cpu_index == 0) { apic_designate_bsp(env->apic_state); } - env->halted = !cpu_is_bsp(cpu); + s->halted = !cpu_is_bsp(cpu); #endif } @@ -2063,27 +2088,65 @@ static void x86_cpu_apic_init(X86CPU *cpu, Error **errp) /* NOTE: the APIC is directly connected to the CPU - it is not on the global memory bus. */ /* XXX: what if the base changes? */ - sysbus_mmio_map(sysbus_from_qdev(env->apic_state), 0, MSI_ADDR_BASE); + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(env->apic_state), 0, + MSI_ADDR_BASE, 0x1000); apic_mapped = 1; } } #endif -void x86_cpu_realize(Object *obj, Error **errp) +static void x86_cpu_realizefn(DeviceState *dev, Error **errp) { - X86CPU *cpu = X86_CPU(obj); + X86CPU *cpu = X86_CPU(dev); + X86CPUClass *xcc = X86_CPU_GET_CLASS(dev); CPUX86State *env = &cpu->env; +#ifndef CONFIG_USER_ONLY + Error *local_err = NULL; +#endif if (env->cpuid_7_0_ebx_features && env->cpuid_level < 7) { env->cpuid_level = 7; } + /* On AMD CPUs, some CPUID[8000_0001].EDX bits must match the bits on + * CPUID[1].EDX. + */ + if (env->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && + env->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && + env->cpuid_vendor3 == CPUID_VENDOR_AMD_3) { + env->cpuid_ext2_features &= ~CPUID_EXT2_AMD_ALIASES; + env->cpuid_ext2_features |= (env->cpuid_features + & CPUID_EXT2_AMD_ALIASES); + } + + if (!kvm_enabled()) { + env->cpuid_features &= TCG_FEATURES; + env->cpuid_ext_features &= TCG_EXT_FEATURES; + env->cpuid_ext2_features &= (TCG_EXT2_FEATURES +#ifdef TARGET_X86_64 + | CPUID_EXT2_SYSCALL | CPUID_EXT2_LM +#endif + ); + env->cpuid_ext3_features &= TCG_EXT3_FEATURES; + env->cpuid_svm_features &= TCG_SVM_FEATURES; + } else { +#ifdef CONFIG_KVM + filter_features_for_kvm(cpu); +#endif + if (check_cpuid && kvm_check_features_against_host(cpu) + && enforce_cpuid) { + error_setg(errp, "Host's CPU doesn't support requested features"); + return; + } + } + #ifndef CONFIG_USER_ONLY qemu_register_reset(x86_cpu_machine_reset_cb, cpu); if (cpu->env.cpuid_features & CPUID_APIC || smp_cpus > 1) { - x86_cpu_apic_init(cpu, errp); - if (error_is_set(errp)) { + x86_cpu_apic_init(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); return; } } @@ -2092,14 +2155,51 @@ void x86_cpu_realize(Object *obj, Error **errp) mce_init(cpu); qemu_init_vcpu(&cpu->env); cpu_reset(CPU(cpu)); + + xcc->parent_realize(dev, errp); +} + +/* Enables contiguous-apic-ID mode, for compatibility */ +static bool compat_apic_id_mode; + +void enable_compat_apic_id_mode(void) +{ + compat_apic_id_mode = true; +} + +/* Calculates initial APIC ID for a specific CPU index + * + * Currently we need to be able to calculate the APIC ID from the CPU index + * alone (without requiring a CPU object), as the QEMU<->Seabios interfaces have + * no concept of "CPU index", and the NUMA tables on fw_cfg need the APIC ID of + * all CPUs up to max_cpus. + */ +uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index) +{ + uint32_t correct_id; + static bool warned; + + correct_id = x86_apicid_from_cpu_idx(smp_cores, smp_threads, cpu_index); + if (compat_apic_id_mode) { + if (cpu_index != correct_id && !warned) { + error_report("APIC IDs set in compatibility mode, " + "CPU topology won't match the configuration"); + warned = true; + } + return cpu_index; + } else { + return correct_id; + } } static void x86_cpu_initfn(Object *obj) { + CPUState *cs = CPU(obj); X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; static int inited; + cs->env_ptr = env; cpu_exec_init(env); object_property_add(obj, "family", "int", @@ -2127,7 +2227,7 @@ static void x86_cpu_initfn(Object *obj) x86_cpuid_get_tsc_freq, x86_cpuid_set_tsc_freq, NULL, NULL, NULL); - env->cpuid_apic_id = env->cpu_index; + env->cpuid_apic_id = x86_cpu_apic_id_from_index(cs->cpu_index); /* init various static tables used in TCG mode */ if (tcg_enabled() && !inited) { @@ -2143,9 +2243,16 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) { X86CPUClass *xcc = X86_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + xcc->parent_realize = dc->realize; + dc->realize = x86_cpu_realizefn; xcc->parent_reset = cc->reset; cc->reset = x86_cpu_reset; + + cc->do_interrupt = x86_cpu_do_interrupt; + cpu_class_set_vmsd(cc, &vmstate_x86_cpu); } static const TypeInfo x86_cpu_type_info = { diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 0709780356..48f41ca3e3 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -231,6 +231,12 @@ #define DR7_TYPE_SHIFT 16 #define DR7_LEN_SHIFT 18 #define DR7_FIXED_1 0x00000400 +#define DR7_LOCAL_BP_MASK 0x55 +#define DR7_MAX_BP 4 +#define DR7_TYPE_BP_INST 0x0 +#define DR7_TYPE_DATA_WR 0x1 +#define DR7_TYPE_IO_RW 0x2 +#define DR7_TYPE_DATA_RW 0x3 #define PG_PRESENT_BIT 0 #define PG_RW_BIT 1 @@ -295,6 +301,7 @@ #define MSR_IA32_APICBASE_BSP (1<<8) #define MSR_IA32_APICBASE_ENABLE (1<<11) #define MSR_IA32_APICBASE_BASE (0xfffff<<12) +#define MSR_TSC_ADJUST 0x0000003b #define MSR_IA32_TSCDEADLINE 0x6e0 #define MSR_MTRRcap 0xfe @@ -360,6 +367,21 @@ #define MSR_VM_HSAVE_PA 0xc0010117 +/* CPUID feature words */ +typedef enum FeatureWord { + FEAT_1_EDX, /* CPUID[1].EDX */ + FEAT_1_ECX, /* CPUID[1].ECX */ + FEAT_7_0_EBX, /* CPUID[EAX=7,ECX=0].EBX */ + FEAT_8000_0001_EDX, /* CPUID[8000_0001].EDX */ + FEAT_8000_0001_ECX, /* CPUID[8000_0001].ECX */ + FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */ + FEAT_KVM, /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */ + FEAT_SVM, /* CPUID[8000_000A].EDX */ + FEATURE_WORDS, +} FeatureWord; + +typedef uint32_t FeatureWordArray[FEATURE_WORDS]; + /* cpuid_features bits */ #define CPUID_FP87 (1 << 0) #define CPUID_VME (1 << 1) @@ -515,14 +537,14 @@ #define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */ #define CPUID_VENDOR_INTEL_2 0x49656e69 /* "ineI" */ #define CPUID_VENDOR_INTEL_3 0x6c65746e /* "ntel" */ +#define CPUID_VENDOR_INTEL "GenuineIntel" #define CPUID_VENDOR_AMD_1 0x68747541 /* "Auth" */ #define CPUID_VENDOR_AMD_2 0x69746e65 /* "enti" */ #define CPUID_VENDOR_AMD_3 0x444d4163 /* "cAMD" */ +#define CPUID_VENDOR_AMD "AuthenticAMD" -#define CPUID_VENDOR_VIA_1 0x746e6543 /* "Cent" */ -#define CPUID_VENDOR_VIA_2 0x48727561 /* "aurH" */ -#define CPUID_VENDOR_VIA_3 0x736c7561 /* "auls" */ +#define CPUID_VENDOR_VIA "CentaurHauls" #define CPUID_MWAIT_IBE (1 << 1) /* Interrupts can exit capability */ #define CPUID_MWAIT_EMX (1 << 0) /* enumeration supported */ @@ -560,7 +582,7 @@ #define CPU_INTERRUPT_TPR CPU_INTERRUPT_TGT_INT_3 -enum { +typedef enum { CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ CC_OP_EFLAGS, /* all cc are explicitly computed, CC_SRC = flags */ @@ -614,8 +636,19 @@ enum { CC_OP_SARL, CC_OP_SARQ, + CC_OP_BMILGB, /* Z,S via CC_DST, C = SRC==0; O=0; P,A undefined */ + CC_OP_BMILGW, + CC_OP_BMILGL, + CC_OP_BMILGQ, + + CC_OP_ADCX, /* CC_DST = C, CC_SRC = rest. */ + CC_OP_ADOX, /* CC_DST = O, CC_SRC = rest. */ + CC_OP_ADCOX, /* CC_DST = C, CC_SRC2 = O, CC_SRC = rest. */ + + CC_OP_CLR, /* Z set, all other flags clear. */ + CC_OP_NB, -}; +} CCOp; typedef struct SegmentCache { uint32_t selector; @@ -703,8 +736,9 @@ typedef struct CPUX86State { stored elsewhere */ /* emulator internal eflags handling */ - target_ulong cc_src; target_ulong cc_dst; + target_ulong cc_src; + target_ulong cc_src2; uint32_t cc_op; int32_t df; /* D flag : 1 if D = 0, -1 if D = 1 */ uint32_t hflags; /* TB flags, see HF_xxx constants. These flags @@ -742,7 +776,6 @@ typedef struct CPUX86State { XMMReg xmm_regs[CPU_NB_REGS]; XMMReg xmm_t0; MMXReg mmx_t0; - target_ulong cc_tmp; /* temporary for rcr/rcl */ /* sysenter registers */ uint32_t sysenter_cs; @@ -774,6 +807,7 @@ typedef struct CPUX86State { uint64_t pv_eoi_en_msr; uint64_t tsc; + uint64_t tsc_adjust; uint64_t tsc_deadline; uint64_t mcg_status; @@ -812,7 +846,6 @@ typedef struct CPUX86State { uint32_t cpuid_ext2_features; uint32_t cpuid_ext3_features; uint32_t cpuid_apic_id; - int cpuid_vendor_override; /* Store the results of Centaur's CPUID instructions */ uint32_t cpuid_xlevel2; uint32_t cpuid_ext4_features; @@ -934,6 +967,7 @@ static inline void cpu_x86_load_seg_cache(CPUX86State *env, static inline void cpu_x86_load_seg_cache_sipi(X86CPU *cpu, int sipi_vector) { + CPUState *cs = CPU(cpu); CPUX86State *env = &cpu->env; env->eip = 0; @@ -941,7 +975,7 @@ static inline void cpu_x86_load_seg_cache_sipi(X86CPU *cpu, sipi_vector << 12, env->segs[R_CS].limit, env->segs[R_CS].flags); - env->halted = 0; + cs->halted = 0; } int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, @@ -980,7 +1014,6 @@ int cpu_x86_signal_handler(int host_signum, void *pinfo, void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); -int cpu_x86_register(X86CPU *cpu, const char *cpu_model); void cpu_clear_apic_feature(CPUX86State *env); void host_cpuid(uint32_t function, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); @@ -989,11 +1022,22 @@ void host_cpuid(uint32_t function, uint32_t count, int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, int is_write, int mmu_idx); #define cpu_handle_mmu_fault cpu_x86_handle_mmu_fault -void cpu_x86_set_a20(CPUX86State *env, int a20_state); +void x86_cpu_set_a20(X86CPU *cpu, int a20_state); -static inline int hw_breakpoint_enabled(unsigned long dr7, int index) +static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index) { - return (dr7 >> (index * 2)) & 3; + return (dr7 >> (index * 2)) & 1; +} + +static inline bool hw_global_breakpoint_enabled(unsigned long dr7, int index) +{ + return (dr7 >> (index * 2)) & 2; + +} +static inline bool hw_breakpoint_enabled(unsigned long dr7, int index) +{ + return hw_global_breakpoint_enabled(dr7, index) || + hw_local_breakpoint_enabled(dr7, index); } static inline int hw_breakpoint_type(unsigned long dr7, int index) @@ -1009,7 +1053,7 @@ static inline int hw_breakpoint_len(unsigned long dr7, int index) void hw_breakpoint_insert(CPUX86State *env, int index); void hw_breakpoint_remove(CPUX86State *env, int index); -int check_hw_breakpoints(CPUX86State *env, int force_dr6_update); +bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update); void breakpoint_handler(CPUX86State *env); /* will be suppressed */ @@ -1049,8 +1093,6 @@ static inline CPUX86State *cpu_init(const char *cpu_model) #define cpu_list x86_cpu_list #define cpudef_setup x86_cpudef_setup -#define CPU_SAVE_VERSION 12 - /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel #define MMU_MODE1_SUFFIX _user @@ -1085,9 +1127,10 @@ static inline int cpu_mmu_index (CPUX86State *env) #define EIP (env->eip) #define DF (env->df) -#define CC_SRC (env->cc_src) -#define CC_DST (env->cc_dst) -#define CC_OP (env->cc_op) +#define CC_DST (env->cc_dst) +#define CC_SRC (env->cc_src) +#define CC_SRC2 (env->cc_src2) +#define CC_OP (env->cc_op) /* n must be a constant to be efficient */ static inline target_long lshift(target_long x, int n) @@ -1124,17 +1167,18 @@ static inline void cpu_clone_regs(CPUX86State *env, target_ulong newsp) #include "hw/apic.h" #endif -static inline bool cpu_has_work(CPUState *cpu) +static inline bool cpu_has_work(CPUState *cs) { - CPUX86State *env = &X86_CPU(cpu)->env; + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; - return ((env->interrupt_request & (CPU_INTERRUPT_HARD | - CPU_INTERRUPT_POLL)) && + return ((cs->interrupt_request & (CPU_INTERRUPT_HARD | + CPU_INTERRUPT_POLL)) && (env->eflags & IF_MASK)) || - (env->interrupt_request & (CPU_INTERRUPT_NMI | - CPU_INTERRUPT_INIT | - CPU_INTERRUPT_SIPI | - CPU_INTERRUPT_MCE)); + (cs->interrupt_request & (CPU_INTERRUPT_NMI | + CPU_INTERRUPT_INIT | + CPU_INTERRUPT_SIPI | + CPU_INTERRUPT_MCE)); } #include "exec/exec-all.h" @@ -1208,14 +1252,19 @@ void cpu_svm_check_intercept_param(CPUX86State *env1, uint32_t type, uint64_t param); void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1); -/* op_helper.c */ -void do_interrupt(CPUX86State *env); +/* seg_helper.c */ void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw); void do_smm_enter(CPUX86State *env1); void cpu_report_tpr_access(CPUX86State *env, TPRAccess access); -void enable_kvm_pv_eoi(void); +void disable_kvm_pv_eoi(void); + +/* Return name of 32-bit register, from a R_* constant */ +const char *get_register_name_32(unsigned int reg); + +uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index); +void enable_compat_apic_id_mode(void); #endif /* CPU_I386_H */ diff --git a/target-i386/helper.c b/target-i386/helper.c index dca1360962..9449a0c49d 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -55,7 +55,7 @@ int cpu_x86_support_mca_broadcast(CPUX86State *env) /***********************************************************/ /* x86 debug */ -static const char *cc_op_str[] = { +static const char *cc_op_str[CC_OP_NB] = { "DYNAMIC", "EFLAGS", @@ -108,6 +108,17 @@ static const char *cc_op_str[] = { "SARW", "SARL", "SARQ", + + "BMILGB", + "BMILGW", + "BMILGL", + "BMILGQ", + + "ADCX", + "ADOX", + "ADCOX", + + "CLR", }; static void @@ -171,6 +182,7 @@ done: void cpu_dump_state(CPUX86State *env, FILE *f, fprintf_function cpu_fprintf, int flags) { + CPUState *cs = CPU(x86_env_get_cpu(env)); int eflags, i, nb; char cc_op_name[32]; static const char *seg_name[6] = { "ES", "CS", "SS", "DS", "FS", "GS" }; @@ -214,7 +226,7 @@ void cpu_dump_state(CPUX86State *env, FILE *f, fprintf_function cpu_fprintf, (env->hflags >> HF_INHIBIT_IRQ_SHIFT) & 1, (env->a20_mask >> 20) & 1, (env->hflags >> HF_SMM_SHIFT) & 1, - env->halted); + cs->halted); } else #endif { @@ -241,7 +253,7 @@ void cpu_dump_state(CPUX86State *env, FILE *f, fprintf_function cpu_fprintf, (env->hflags >> HF_INHIBIT_IRQ_SHIFT) & 1, (env->a20_mask >> 20) & 1, (env->hflags >> HF_SMM_SHIFT) & 1, - env->halted); + cs->halted); } for(i = 0; i < 6; i++) { @@ -366,8 +378,10 @@ void cpu_dump_state(CPUX86State *env, FILE *f, fprintf_function cpu_fprintf, /* x86 mmu */ /* XXX: add PGE support */ -void cpu_x86_set_a20(CPUX86State *env, int a20_state) +void x86_cpu_set_a20(X86CPU *cpu, int a20_state) { + CPUX86State *env = &cpu->env; + a20_state = (a20_state != 0); if (a20_state != ((env->a20_mask >> 20) & 1)) { #if defined(DEBUG_MMU) @@ -375,7 +389,7 @@ void cpu_x86_set_a20(CPUX86State *env, int a20_state) #endif /* if the cpu is currently executing code, we must unlink it and all the potentially executing TB */ - cpu_interrupt(env, CPU_INTERRUPT_EXITTB); + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_EXITTB); /* when a20 is changed, all the MMU mappings are invalid, so we must flush everything */ @@ -966,30 +980,35 @@ hwaddr cpu_get_phys_page_debug(CPUX86State *env, target_ulong addr) void hw_breakpoint_insert(CPUX86State *env, int index) { - int type, err = 0; + int type = 0, err = 0; switch (hw_breakpoint_type(env->dr[7], index)) { - case 0: - if (hw_breakpoint_enabled(env->dr[7], index)) + case DR7_TYPE_BP_INST: + if (hw_breakpoint_enabled(env->dr[7], index)) { err = cpu_breakpoint_insert(env, env->dr[index], BP_CPU, &env->cpu_breakpoint[index]); + } break; - case 1: + case DR7_TYPE_DATA_WR: type = BP_CPU | BP_MEM_WRITE; - goto insert_wp; - case 2: - /* No support for I/O watchpoints yet */ break; - case 3: + case DR7_TYPE_IO_RW: + /* No support for I/O watchpoints yet */ + break; + case DR7_TYPE_DATA_RW: type = BP_CPU | BP_MEM_ACCESS; - insert_wp: + break; + } + + if (type != 0) { err = cpu_watchpoint_insert(env, env->dr[index], hw_breakpoint_len(env->dr[7], index), type, &env->cpu_watchpoint[index]); - break; } - if (err) + + if (err) { env->cpu_breakpoint[index] = NULL; + } } void hw_breakpoint_remove(CPUX86State *env, int index) @@ -997,39 +1016,60 @@ void hw_breakpoint_remove(CPUX86State *env, int index) if (!env->cpu_breakpoint[index]) return; switch (hw_breakpoint_type(env->dr[7], index)) { - case 0: - if (hw_breakpoint_enabled(env->dr[7], index)) + case DR7_TYPE_BP_INST: + if (hw_breakpoint_enabled(env->dr[7], index)) { cpu_breakpoint_remove_by_ref(env, env->cpu_breakpoint[index]); + } break; - case 1: - case 3: + case DR7_TYPE_DATA_WR: + case DR7_TYPE_DATA_RW: cpu_watchpoint_remove_by_ref(env, env->cpu_watchpoint[index]); break; - case 2: + case DR7_TYPE_IO_RW: /* No support for I/O watchpoints yet */ break; } } -int check_hw_breakpoints(CPUX86State *env, int force_dr6_update) +bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update) { target_ulong dr6; - int reg, type; - int hit_enabled = 0; + int reg; + bool hit_enabled = false; dr6 = env->dr[6] & ~0xf; - for (reg = 0; reg < 4; reg++) { - type = hw_breakpoint_type(env->dr[7], reg); - if ((type == 0 && env->dr[reg] == env->eip) || - ((type & 1) && env->cpu_watchpoint[reg] && - (env->cpu_watchpoint[reg]->flags & BP_WATCHPOINT_HIT))) { + for (reg = 0; reg < DR7_MAX_BP; reg++) { + bool bp_match = false; + bool wp_match = false; + + switch (hw_breakpoint_type(env->dr[7], reg)) { + case DR7_TYPE_BP_INST: + if (env->dr[reg] == env->eip) { + bp_match = true; + } + break; + case DR7_TYPE_DATA_WR: + case DR7_TYPE_DATA_RW: + if (env->cpu_watchpoint[reg] && + env->cpu_watchpoint[reg]->flags & BP_WATCHPOINT_HIT) { + wp_match = true; + } + break; + case DR7_TYPE_IO_RW: + break; + } + if (bp_match || wp_match) { dr6 |= 1 << reg; - if (hw_breakpoint_enabled(env->dr[7], reg)) - hit_enabled = 1; + if (hw_breakpoint_enabled(env->dr[7], reg)) { + hit_enabled = true; + } } } - if (hit_enabled || force_dr6_update) + + if (hit_enabled || force_dr6_update) { env->dr[6] = dr6; + } + return hit_enabled; } @@ -1040,16 +1080,17 @@ void breakpoint_handler(CPUX86State *env) if (env->watchpoint_hit) { if (env->watchpoint_hit->flags & BP_CPU) { env->watchpoint_hit = NULL; - if (check_hw_breakpoints(env, 0)) + if (check_hw_breakpoints(env, false)) { raise_exception(env, EXCP01_DB); - else + } else { cpu_resume_from_signal(env, NULL); + } } } else { QTAILQ_FOREACH(bp, &env->breakpoints, entry) if (bp->pc == env->eip) { if (bp->flags & BP_CPU) { - check_hw_breakpoints(env, 1); + check_hw_breakpoints(env, true); raise_exception(env, EXCP01_DB); } break; @@ -1059,7 +1100,7 @@ void breakpoint_handler(CPUX86State *env) typedef struct MCEInjectionParams { Monitor *mon; - CPUX86State *env; + X86CPU *cpu; int bank; uint64_t status; uint64_t mcg_status; @@ -1071,7 +1112,8 @@ typedef struct MCEInjectionParams { static void do_inject_x86_mce(void *data) { MCEInjectionParams *params = data; - CPUX86State *cenv = params->env; + CPUX86State *cenv = ¶ms->cpu->env; + CPUState *cpu = CPU(params->cpu); uint64_t *banks = cenv->mce_banks + 4 * params->bank; cpu_synchronize_state(cenv); @@ -1094,7 +1136,7 @@ static void do_inject_x86_mce(void *data) if ((cenv->mcg_cap & MCG_CTL_P) && cenv->mcg_ctl != ~(uint64_t)0) { monitor_printf(params->mon, "CPU %d: Uncorrected error reporting disabled\n", - cenv->cpu_index); + cpu->cpu_index); return; } @@ -1106,7 +1148,7 @@ static void do_inject_x86_mce(void *data) monitor_printf(params->mon, "CPU %d: Uncorrected error reporting disabled for" " bank %d\n", - cenv->cpu_index, params->bank); + cpu->cpu_index, params->bank); return; } @@ -1115,7 +1157,7 @@ static void do_inject_x86_mce(void *data) monitor_printf(params->mon, "CPU %d: Previous MCE still in progress, raising" " triple fault\n", - cenv->cpu_index); + cpu->cpu_index); qemu_log_mask(CPU_LOG_RESET, "Triple fault\n"); qemu_system_reset_request(); return; @@ -1127,7 +1169,7 @@ static void do_inject_x86_mce(void *data) banks[3] = params->misc; cenv->mcg_status = params->mcg_status; banks[1] = params->status; - cpu_interrupt(cenv, CPU_INTERRUPT_MCE); + cpu_interrupt(cpu, CPU_INTERRUPT_MCE); } else if (!(banks[1] & MCI_STATUS_VAL) || !(banks[1] & MCI_STATUS_UC)) { if (banks[1] & MCI_STATUS_VAL) { @@ -1148,7 +1190,7 @@ void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank, CPUX86State *cenv = &cpu->env; MCEInjectionParams params = { .mon = mon, - .env = cenv, + .cpu = cpu, .bank = bank, .status = status, .mcg_status = mcg_status, @@ -1188,7 +1230,7 @@ void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank, if (cenv == env) { continue; } - params.env = env; + params.cpu = x86_env_get_cpu(env); run_on_cpu(CPU(cpu), do_inject_x86_mce, ¶ms); } } @@ -1199,7 +1241,7 @@ void cpu_report_tpr_access(CPUX86State *env, TPRAccess access) if (kvm_enabled()) { env->tpr_access_type = access; - cpu_interrupt(env, CPU_INTERRUPT_TPR); + cpu_interrupt(CPU(x86_env_get_cpu(env)), CPU_INTERRUPT_TPR); } else { cpu_restore_state(env, env->mem_io_pc); @@ -1237,39 +1279,16 @@ int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, return 1; } -X86CPU *cpu_x86_init(const char *cpu_model) -{ - X86CPU *cpu; - CPUX86State *env; - Error *error = NULL; - - cpu = X86_CPU(object_new(TYPE_X86_CPU)); - env = &cpu->env; - env->cpu_model_str = cpu_model; - - if (cpu_x86_register(cpu, cpu_model) < 0) { - object_delete(OBJECT(cpu)); - return NULL; - } - - x86_cpu_realize(OBJECT(cpu), &error); - if (error) { - error_free(error); - object_delete(OBJECT(cpu)); - return NULL; - } - return cpu; -} - #if !defined(CONFIG_USER_ONLY) void do_cpu_init(X86CPU *cpu) { + CPUState *cs = CPU(cpu); CPUX86State *env = &cpu->env; - int sipi = env->interrupt_request & CPU_INTERRUPT_SIPI; + int sipi = cs->interrupt_request & CPU_INTERRUPT_SIPI; uint64_t pat = env->pat; - cpu_reset(CPU(cpu)); - env->interrupt_request = sipi; + cpu_reset(cs); + cs->interrupt_request = sipi; env->pat = pat; apic_init_reset(env->apic_state); } diff --git a/target-i386/helper.h b/target-i386/helper.h index 9ed720d0ed..d6974dfd6b 100644 --- a/target-i386/helper.h +++ b/target-i386/helper.h @@ -1,7 +1,7 @@ #include "exec/def-helper.h" -DEF_HELPER_FLAGS_2(cc_compute_all, TCG_CALL_NO_SE, i32, env, int) -DEF_HELPER_FLAGS_2(cc_compute_c, TCG_CALL_NO_SE, i32, env, int) +DEF_HELPER_FLAGS_4(cc_compute_all, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) +DEF_HELPER_FLAGS_4(cc_compute_c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) DEF_HELPER_0(lock, void) DEF_HELPER_0(unlock, void) @@ -14,9 +14,6 @@ DEF_HELPER_2(idivw_AX, void, env, tl) DEF_HELPER_2(divl_EAX, void, env, tl) DEF_HELPER_2(idivl_EAX, void, env, tl) #ifdef TARGET_X86_64 -DEF_HELPER_2(mulq_EAX_T0, void, env, tl) -DEF_HELPER_2(imulq_EAX_T0, void, env, tl) -DEF_HELPER_3(imulq_T0_T1, tl, env, tl, tl) DEF_HELPER_2(divq_EAX, void, env, tl) DEF_HELPER_2(idivq_EAX, void, env, tl) #endif @@ -193,9 +190,11 @@ DEF_HELPER_3(fsave, void, env, tl, int) DEF_HELPER_3(frstor, void, env, tl, int) DEF_HELPER_3(fxsave, void, env, tl, int) DEF_HELPER_3(fxrstor, void, env, tl, int) -DEF_HELPER_1(bsf, tl, tl) -DEF_HELPER_1(bsr, tl, tl) -DEF_HELPER_2(lzcnt, tl, tl, int) + +DEF_HELPER_FLAGS_1(clz, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(ctz, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_2(pdep, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(pext, TCG_CALL_NO_RWG_SE, tl, tl, tl) /* MMX/SSE */ diff --git a/target-i386/int_helper.c b/target-i386/int_helper.c index 84b812dcca..74c7c36124 100644 --- a/target-i386/int_helper.c +++ b/target-i386/int_helper.c @@ -374,39 +374,6 @@ static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b) return 0; } -void helper_mulq_EAX_T0(CPUX86State *env, target_ulong t0) -{ - uint64_t r0, r1; - - mulu64(&r0, &r1, EAX, t0); - EAX = r0; - EDX = r1; - CC_DST = r0; - CC_SRC = r1; -} - -void helper_imulq_EAX_T0(CPUX86State *env, target_ulong t0) -{ - uint64_t r0, r1; - - muls64(&r0, &r1, EAX, t0); - EAX = r0; - EDX = r1; - CC_DST = r0; - CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63)); -} - -target_ulong helper_imulq_T0_T1(CPUX86State *env, target_ulong t0, - target_ulong t1) -{ - uint64_t r0, r1; - - muls64(&r0, &r1, t0, t1); - CC_DST = r0; - CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63)); - return r0; -} - void helper_divq_EAX(CPUX86State *env, target_ulong t0) { uint64_t r0, r1; @@ -440,45 +407,49 @@ void helper_idivq_EAX(CPUX86State *env, target_ulong t0) } #endif +#if TARGET_LONG_BITS == 32 +# define ctztl ctz32 +# define clztl clz32 +#else +# define ctztl ctz64 +# define clztl clz64 +#endif + /* bit operations */ -target_ulong helper_bsf(target_ulong t0) +target_ulong helper_ctz(target_ulong t0) { - int count; - target_ulong res; - - res = t0; - count = 0; - while ((res & 1) == 0) { - count++; - res >>= 1; - } - return count; + return ctztl(t0); } -target_ulong helper_lzcnt(target_ulong t0, int wordsize) +target_ulong helper_clz(target_ulong t0) { - int count; - target_ulong res, mask; - - if (wordsize > 0 && t0 == 0) { - return wordsize; - } - res = t0; - count = TARGET_LONG_BITS - 1; - mask = (target_ulong)1 << (TARGET_LONG_BITS - 1); - while ((res & mask) == 0) { - count--; - res <<= 1; - } - if (wordsize > 0) { - return wordsize - 1 - count; - } - return count; + return clztl(t0); } -target_ulong helper_bsr(target_ulong t0) +target_ulong helper_pdep(target_ulong src, target_ulong mask) { - return helper_lzcnt(t0, 0); + target_ulong dest = 0; + int i, o; + + for (i = 0; mask != 0; i++) { + o = ctztl(mask); + mask &= mask - 1; + dest |= ((src >> i) & 1) << o; + } + return dest; +} + +target_ulong helper_pext(target_ulong src, target_ulong mask) +{ + target_ulong dest = 0; + int i, o; + + for (o = 0; mask != 0; o++) { + i = ctztl(mask); + mask &= mask - 1; + dest |= ((src >> i) & 1) << o; + } + return dest; } #define SHIFT 0 diff --git a/target-i386/kvm.c b/target-i386/kvm.c index f63b1fbfda..df30fa6ed6 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -63,6 +63,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { static bool has_msr_star; static bool has_msr_hsave_pa; +static bool has_msr_tsc_adjust; static bool has_msr_tsc_deadline; static bool has_msr_async_pf_en; static bool has_msr_pv_eoi_en; @@ -307,16 +308,17 @@ static void hardware_memory_error(void) exit(1); } -int kvm_arch_on_sigbus_vcpu(CPUX86State *env, int code, void *addr) +int kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr) { - X86CPU *cpu = x86_env_get_cpu(env); + X86CPU *cpu = X86_CPU(c); + CPUX86State *env = &cpu->env; ram_addr_t ram_addr; hwaddr paddr; if ((env->mcg_cap & MCG_SER_P) && addr && (code == BUS_MCEERR_AR || code == BUS_MCEERR_AO)) { if (qemu_ram_addr_from_host(addr, &ram_addr) || - !kvm_physical_memory_addr_from_host(env->kvm_state, addr, &paddr)) { + !kvm_physical_memory_addr_from_host(c->kvm_state, addr, &paddr)) { fprintf(stderr, "Hardware memory error for memory used by " "QEMU itself instead of guest system!\n"); /* Hope we are lucky for AO MCE */ @@ -348,8 +350,8 @@ int kvm_arch_on_sigbus(int code, void *addr) /* Hope we are lucky for AO MCE */ if (qemu_ram_addr_from_host(addr, &ram_addr) || - !kvm_physical_memory_addr_from_host(first_cpu->kvm_state, addr, - &paddr)) { + !kvm_physical_memory_addr_from_host(CPU(first_cpu)->kvm_state, + addr, &paddr)) { fprintf(stderr, "Hardware memory error for memory used by " "QEMU itself instead of guest system!: %p\n", addr); return 0; @@ -368,8 +370,10 @@ int kvm_arch_on_sigbus(int code, void *addr) return 0; } -static int kvm_inject_mce_oldstyle(CPUX86State *env) +static int kvm_inject_mce_oldstyle(X86CPU *cpu) { + CPUX86State *env = &cpu->env; + if (!kvm_has_vcpu_events() && env->exception_injected == EXCP12_MCHK) { unsigned int bank, bank_num = env->mcg_cap & 0xff; struct kvm_x86_mce mce; @@ -393,7 +397,7 @@ static int kvm_inject_mce_oldstyle(CPUX86State *env) mce.addr = env->mce_banks[bank * 4 + 2]; mce.misc = env->mce_banks[bank * 4 + 3]; - return kvm_vcpu_ioctl(env, KVM_X86_SET_MCE, &mce); + return kvm_vcpu_ioctl(CPU(cpu), KVM_X86_SET_MCE, &mce); } return 0; } @@ -407,12 +411,22 @@ static void cpu_update_state(void *opaque, int running, RunState state) } } -int kvm_arch_init_vcpu(CPUX86State *env) +unsigned long kvm_arch_vcpu_id(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + return cpu->env.cpuid_apic_id; +} + +#define KVM_MAX_CPUID_ENTRIES 100 + +int kvm_arch_init_vcpu(CPUState *cs) { struct { struct kvm_cpuid2 cpuid; - struct kvm_cpuid_entry2 entries[100]; + struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES]; } QEMU_PACKED cpuid_data; + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; uint32_t limit, i, j, cpuid_i; uint32_t unused; struct kvm_cpuid_entry2 *c; @@ -496,6 +510,10 @@ int kvm_arch_init_vcpu(CPUX86State *env) cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); for (i = 0; i <= limit; i++) { + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "unsupported level value: 0x%x\n", limit); + abort(); + } c = &cpuid_data.entries[cpuid_i++]; switch (i) { @@ -510,6 +528,11 @@ int kvm_arch_init_vcpu(CPUX86State *env) times = c->eax & 0xff; for (j = 1; j < times; ++j) { + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "cpuid_data is full, no space for " + "cpuid(eax:2):eax & 0xf = 0x%x\n", times); + abort(); + } c = &cpuid_data.entries[cpuid_i++]; c->function = i; c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC; @@ -538,6 +561,11 @@ int kvm_arch_init_vcpu(CPUX86State *env) if (i == 0xd && c->eax == 0) { continue; } + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "cpuid_data is full, no space for " + "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); + abort(); + } c = &cpuid_data.entries[cpuid_i++]; } break; @@ -551,6 +579,10 @@ int kvm_arch_init_vcpu(CPUX86State *env) cpu_x86_cpuid(env, 0x80000000, 0, &limit, &unused, &unused, &unused); for (i = 0x80000000; i <= limit; i++) { + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "unsupported xlevel value: 0x%x\n", limit); + abort(); + } c = &cpuid_data.entries[cpuid_i++]; c->function = i; @@ -563,6 +595,10 @@ int kvm_arch_init_vcpu(CPUX86State *env) cpu_x86_cpuid(env, 0xC0000000, 0, &limit, &unused, &unused, &unused); for (i = 0xC0000000; i <= limit; i++) { + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "unsupported xlevel2 value: 0x%x\n", limit); + abort(); + } c = &cpuid_data.entries[cpuid_i++]; c->function = i; @@ -575,12 +611,12 @@ int kvm_arch_init_vcpu(CPUX86State *env) if (((env->cpuid_version >> 8)&0xF) >= 6 && (env->cpuid_features&(CPUID_MCE|CPUID_MCA)) == (CPUID_MCE|CPUID_MCA) - && kvm_check_extension(env->kvm_state, KVM_CAP_MCE) > 0) { + && kvm_check_extension(cs->kvm_state, KVM_CAP_MCE) > 0) { uint64_t mcg_cap; int banks; int ret; - ret = kvm_get_mce_cap_supported(env->kvm_state, &mcg_cap, &banks); + ret = kvm_get_mce_cap_supported(cs->kvm_state, &mcg_cap, &banks); if (ret < 0) { fprintf(stderr, "kvm_get_mce_cap_supported: %s", strerror(-ret)); return ret; @@ -591,7 +627,7 @@ int kvm_arch_init_vcpu(CPUX86State *env) } mcg_cap &= MCE_CAP_DEF; mcg_cap |= banks; - ret = kvm_vcpu_ioctl(env, KVM_X86_SETUP_MCE, &mcg_cap); + ret = kvm_vcpu_ioctl(cs, KVM_X86_SETUP_MCE, &mcg_cap); if (ret < 0) { fprintf(stderr, "KVM_X86_SETUP_MCE: %s", strerror(-ret)); return ret; @@ -603,14 +639,14 @@ int kvm_arch_init_vcpu(CPUX86State *env) qemu_add_vm_change_state_handler(cpu_update_state, env); cpuid_data.cpuid.padding = 0; - r = kvm_vcpu_ioctl(env, KVM_SET_CPUID2, &cpuid_data); + r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data); if (r) { return r; } - r = kvm_check_extension(env->kvm_state, KVM_CAP_TSC_CONTROL); + r = kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL); if (r && env->tsc_khz) { - r = kvm_vcpu_ioctl(env, KVM_SET_TSC_KHZ, env->tsc_khz); + r = kvm_vcpu_ioctl(cs, KVM_SET_TSC_KHZ, env->tsc_khz); if (r < 0) { fprintf(stderr, "KVM_SET_TSC_KHZ failed\n"); return r; @@ -624,9 +660,10 @@ int kvm_arch_init_vcpu(CPUX86State *env) return 0; } -void kvm_arch_reset_vcpu(CPUX86State *env) +void kvm_arch_reset_vcpu(CPUState *cs) { - X86CPU *cpu = x86_env_get_cpu(env); + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; env->exception_injected = -1; env->interrupt_injected = -1; @@ -677,6 +714,10 @@ static int kvm_get_supported_msrs(KVMState *s) has_msr_hsave_pa = true; continue; } + if (kvm_msr_list->indices[i] == MSR_TSC_ADJUST) { + has_msr_tsc_adjust = true; + continue; + } if (kvm_msr_list->indices[i] == MSR_IA32_TSCDEADLINE) { has_msr_tsc_deadline = true; continue; @@ -817,13 +858,14 @@ static void kvm_getput_reg(__u64 *kvm_reg, target_ulong *qemu_reg, int set) } } -static int kvm_getput_regs(CPUX86State *env, int set) +static int kvm_getput_regs(X86CPU *cpu, int set) { + CPUX86State *env = &cpu->env; struct kvm_regs regs; int ret = 0; if (!set) { - ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, ®s); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_REGS, ®s); if (ret < 0) { return ret; } @@ -852,14 +894,15 @@ static int kvm_getput_regs(CPUX86State *env, int set) kvm_getput_reg(®s.rip, &env->eip, set); if (set) { - ret = kvm_vcpu_ioctl(env, KVM_SET_REGS, ®s); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_REGS, ®s); } return ret; } -static int kvm_put_fpu(CPUX86State *env) +static int kvm_put_fpu(X86CPU *cpu) { + CPUX86State *env = &cpu->env; struct kvm_fpu fpu; int i; @@ -877,7 +920,7 @@ static int kvm_put_fpu(CPUX86State *env) memcpy(fpu.xmm, env->xmm_regs, sizeof env->xmm_regs); fpu.mxcsr = env->mxcsr; - return kvm_vcpu_ioctl(env, KVM_SET_FPU, &fpu); + return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_FPU, &fpu); } #define XSAVE_FCW_FSW 0 @@ -890,14 +933,15 @@ static int kvm_put_fpu(CPUX86State *env) #define XSAVE_XSTATE_BV 128 #define XSAVE_YMMH_SPACE 144 -static int kvm_put_xsave(CPUX86State *env) +static int kvm_put_xsave(X86CPU *cpu) { + CPUX86State *env = &cpu->env; struct kvm_xsave* xsave = env->kvm_xsave_buf; uint16_t cwd, swd, twd; int i, r; if (!kvm_has_xsave()) { - return kvm_put_fpu(env); + return kvm_put_fpu(cpu); } memset(xsave, 0, sizeof(struct kvm_xsave)); @@ -920,12 +964,13 @@ static int kvm_put_xsave(CPUX86State *env) *(uint64_t *)&xsave->region[XSAVE_XSTATE_BV] = env->xstate_bv; memcpy(&xsave->region[XSAVE_YMMH_SPACE], env->ymmh_regs, sizeof env->ymmh_regs); - r = kvm_vcpu_ioctl(env, KVM_SET_XSAVE, xsave); + r = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_XSAVE, xsave); return r; } -static int kvm_put_xcrs(CPUX86State *env) +static int kvm_put_xcrs(X86CPU *cpu) { + CPUX86State *env = &cpu->env; struct kvm_xcrs xcrs; if (!kvm_has_xcrs()) { @@ -936,11 +981,12 @@ static int kvm_put_xcrs(CPUX86State *env) xcrs.flags = 0; xcrs.xcrs[0].xcr = 0; xcrs.xcrs[0].value = env->xcr0; - return kvm_vcpu_ioctl(env, KVM_SET_XCRS, &xcrs); + return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_XCRS, &xcrs); } -static int kvm_put_sregs(CPUX86State *env) +static int kvm_put_sregs(X86CPU *cpu) { + CPUX86State *env = &cpu->env; struct kvm_sregs sregs; memset(sregs.interrupt_bitmap, 0, sizeof(sregs.interrupt_bitmap)); @@ -985,7 +1031,7 @@ static int kvm_put_sregs(CPUX86State *env) sregs.efer = env->efer; - return kvm_vcpu_ioctl(env, KVM_SET_SREGS, &sregs); + return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_SREGS, &sregs); } static void kvm_msr_entry_set(struct kvm_msr_entry *entry, @@ -995,8 +1041,9 @@ static void kvm_msr_entry_set(struct kvm_msr_entry *entry, entry->data = value; } -static int kvm_put_msrs(CPUX86State *env, int level) +static int kvm_put_msrs(X86CPU *cpu, int level) { + CPUX86State *env = &cpu->env; struct { struct kvm_msrs info; struct kvm_msr_entry entries[100]; @@ -1014,6 +1061,9 @@ static int kvm_put_msrs(CPUX86State *env, int level) if (has_msr_hsave_pa) { kvm_msr_entry_set(&msrs[n++], MSR_VM_HSAVE_PA, env->vm_hsave); } + if (has_msr_tsc_adjust) { + kvm_msr_entry_set(&msrs[n++], MSR_TSC_ADJUST, env->tsc_adjust); + } if (has_msr_tsc_deadline) { kvm_msr_entry_set(&msrs[n++], MSR_IA32_TSCDEADLINE, env->tsc_deadline); } @@ -1077,17 +1127,18 @@ static int kvm_put_msrs(CPUX86State *env, int level) msr_data.info.nmsrs = n; - return kvm_vcpu_ioctl(env, KVM_SET_MSRS, &msr_data); + return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, &msr_data); } -static int kvm_get_fpu(CPUX86State *env) +static int kvm_get_fpu(X86CPU *cpu) { + CPUX86State *env = &cpu->env; struct kvm_fpu fpu; int i, ret; - ret = kvm_vcpu_ioctl(env, KVM_GET_FPU, &fpu); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_FPU, &fpu); if (ret < 0) { return ret; } @@ -1108,17 +1159,18 @@ static int kvm_get_fpu(CPUX86State *env) return 0; } -static int kvm_get_xsave(CPUX86State *env) +static int kvm_get_xsave(X86CPU *cpu) { + CPUX86State *env = &cpu->env; struct kvm_xsave* xsave = env->kvm_xsave_buf; int ret, i; uint16_t cwd, swd, twd; if (!kvm_has_xsave()) { - return kvm_get_fpu(env); + return kvm_get_fpu(cpu); } - ret = kvm_vcpu_ioctl(env, KVM_GET_XSAVE, xsave); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_XSAVE, xsave); if (ret < 0) { return ret; } @@ -1146,8 +1198,9 @@ static int kvm_get_xsave(CPUX86State *env) return 0; } -static int kvm_get_xcrs(CPUX86State *env) +static int kvm_get_xcrs(X86CPU *cpu) { + CPUX86State *env = &cpu->env; int i, ret; struct kvm_xcrs xcrs; @@ -1155,7 +1208,7 @@ static int kvm_get_xcrs(CPUX86State *env) return 0; } - ret = kvm_vcpu_ioctl(env, KVM_GET_XCRS, &xcrs); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_XCRS, &xcrs); if (ret < 0) { return ret; } @@ -1170,13 +1223,14 @@ static int kvm_get_xcrs(CPUX86State *env) return 0; } -static int kvm_get_sregs(CPUX86State *env) +static int kvm_get_sregs(X86CPU *cpu) { + CPUX86State *env = &cpu->env; struct kvm_sregs sregs; uint32_t hflags; int bit, i, ret; - ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_SREGS, &sregs); if (ret < 0) { return ret; } @@ -1254,8 +1308,9 @@ static int kvm_get_sregs(CPUX86State *env) return 0; } -static int kvm_get_msrs(CPUX86State *env) +static int kvm_get_msrs(X86CPU *cpu) { + CPUX86State *env = &cpu->env; struct { struct kvm_msrs info; struct kvm_msr_entry entries[100]; @@ -1274,6 +1329,9 @@ static int kvm_get_msrs(CPUX86State *env) if (has_msr_hsave_pa) { msrs[n++].index = MSR_VM_HSAVE_PA; } + if (has_msr_tsc_adjust) { + msrs[n++].index = MSR_TSC_ADJUST; + } if (has_msr_tsc_deadline) { msrs[n++].index = MSR_IA32_TSCDEADLINE; } @@ -1312,7 +1370,7 @@ static int kvm_get_msrs(CPUX86State *env) } msr_data.info.nmsrs = n; - ret = kvm_vcpu_ioctl(env, KVM_GET_MSRS, &msr_data); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, &msr_data); if (ret < 0) { return ret; } @@ -1351,6 +1409,9 @@ static int kvm_get_msrs(CPUX86State *env) case MSR_IA32_TSC: env->tsc = msrs[i].data; break; + case MSR_TSC_ADJUST: + env->tsc_adjust = msrs[i].data; + break; case MSR_IA32_TSCDEADLINE: env->tsc_deadline = msrs[i].data; break; @@ -1390,38 +1451,40 @@ static int kvm_get_msrs(CPUX86State *env) return 0; } -static int kvm_put_mp_state(CPUX86State *env) +static int kvm_put_mp_state(X86CPU *cpu) { - struct kvm_mp_state mp_state = { .mp_state = env->mp_state }; + struct kvm_mp_state mp_state = { .mp_state = cpu->env.mp_state }; - return kvm_vcpu_ioctl(env, KVM_SET_MP_STATE, &mp_state); + return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state); } static int kvm_get_mp_state(X86CPU *cpu) { + CPUState *cs = CPU(cpu); CPUX86State *env = &cpu->env; struct kvm_mp_state mp_state; int ret; - ret = kvm_vcpu_ioctl(env, KVM_GET_MP_STATE, &mp_state); + ret = kvm_vcpu_ioctl(cs, KVM_GET_MP_STATE, &mp_state); if (ret < 0) { return ret; } env->mp_state = mp_state.mp_state; if (kvm_irqchip_in_kernel()) { - env->halted = (mp_state.mp_state == KVM_MP_STATE_HALTED); + cs->halted = (mp_state.mp_state == KVM_MP_STATE_HALTED); } return 0; } -static int kvm_get_apic(CPUX86State *env) +static int kvm_get_apic(X86CPU *cpu) { + CPUX86State *env = &cpu->env; DeviceState *apic = env->apic_state; struct kvm_lapic_state kapic; int ret; if (apic && kvm_irqchip_in_kernel()) { - ret = kvm_vcpu_ioctl(env, KVM_GET_LAPIC, &kapic); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_LAPIC, &kapic); if (ret < 0) { return ret; } @@ -1431,21 +1494,23 @@ static int kvm_get_apic(CPUX86State *env) return 0; } -static int kvm_put_apic(CPUX86State *env) +static int kvm_put_apic(X86CPU *cpu) { + CPUX86State *env = &cpu->env; DeviceState *apic = env->apic_state; struct kvm_lapic_state kapic; if (apic && kvm_irqchip_in_kernel()) { kvm_put_apic_state(apic, &kapic); - return kvm_vcpu_ioctl(env, KVM_SET_LAPIC, &kapic); + return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_LAPIC, &kapic); } return 0; } -static int kvm_put_vcpu_events(CPUX86State *env, int level) +static int kvm_put_vcpu_events(X86CPU *cpu, int level) { + CPUX86State *env = &cpu->env; struct kvm_vcpu_events events; if (!kvm_has_vcpu_events()) { @@ -1475,11 +1540,12 @@ static int kvm_put_vcpu_events(CPUX86State *env, int level) KVM_VCPUEVENT_VALID_NMI_PENDING | KVM_VCPUEVENT_VALID_SIPI_VECTOR; } - return kvm_vcpu_ioctl(env, KVM_SET_VCPU_EVENTS, &events); + return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_VCPU_EVENTS, &events); } -static int kvm_get_vcpu_events(CPUX86State *env) +static int kvm_get_vcpu_events(X86CPU *cpu) { + CPUX86State *env = &cpu->env; struct kvm_vcpu_events events; int ret; @@ -1487,7 +1553,7 @@ static int kvm_get_vcpu_events(CPUX86State *env) return 0; } - ret = kvm_vcpu_ioctl(env, KVM_GET_VCPU_EVENTS, &events); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_VCPU_EVENTS, &events); if (ret < 0) { return ret; } @@ -1513,8 +1579,9 @@ static int kvm_get_vcpu_events(CPUX86State *env) return 0; } -static int kvm_guest_debug_workarounds(CPUX86State *env) +static int kvm_guest_debug_workarounds(X86CPU *cpu) { + CPUX86State *env = &cpu->env; int ret = 0; unsigned long reinject_trap = 0; @@ -1542,8 +1609,9 @@ static int kvm_guest_debug_workarounds(CPUX86State *env) return ret; } -static int kvm_put_debugregs(CPUX86State *env) +static int kvm_put_debugregs(X86CPU *cpu) { + CPUX86State *env = &cpu->env; struct kvm_debugregs dbgregs; int i; @@ -1558,11 +1626,12 @@ static int kvm_put_debugregs(CPUX86State *env) dbgregs.dr7 = env->dr[7]; dbgregs.flags = 0; - return kvm_vcpu_ioctl(env, KVM_SET_DEBUGREGS, &dbgregs); + return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_DEBUGREGS, &dbgregs); } -static int kvm_get_debugregs(CPUX86State *env) +static int kvm_get_debugregs(X86CPU *cpu) { + CPUX86State *env = &cpu->env; struct kvm_debugregs dbgregs; int i, ret; @@ -1570,7 +1639,7 @@ static int kvm_get_debugregs(CPUX86State *env) return 0; } - ret = kvm_vcpu_ioctl(env, KVM_GET_DEBUGREGS, &dbgregs); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_DEBUGREGS, &dbgregs); if (ret < 0) { return ret; } @@ -1583,88 +1652,88 @@ static int kvm_get_debugregs(CPUX86State *env) return 0; } -int kvm_arch_put_registers(CPUX86State *env, int level) +int kvm_arch_put_registers(CPUState *cpu, int level) { - CPUState *cpu = ENV_GET_CPU(env); + X86CPU *x86_cpu = X86_CPU(cpu); int ret; assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); - ret = kvm_getput_regs(env, 1); + ret = kvm_getput_regs(x86_cpu, 1); if (ret < 0) { return ret; } - ret = kvm_put_xsave(env); + ret = kvm_put_xsave(x86_cpu); if (ret < 0) { return ret; } - ret = kvm_put_xcrs(env); + ret = kvm_put_xcrs(x86_cpu); if (ret < 0) { return ret; } - ret = kvm_put_sregs(env); + ret = kvm_put_sregs(x86_cpu); if (ret < 0) { return ret; } /* must be before kvm_put_msrs */ - ret = kvm_inject_mce_oldstyle(env); + ret = kvm_inject_mce_oldstyle(x86_cpu); if (ret < 0) { return ret; } - ret = kvm_put_msrs(env, level); + ret = kvm_put_msrs(x86_cpu, level); if (ret < 0) { return ret; } if (level >= KVM_PUT_RESET_STATE) { - ret = kvm_put_mp_state(env); + ret = kvm_put_mp_state(x86_cpu); if (ret < 0) { return ret; } - ret = kvm_put_apic(env); + ret = kvm_put_apic(x86_cpu); if (ret < 0) { return ret; } } - ret = kvm_put_vcpu_events(env, level); + ret = kvm_put_vcpu_events(x86_cpu, level); if (ret < 0) { return ret; } - ret = kvm_put_debugregs(env); + ret = kvm_put_debugregs(x86_cpu); if (ret < 0) { return ret; } /* must be last */ - ret = kvm_guest_debug_workarounds(env); + ret = kvm_guest_debug_workarounds(x86_cpu); if (ret < 0) { return ret; } return 0; } -int kvm_arch_get_registers(CPUX86State *env) +int kvm_arch_get_registers(CPUState *cs) { - X86CPU *cpu = x86_env_get_cpu(env); + X86CPU *cpu = X86_CPU(cs); int ret; - assert(cpu_is_stopped(CPU(cpu)) || qemu_cpu_is_self(CPU(cpu))); + assert(cpu_is_stopped(cs) || qemu_cpu_is_self(cs)); - ret = kvm_getput_regs(env, 0); + ret = kvm_getput_regs(cpu, 0); if (ret < 0) { return ret; } - ret = kvm_get_xsave(env); + ret = kvm_get_xsave(cpu); if (ret < 0) { return ret; } - ret = kvm_get_xcrs(env); + ret = kvm_get_xcrs(cpu); if (ret < 0) { return ret; } - ret = kvm_get_sregs(env); + ret = kvm_get_sregs(cpu); if (ret < 0) { return ret; } - ret = kvm_get_msrs(env); + ret = kvm_get_msrs(cpu); if (ret < 0) { return ret; } @@ -1672,30 +1741,32 @@ int kvm_arch_get_registers(CPUX86State *env) if (ret < 0) { return ret; } - ret = kvm_get_apic(env); + ret = kvm_get_apic(cpu); if (ret < 0) { return ret; } - ret = kvm_get_vcpu_events(env); + ret = kvm_get_vcpu_events(cpu); if (ret < 0) { return ret; } - ret = kvm_get_debugregs(env); + ret = kvm_get_debugregs(cpu); if (ret < 0) { return ret; } return 0; } -void kvm_arch_pre_run(CPUX86State *env, struct kvm_run *run) +void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) { + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; int ret; /* Inject NMI */ - if (env->interrupt_request & CPU_INTERRUPT_NMI) { - env->interrupt_request &= ~CPU_INTERRUPT_NMI; + if (cpu->interrupt_request & CPU_INTERRUPT_NMI) { + cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; DPRINTF("injected NMI\n"); - ret = kvm_vcpu_ioctl(env, KVM_NMI); + ret = kvm_vcpu_ioctl(cpu, KVM_NMI); if (ret < 0) { fprintf(stderr, "KVM: injection failed, NMI lost (%s)\n", strerror(-ret)); @@ -1705,25 +1776,25 @@ void kvm_arch_pre_run(CPUX86State *env, struct kvm_run *run) if (!kvm_irqchip_in_kernel()) { /* Force the VCPU out of its inner loop to process any INIT requests * or pending TPR access reports. */ - if (env->interrupt_request & + if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { - env->exit_request = 1; + cpu->exit_request = 1; } /* Try to inject an interrupt if the guest can accept it */ if (run->ready_for_interrupt_injection && - (env->interrupt_request & CPU_INTERRUPT_HARD) && + (cpu->interrupt_request & CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) { int irq; - env->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; irq = cpu_get_pic_interrupt(env); if (irq >= 0) { struct kvm_interrupt intr; intr.irq = irq; DPRINTF("injected interrupt %d\n", irq); - ret = kvm_vcpu_ioctl(env, KVM_INTERRUPT, &intr); + ret = kvm_vcpu_ioctl(cpu, KVM_INTERRUPT, &intr); if (ret < 0) { fprintf(stderr, "KVM: injection failed, interrupt lost (%s)\n", @@ -1736,7 +1807,7 @@ void kvm_arch_pre_run(CPUX86State *env, struct kvm_run *run) * interrupt, request an interrupt window exit. This will * cause a return to userspace as soon as the guest is ready to * receive interrupts. */ - if ((env->interrupt_request & CPU_INTERRUPT_HARD)) { + if ((cpu->interrupt_request & CPU_INTERRUPT_HARD)) { run->request_interrupt_window = 1; } else { run->request_interrupt_window = 0; @@ -1747,8 +1818,11 @@ void kvm_arch_pre_run(CPUX86State *env, struct kvm_run *run) } } -void kvm_arch_post_run(CPUX86State *env, struct kvm_run *run) +void kvm_arch_post_run(CPUState *cpu, struct kvm_run *run) { + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + if (run->if_flag) { env->eflags |= IF_MASK; } else { @@ -1758,28 +1832,29 @@ void kvm_arch_post_run(CPUX86State *env, struct kvm_run *run) cpu_set_apic_base(env->apic_state, run->apic_base); } -int kvm_arch_process_async_events(CPUX86State *env) +int kvm_arch_process_async_events(CPUState *cs) { - X86CPU *cpu = x86_env_get_cpu(env); + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; - if (env->interrupt_request & CPU_INTERRUPT_MCE) { + if (cs->interrupt_request & CPU_INTERRUPT_MCE) { /* We must not raise CPU_INTERRUPT_MCE if it's not supported. */ assert(env->mcg_cap); - env->interrupt_request &= ~CPU_INTERRUPT_MCE; + cs->interrupt_request &= ~CPU_INTERRUPT_MCE; kvm_cpu_synchronize_state(env); if (env->exception_injected == EXCP08_DBLE) { /* this means triple fault */ qemu_system_reset_request(); - env->exit_request = 1; + cs->exit_request = 1; return 0; } env->exception_injected = EXCP12_MCHK; env->has_error_code = 0; - env->halted = 0; + cs->halted = 0; if (kvm_irqchip_in_kernel() && env->mp_state == KVM_MP_STATE_HALTED) { env->mp_state = KVM_MP_STATE_RUNNABLE; } @@ -1789,50 +1864,53 @@ int kvm_arch_process_async_events(CPUX86State *env) return 0; } - if (env->interrupt_request & CPU_INTERRUPT_POLL) { - env->interrupt_request &= ~CPU_INTERRUPT_POLL; + if (cs->interrupt_request & CPU_INTERRUPT_POLL) { + cs->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(env->apic_state); } - if (((env->interrupt_request & CPU_INTERRUPT_HARD) && + if (((cs->interrupt_request & CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (env->interrupt_request & CPU_INTERRUPT_NMI)) { - env->halted = 0; + (cs->interrupt_request & CPU_INTERRUPT_NMI)) { + cs->halted = 0; } - if (env->interrupt_request & CPU_INTERRUPT_INIT) { + if (cs->interrupt_request & CPU_INTERRUPT_INIT) { kvm_cpu_synchronize_state(env); do_cpu_init(cpu); } - if (env->interrupt_request & CPU_INTERRUPT_SIPI) { + if (cs->interrupt_request & CPU_INTERRUPT_SIPI) { kvm_cpu_synchronize_state(env); do_cpu_sipi(cpu); } - if (env->interrupt_request & CPU_INTERRUPT_TPR) { - env->interrupt_request &= ~CPU_INTERRUPT_TPR; + if (cs->interrupt_request & CPU_INTERRUPT_TPR) { + cs->interrupt_request &= ~CPU_INTERRUPT_TPR; kvm_cpu_synchronize_state(env); apic_handle_tpr_access_report(env->apic_state, env->eip, env->tpr_access_type); } - return env->halted; + return cs->halted; } static int kvm_handle_halt(X86CPU *cpu) { + CPUState *cs = CPU(cpu); CPUX86State *env = &cpu->env; - if (!((env->interrupt_request & CPU_INTERRUPT_HARD) && + if (!((cs->interrupt_request & CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) && - !(env->interrupt_request & CPU_INTERRUPT_NMI)) { - env->halted = 1; + !(cs->interrupt_request & CPU_INTERRUPT_NMI)) { + cs->halted = 1; return EXCP_HLT; } return 0; } -static int kvm_handle_tpr_access(CPUX86State *env) +static int kvm_handle_tpr_access(X86CPU *cpu) { - struct kvm_run *run = env->kvm_run; + CPUX86State *env = &cpu->env; + CPUState *cs = CPU(cpu); + struct kvm_run *run = cs->kvm_run; apic_handle_tpr_access_report(env->apic_state, run->tpr_access.rip, run->tpr_access.is_write ? TPR_ACCESS_WRITE @@ -1840,8 +1918,9 @@ static int kvm_handle_tpr_access(CPUX86State *env) return 1; } -int kvm_arch_insert_sw_breakpoint(CPUX86State *env, struct kvm_sw_breakpoint *bp) +int kvm_arch_insert_sw_breakpoint(CPUState *cpu, struct kvm_sw_breakpoint *bp) { + CPUX86State *env = &X86_CPU(cpu)->env; static const uint8_t int3 = 0xcc; if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 0) || @@ -1851,8 +1930,9 @@ int kvm_arch_insert_sw_breakpoint(CPUX86State *env, struct kvm_sw_breakpoint *bp return 0; } -int kvm_arch_remove_sw_breakpoint(CPUX86State *env, struct kvm_sw_breakpoint *bp) +int kvm_arch_remove_sw_breakpoint(CPUState *cpu, struct kvm_sw_breakpoint *bp) { + CPUX86State *env = &X86_CPU(cpu)->env; uint8_t int3; if (cpu_memory_rw_debug(env, bp->pc, &int3, 1, 0) || int3 != 0xcc || @@ -1946,9 +2026,10 @@ void kvm_arch_remove_all_hw_breakpoints(void) static CPUWatchpoint hw_watchpoint; -static int kvm_handle_debug(CPUX86State *env, +static int kvm_handle_debug(X86CPU *cpu, struct kvm_debug_exit_arch *arch_info) { + CPUX86State *env = &cpu->env; int ret = 0; int n; @@ -1980,7 +2061,7 @@ static int kvm_handle_debug(CPUX86State *env, } } } - } else if (kvm_find_sw_breakpoint(env, arch_info->pc)) { + } else if (kvm_find_sw_breakpoint(CPU(cpu), arch_info->pc)) { ret = EXCP_DEBUG; } if (ret == 0) { @@ -1995,7 +2076,7 @@ static int kvm_handle_debug(CPUX86State *env, return ret; } -void kvm_arch_update_guest_debug(CPUX86State *env, struct kvm_guest_debug *dbg) +void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) { const uint8_t type_code[] = { [GDB_BREAKPOINT_HW] = 0x0, @@ -2007,7 +2088,7 @@ void kvm_arch_update_guest_debug(CPUX86State *env, struct kvm_guest_debug *dbg) }; int n; - if (kvm_sw_breakpoints_active(env)) { + if (kvm_sw_breakpoints_active(cpu)) { dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; } if (nb_hw_breakpoint > 0) { @@ -2032,9 +2113,9 @@ static bool host_supports_vmx(void) #define VMX_INVALID_GUEST_STATE 0x80000021 -int kvm_arch_handle_exit(CPUX86State *env, struct kvm_run *run) +int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { - X86CPU *cpu = x86_env_get_cpu(env); + X86CPU *cpu = X86_CPU(cs); uint64_t code; int ret; @@ -2047,7 +2128,7 @@ int kvm_arch_handle_exit(CPUX86State *env, struct kvm_run *run) ret = 0; break; case KVM_EXIT_TPR_ACCESS: - ret = kvm_handle_tpr_access(env); + ret = kvm_handle_tpr_access(cpu); break; case KVM_EXIT_FAIL_ENTRY: code = run->fail_entry.hardware_entry_failure_reason; @@ -2073,7 +2154,7 @@ int kvm_arch_handle_exit(CPUX86State *env, struct kvm_run *run) break; case KVM_EXIT_DEBUG: DPRINTF("kvm_exit_debug\n"); - ret = kvm_handle_debug(env, &run->debug.arch); + ret = kvm_handle_debug(cpu, &run->debug.arch); break; default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); @@ -2084,8 +2165,11 @@ int kvm_arch_handle_exit(CPUX86State *env, struct kvm_run *run) return ret; } -bool kvm_arch_stop_on_emulation_error(CPUX86State *env) +bool kvm_arch_stop_on_emulation_error(CPUState *cs) { + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + kvm_cpu_synchronize_state(env); return !(env->cr[0] & CR0_PE_MASK) || ((env->segs[R_CS].selector & 3) != 3); diff --git a/target-i386/machine.c b/target-i386/machine.c index 8c1fed1005..b80a5f4470 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -171,14 +171,16 @@ static const VMStateInfo vmstate_fpreg_1_no_mmx = { static bool fpregs_is_0(void *opaque, int version_id) { - CPUX86State *env = opaque; + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; return (env->fpregs_format_vmstate == 0); } static bool fpregs_is_1_mmx(void *opaque, int version_id) { - CPUX86State *env = opaque; + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; int guess_mmx; guess_mmx = ((env->fptag_vmstate == 0xff) && @@ -188,7 +190,8 @@ static bool fpregs_is_1_mmx(void *opaque, int version_id) static bool fpregs_is_1_no_mmx(void *opaque, int version_id) { - CPUX86State *env = opaque; + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; int guess_mmx; guess_mmx = ((env->fptag_vmstate == 0xff) && @@ -237,7 +240,8 @@ static const VMStateInfo vmstate_hack_uint64_as_uint32 = { static void cpu_pre_save(void *opaque) { - CPUX86State *env = opaque; + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; int i; /* FPU */ @@ -252,7 +256,8 @@ static void cpu_pre_save(void *opaque) static int cpu_post_load(void *opaque, int version_id) { - CPUX86State *env = opaque; + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; int i; /* XXX: restore FPU round state */ @@ -265,25 +270,26 @@ static int cpu_post_load(void *opaque, int version_id) cpu_breakpoint_remove_all(env, BP_CPU); cpu_watchpoint_remove_all(env, BP_CPU); - for (i = 0; i < 4; i++) + for (i = 0; i < DR7_MAX_BP; i++) { hw_breakpoint_insert(env, i); - + } tlb_flush(env, 1); + return 0; } static bool async_pf_msr_needed(void *opaque) { - CPUX86State *cpu = opaque; + X86CPU *cpu = opaque; - return cpu->async_pf_en_msr != 0; + return cpu->env.async_pf_en_msr != 0; } static bool pv_eoi_msr_needed(void *opaque) { - CPUX86State *cpu = opaque; + X86CPU *cpu = opaque; - return cpu->pv_eoi_en_msr != 0; + return cpu->env.pv_eoi_en_msr != 0; } static const VMStateDescription vmstate_async_pf_msr = { @@ -292,7 +298,7 @@ static const VMStateDescription vmstate_async_pf_msr = { .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField []) { - VMSTATE_UINT64(async_pf_en_msr, CPUX86State), + VMSTATE_UINT64(env.async_pf_en_msr, X86CPU), VMSTATE_END_OF_LIST() } }; @@ -303,14 +309,15 @@ static const VMStateDescription vmstate_pv_eoi_msr = { .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField []) { - VMSTATE_UINT64(pv_eoi_en_msr, CPUX86State), + VMSTATE_UINT64(env.pv_eoi_en_msr, X86CPU), VMSTATE_END_OF_LIST() } }; static bool fpop_ip_dp_needed(void *opaque) { - CPUX86State *env = opaque; + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; return env->fpop != 0 || env->fpip != 0 || env->fpdp != 0; } @@ -321,16 +328,36 @@ static const VMStateDescription vmstate_fpop_ip_dp = { .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField []) { - VMSTATE_UINT16(fpop, CPUX86State), - VMSTATE_UINT64(fpip, CPUX86State), - VMSTATE_UINT64(fpdp, CPUX86State), + VMSTATE_UINT16(env.fpop, X86CPU), + VMSTATE_UINT64(env.fpip, X86CPU), + VMSTATE_UINT64(env.fpdp, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + +static bool tsc_adjust_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return env->tsc_adjust != 0; +} + +static const VMStateDescription vmstate_msr_tsc_adjust = { + .name = "cpu/msr_tsc_adjust", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.tsc_adjust, X86CPU), VMSTATE_END_OF_LIST() } }; static bool tscdeadline_needed(void *opaque) { - CPUX86State *env = opaque; + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; return env->tsc_deadline != 0; } @@ -341,14 +368,15 @@ static const VMStateDescription vmstate_msr_tscdeadline = { .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField []) { - VMSTATE_UINT64(tsc_deadline, CPUX86State), + VMSTATE_UINT64(env.tsc_deadline, X86CPU), VMSTATE_END_OF_LIST() } }; static bool misc_enable_needed(void *opaque) { - CPUX86State *env = opaque; + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; return env->msr_ia32_misc_enable != MSR_IA32_MISC_ENABLE_DEFAULT; } @@ -359,111 +387,111 @@ static const VMStateDescription vmstate_msr_ia32_misc_enable = { .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField []) { - VMSTATE_UINT64(msr_ia32_misc_enable, CPUX86State), + VMSTATE_UINT64(env.msr_ia32_misc_enable, X86CPU), VMSTATE_END_OF_LIST() } }; -static const VMStateDescription vmstate_cpu = { +const VMStateDescription vmstate_x86_cpu = { .name = "cpu", - .version_id = CPU_SAVE_VERSION, + .version_id = 12, .minimum_version_id = 3, .minimum_version_id_old = 3, .pre_save = cpu_pre_save, .post_load = cpu_post_load, .fields = (VMStateField []) { - VMSTATE_UINTTL_ARRAY(regs, CPUX86State, CPU_NB_REGS), - VMSTATE_UINTTL(eip, CPUX86State), - VMSTATE_UINTTL(eflags, CPUX86State), - VMSTATE_UINT32(hflags, CPUX86State), + VMSTATE_UINTTL_ARRAY(env.regs, X86CPU, CPU_NB_REGS), + VMSTATE_UINTTL(env.eip, X86CPU), + VMSTATE_UINTTL(env.eflags, X86CPU), + VMSTATE_UINT32(env.hflags, X86CPU), /* FPU */ - VMSTATE_UINT16(fpuc, CPUX86State), - VMSTATE_UINT16(fpus_vmstate, CPUX86State), - VMSTATE_UINT16(fptag_vmstate, CPUX86State), - VMSTATE_UINT16(fpregs_format_vmstate, CPUX86State), - VMSTATE_FP_REGS(fpregs, CPUX86State, 8), + VMSTATE_UINT16(env.fpuc, X86CPU), + VMSTATE_UINT16(env.fpus_vmstate, X86CPU), + VMSTATE_UINT16(env.fptag_vmstate, X86CPU), + VMSTATE_UINT16(env.fpregs_format_vmstate, X86CPU), + VMSTATE_FP_REGS(env.fpregs, X86CPU, 8), - VMSTATE_SEGMENT_ARRAY(segs, CPUX86State, 6), - VMSTATE_SEGMENT(ldt, CPUX86State), - VMSTATE_SEGMENT(tr, CPUX86State), - VMSTATE_SEGMENT(gdt, CPUX86State), - VMSTATE_SEGMENT(idt, CPUX86State), + VMSTATE_SEGMENT_ARRAY(env.segs, X86CPU, 6), + VMSTATE_SEGMENT(env.ldt, X86CPU), + VMSTATE_SEGMENT(env.tr, X86CPU), + VMSTATE_SEGMENT(env.gdt, X86CPU), + VMSTATE_SEGMENT(env.idt, X86CPU), - VMSTATE_UINT32(sysenter_cs, CPUX86State), + VMSTATE_UINT32(env.sysenter_cs, X86CPU), #ifdef TARGET_X86_64 /* Hack: In v7 size changed from 32 to 64 bits on x86_64 */ - VMSTATE_HACK_UINT32(sysenter_esp, CPUX86State, less_than_7), - VMSTATE_HACK_UINT32(sysenter_eip, CPUX86State, less_than_7), - VMSTATE_UINTTL_V(sysenter_esp, CPUX86State, 7), - VMSTATE_UINTTL_V(sysenter_eip, CPUX86State, 7), + VMSTATE_HACK_UINT32(env.sysenter_esp, X86CPU, less_than_7), + VMSTATE_HACK_UINT32(env.sysenter_eip, X86CPU, less_than_7), + VMSTATE_UINTTL_V(env.sysenter_esp, X86CPU, 7), + VMSTATE_UINTTL_V(env.sysenter_eip, X86CPU, 7), #else - VMSTATE_UINTTL(sysenter_esp, CPUX86State), - VMSTATE_UINTTL(sysenter_eip, CPUX86State), + VMSTATE_UINTTL(env.sysenter_esp, X86CPU), + VMSTATE_UINTTL(env.sysenter_eip, X86CPU), #endif - VMSTATE_UINTTL(cr[0], CPUX86State), - VMSTATE_UINTTL(cr[2], CPUX86State), - VMSTATE_UINTTL(cr[3], CPUX86State), - VMSTATE_UINTTL(cr[4], CPUX86State), - VMSTATE_UINTTL_ARRAY(dr, CPUX86State, 8), + VMSTATE_UINTTL(env.cr[0], X86CPU), + VMSTATE_UINTTL(env.cr[2], X86CPU), + VMSTATE_UINTTL(env.cr[3], X86CPU), + VMSTATE_UINTTL(env.cr[4], X86CPU), + VMSTATE_UINTTL_ARRAY(env.dr, X86CPU, 8), /* MMU */ - VMSTATE_INT32(a20_mask, CPUX86State), + VMSTATE_INT32(env.a20_mask, X86CPU), /* XMM */ - VMSTATE_UINT32(mxcsr, CPUX86State), - VMSTATE_XMM_REGS(xmm_regs, CPUX86State, CPU_NB_REGS), + VMSTATE_UINT32(env.mxcsr, X86CPU), + VMSTATE_XMM_REGS(env.xmm_regs, X86CPU, CPU_NB_REGS), #ifdef TARGET_X86_64 - VMSTATE_UINT64(efer, CPUX86State), - VMSTATE_UINT64(star, CPUX86State), - VMSTATE_UINT64(lstar, CPUX86State), - VMSTATE_UINT64(cstar, CPUX86State), - VMSTATE_UINT64(fmask, CPUX86State), - VMSTATE_UINT64(kernelgsbase, CPUX86State), + VMSTATE_UINT64(env.efer, X86CPU), + VMSTATE_UINT64(env.star, X86CPU), + VMSTATE_UINT64(env.lstar, X86CPU), + VMSTATE_UINT64(env.cstar, X86CPU), + VMSTATE_UINT64(env.fmask, X86CPU), + VMSTATE_UINT64(env.kernelgsbase, X86CPU), #endif - VMSTATE_UINT32_V(smbase, CPUX86State, 4), + VMSTATE_UINT32_V(env.smbase, X86CPU, 4), - VMSTATE_UINT64_V(pat, CPUX86State, 5), - VMSTATE_UINT32_V(hflags2, CPUX86State, 5), + VMSTATE_UINT64_V(env.pat, X86CPU, 5), + VMSTATE_UINT32_V(env.hflags2, X86CPU, 5), - VMSTATE_UINT32_TEST(halted, CPUX86State, version_is_5), - VMSTATE_UINT64_V(vm_hsave, CPUX86State, 5), - VMSTATE_UINT64_V(vm_vmcb, CPUX86State, 5), - VMSTATE_UINT64_V(tsc_offset, CPUX86State, 5), - VMSTATE_UINT64_V(intercept, CPUX86State, 5), - VMSTATE_UINT16_V(intercept_cr_read, CPUX86State, 5), - VMSTATE_UINT16_V(intercept_cr_write, CPUX86State, 5), - VMSTATE_UINT16_V(intercept_dr_read, CPUX86State, 5), - VMSTATE_UINT16_V(intercept_dr_write, CPUX86State, 5), - VMSTATE_UINT32_V(intercept_exceptions, CPUX86State, 5), - VMSTATE_UINT8_V(v_tpr, CPUX86State, 5), + VMSTATE_UINT32_TEST(parent_obj.halted, X86CPU, version_is_5), + VMSTATE_UINT64_V(env.vm_hsave, X86CPU, 5), + VMSTATE_UINT64_V(env.vm_vmcb, X86CPU, 5), + VMSTATE_UINT64_V(env.tsc_offset, X86CPU, 5), + VMSTATE_UINT64_V(env.intercept, X86CPU, 5), + VMSTATE_UINT16_V(env.intercept_cr_read, X86CPU, 5), + VMSTATE_UINT16_V(env.intercept_cr_write, X86CPU, 5), + VMSTATE_UINT16_V(env.intercept_dr_read, X86CPU, 5), + VMSTATE_UINT16_V(env.intercept_dr_write, X86CPU, 5), + VMSTATE_UINT32_V(env.intercept_exceptions, X86CPU, 5), + VMSTATE_UINT8_V(env.v_tpr, X86CPU, 5), /* MTRRs */ - VMSTATE_UINT64_ARRAY_V(mtrr_fixed, CPUX86State, 11, 8), - VMSTATE_UINT64_V(mtrr_deftype, CPUX86State, 8), - VMSTATE_MTRR_VARS(mtrr_var, CPUX86State, 8, 8), + VMSTATE_UINT64_ARRAY_V(env.mtrr_fixed, X86CPU, 11, 8), + VMSTATE_UINT64_V(env.mtrr_deftype, X86CPU, 8), + VMSTATE_MTRR_VARS(env.mtrr_var, X86CPU, 8, 8), /* KVM-related states */ - VMSTATE_INT32_V(interrupt_injected, CPUX86State, 9), - VMSTATE_UINT32_V(mp_state, CPUX86State, 9), - VMSTATE_UINT64_V(tsc, CPUX86State, 9), - VMSTATE_INT32_V(exception_injected, CPUX86State, 11), - VMSTATE_UINT8_V(soft_interrupt, CPUX86State, 11), - VMSTATE_UINT8_V(nmi_injected, CPUX86State, 11), - VMSTATE_UINT8_V(nmi_pending, CPUX86State, 11), - VMSTATE_UINT8_V(has_error_code, CPUX86State, 11), - VMSTATE_UINT32_V(sipi_vector, CPUX86State, 11), + VMSTATE_INT32_V(env.interrupt_injected, X86CPU, 9), + VMSTATE_UINT32_V(env.mp_state, X86CPU, 9), + VMSTATE_UINT64_V(env.tsc, X86CPU, 9), + VMSTATE_INT32_V(env.exception_injected, X86CPU, 11), + VMSTATE_UINT8_V(env.soft_interrupt, X86CPU, 11), + VMSTATE_UINT8_V(env.nmi_injected, X86CPU, 11), + VMSTATE_UINT8_V(env.nmi_pending, X86CPU, 11), + VMSTATE_UINT8_V(env.has_error_code, X86CPU, 11), + VMSTATE_UINT32_V(env.sipi_vector, X86CPU, 11), /* MCE */ - VMSTATE_UINT64_V(mcg_cap, CPUX86State, 10), - VMSTATE_UINT64_V(mcg_status, CPUX86State, 10), - VMSTATE_UINT64_V(mcg_ctl, CPUX86State, 10), - VMSTATE_UINT64_ARRAY_V(mce_banks, CPUX86State, MCE_BANKS_DEF *4, 10), + VMSTATE_UINT64_V(env.mcg_cap, X86CPU, 10), + VMSTATE_UINT64_V(env.mcg_status, X86CPU, 10), + VMSTATE_UINT64_V(env.mcg_ctl, X86CPU, 10), + VMSTATE_UINT64_ARRAY_V(env.mce_banks, X86CPU, MCE_BANKS_DEF * 4, 10), /* rdtscp */ - VMSTATE_UINT64_V(tsc_aux, CPUX86State, 11), + VMSTATE_UINT64_V(env.tsc_aux, X86CPU, 11), /* KVM pvclock msr */ - VMSTATE_UINT64_V(system_time_msr, CPUX86State, 11), - VMSTATE_UINT64_V(wall_clock_msr, CPUX86State, 11), + VMSTATE_UINT64_V(env.system_time_msr, X86CPU, 11), + VMSTATE_UINT64_V(env.wall_clock_msr, X86CPU, 11), /* XSAVE related fields */ - VMSTATE_UINT64_V(xcr0, CPUX86State, 12), - VMSTATE_UINT64_V(xstate_bv, CPUX86State, 12), - VMSTATE_YMMH_REGS_VARS(ymmh_regs, CPUX86State, CPU_NB_REGS, 12), + VMSTATE_UINT64_V(env.xcr0, X86CPU, 12), + VMSTATE_UINT64_V(env.xstate_bv, X86CPU, 12), + VMSTATE_YMMH_REGS_VARS(env.ymmh_regs, X86CPU, CPU_NB_REGS, 12), VMSTATE_END_OF_LIST() /* The above list is not sorted /wrt version numbers, watch out! */ }, @@ -477,6 +505,9 @@ static const VMStateDescription vmstate_cpu = { } , { .vmsd = &vmstate_fpop_ip_dp, .needed = fpop_ip_dp_needed, + }, { + .vmsd = &vmstate_msr_tsc_adjust, + .needed = tsc_adjust_needed, }, { .vmsd = &vmstate_msr_tscdeadline, .needed = tscdeadline_needed, @@ -488,13 +519,3 @@ static const VMStateDescription vmstate_cpu = { } } }; - -void cpu_save(QEMUFile *f, void *opaque) -{ - vmstate_save_state(f, &vmstate_cpu, opaque); -} - -int cpu_load(QEMUFile *f, void *opaque, int version_id) -{ - return vmstate_load_state(f, &vmstate_cpu, opaque, version_id); -} diff --git a/target-i386/misc_helper.c b/target-i386/misc_helper.c index db3126b79b..dfbc07b7f8 100644 --- a/target-i386/misc_helper.c +++ b/target-i386/misc_helper.c @@ -110,7 +110,7 @@ void helper_into(CPUX86State *env, int next_eip_addend) void helper_single_step(CPUX86State *env) { #ifndef CONFIG_USER_ONLY - check_hw_breakpoints(env, 1); + check_hw_breakpoints(env, true); env->dr[6] |= DR6_BS; #endif raise_exception(env, EXCP01_DB); @@ -197,11 +197,11 @@ void helper_movl_drN_T0(CPUX86State *env, int reg, target_ulong t0) env->dr[reg] = t0; hw_breakpoint_insert(env, reg); } else if (reg == 7) { - for (i = 0; i < 4; i++) { + for (i = 0; i < DR7_MAX_BP; i++) { hw_breakpoint_remove(env, i); } env->dr[7] = t0; - for (i = 0; i < 4; i++) { + for (i = 0; i < DR7_MAX_BP; i++) { hw_breakpoint_insert(env, i); } } else { @@ -553,20 +553,25 @@ void helper_rdmsr(CPUX86State *env) } #endif -static void do_hlt(CPUX86State *env) +static void do_hlt(X86CPU *cpu) { + CPUState *cs = CPU(cpu); + CPUX86State *env = &cpu->env; + env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */ - env->halted = 1; + cs->halted = 1; env->exception_index = EXCP_HLT; cpu_loop_exit(env); } void helper_hlt(CPUX86State *env, int next_eip_addend) { + X86CPU *cpu = x86_env_get_cpu(env); + cpu_svm_check_intercept_param(env, SVM_EXIT_HLT, 0); EIP += next_eip_addend; - do_hlt(env); + do_hlt(cpu); } void helper_monitor(CPUX86State *env, target_ulong ptr) @@ -580,18 +585,23 @@ void helper_monitor(CPUX86State *env, target_ulong ptr) void helper_mwait(CPUX86State *env, int next_eip_addend) { + CPUState *cs; + X86CPU *cpu; + if ((uint32_t)ECX != 0) { raise_exception(env, EXCP0D_GPF); } cpu_svm_check_intercept_param(env, SVM_EXIT_MWAIT, 0); EIP += next_eip_addend; + cpu = x86_env_get_cpu(env); + cs = CPU(cpu); /* XXX: not complete but not completely erroneous */ - if (env->cpu_index != 0 || env->next_cpu != NULL) { + if (cs->cpu_index != 0 || env->next_cpu != NULL) { /* more than one CPU: do not sleep because another CPU may wake this one */ } else { - do_hlt(env); + do_hlt(cpu); } } diff --git a/target-i386/seg_helper.c b/target-i386/seg_helper.c index c2a99ee9bc..906e4f3d20 100644 --- a/target-i386/seg_helper.c +++ b/target-i386/seg_helper.c @@ -465,13 +465,14 @@ static void switch_tss(CPUX86State *env, int tss_selector, #ifndef CONFIG_USER_ONLY /* reset local breakpoints */ - if (env->dr[7] & 0x55) { - for (i = 0; i < 4; i++) { - if (hw_breakpoint_enabled(env->dr[7], i) == 0x1) { + if (env->dr[7] & DR7_LOCAL_BP_MASK) { + for (i = 0; i < DR7_MAX_BP; i++) { + if (hw_local_breakpoint_enabled(env->dr[7], i) && + !hw_global_breakpoint_enabled(env->dr[7], i)) { hw_breakpoint_remove(env, i); } } - env->dr[7] &= ~0x55; + env->dr[7] &= ~DR7_LOCAL_BP_MASK; } #endif } @@ -1230,8 +1231,11 @@ static void do_interrupt_all(CPUX86State *env, int intno, int is_int, #endif } -void do_interrupt(CPUX86State *env) +void x86_cpu_do_interrupt(CPUState *cs) { + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + #if defined(CONFIG_USER_ONLY) /* if user mode only, we simulate a fake exception which will be handled outside the cpu execution diff --git a/target-i386/shift_helper_template.h b/target-i386/shift_helper_template.h index dda0da30cf..cf91a2d284 100644 --- a/target-i386/shift_helper_template.h +++ b/target-i386/shift_helper_template.h @@ -55,7 +55,7 @@ target_ulong glue(helper_rcl, SUFFIX)(CPUX86State *env, target_ulong t0, count = rclb_table[count]; #endif if (count) { - eflags = helper_cc_compute_all(env, CC_OP); + eflags = env->cc_src; t0 &= DATA_MASK; src = t0; res = (t0 << count) | ((target_ulong)(eflags & CC_C) << (count - 1)); @@ -63,11 +63,9 @@ target_ulong glue(helper_rcl, SUFFIX)(CPUX86State *env, target_ulong t0, res |= t0 >> (DATA_BITS + 1 - count); } t0 = res; - env->cc_tmp = (eflags & ~(CC_C | CC_O)) | + env->cc_src = (eflags & ~(CC_C | CC_O)) | (lshift(src ^ t0, 11 - (DATA_BITS - 1)) & CC_O) | ((src >> (DATA_BITS - count)) & CC_C); - } else { - env->cc_tmp = -1; } return t0; } @@ -86,7 +84,7 @@ target_ulong glue(helper_rcr, SUFFIX)(CPUX86State *env, target_ulong t0, count = rclb_table[count]; #endif if (count) { - eflags = helper_cc_compute_all(env, CC_OP); + eflags = env->cc_src; t0 &= DATA_MASK; src = t0; res = (t0 >> count) | @@ -95,11 +93,9 @@ target_ulong glue(helper_rcr, SUFFIX)(CPUX86State *env, target_ulong t0, res |= t0 << (DATA_BITS + 1 - count); } t0 = res; - env->cc_tmp = (eflags & ~(CC_C | CC_O)) | + env->cc_src = (eflags & ~(CC_C | CC_O)) | (lshift(src ^ t0, 11 - (DATA_BITS - 1)) & CC_O) | ((src >> (count - 1)) & CC_C); - } else { - env->cc_tmp = -1; } return t0; } diff --git a/target-i386/svm_helper.c b/target-i386/svm_helper.c index 3f246e9073..c46a213c9c 100644 --- a/target-i386/svm_helper.c +++ b/target-i386/svm_helper.c @@ -271,7 +271,9 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->hflags2 |= HF2_GIF_MASK; if (int_ctl & V_IRQ_MASK) { - env->interrupt_request |= CPU_INTERRUPT_VIRQ; + CPUState *cs = CPU(x86_env_get_cpu(env)); + + cs->interrupt_request |= CPU_INTERRUPT_VIRQ; } /* maybe we need to inject an event */ @@ -548,6 +550,7 @@ void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param, /* Note: currently only 32 bits of exit_code are used */ void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) { + CPUState *cs = CPU(x86_env_get_cpu(env)); uint32_t int_ctl; qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmexit(%08x, %016" PRIx64 ", %016" @@ -594,7 +597,7 @@ void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) int_ctl = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)); int_ctl &= ~(V_TPR_MASK | V_IRQ_MASK); int_ctl |= env->v_tpr & V_TPR_MASK; - if (env->interrupt_request & CPU_INTERRUPT_VIRQ) { + if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { int_ctl |= V_IRQ_MASK; } stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), int_ctl); @@ -615,7 +618,7 @@ void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) env->hflags &= ~HF_SVMI_MASK; env->intercept = 0; env->intercept_exceptions = 0; - env->interrupt_request &= ~CPU_INTERRUPT_VIRQ; + cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; env->tsc_offset = 0; env->gdt.base = ldq_phys(env->vm_hsave + offsetof(struct vmcb, diff --git a/target-i386/topology.h b/target-i386/topology.h new file mode 100644 index 0000000000..07a6c5fb55 --- /dev/null +++ b/target-i386/topology.h @@ -0,0 +1,134 @@ +/* + * x86 CPU topology data structures and functions + * + * Copyright (c) 2012 Red Hat Inc. + * + * 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 TARGET_I386_TOPOLOGY_H +#define TARGET_I386_TOPOLOGY_H + +/* This file implements the APIC-ID-based CPU topology enumeration logic, + * documented at the following document: + * Intel® 64 Architecture Processor Topology Enumeration + * http://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration/ + * + * This code should be compatible with AMD's "Extended Method" described at: + * AMD CPUID Specification (Publication #25481) + * Section 3: Multiple Core Calcuation + * as long as: + * nr_threads is set to 1; + * OFFSET_IDX is assumed to be 0; + * CPUID Fn8000_0008_ECX[ApicIdCoreIdSize[3:0]] is set to apicid_core_width(). + */ + +#include +#include + +#include "qemu/bitops.h" + +/* APIC IDs can be 32-bit, but beware: APIC IDs > 255 require x2APIC support + */ +typedef uint32_t apic_id_t; + +/* Return the bit width needed for 'count' IDs + */ +static unsigned apicid_bitwidth_for_count(unsigned count) +{ + g_assert(count >= 1); + count -= 1; + return count ? 32 - clz32(count) : 0; +} + +/* Bit width of the SMT_ID (thread ID) field on the APIC ID + */ +static inline unsigned apicid_smt_width(unsigned nr_cores, unsigned nr_threads) +{ + return apicid_bitwidth_for_count(nr_threads); +} + +/* Bit width of the Core_ID field + */ +static inline unsigned apicid_core_width(unsigned nr_cores, unsigned nr_threads) +{ + return apicid_bitwidth_for_count(nr_cores); +} + +/* Bit offset of the Core_ID field + */ +static inline unsigned apicid_core_offset(unsigned nr_cores, + unsigned nr_threads) +{ + return apicid_smt_width(nr_cores, nr_threads); +} + +/* Bit offset of the Pkg_ID (socket ID) field + */ +static inline unsigned apicid_pkg_offset(unsigned nr_cores, unsigned nr_threads) +{ + return apicid_core_offset(nr_cores, nr_threads) + + apicid_core_width(nr_cores, nr_threads); +} + +/* Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID + * + * The caller must make sure core_id < nr_cores and smt_id < nr_threads. + */ +static inline apic_id_t apicid_from_topo_ids(unsigned nr_cores, + unsigned nr_threads, + unsigned pkg_id, + unsigned core_id, + unsigned smt_id) +{ + return (pkg_id << apicid_pkg_offset(nr_cores, nr_threads)) | + (core_id << apicid_core_offset(nr_cores, nr_threads)) | + smt_id; +} + +/* Calculate thread/core/package IDs for a specific topology, + * based on (contiguous) CPU index + */ +static inline void x86_topo_ids_from_idx(unsigned nr_cores, + unsigned nr_threads, + unsigned cpu_index, + unsigned *pkg_id, + unsigned *core_id, + unsigned *smt_id) +{ + unsigned core_index = cpu_index / nr_threads; + *smt_id = cpu_index % nr_threads; + *core_id = core_index % nr_cores; + *pkg_id = core_index / nr_cores; +} + +/* Make APIC ID for the CPU 'cpu_index' + * + * 'cpu_index' is a sequential, contiguous ID for the CPU. + */ +static inline apic_id_t x86_apicid_from_cpu_idx(unsigned nr_cores, + unsigned nr_threads, + unsigned cpu_index) +{ + unsigned pkg_id, core_id, smt_id; + x86_topo_ids_from_idx(nr_cores, nr_threads, cpu_index, + &pkg_id, &core_id, &smt_id); + return apicid_from_topo_ids(nr_cores, nr_threads, pkg_id, core_id, smt_id); +} + +#endif /* TARGET_I386_TOPOLOGY_H */ diff --git a/target-i386/translate.c b/target-i386/translate.c index 32d21f52d0..705147a00b 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -23,6 +23,7 @@ #include #include +#include "qemu/host-utils.h" #include "cpu.h" #include "disas/disas.h" #include "tcg-op.h" @@ -36,6 +37,7 @@ #define PREFIX_LOCK 0x04 #define PREFIX_DATA 0x08 #define PREFIX_ADR 0x10 +#define PREFIX_VEX 0x20 #ifdef TARGET_X86_64 #define CODE64(s) ((s)->code64) @@ -47,21 +49,29 @@ #define REX_B(s) 0 #endif +#ifdef TARGET_X86_64 +# define ctztl ctz64 +# define clztl clz64 +#else +# define ctztl ctz32 +# define clztl clz32 +#endif + //#define MACRO_TEST 1 /* global register indexes */ static TCGv_ptr cpu_env; -static TCGv cpu_A0, cpu_cc_src, cpu_cc_dst, cpu_cc_tmp; +static TCGv cpu_A0; +static TCGv cpu_cc_dst, cpu_cc_src, cpu_cc_src2, cpu_cc_srcT; static TCGv_i32 cpu_cc_op; static TCGv cpu_regs[CPU_NB_REGS]; /* local temps */ -static TCGv cpu_T[2], cpu_T3; +static TCGv cpu_T[2]; /* local register indexes (only used inside old micro ops) */ static TCGv cpu_tmp0, cpu_tmp4; static TCGv_ptr cpu_ptr0, cpu_ptr1; static TCGv_i32 cpu_tmp2_i32, cpu_tmp3_i32; static TCGv_i64 cpu_tmp1_i64; -static TCGv cpu_tmp5; static uint8_t gen_opc_cc_op[OPC_BUF_SIZE]; @@ -88,8 +98,11 @@ typedef struct DisasContext { int code64; /* 64 bit code segment */ int rex_x, rex_b; #endif + int vex_l; /* vex vector length */ + int vex_v; /* vex vvvv register, without 1's compliment. */ int ss32; /* 32 bit stack segment */ - int cc_op; /* current CC operation */ + CCOp cc_op; /* current CC operation */ + bool cc_op_dirty; int addseg; /* non zero if either DS/ES/SS have a non zero base */ int f_st; /* currently unused */ int vm86; /* vm86 mode */ @@ -113,6 +126,7 @@ typedef struct DisasContext { static void gen_eob(DisasContext *s); static void gen_jmp(DisasContext *s, target_ulong eip); static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num); +static void gen_op(DisasContext *s1, int op, int ot, int d); /* i386 arith/logic operations */ enum { @@ -173,6 +187,79 @@ enum { OR_A0, /* temporary register used when doing address evaluation */ }; +enum { + USES_CC_DST = 1, + USES_CC_SRC = 2, + USES_CC_SRC2 = 4, + USES_CC_SRCT = 8, +}; + +/* Bit set if the global variable is live after setting CC_OP to X. */ +static const uint8_t cc_op_live[CC_OP_NB] = { + [CC_OP_DYNAMIC] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, + [CC_OP_EFLAGS] = USES_CC_SRC, + [CC_OP_MULB ... CC_OP_MULQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_ADDB ... CC_OP_ADDQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_ADCB ... CC_OP_ADCQ] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, + [CC_OP_SUBB ... CC_OP_SUBQ] = USES_CC_DST | USES_CC_SRC | USES_CC_SRCT, + [CC_OP_SBBB ... CC_OP_SBBQ] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, + [CC_OP_LOGICB ... CC_OP_LOGICQ] = USES_CC_DST, + [CC_OP_INCB ... CC_OP_INCQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_DECB ... CC_OP_DECQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_SHLB ... CC_OP_SHLQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_SARB ... CC_OP_SARQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_BMILGB ... CC_OP_BMILGQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_ADCX] = USES_CC_DST | USES_CC_SRC, + [CC_OP_ADOX] = USES_CC_SRC | USES_CC_SRC2, + [CC_OP_ADCOX] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, + [CC_OP_CLR] = 0, +}; + +static void set_cc_op(DisasContext *s, CCOp op) +{ + int dead; + + if (s->cc_op == op) { + return; + } + + /* Discard CC computation that will no longer be used. */ + dead = cc_op_live[s->cc_op] & ~cc_op_live[op]; + if (dead & USES_CC_DST) { + tcg_gen_discard_tl(cpu_cc_dst); + } + if (dead & USES_CC_SRC) { + tcg_gen_discard_tl(cpu_cc_src); + } + if (dead & USES_CC_SRC2) { + tcg_gen_discard_tl(cpu_cc_src2); + } + if (dead & USES_CC_SRCT) { + tcg_gen_discard_tl(cpu_cc_srcT); + } + + if (op == CC_OP_DYNAMIC) { + /* The DYNAMIC setting is translator only, and should never be + stored. Thus we always consider it clean. */ + s->cc_op_dirty = false; + } else { + /* Discard any computed CC_OP value (see shifts). */ + if (s->cc_op == CC_OP_DYNAMIC) { + tcg_gen_discard_i32(cpu_cc_op); + } + s->cc_op_dirty = true; + } + s->cc_op = op; +} + +static void gen_update_cc_op(DisasContext *s) +{ + if (s->cc_op_dirty) { + tcg_gen_movi_i32(cpu_cc_op, s->cc_op); + s->cc_op_dirty = false; + } +} + static inline void gen_op_movl_T0_0(void) { tcg_gen_movi_tl(cpu_T[0], 0); @@ -323,17 +410,17 @@ static inline void gen_op_mov_reg_T1(int ot, int reg) static inline void gen_op_mov_reg_A0(int size, int reg) { switch(size) { - case 0: + case OT_BYTE: tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_A0, 0, 16); break; default: /* XXX this shouldn't be reached; abort? */ - case 1: + case OT_WORD: /* For x86_64, this sets the higher half of register to zero. For i386, this is equivalent to a mov. */ tcg_gen_ext32u_tl(cpu_regs[reg], cpu_A0); break; #ifdef TARGET_X86_64 - case 2: + case OT_LONG: tcg_gen_mov_tl(cpu_regs[reg], cpu_A0); break; #endif @@ -398,11 +485,11 @@ static inline void gen_op_jmp_T0(void) static inline void gen_op_add_reg_im(int size, int reg, int32_t val) { switch(size) { - case 0: + case OT_BYTE: tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val); tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0, 0, 16); break; - case 1: + case OT_WORD: tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val); /* For x86_64, this sets the higher half of register to zero. For i386, this is equivalent to a nop. */ @@ -410,7 +497,7 @@ static inline void gen_op_add_reg_im(int size, int reg, int32_t val) tcg_gen_mov_tl(cpu_regs[reg], cpu_tmp0); break; #ifdef TARGET_X86_64 - case 2: + case OT_LONG: tcg_gen_addi_tl(cpu_regs[reg], cpu_regs[reg], val); break; #endif @@ -420,11 +507,11 @@ static inline void gen_op_add_reg_im(int size, int reg, int32_t val) static inline void gen_op_add_reg_T0(int size, int reg) { switch(size) { - case 0: + case OT_BYTE: tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]); tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0, 0, 16); break; - case 1: + case OT_WORD: tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]); /* For x86_64, this sets the higher half of register to zero. For i386, this is equivalent to a nop. */ @@ -432,18 +519,13 @@ static inline void gen_op_add_reg_T0(int size, int reg) tcg_gen_mov_tl(cpu_regs[reg], cpu_tmp0); break; #ifdef TARGET_X86_64 - case 2: + case OT_LONG: tcg_gen_add_tl(cpu_regs[reg], cpu_regs[reg], cpu_T[0]); break; #endif } } -static inline void gen_op_set_cc_op(int32_t val) -{ - tcg_gen_movi_i32(cpu_cc_op, val); -} - static inline void gen_op_addl_A0_reg_sN(int shift, int reg) { tcg_gen_mov_tl(cpu_tmp0, cpu_regs[reg]); @@ -506,14 +588,14 @@ static inline void gen_op_lds_T0_A0(int idx) { int mem_index = (idx >> 2) - 1; switch(idx & 3) { - case 0: + case OT_BYTE: tcg_gen_qemu_ld8s(cpu_T[0], cpu_A0, mem_index); break; - case 1: + case OT_WORD: tcg_gen_qemu_ld16s(cpu_T[0], cpu_A0, mem_index); break; default: - case 2: + case OT_LONG: tcg_gen_qemu_ld32s(cpu_T[0], cpu_A0, mem_index); break; } @@ -523,17 +605,17 @@ static inline void gen_op_ld_v(int idx, TCGv t0, TCGv a0) { int mem_index = (idx >> 2) - 1; switch(idx & 3) { - case 0: + case OT_BYTE: tcg_gen_qemu_ld8u(t0, a0, mem_index); break; - case 1: + case OT_WORD: tcg_gen_qemu_ld16u(t0, a0, mem_index); break; - case 2: + case OT_LONG: tcg_gen_qemu_ld32u(t0, a0, mem_index); break; default: - case 3: + case OT_QUAD: /* Should never happen on 32-bit targets. */ #ifdef TARGET_X86_64 tcg_gen_qemu_ld64(t0, a0, mem_index); @@ -562,17 +644,17 @@ static inline void gen_op_st_v(int idx, TCGv t0, TCGv a0) { int mem_index = (idx >> 2) - 1; switch(idx & 3) { - case 0: + case OT_BYTE: tcg_gen_qemu_st8(t0, a0, mem_index); break; - case 1: + case OT_WORD: tcg_gen_qemu_st16(t0, a0, mem_index); break; - case 2: + case OT_LONG: tcg_gen_qemu_st32(t0, a0, mem_index); break; default: - case 3: + case OT_QUAD: /* Should never happen on 32-bit targets. */ #ifdef TARGET_X86_64 tcg_gen_qemu_st64(t0, a0, mem_index); @@ -659,38 +741,45 @@ static inline void gen_op_movl_T0_Dshift(int ot) tcg_gen_shli_tl(cpu_T[0], cpu_T[0], ot); }; +static TCGv gen_ext_tl(TCGv dst, TCGv src, int size, bool sign) +{ + switch (size) { + case OT_BYTE: + if (sign) { + tcg_gen_ext8s_tl(dst, src); + } else { + tcg_gen_ext8u_tl(dst, src); + } + return dst; + case OT_WORD: + if (sign) { + tcg_gen_ext16s_tl(dst, src); + } else { + tcg_gen_ext16u_tl(dst, src); + } + return dst; +#ifdef TARGET_X86_64 + case OT_LONG: + if (sign) { + tcg_gen_ext32s_tl(dst, src); + } else { + tcg_gen_ext32u_tl(dst, src); + } + return dst; +#endif + default: + return src; + } +} + static void gen_extu(int ot, TCGv reg) { - switch(ot) { - case OT_BYTE: - tcg_gen_ext8u_tl(reg, reg); - break; - case OT_WORD: - tcg_gen_ext16u_tl(reg, reg); - break; - case OT_LONG: - tcg_gen_ext32u_tl(reg, reg); - break; - default: - break; - } + gen_ext_tl(reg, reg, ot, false); } static void gen_exts(int ot, TCGv reg) { - switch(ot) { - case OT_BYTE: - tcg_gen_ext8s_tl(reg, reg); - break; - case OT_WORD: - tcg_gen_ext16s_tl(reg, reg); - break; - case OT_LONG: - tcg_gen_ext32s_tl(reg, reg); - break; - default: - break; - } + gen_ext_tl(reg, reg, ot, true); } static inline void gen_op_jnz_ecx(int size, int label1) @@ -710,21 +799,31 @@ static inline void gen_op_jz_ecx(int size, int label1) static void gen_helper_in_func(int ot, TCGv v, TCGv_i32 n) { switch (ot) { - case 0: gen_helper_inb(v, n); break; - case 1: gen_helper_inw(v, n); break; - case 2: gen_helper_inl(v, n); break; + case OT_BYTE: + gen_helper_inb(v, n); + break; + case OT_WORD: + gen_helper_inw(v, n); + break; + case OT_LONG: + gen_helper_inl(v, n); + break; } - } static void gen_helper_out_func(int ot, TCGv_i32 v, TCGv_i32 n) { switch (ot) { - case 0: gen_helper_outb(v, n); break; - case 1: gen_helper_outw(v, n); break; - case 2: gen_helper_outl(v, n); break; + case OT_BYTE: + gen_helper_outb(v, n); + break; + case OT_WORD: + gen_helper_outw(v, n); + break; + case OT_LONG: + gen_helper_outl(v, n); + break; } - } static void gen_check_io(DisasContext *s, int ot, target_ulong cur_eip, @@ -735,27 +834,25 @@ static void gen_check_io(DisasContext *s, int ot, target_ulong cur_eip, state_saved = 0; if (s->pe && (s->cpl > s->iopl || s->vm86)) { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(cur_eip); state_saved = 1; tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); switch (ot) { - case 0: + case OT_BYTE: gen_helper_check_iob(cpu_env, cpu_tmp2_i32); break; - case 1: + case OT_WORD: gen_helper_check_iow(cpu_env, cpu_tmp2_i32); break; - case 2: + case OT_LONG: gen_helper_check_iol(cpu_env, cpu_tmp2_i32); break; } } if(s->flags & HF_SVMI_MASK) { if (!state_saved) { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(cur_eip); } svm_flags |= (1 << (4 + ot)); @@ -778,17 +875,8 @@ static inline void gen_movs(DisasContext *s, int ot) gen_op_add_reg_T0(s->aflag, R_EDI); } -static inline void gen_update_cc_op(DisasContext *s) -{ - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } -} - static void gen_op_update1_cc(void) { - tcg_gen_discard_tl(cpu_cc_src); tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); } @@ -798,339 +886,393 @@ static void gen_op_update2_cc(void) tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); } -static inline void gen_op_cmpl_T0_T1_cc(void) +static void gen_op_update3_cc(TCGv reg) { + tcg_gen_mov_tl(cpu_cc_src2, reg); tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]); - tcg_gen_sub_tl(cpu_cc_dst, cpu_T[0], cpu_T[1]); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); } static inline void gen_op_testl_T0_T1_cc(void) { - tcg_gen_discard_tl(cpu_cc_src); tcg_gen_and_tl(cpu_cc_dst, cpu_T[0], cpu_T[1]); } static void gen_op_update_neg_cc(void) { - tcg_gen_neg_tl(cpu_cc_src, cpu_T[0]); tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); -} - -/* compute eflags.C to reg */ -static void gen_compute_eflags_c(TCGv reg) -{ - gen_helper_cc_compute_c(cpu_tmp2_i32, cpu_env, cpu_cc_op); - tcg_gen_extu_i32_tl(reg, cpu_tmp2_i32); + tcg_gen_neg_tl(cpu_cc_src, cpu_T[0]); + tcg_gen_movi_tl(cpu_cc_srcT, 0); } /* compute all eflags to cc_src */ -static void gen_compute_eflags(TCGv reg) +static void gen_compute_eflags(DisasContext *s) { - gen_helper_cc_compute_all(cpu_tmp2_i32, cpu_env, cpu_cc_op); - tcg_gen_extu_i32_tl(reg, cpu_tmp2_i32); -} + TCGv zero, dst, src1, src2; + int live, dead; -static inline void gen_setcc_slow_T0(DisasContext *s, int jcc_op) -{ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); - switch(jcc_op) { - case JCC_O: - gen_compute_eflags(cpu_T[0]); - tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 11); - tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1); - break; - case JCC_B: - gen_compute_eflags_c(cpu_T[0]); - break; - case JCC_Z: - gen_compute_eflags(cpu_T[0]); - tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 6); - tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1); - break; - case JCC_BE: - gen_compute_eflags(cpu_tmp0); - tcg_gen_shri_tl(cpu_T[0], cpu_tmp0, 6); - tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_tmp0); - tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1); - break; - case JCC_S: - gen_compute_eflags(cpu_T[0]); - tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 7); - tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1); - break; - case JCC_P: - gen_compute_eflags(cpu_T[0]); - tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 2); - tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1); - break; - case JCC_L: - gen_compute_eflags(cpu_tmp0); - tcg_gen_shri_tl(cpu_T[0], cpu_tmp0, 11); /* CC_O */ - tcg_gen_shri_tl(cpu_tmp0, cpu_tmp0, 7); /* CC_S */ - tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_tmp0); - tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1); - break; - default: - case JCC_LE: - gen_compute_eflags(cpu_tmp0); - tcg_gen_shri_tl(cpu_T[0], cpu_tmp0, 11); /* CC_O */ - tcg_gen_shri_tl(cpu_tmp4, cpu_tmp0, 7); /* CC_S */ - tcg_gen_shri_tl(cpu_tmp0, cpu_tmp0, 6); /* CC_Z */ - tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_tmp4); - tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_tmp0); - tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1); - break; + if (s->cc_op == CC_OP_EFLAGS) { + return; + } + if (s->cc_op == CC_OP_CLR) { + tcg_gen_movi_tl(cpu_cc_src, CC_Z); + set_cc_op(s, CC_OP_EFLAGS); + return; + } + + TCGV_UNUSED(zero); + dst = cpu_cc_dst; + src1 = cpu_cc_src; + src2 = cpu_cc_src2; + + /* Take care to not read values that are not live. */ + live = cc_op_live[s->cc_op] & ~USES_CC_SRCT; + dead = live ^ (USES_CC_DST | USES_CC_SRC | USES_CC_SRC2); + if (dead) { + zero = tcg_const_tl(0); + if (dead & USES_CC_DST) { + dst = zero; + } + if (dead & USES_CC_SRC) { + src1 = zero; + } + if (dead & USES_CC_SRC2) { + src2 = zero; + } + } + + gen_update_cc_op(s); + gen_helper_cc_compute_all(cpu_cc_src, dst, src1, src2, cpu_cc_op); + set_cc_op(s, CC_OP_EFLAGS); + + if (dead) { + tcg_temp_free(zero); } } -/* return true if setcc_slow is not needed (WARNING: must be kept in - sync with gen_jcc1) */ -static int is_fast_jcc_case(DisasContext *s, int b) +typedef struct CCPrepare { + TCGCond cond; + TCGv reg; + TCGv reg2; + target_ulong imm; + target_ulong mask; + bool use_reg2; + bool no_setcond; +} CCPrepare; + +/* compute eflags.C to reg */ +static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) { - int jcc_op; - jcc_op = (b >> 1) & 7; - switch(s->cc_op) { - /* we optimize the cmp/jcc case */ - case CC_OP_SUBB: - case CC_OP_SUBW: - case CC_OP_SUBL: - case CC_OP_SUBQ: - if (jcc_op == JCC_O || jcc_op == JCC_P) - goto slow_jcc; - break; + TCGv t0, t1; + int size, shift; - /* some jumps are easy to compute */ - case CC_OP_ADDB: - case CC_OP_ADDW: - case CC_OP_ADDL: - case CC_OP_ADDQ: + switch (s->cc_op) { + case CC_OP_SUBB ... CC_OP_SUBQ: + /* (DATA_TYPE)CC_SRCT < (DATA_TYPE)CC_SRC */ + size = s->cc_op - CC_OP_SUBB; + t1 = gen_ext_tl(cpu_tmp0, cpu_cc_src, size, false); + /* If no temporary was used, be careful not to alias t1 and t0. */ + t0 = TCGV_EQUAL(t1, cpu_cc_src) ? cpu_tmp0 : reg; + tcg_gen_mov_tl(t0, cpu_cc_srcT); + gen_extu(size, t0); + goto add_sub; - case CC_OP_LOGICB: - case CC_OP_LOGICW: - case CC_OP_LOGICL: - case CC_OP_LOGICQ: + case CC_OP_ADDB ... CC_OP_ADDQ: + /* (DATA_TYPE)CC_DST < (DATA_TYPE)CC_SRC */ + size = s->cc_op - CC_OP_ADDB; + t1 = gen_ext_tl(cpu_tmp0, cpu_cc_src, size, false); + t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); + add_sub: + return (CCPrepare) { .cond = TCG_COND_LTU, .reg = t0, + .reg2 = t1, .mask = -1, .use_reg2 = true }; - case CC_OP_INCB: - case CC_OP_INCW: - case CC_OP_INCL: - case CC_OP_INCQ: + case CC_OP_LOGICB ... CC_OP_LOGICQ: + case CC_OP_CLR: + return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; - case CC_OP_DECB: - case CC_OP_DECW: - case CC_OP_DECL: - case CC_OP_DECQ: + case CC_OP_INCB ... CC_OP_INCQ: + case CC_OP_DECB ... CC_OP_DECQ: + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = -1, .no_setcond = true }; + + case CC_OP_SHLB ... CC_OP_SHLQ: + /* (CC_SRC >> (DATA_BITS - 1)) & 1 */ + size = s->cc_op - CC_OP_SHLB; + shift = (8 << size) - 1; + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = (target_ulong)1 << shift }; + + case CC_OP_MULB ... CC_OP_MULQ: + return (CCPrepare) { .cond = TCG_COND_NE, + .reg = cpu_cc_src, .mask = -1 }; + + case CC_OP_BMILGB ... CC_OP_BMILGQ: + size = s->cc_op - CC_OP_BMILGB; + t0 = gen_ext_tl(reg, cpu_cc_src, size, false); + return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 }; + + case CC_OP_ADCX: + case CC_OP_ADCOX: + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_dst, + .mask = -1, .no_setcond = true }; + + case CC_OP_EFLAGS: + case CC_OP_SARB ... CC_OP_SARQ: + /* CC_SRC & 1 */ + return (CCPrepare) { .cond = TCG_COND_NE, + .reg = cpu_cc_src, .mask = CC_C }; - case CC_OP_SHLB: - case CC_OP_SHLW: - case CC_OP_SHLL: - case CC_OP_SHLQ: - if (jcc_op != JCC_Z && jcc_op != JCC_S) - goto slow_jcc; - break; default: - slow_jcc: - return 0; + /* The need to compute only C from CC_OP_DYNAMIC is important + in efficiently implementing e.g. INC at the start of a TB. */ + gen_update_cc_op(s); + gen_helper_cc_compute_c(reg, cpu_cc_dst, cpu_cc_src, + cpu_cc_src2, cpu_cc_op); + return (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, + .mask = -1, .no_setcond = true }; } - return 1; } -/* generate a conditional jump to label 'l1' according to jump opcode +/* compute eflags.P to reg */ +static CCPrepare gen_prepare_eflags_p(DisasContext *s, TCGv reg) +{ + gen_compute_eflags(s); + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = CC_P }; +} + +/* compute eflags.S to reg */ +static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg) +{ + switch (s->cc_op) { + case CC_OP_DYNAMIC: + gen_compute_eflags(s); + /* FALLTHRU */ + case CC_OP_EFLAGS: + case CC_OP_ADCX: + case CC_OP_ADOX: + case CC_OP_ADCOX: + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = CC_S }; + case CC_OP_CLR: + return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; + default: + { + int size = (s->cc_op - CC_OP_ADDB) & 3; + TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, true); + return (CCPrepare) { .cond = TCG_COND_LT, .reg = t0, .mask = -1 }; + } + } +} + +/* compute eflags.O to reg */ +static CCPrepare gen_prepare_eflags_o(DisasContext *s, TCGv reg) +{ + switch (s->cc_op) { + case CC_OP_ADOX: + case CC_OP_ADCOX: + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src2, + .mask = -1, .no_setcond = true }; + case CC_OP_CLR: + return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; + default: + gen_compute_eflags(s); + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = CC_O }; + } +} + +/* compute eflags.Z to reg */ +static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) +{ + switch (s->cc_op) { + case CC_OP_DYNAMIC: + gen_compute_eflags(s); + /* FALLTHRU */ + case CC_OP_EFLAGS: + case CC_OP_ADCX: + case CC_OP_ADOX: + case CC_OP_ADCOX: + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = CC_Z }; + case CC_OP_CLR: + return (CCPrepare) { .cond = TCG_COND_ALWAYS, .mask = -1 }; + default: + { + int size = (s->cc_op - CC_OP_ADDB) & 3; + TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); + return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 }; + } + } +} + +/* perform a conditional store into register 'reg' according to jump opcode value 'b'. In the fast case, T0 is guaranted not to be used. */ -static inline void gen_jcc1(DisasContext *s, int cc_op, int b, int l1) +static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) { int inv, jcc_op, size, cond; + CCPrepare cc; TCGv t0; inv = b & 1; jcc_op = (b >> 1) & 7; - switch(cc_op) { - /* we optimize the cmp/jcc case */ - case CC_OP_SUBB: - case CC_OP_SUBW: - case CC_OP_SUBL: - case CC_OP_SUBQ: - - size = cc_op - CC_OP_SUBB; - switch(jcc_op) { - case JCC_Z: - fast_jcc_z: - switch(size) { - case 0: - tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0xff); - t0 = cpu_tmp0; - break; - case 1: - tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0xffff); - t0 = cpu_tmp0; - break; -#ifdef TARGET_X86_64 - case 2: - tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0xffffffff); - t0 = cpu_tmp0; - break; -#endif - default: - t0 = cpu_cc_dst; - break; - } - tcg_gen_brcondi_tl(inv ? TCG_COND_NE : TCG_COND_EQ, t0, 0, l1); - break; - case JCC_S: - fast_jcc_s: - switch(size) { - case 0: - tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0x80); - tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, cpu_tmp0, - 0, l1); - break; - case 1: - tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0x8000); - tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, cpu_tmp0, - 0, l1); - break; -#ifdef TARGET_X86_64 - case 2: - tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0x80000000); - tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, cpu_tmp0, - 0, l1); - break; -#endif - default: - tcg_gen_brcondi_tl(inv ? TCG_COND_GE : TCG_COND_LT, cpu_cc_dst, - 0, l1); - break; - } - break; - - case JCC_B: - cond = inv ? TCG_COND_GEU : TCG_COND_LTU; - goto fast_jcc_b; + switch (s->cc_op) { + case CC_OP_SUBB ... CC_OP_SUBQ: + /* We optimize relational operators for the cmp/jcc case. */ + size = s->cc_op - CC_OP_SUBB; + switch (jcc_op) { case JCC_BE: - cond = inv ? TCG_COND_GTU : TCG_COND_LEU; - fast_jcc_b: - tcg_gen_add_tl(cpu_tmp4, cpu_cc_dst, cpu_cc_src); - switch(size) { - case 0: - t0 = cpu_tmp0; - tcg_gen_andi_tl(cpu_tmp4, cpu_tmp4, 0xff); - tcg_gen_andi_tl(t0, cpu_cc_src, 0xff); - break; - case 1: - t0 = cpu_tmp0; - tcg_gen_andi_tl(cpu_tmp4, cpu_tmp4, 0xffff); - tcg_gen_andi_tl(t0, cpu_cc_src, 0xffff); - break; -#ifdef TARGET_X86_64 - case 2: - t0 = cpu_tmp0; - tcg_gen_andi_tl(cpu_tmp4, cpu_tmp4, 0xffffffff); - tcg_gen_andi_tl(t0, cpu_cc_src, 0xffffffff); - break; -#endif - default: - t0 = cpu_cc_src; - break; - } - tcg_gen_brcond_tl(cond, cpu_tmp4, t0, l1); + tcg_gen_mov_tl(cpu_tmp4, cpu_cc_srcT); + gen_extu(size, cpu_tmp4); + t0 = gen_ext_tl(cpu_tmp0, cpu_cc_src, size, false); + cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = cpu_tmp4, + .reg2 = t0, .mask = -1, .use_reg2 = true }; break; - + case JCC_L: - cond = inv ? TCG_COND_GE : TCG_COND_LT; + cond = TCG_COND_LT; goto fast_jcc_l; case JCC_LE: - cond = inv ? TCG_COND_GT : TCG_COND_LE; + cond = TCG_COND_LE; fast_jcc_l: - tcg_gen_add_tl(cpu_tmp4, cpu_cc_dst, cpu_cc_src); - switch(size) { - case 0: - t0 = cpu_tmp0; - tcg_gen_ext8s_tl(cpu_tmp4, cpu_tmp4); - tcg_gen_ext8s_tl(t0, cpu_cc_src); - break; - case 1: - t0 = cpu_tmp0; - tcg_gen_ext16s_tl(cpu_tmp4, cpu_tmp4); - tcg_gen_ext16s_tl(t0, cpu_cc_src); - break; -#ifdef TARGET_X86_64 - case 2: - t0 = cpu_tmp0; - tcg_gen_ext32s_tl(cpu_tmp4, cpu_tmp4); - tcg_gen_ext32s_tl(t0, cpu_cc_src); - break; -#endif - default: - t0 = cpu_cc_src; - break; - } - tcg_gen_brcond_tl(cond, cpu_tmp4, t0, l1); + tcg_gen_mov_tl(cpu_tmp4, cpu_cc_srcT); + gen_exts(size, cpu_tmp4); + t0 = gen_ext_tl(cpu_tmp0, cpu_cc_src, size, true); + cc = (CCPrepare) { .cond = cond, .reg = cpu_tmp4, + .reg2 = t0, .mask = -1, .use_reg2 = true }; break; - - default: - goto slow_jcc; - } - break; - - /* some jumps are easy to compute */ - case CC_OP_ADDB: - case CC_OP_ADDW: - case CC_OP_ADDL: - case CC_OP_ADDQ: - - case CC_OP_ADCB: - case CC_OP_ADCW: - case CC_OP_ADCL: - case CC_OP_ADCQ: - - case CC_OP_SBBB: - case CC_OP_SBBW: - case CC_OP_SBBL: - case CC_OP_SBBQ: - - case CC_OP_LOGICB: - case CC_OP_LOGICW: - case CC_OP_LOGICL: - case CC_OP_LOGICQ: - - case CC_OP_INCB: - case CC_OP_INCW: - case CC_OP_INCL: - case CC_OP_INCQ: - - case CC_OP_DECB: - case CC_OP_DECW: - case CC_OP_DECL: - case CC_OP_DECQ: - - case CC_OP_SHLB: - case CC_OP_SHLW: - case CC_OP_SHLL: - case CC_OP_SHLQ: - - case CC_OP_SARB: - case CC_OP_SARW: - case CC_OP_SARL: - case CC_OP_SARQ: - switch(jcc_op) { - case JCC_Z: - size = (cc_op - CC_OP_ADDB) & 3; - goto fast_jcc_z; - case JCC_S: - size = (cc_op - CC_OP_ADDB) & 3; - goto fast_jcc_s; + default: goto slow_jcc; } break; + default: slow_jcc: - gen_setcc_slow_T0(s, jcc_op); - tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, - cpu_T[0], 0, l1); + /* This actually generates good code for JC, JZ and JS. */ + switch (jcc_op) { + case JCC_O: + cc = gen_prepare_eflags_o(s, reg); + break; + case JCC_B: + cc = gen_prepare_eflags_c(s, reg); + break; + case JCC_Z: + cc = gen_prepare_eflags_z(s, reg); + break; + case JCC_BE: + gen_compute_eflags(s); + cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = CC_Z | CC_C }; + break; + case JCC_S: + cc = gen_prepare_eflags_s(s, reg); + break; + case JCC_P: + cc = gen_prepare_eflags_p(s, reg); + break; + case JCC_L: + gen_compute_eflags(s); + if (TCGV_EQUAL(reg, cpu_cc_src)) { + reg = cpu_tmp0; + } + tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ + tcg_gen_xor_tl(reg, reg, cpu_cc_src); + cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, + .mask = CC_S }; + break; + default: + case JCC_LE: + gen_compute_eflags(s); + if (TCGV_EQUAL(reg, cpu_cc_src)) { + reg = cpu_tmp0; + } + tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ + tcg_gen_xor_tl(reg, reg, cpu_cc_src); + cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, + .mask = CC_S | CC_Z }; + break; + } break; } + + if (inv) { + cc.cond = tcg_invert_cond(cc.cond); + } + return cc; +} + +static void gen_setcc1(DisasContext *s, int b, TCGv reg) +{ + CCPrepare cc = gen_prepare_cc(s, b, reg); + + if (cc.no_setcond) { + if (cc.cond == TCG_COND_EQ) { + tcg_gen_xori_tl(reg, cc.reg, 1); + } else { + tcg_gen_mov_tl(reg, cc.reg); + } + return; + } + + if (cc.cond == TCG_COND_NE && !cc.use_reg2 && cc.imm == 0 && + cc.mask != 0 && (cc.mask & (cc.mask - 1)) == 0) { + tcg_gen_shri_tl(reg, cc.reg, ctztl(cc.mask)); + tcg_gen_andi_tl(reg, reg, 1); + return; + } + if (cc.mask != -1) { + tcg_gen_andi_tl(reg, cc.reg, cc.mask); + cc.reg = reg; + } + if (cc.use_reg2) { + tcg_gen_setcond_tl(cc.cond, reg, cc.reg, cc.reg2); + } else { + tcg_gen_setcondi_tl(cc.cond, reg, cc.reg, cc.imm); + } +} + +static inline void gen_compute_eflags_c(DisasContext *s, TCGv reg) +{ + gen_setcc1(s, JCC_B << 1, reg); +} + +/* generate a conditional jump to label 'l1' according to jump opcode + value 'b'. In the fast case, T0 is guaranted not to be used. */ +static inline void gen_jcc1_noeob(DisasContext *s, int b, int l1) +{ + CCPrepare cc = gen_prepare_cc(s, b, cpu_T[0]); + + if (cc.mask != -1) { + tcg_gen_andi_tl(cpu_T[0], cc.reg, cc.mask); + cc.reg = cpu_T[0]; + } + if (cc.use_reg2) { + tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); + } else { + tcg_gen_brcondi_tl(cc.cond, cc.reg, cc.imm, l1); + } +} + +/* Generate a conditional jump to label 'l1' according to jump opcode + value 'b'. In the fast case, T0 is guaranted not to be used. + A translation block must end soon. */ +static inline void gen_jcc1(DisasContext *s, int b, int l1) +{ + CCPrepare cc = gen_prepare_cc(s, b, cpu_T[0]); + + gen_update_cc_op(s); + if (cc.mask != -1) { + tcg_gen_andi_tl(cpu_T[0], cc.reg, cc.mask); + cc.reg = cpu_T[0]; + } + set_cc_op(s, CC_OP_DYNAMIC); + if (cc.use_reg2) { + tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); + } else { + tcg_gen_brcondi_tl(cc.cond, cc.reg, cc.imm, l1); + } } /* XXX: does not work with gdbstub "ice" single step - not a @@ -1168,21 +1310,19 @@ static inline void gen_lods(DisasContext *s, int ot) static inline void gen_scas(DisasContext *s, int ot) { - gen_op_mov_TN_reg(OT_LONG, 0, R_EAX); gen_string_movl_A0_EDI(s); gen_op_ld_T1_A0(ot + s->mem_index); - gen_op_cmpl_T0_T1_cc(); + gen_op(s, OP_CMPL, ot, R_EAX); gen_op_movl_T0_Dshift(ot); gen_op_add_reg_T0(s->aflag, R_EDI); } static inline void gen_cmps(DisasContext *s, int ot) { - gen_string_movl_A0_ESI(s); - gen_op_ld_T0_A0(ot + s->mem_index); gen_string_movl_A0_EDI(s); gen_op_ld_T1_A0(ot + s->mem_index); - gen_op_cmpl_T0_T1_cc(); + gen_string_movl_A0_ESI(s); + gen_op(s, OP_CMPL, ot, OR_TMP0); gen_op_movl_T0_Dshift(ot); gen_op_add_reg_T0(s->aflag, R_ESI); gen_op_add_reg_T0(s->aflag, R_EDI); @@ -1256,8 +1396,8 @@ static inline void gen_repz_ ## op(DisasContext *s, int ot, \ l2 = gen_jz_ecx_string(s, next_eip); \ gen_ ## op(s, ot); \ gen_op_add_reg_im(s->aflag, R_ECX, -1); \ - gen_op_set_cc_op(CC_OP_SUBB + ot); \ - gen_jcc1(s, CC_OP_SUBB + ot, (JCC_Z << 1) | (nz ^ 1), l2); \ + gen_update_cc_op(s); \ + gen_jcc1(s, (JCC_Z << 1) | (nz ^ 1), l2); \ if (!s->jmp_opt) \ gen_op_jz_ecx(s->aflag, l2); \ gen_jmp(s, cur_eip); \ @@ -1337,38 +1477,26 @@ static void gen_op(DisasContext *s1, int op, int ot, int d) } switch(op) { case OP_ADCL: - if (s1->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s1->cc_op); - gen_compute_eflags_c(cpu_tmp4); + gen_compute_eflags_c(s1, cpu_tmp4); tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]); tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_tmp4); if (d != OR_TMP0) gen_op_mov_reg_T0(ot, d); else gen_op_st_T0_A0(ot + s1->mem_index); - tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_tmp4); - tcg_gen_shli_i32(cpu_tmp2_i32, cpu_tmp2_i32, 2); - tcg_gen_addi_i32(cpu_cc_op, cpu_tmp2_i32, CC_OP_ADDB + ot); - s1->cc_op = CC_OP_DYNAMIC; + gen_op_update3_cc(cpu_tmp4); + set_cc_op(s1, CC_OP_ADCB + ot); break; case OP_SBBL: - if (s1->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s1->cc_op); - gen_compute_eflags_c(cpu_tmp4); + gen_compute_eflags_c(s1, cpu_tmp4); tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_T[1]); tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_tmp4); if (d != OR_TMP0) gen_op_mov_reg_T0(ot, d); else gen_op_st_T0_A0(ot + s1->mem_index); - tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_tmp4); - tcg_gen_shli_i32(cpu_tmp2_i32, cpu_tmp2_i32, 2); - tcg_gen_addi_i32(cpu_cc_op, cpu_tmp2_i32, CC_OP_SUBB + ot); - s1->cc_op = CC_OP_DYNAMIC; + gen_op_update3_cc(cpu_tmp4); + set_cc_op(s1, CC_OP_SBBB + ot); break; case OP_ADDL: gen_op_addl_T0_T1(); @@ -1377,16 +1505,17 @@ static void gen_op(DisasContext *s1, int op, int ot, int d) else gen_op_st_T0_A0(ot + s1->mem_index); gen_op_update2_cc(); - s1->cc_op = CC_OP_ADDB + ot; + set_cc_op(s1, CC_OP_ADDB + ot); break; case OP_SUBL: + tcg_gen_mov_tl(cpu_cc_srcT, cpu_T[0]); tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_T[1]); if (d != OR_TMP0) gen_op_mov_reg_T0(ot, d); else gen_op_st_T0_A0(ot + s1->mem_index); gen_op_update2_cc(); - s1->cc_op = CC_OP_SUBB + ot; + set_cc_op(s1, CC_OP_SUBB + ot); break; default: case OP_ANDL: @@ -1396,7 +1525,7 @@ static void gen_op(DisasContext *s1, int op, int ot, int d) else gen_op_st_T0_A0(ot + s1->mem_index); gen_op_update1_cc(); - s1->cc_op = CC_OP_LOGICB + ot; + set_cc_op(s1, CC_OP_LOGICB + ot); break; case OP_ORL: tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_T[1]); @@ -1405,7 +1534,7 @@ static void gen_op(DisasContext *s1, int op, int ot, int d) else gen_op_st_T0_A0(ot + s1->mem_index); gen_op_update1_cc(); - s1->cc_op = CC_OP_LOGICB + ot; + set_cc_op(s1, CC_OP_LOGICB + ot); break; case OP_XORL: tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_T[1]); @@ -1414,11 +1543,13 @@ static void gen_op(DisasContext *s1, int op, int ot, int d) else gen_op_st_T0_A0(ot + s1->mem_index); gen_op_update1_cc(); - s1->cc_op = CC_OP_LOGICB + ot; + set_cc_op(s1, CC_OP_LOGICB + ot); break; case OP_CMPL: - gen_op_cmpl_T0_T1_cc(); - s1->cc_op = CC_OP_SUBB + ot; + tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]); + tcg_gen_mov_tl(cpu_cc_srcT, cpu_T[0]); + tcg_gen_sub_tl(cpu_cc_dst, cpu_T[0], cpu_T[1]); + set_cc_op(s1, CC_OP_SUBB + ot); break; } } @@ -1430,35 +1561,70 @@ static void gen_inc(DisasContext *s1, int ot, int d, int c) gen_op_mov_TN_reg(ot, 0, d); else gen_op_ld_T0_A0(ot + s1->mem_index); - if (s1->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s1->cc_op); + gen_compute_eflags_c(s1, cpu_cc_src); if (c > 0) { tcg_gen_addi_tl(cpu_T[0], cpu_T[0], 1); - s1->cc_op = CC_OP_INCB + ot; + set_cc_op(s1, CC_OP_INCB + ot); } else { tcg_gen_addi_tl(cpu_T[0], cpu_T[0], -1); - s1->cc_op = CC_OP_DECB + ot; + set_cc_op(s1, CC_OP_DECB + ot); } if (d != OR_TMP0) gen_op_mov_reg_T0(ot, d); else gen_op_st_T0_A0(ot + s1->mem_index); - gen_compute_eflags_c(cpu_cc_src); tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); } +static void gen_shift_flags(DisasContext *s, int ot, TCGv result, TCGv shm1, + TCGv count, bool is_right) +{ + TCGv_i32 z32, s32, oldop; + TCGv z_tl; + + /* Store the results into the CC variables. If we know that the + variable must be dead, store unconditionally. Otherwise we'll + need to not disrupt the current contents. */ + z_tl = tcg_const_tl(0); + if (cc_op_live[s->cc_op] & USES_CC_DST) { + tcg_gen_movcond_tl(TCG_COND_NE, cpu_cc_dst, count, z_tl, + result, cpu_cc_dst); + } else { + tcg_gen_mov_tl(cpu_cc_dst, result); + } + if (cc_op_live[s->cc_op] & USES_CC_SRC) { + tcg_gen_movcond_tl(TCG_COND_NE, cpu_cc_src, count, z_tl, + shm1, cpu_cc_src); + } else { + tcg_gen_mov_tl(cpu_cc_src, shm1); + } + tcg_temp_free(z_tl); + + /* Get the two potential CC_OP values into temporaries. */ + tcg_gen_movi_i32(cpu_tmp2_i32, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); + if (s->cc_op == CC_OP_DYNAMIC) { + oldop = cpu_cc_op; + } else { + tcg_gen_movi_i32(cpu_tmp3_i32, s->cc_op); + oldop = cpu_tmp3_i32; + } + + /* Conditionally store the CC_OP value. */ + z32 = tcg_const_i32(0); + s32 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(s32, count); + tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, s32, z32, cpu_tmp2_i32, oldop); + tcg_temp_free_i32(z32); + tcg_temp_free_i32(s32); + + /* The CC_OP value is no longer predictable. */ + set_cc_op(s, CC_OP_DYNAMIC); +} + static void gen_shift_rm_T1(DisasContext *s, int ot, int op1, int is_right, int is_arith) { - target_ulong mask; - int shift_label; - TCGv t0, t1, t2; - - if (ot == OT_QUAD) { - mask = 0x3f; - } else { - mask = 0x1f; - } + target_ulong mask = (ot == OT_QUAD ? 0x3f : 0x1f); /* load */ if (op1 == OR_TMP0) { @@ -1467,25 +1633,22 @@ static void gen_shift_rm_T1(DisasContext *s, int ot, int op1, gen_op_mov_TN_reg(ot, 0, op1); } - t0 = tcg_temp_local_new(); - t1 = tcg_temp_local_new(); - t2 = tcg_temp_local_new(); - - tcg_gen_andi_tl(t2, cpu_T[1], mask); + tcg_gen_andi_tl(cpu_T[1], cpu_T[1], mask); + tcg_gen_subi_tl(cpu_tmp0, cpu_T[1], 1); if (is_right) { if (is_arith) { gen_exts(ot, cpu_T[0]); - tcg_gen_mov_tl(t0, cpu_T[0]); - tcg_gen_sar_tl(cpu_T[0], cpu_T[0], t2); + tcg_gen_sar_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); + tcg_gen_sar_tl(cpu_T[0], cpu_T[0], cpu_T[1]); } else { gen_extu(ot, cpu_T[0]); - tcg_gen_mov_tl(t0, cpu_T[0]); - tcg_gen_shr_tl(cpu_T[0], cpu_T[0], t2); + tcg_gen_shr_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); + tcg_gen_shr_tl(cpu_T[0], cpu_T[0], cpu_T[1]); } } else { - tcg_gen_mov_tl(t0, cpu_T[0]); - tcg_gen_shl_tl(cpu_T[0], cpu_T[0], t2); + tcg_gen_shl_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); + tcg_gen_shl_tl(cpu_T[0], cpu_T[0], cpu_T[1]); } /* store */ @@ -1495,52 +1658,13 @@ static void gen_shift_rm_T1(DisasContext *s, int ot, int op1, gen_op_mov_reg_T0(ot, op1); } - /* update eflags if non zero shift */ - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - } - - tcg_gen_mov_tl(t1, cpu_T[0]); - - shift_label = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, shift_label); - - tcg_gen_addi_tl(t2, t2, -1); - tcg_gen_mov_tl(cpu_cc_dst, t1); - - if (is_right) { - if (is_arith) { - tcg_gen_sar_tl(cpu_cc_src, t0, t2); - } else { - tcg_gen_shr_tl(cpu_cc_src, t0, t2); - } - } else { - tcg_gen_shl_tl(cpu_cc_src, t0, t2); - } - - if (is_right) { - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SARB + ot); - } else { - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SHLB + ot); - } - - gen_set_label(shift_label); - s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */ - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); + gen_shift_flags(s, ot, cpu_T[0], cpu_tmp0, cpu_T[1], is_right); } static void gen_shift_rm_im(DisasContext *s, int ot, int op1, int op2, int is_right, int is_arith) { - int mask; - - if (ot == OT_QUAD) - mask = 0x3f; - else - mask = 0x1f; + int mask = (ot == OT_QUAD ? 0x3f : 0x1f); /* load */ if (op1 == OR_TMP0) @@ -1576,10 +1700,7 @@ static void gen_shift_rm_im(DisasContext *s, int ot, int op1, int op2, if (op2 != 0) { tcg_gen_mov_tl(cpu_cc_src, cpu_tmp4); tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - if (is_right) - s->cc_op = CC_OP_SARB + ot; - else - s->cc_op = CC_OP_SHLB + ot; + set_cc_op(s, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); } } @@ -1591,187 +1712,180 @@ static inline void tcg_gen_lshift(TCGv ret, TCGv arg1, target_long arg2) tcg_gen_shri_tl(ret, arg1, -arg2); } -static void gen_rot_rm_T1(DisasContext *s, int ot, int op1, - int is_right) +static void gen_rot_rm_T1(DisasContext *s, int ot, int op1, int is_right) { - target_ulong mask; - int label1, label2, data_bits; - TCGv t0, t1, t2, a0; - - /* XXX: inefficient, but we must use local temps */ - t0 = tcg_temp_local_new(); - t1 = tcg_temp_local_new(); - t2 = tcg_temp_local_new(); - a0 = tcg_temp_local_new(); - - if (ot == OT_QUAD) - mask = 0x3f; - else - mask = 0x1f; + target_ulong mask = (ot == OT_QUAD ? 0x3f : 0x1f); + TCGv_i32 t0, t1; /* load */ if (op1 == OR_TMP0) { - tcg_gen_mov_tl(a0, cpu_A0); - gen_op_ld_v(ot + s->mem_index, t0, a0); + gen_op_ld_T0_A0(ot + s->mem_index); } else { - gen_op_mov_v_reg(ot, t0, op1); + gen_op_mov_TN_reg(ot, 0, op1); } - tcg_gen_mov_tl(t1, cpu_T[1]); + tcg_gen_andi_tl(cpu_T[1], cpu_T[1], mask); - tcg_gen_andi_tl(t1, t1, mask); - - /* Must test zero case to avoid using undefined behaviour in TCG - shifts. */ - label1 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, label1); - - if (ot <= OT_WORD) - tcg_gen_andi_tl(cpu_tmp0, t1, (1 << (3 + ot)) - 1); - else - tcg_gen_mov_tl(cpu_tmp0, t1); - - gen_extu(ot, t0); - tcg_gen_mov_tl(t2, t0); - - data_bits = 8 << ot; - /* XXX: rely on behaviour of shifts when operand 2 overflows (XXX: - fix TCG definition) */ - if (is_right) { - tcg_gen_shr_tl(cpu_tmp4, t0, cpu_tmp0); - tcg_gen_subfi_tl(cpu_tmp0, data_bits, cpu_tmp0); - tcg_gen_shl_tl(t0, t0, cpu_tmp0); - } else { - tcg_gen_shl_tl(cpu_tmp4, t0, cpu_tmp0); - tcg_gen_subfi_tl(cpu_tmp0, data_bits, cpu_tmp0); - tcg_gen_shr_tl(t0, t0, cpu_tmp0); + switch (ot) { + case OT_BYTE: + /* Replicate the 8-bit input so that a 32-bit rotate works. */ + tcg_gen_ext8u_tl(cpu_T[0], cpu_T[0]); + tcg_gen_muli_tl(cpu_T[0], cpu_T[0], 0x01010101); + goto do_long; + case OT_WORD: + /* Replicate the 16-bit input so that a 32-bit rotate works. */ + tcg_gen_deposit_tl(cpu_T[0], cpu_T[0], cpu_T[0], 16, 16); + goto do_long; + do_long: +#ifdef TARGET_X86_64 + case OT_LONG: + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); + if (is_right) { + tcg_gen_rotr_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); + } else { + tcg_gen_rotl_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); + } + tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); + break; +#endif + default: + if (is_right) { + tcg_gen_rotr_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + } else { + tcg_gen_rotl_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + } + break; } - tcg_gen_or_tl(t0, t0, cpu_tmp4); - gen_set_label(label1); /* store */ if (op1 == OR_TMP0) { - gen_op_st_v(ot + s->mem_index, t0, a0); + gen_op_st_T0_A0(ot + s->mem_index); } else { - gen_op_mov_reg_v(ot, op1, t0); + gen_op_mov_reg_T0(ot, op1); } - - /* update eflags */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); - label2 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, label2); + /* We'll need the flags computed into CC_SRC. */ + gen_compute_eflags(s); - gen_compute_eflags(cpu_cc_src); - tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~(CC_O | CC_C)); - tcg_gen_xor_tl(cpu_tmp0, t2, t0); - tcg_gen_lshift(cpu_tmp0, cpu_tmp0, 11 - (data_bits - 1)); - tcg_gen_andi_tl(cpu_tmp0, cpu_tmp0, CC_O); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, cpu_tmp0); + /* The value that was "rotated out" is now present at the other end + of the word. Compute C into CC_DST and O into CC_SRC2. Note that + since we've computed the flags into CC_SRC, these variables are + currently dead. */ if (is_right) { - tcg_gen_shri_tl(t0, t0, data_bits - 1); + tcg_gen_shri_tl(cpu_cc_src2, cpu_T[0], mask - 1); + tcg_gen_shri_tl(cpu_cc_dst, cpu_T[0], mask); + } else { + tcg_gen_shri_tl(cpu_cc_src2, cpu_T[0], mask); + tcg_gen_andi_tl(cpu_cc_dst, cpu_T[0], 1); } - tcg_gen_andi_tl(t0, t0, CC_C); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t0); - - tcg_gen_discard_tl(cpu_cc_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_EFLAGS); - - gen_set_label(label2); - s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */ + tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); + tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - tcg_temp_free(a0); + /* Now conditionally store the new CC_OP value. If the shift count + is 0 we keep the CC_OP_EFLAGS setting so that only CC_SRC is live. + Otherwise reuse CC_OP_ADCOX which have the C and O flags split out + exactly as we computed above. */ + t0 = tcg_const_i32(0); + t1 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t1, cpu_T[1]); + tcg_gen_movi_i32(cpu_tmp2_i32, CC_OP_ADCOX); + tcg_gen_movi_i32(cpu_tmp3_i32, CC_OP_EFLAGS); + tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, t1, t0, + cpu_tmp2_i32, cpu_tmp3_i32); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + + /* The CC_OP value is no longer predictable. */ + set_cc_op(s, CC_OP_DYNAMIC); } static void gen_rot_rm_im(DisasContext *s, int ot, int op1, int op2, int is_right) { - int mask; - int data_bits; - TCGv t0, t1, a0; - - /* XXX: inefficient, but we must use local temps */ - t0 = tcg_temp_local_new(); - t1 = tcg_temp_local_new(); - a0 = tcg_temp_local_new(); - - if (ot == OT_QUAD) - mask = 0x3f; - else - mask = 0x1f; + int mask = (ot == OT_QUAD ? 0x3f : 0x1f); + int shift; /* load */ if (op1 == OR_TMP0) { - tcg_gen_mov_tl(a0, cpu_A0); - gen_op_ld_v(ot + s->mem_index, t0, a0); + gen_op_ld_T0_A0(ot + s->mem_index); } else { - gen_op_mov_v_reg(ot, t0, op1); + gen_op_mov_TN_reg(ot, 0, op1); } - gen_extu(ot, t0); - tcg_gen_mov_tl(t1, t0); - op2 &= mask; - data_bits = 8 << ot; if (op2 != 0) { - int shift = op2 & ((1 << (3 + ot)) - 1); - if (is_right) { - tcg_gen_shri_tl(cpu_tmp4, t0, shift); - tcg_gen_shli_tl(t0, t0, data_bits - shift); + switch (ot) { +#ifdef TARGET_X86_64 + case OT_LONG: + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + if (is_right) { + tcg_gen_rotri_i32(cpu_tmp2_i32, cpu_tmp2_i32, op2); + } else { + tcg_gen_rotli_i32(cpu_tmp2_i32, cpu_tmp2_i32, op2); + } + tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); + break; +#endif + default: + if (is_right) { + tcg_gen_rotri_tl(cpu_T[0], cpu_T[0], op2); + } else { + tcg_gen_rotli_tl(cpu_T[0], cpu_T[0], op2); + } + break; + case OT_BYTE: + mask = 7; + goto do_shifts; + case OT_WORD: + mask = 15; + do_shifts: + shift = op2 & mask; + if (is_right) { + shift = mask + 1 - shift; + } + gen_extu(ot, cpu_T[0]); + tcg_gen_shli_tl(cpu_tmp0, cpu_T[0], shift); + tcg_gen_shri_tl(cpu_T[0], cpu_T[0], mask + 1 - shift); + tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_tmp0); + break; } - else { - tcg_gen_shli_tl(cpu_tmp4, t0, shift); - tcg_gen_shri_tl(t0, t0, data_bits - shift); - } - tcg_gen_or_tl(t0, t0, cpu_tmp4); } /* store */ if (op1 == OR_TMP0) { - gen_op_st_v(ot + s->mem_index, t0, a0); + gen_op_st_T0_A0(ot + s->mem_index); } else { - gen_op_mov_reg_v(ot, op1, t0); + gen_op_mov_reg_T0(ot, op1); } if (op2 != 0) { - /* update eflags */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + /* Compute the flags into CC_SRC. */ + gen_compute_eflags(s); - gen_compute_eflags(cpu_cc_src); - tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~(CC_O | CC_C)); - tcg_gen_xor_tl(cpu_tmp0, t1, t0); - tcg_gen_lshift(cpu_tmp0, cpu_tmp0, 11 - (data_bits - 1)); - tcg_gen_andi_tl(cpu_tmp0, cpu_tmp0, CC_O); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, cpu_tmp0); + /* The value that was "rotated out" is now present at the other end + of the word. Compute C into CC_DST and O into CC_SRC2. Note that + since we've computed the flags into CC_SRC, these variables are + currently dead. */ if (is_right) { - tcg_gen_shri_tl(t0, t0, data_bits - 1); + tcg_gen_shri_tl(cpu_cc_src2, cpu_T[0], mask - 1); + tcg_gen_shri_tl(cpu_cc_dst, cpu_T[0], mask); + } else { + tcg_gen_shri_tl(cpu_cc_src2, cpu_T[0], mask); + tcg_gen_andi_tl(cpu_cc_dst, cpu_T[0], 1); } - tcg_gen_andi_tl(t0, t0, CC_C); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t0); - - tcg_gen_discard_tl(cpu_cc_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_EFLAGS); - s->cc_op = CC_OP_EFLAGS; + tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); + tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); + set_cc_op(s, CC_OP_ADCOX); } - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(a0); } /* XXX: add faster immediate = 1 case */ static void gen_rotc_rm_T1(DisasContext *s, int ot, int op1, int is_right) { - int label1; - - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_compute_eflags(s); + assert(s->cc_op == CC_OP_EFLAGS); /* load */ if (op1 == OR_TMP0) @@ -1781,34 +1895,34 @@ static void gen_rotc_rm_T1(DisasContext *s, int ot, int op1, if (is_right) { switch (ot) { - case 0: + case OT_BYTE: gen_helper_rcrb(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); break; - case 1: + case OT_WORD: gen_helper_rcrw(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); break; - case 2: + case OT_LONG: gen_helper_rcrl(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); break; #ifdef TARGET_X86_64 - case 3: + case OT_QUAD: gen_helper_rcrq(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); break; #endif } } else { switch (ot) { - case 0: + case OT_BYTE: gen_helper_rclb(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); break; - case 1: + case OT_WORD: gen_helper_rclw(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); break; - case 2: + case OT_LONG: gen_helper_rcll(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); break; #ifdef TARGET_X86_64 - case 3: + case OT_QUAD: gen_helper_rclq(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); break; #endif @@ -1819,146 +1933,92 @@ static void gen_rotc_rm_T1(DisasContext *s, int ot, int op1, gen_op_st_T0_A0(ot + s->mem_index); else gen_op_mov_reg_T0(ot, op1); - - /* update eflags */ - label1 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_cc_tmp, -1, label1); - - tcg_gen_mov_tl(cpu_cc_src, cpu_cc_tmp); - tcg_gen_discard_tl(cpu_cc_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_EFLAGS); - - gen_set_label(label1); - s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */ } /* XXX: add faster immediate case */ -static void gen_shiftd_rm_T1_T3(DisasContext *s, int ot, int op1, - int is_right) +static void gen_shiftd_rm_T1(DisasContext *s, int ot, int op1, + bool is_right, TCGv count_in) { - int label1, label2, data_bits; - target_ulong mask; - TCGv t0, t1, t2, a0; - - t0 = tcg_temp_local_new(); - t1 = tcg_temp_local_new(); - t2 = tcg_temp_local_new(); - a0 = tcg_temp_local_new(); - - if (ot == OT_QUAD) - mask = 0x3f; - else - mask = 0x1f; + target_ulong mask = (ot == OT_QUAD ? 63 : 31); + TCGv count; /* load */ if (op1 == OR_TMP0) { - tcg_gen_mov_tl(a0, cpu_A0); - gen_op_ld_v(ot + s->mem_index, t0, a0); + gen_op_ld_T0_A0(ot + s->mem_index); } else { - gen_op_mov_v_reg(ot, t0, op1); + gen_op_mov_TN_reg(ot, 0, op1); } - tcg_gen_andi_tl(cpu_T3, cpu_T3, mask); + count = tcg_temp_new(); + tcg_gen_andi_tl(count, count_in, mask); - tcg_gen_mov_tl(t1, cpu_T[1]); - tcg_gen_mov_tl(t2, cpu_T3); - - /* Must test zero case to avoid using undefined behaviour in TCG - shifts. */ - label1 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, label1); - - tcg_gen_addi_tl(cpu_tmp5, t2, -1); - if (ot == OT_WORD) { - /* Note: we implement the Intel behaviour for shift count > 16 */ + switch (ot) { + case OT_WORD: + /* Note: we implement the Intel behaviour for shift count > 16. + This means "shrdw C, B, A" shifts A:B:A >> C. Build the B:A + portion by constructing it as a 32-bit value. */ if (is_right) { - tcg_gen_andi_tl(t0, t0, 0xffff); - tcg_gen_shli_tl(cpu_tmp0, t1, 16); - tcg_gen_or_tl(t0, t0, cpu_tmp0); - tcg_gen_ext32u_tl(t0, t0); - - tcg_gen_shr_tl(cpu_tmp4, t0, cpu_tmp5); - - /* only needed if count > 16, but a test would complicate */ - tcg_gen_subfi_tl(cpu_tmp5, 32, t2); - tcg_gen_shl_tl(cpu_tmp0, t0, cpu_tmp5); - - tcg_gen_shr_tl(t0, t0, t2); - - tcg_gen_or_tl(t0, t0, cpu_tmp0); + tcg_gen_deposit_tl(cpu_tmp0, cpu_T[0], cpu_T[1], 16, 16); + tcg_gen_mov_tl(cpu_T[1], cpu_T[0]); + tcg_gen_mov_tl(cpu_T[0], cpu_tmp0); } else { - /* XXX: not optimal */ - tcg_gen_andi_tl(t0, t0, 0xffff); - tcg_gen_shli_tl(t1, t1, 16); - tcg_gen_or_tl(t1, t1, t0); - tcg_gen_ext32u_tl(t1, t1); - - tcg_gen_shl_tl(cpu_tmp4, t0, cpu_tmp5); - tcg_gen_subfi_tl(cpu_tmp0, 32, cpu_tmp5); - tcg_gen_shr_tl(cpu_tmp5, t1, cpu_tmp0); - tcg_gen_or_tl(cpu_tmp4, cpu_tmp4, cpu_tmp5); - - tcg_gen_shl_tl(t0, t0, t2); - tcg_gen_subfi_tl(cpu_tmp5, 32, t2); - tcg_gen_shr_tl(t1, t1, cpu_tmp5); - tcg_gen_or_tl(t0, t0, t1); + tcg_gen_deposit_tl(cpu_T[1], cpu_T[0], cpu_T[1], 16, 16); } - } else { - data_bits = 8 << ot; + /* FALLTHRU */ +#ifdef TARGET_X86_64 + case OT_LONG: + /* Concatenate the two 32-bit values and use a 64-bit shift. */ + tcg_gen_subi_tl(cpu_tmp0, count, 1); if (is_right) { - if (ot == OT_LONG) - tcg_gen_ext32u_tl(t0, t0); - - tcg_gen_shr_tl(cpu_tmp4, t0, cpu_tmp5); - - tcg_gen_shr_tl(t0, t0, t2); - tcg_gen_subfi_tl(cpu_tmp5, data_bits, t2); - tcg_gen_shl_tl(t1, t1, cpu_tmp5); - tcg_gen_or_tl(t0, t0, t1); - + tcg_gen_concat_tl_i64(cpu_T[0], cpu_T[0], cpu_T[1]); + tcg_gen_shr_i64(cpu_tmp0, cpu_T[0], cpu_tmp0); + tcg_gen_shr_i64(cpu_T[0], cpu_T[0], count); } else { - if (ot == OT_LONG) - tcg_gen_ext32u_tl(t1, t1); - - tcg_gen_shl_tl(cpu_tmp4, t0, cpu_tmp5); - - tcg_gen_shl_tl(t0, t0, t2); - tcg_gen_subfi_tl(cpu_tmp5, data_bits, t2); - tcg_gen_shr_tl(t1, t1, cpu_tmp5); - tcg_gen_or_tl(t0, t0, t1); + tcg_gen_concat_tl_i64(cpu_T[0], cpu_T[1], cpu_T[0]); + tcg_gen_shl_i64(cpu_tmp0, cpu_T[0], cpu_tmp0); + tcg_gen_shl_i64(cpu_T[0], cpu_T[0], count); + tcg_gen_shri_i64(cpu_tmp0, cpu_tmp0, 32); + tcg_gen_shri_i64(cpu_T[0], cpu_T[0], 32); } + break; +#endif + default: + tcg_gen_subi_tl(cpu_tmp0, count, 1); + if (is_right) { + tcg_gen_shr_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); + + tcg_gen_subfi_tl(cpu_tmp4, mask + 1, count); + tcg_gen_shr_tl(cpu_T[0], cpu_T[0], count); + tcg_gen_shl_tl(cpu_T[1], cpu_T[1], cpu_tmp4); + } else { + tcg_gen_shl_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); + if (ot == OT_WORD) { + /* Only needed if count > 16, for Intel behaviour. */ + tcg_gen_subfi_tl(cpu_tmp4, 33, count); + tcg_gen_shr_tl(cpu_tmp4, cpu_T[1], cpu_tmp4); + tcg_gen_or_tl(cpu_tmp0, cpu_tmp0, cpu_tmp4); + } + + tcg_gen_subfi_tl(cpu_tmp4, mask + 1, count); + tcg_gen_shl_tl(cpu_T[0], cpu_T[0], count); + tcg_gen_shr_tl(cpu_T[1], cpu_T[1], cpu_tmp4); + } + tcg_gen_movi_tl(cpu_tmp4, 0); + tcg_gen_movcond_tl(TCG_COND_EQ, cpu_T[1], count, cpu_tmp4, + cpu_tmp4, cpu_T[1]); + tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + break; } - tcg_gen_mov_tl(t1, cpu_tmp4); - gen_set_label(label1); /* store */ if (op1 == OR_TMP0) { - gen_op_st_v(ot + s->mem_index, t0, a0); + gen_op_st_T0_A0(ot + s->mem_index); } else { - gen_op_mov_reg_v(ot, op1, t0); + gen_op_mov_reg_T0(ot, op1); } - - /* update eflags */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); - label2 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, label2); - - tcg_gen_mov_tl(cpu_cc_src, t1); - tcg_gen_mov_tl(cpu_cc_dst, t0); - if (is_right) { - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SARB + ot); - } else { - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SHLB + ot); - } - gen_set_label(label2); - s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */ - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - tcg_temp_free(a0); + gen_shift_flags(s, ot, cpu_T[0], cpu_tmp0, count, is_right); + tcg_temp_free(count); } static void gen_shift(DisasContext *s1, int op, int ot, int d, int s) @@ -2362,24 +2422,21 @@ static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip) static inline void gen_jcc(DisasContext *s, int b, target_ulong val, target_ulong next_eip) { - int l1, l2, cc_op; + int l1, l2; - cc_op = s->cc_op; - gen_update_cc_op(s); if (s->jmp_opt) { l1 = gen_new_label(); - gen_jcc1(s, cc_op, b, l1); - + gen_jcc1(s, b, l1); + gen_goto_tb(s, 0, next_eip); gen_set_label(l1); gen_goto_tb(s, 1, val); s->is_jmp = DISAS_TB_JUMP; } else { - l1 = gen_new_label(); l2 = gen_new_label(); - gen_jcc1(s, cc_op, b, l1); + gen_jcc1(s, b, l1); gen_jmp_im(next_eip); tcg_gen_br(l2); @@ -2391,32 +2448,32 @@ static inline void gen_jcc(DisasContext *s, int b, } } -static void gen_setcc(DisasContext *s, int b) +static void gen_cmovcc1(CPUX86State *env, DisasContext *s, int ot, int b, + int modrm, int reg) { - int inv, jcc_op, l1; - TCGv t0; + CCPrepare cc; - if (is_fast_jcc_case(s, b)) { - /* nominal case: we use a jump */ - /* XXX: make it faster by adding new instructions in TCG */ - t0 = tcg_temp_local_new(); - tcg_gen_movi_tl(t0, 0); - l1 = gen_new_label(); - gen_jcc1(s, s->cc_op, b ^ 1, l1); - tcg_gen_movi_tl(t0, 1); - gen_set_label(l1); - tcg_gen_mov_tl(cpu_T[0], t0); - tcg_temp_free(t0); - } else { - /* slow case: it is more efficient not to generate a jump, - although it is questionnable whether this optimization is - worth to */ - inv = b & 1; - jcc_op = (b >> 1) & 7; - gen_setcc_slow_T0(s, jcc_op); - if (inv) { - tcg_gen_xori_tl(cpu_T[0], cpu_T[0], 1); - } + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + + cc = gen_prepare_cc(s, b, cpu_T[1]); + if (cc.mask != -1) { + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cc.reg, cc.mask); + cc.reg = t0; + } + if (!cc.use_reg2) { + cc.reg2 = tcg_const_tl(cc.imm); + } + + tcg_gen_movcond_tl(cc.cond, cpu_T[0], cc.reg, cc.reg2, + cpu_T[0], cpu_regs[reg]); + gen_op_mov_reg_T0(ot, reg); + + if (cc.mask != -1) { + tcg_temp_free(cc.reg); + } + if (!cc.use_reg2) { + tcg_temp_free(cc.reg2); } } @@ -2442,8 +2499,7 @@ static void gen_movl_seg_T0(DisasContext *s, int seg_reg, target_ulong cur_eip) { if (s->pe && !s->vm86) { /* XXX: optimize by finding processor state dynamically */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(cur_eip); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_load_seg(cpu_env, tcg_const_i32(seg_reg), cpu_tmp2_i32); @@ -2472,8 +2528,7 @@ gen_svm_check_intercept_param(DisasContext *s, target_ulong pc_start, /* no SVM activated; fast case */ if (likely(!(s->flags & HF_SVMI_MASK))) return; - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_svm_check_intercept_param(cpu_env, tcg_const_i32(type), tcg_const_i64(param)); @@ -2720,8 +2775,7 @@ static void gen_enter(DisasContext *s, int esp_addend, int level) static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip) { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(cur_eip); gen_helper_raise_exception(cpu_env, tcg_const_i32(trapno)); s->is_jmp = DISAS_TB_JUMP; @@ -2732,8 +2786,7 @@ static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip) static void gen_interrupt(DisasContext *s, int intno, target_ulong cur_eip, target_ulong next_eip) { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(cur_eip); gen_helper_raise_interrupt(cpu_env, tcg_const_i32(intno), tcg_const_i32(next_eip - cur_eip)); @@ -2742,8 +2795,7 @@ static void gen_interrupt(DisasContext *s, int intno, static void gen_debug(DisasContext *s, target_ulong cur_eip) { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(cur_eip); gen_helper_debug(cpu_env); s->is_jmp = DISAS_TB_JUMP; @@ -2753,8 +2805,7 @@ static void gen_debug(DisasContext *s, target_ulong cur_eip) if needed */ static void gen_eob(DisasContext *s) { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); if (s->tb->flags & HF_INHIBIT_IRQ_MASK) { gen_helper_reset_inhibit_irq(cpu_env); } @@ -2775,8 +2826,9 @@ static void gen_eob(DisasContext *s) direct call to the next block may occur */ static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num) { + gen_update_cc_op(s); + set_cc_op(s, CC_OP_DYNAMIC); if (s->jmp_opt) { - gen_update_cc_op(s); gen_goto_tb(s, tb_num, eip); s->is_jmp = DISAS_TB_JUMP; } else { @@ -2912,8 +2964,9 @@ static const SSEFunc_0_epp sse_op_table1[256][4] = { [0xc6] = { (SSEFunc_0_epp)gen_helper_shufps, (SSEFunc_0_epp)gen_helper_shufpd }, /* XXX: casts */ - [0x38] = { SSE_SPECIAL, SSE_SPECIAL, NULL, SSE_SPECIAL }, /* SSSE3/SSE4 */ - [0x3a] = { SSE_SPECIAL, SSE_SPECIAL }, /* SSSE3/SSE4 */ + /* SSSE3, SSE4, MOVBE, CRC32, BMI1, BMI2, ADX. */ + [0x38] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, + [0x3a] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* MMX ops and their SSE extensions */ [0x60] = MMX_OP2(punpcklbw), @@ -3794,11 +3847,13 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, reg = ((modrm >> 3) & 7) | rex_r; gen_op_mov_reg_T0(OT_LONG, reg); break; + case 0x138: - if (s->prefix & PREFIX_REPNZ) - goto crc32; case 0x038: b = modrm; + if ((b & 0xf0) == 0xf0) { + goto do_0f_38_fx; + } modrm = cpu_ldub_code(env, s->pc++); rm = modrm & 7; reg = ((modrm >> 3) & 7) | rex_r; @@ -3867,39 +3922,405 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); sse_fn_epp(cpu_env, cpu_ptr0, cpu_ptr1); - if (b == 0x17) - s->cc_op = CC_OP_EFLAGS; + if (b == 0x17) { + set_cc_op(s, CC_OP_EFLAGS); + } break; - case 0x338: /* crc32 */ - crc32: - b = modrm; + + case 0x238: + case 0x338: + do_0f_38_fx: + /* Various integer extensions at 0f 38 f[0-f]. */ + b = modrm | (b1 << 8); modrm = cpu_ldub_code(env, s->pc++); reg = ((modrm >> 3) & 7) | rex_r; - if (b != 0xf0 && b != 0xf1) + switch (b) { + case 0x3f0: /* crc32 Gd,Eb */ + case 0x3f1: /* crc32 Gd,Ey */ + do_crc32: + if (!(s->cpuid_ext_features & CPUID_EXT_SSE42)) { + goto illegal_op; + } + if ((b & 0xff) == 0xf0) { + ot = OT_BYTE; + } else if (s->dflag != 2) { + ot = (s->prefix & PREFIX_DATA ? OT_WORD : OT_LONG); + } else { + ot = OT_QUAD; + } + + gen_op_mov_TN_reg(OT_LONG, 0, reg); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + gen_helper_crc32(cpu_T[0], cpu_tmp2_i32, + cpu_T[0], tcg_const_i32(8 << ot)); + + ot = (s->dflag == 2) ? OT_QUAD : OT_LONG; + gen_op_mov_reg_T0(ot, reg); + break; + + case 0x1f0: /* crc32 or movbe */ + case 0x1f1: + /* For these insns, the f3 prefix is supposed to have priority + over the 66 prefix, but that's not what we implement above + setting b1. */ + if (s->prefix & PREFIX_REPNZ) { + goto do_crc32; + } + /* FALLTHRU */ + case 0x0f0: /* movbe Gy,My */ + case 0x0f1: /* movbe My,Gy */ + if (!(s->cpuid_ext_features & CPUID_EXT_MOVBE)) { + goto illegal_op; + } + if (s->dflag != 2) { + ot = (s->prefix & PREFIX_DATA ? OT_WORD : OT_LONG); + } else { + ot = OT_QUAD; + } + + /* Load the data incoming to the bswap. Note that the TCG + implementation of bswap requires the input be zero + extended. In the case of the loads, we simply know that + gen_op_ld_v via gen_ldst_modrm does that already. */ + if ((b & 1) == 0) { + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + } else { + switch (ot) { + case OT_WORD: + tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[reg]); + break; + default: + tcg_gen_ext32u_tl(cpu_T[0], cpu_regs[reg]); + break; + case OT_QUAD: + tcg_gen_mov_tl(cpu_T[0], cpu_regs[reg]); + break; + } + } + + switch (ot) { + case OT_WORD: + tcg_gen_bswap16_tl(cpu_T[0], cpu_T[0]); + break; + default: + tcg_gen_bswap32_tl(cpu_T[0], cpu_T[0]); + break; +#ifdef TARGET_X86_64 + case OT_QUAD: + tcg_gen_bswap64_tl(cpu_T[0], cpu_T[0]); + break; +#endif + } + + if ((b & 1) == 0) { + gen_op_mov_reg_T0(ot, reg); + } else { + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); + } + break; + + case 0x0f2: /* andn Gy, By, Ey */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = s->dflag == 2 ? OT_QUAD : OT_LONG; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + tcg_gen_andc_tl(cpu_T[0], cpu_regs[s->vex_v], cpu_T[0]); + gen_op_mov_reg_T0(ot, reg); + gen_op_update1_cc(); + set_cc_op(s, CC_OP_LOGICB + ot); + break; + + case 0x0f7: /* bextr Gy, Ey, By */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = s->dflag == 2 ? OT_QUAD : OT_LONG; + { + TCGv bound, zero; + + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + /* Extract START, and shift the operand. + Shifts larger than operand size get zeros. */ + tcg_gen_ext8u_tl(cpu_A0, cpu_regs[s->vex_v]); + tcg_gen_shr_tl(cpu_T[0], cpu_T[0], cpu_A0); + + bound = tcg_const_tl(ot == OT_QUAD ? 63 : 31); + zero = tcg_const_tl(0); + tcg_gen_movcond_tl(TCG_COND_LEU, cpu_T[0], cpu_A0, bound, + cpu_T[0], zero); + tcg_temp_free(zero); + + /* Extract the LEN into a mask. Lengths larger than + operand size get all ones. */ + tcg_gen_shri_tl(cpu_A0, cpu_regs[s->vex_v], 8); + tcg_gen_ext8u_tl(cpu_A0, cpu_A0); + tcg_gen_movcond_tl(TCG_COND_LEU, cpu_A0, cpu_A0, bound, + cpu_A0, bound); + tcg_temp_free(bound); + tcg_gen_movi_tl(cpu_T[1], 1); + tcg_gen_shl_tl(cpu_T[1], cpu_T[1], cpu_A0); + tcg_gen_subi_tl(cpu_T[1], cpu_T[1], 1); + tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + + gen_op_mov_reg_T0(ot, reg); + gen_op_update1_cc(); + set_cc_op(s, CC_OP_LOGICB + ot); + } + break; + + case 0x0f5: /* bzhi Gy, Ey, By */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = s->dflag == 2 ? OT_QUAD : OT_LONG; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + tcg_gen_ext8u_tl(cpu_T[1], cpu_regs[s->vex_v]); + { + TCGv bound = tcg_const_tl(ot == OT_QUAD ? 63 : 31); + /* Note that since we're using BMILG (in order to get O + cleared) we need to store the inverse into C. */ + tcg_gen_setcond_tl(TCG_COND_LT, cpu_cc_src, + cpu_T[1], bound); + tcg_gen_movcond_tl(TCG_COND_GT, cpu_T[1], cpu_T[1], + bound, bound, cpu_T[1]); + tcg_temp_free(bound); + } + tcg_gen_movi_tl(cpu_A0, -1); + tcg_gen_shl_tl(cpu_A0, cpu_A0, cpu_T[1]); + tcg_gen_andc_tl(cpu_T[0], cpu_T[0], cpu_A0); + gen_op_mov_reg_T0(ot, reg); + gen_op_update1_cc(); + set_cc_op(s, CC_OP_BMILGB + ot); + break; + + case 0x3f6: /* mulx By, Gy, rdx, Ey */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = s->dflag == 2 ? OT_QUAD : OT_LONG; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + switch (ot) { + default: + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_regs[R_EDX]); + tcg_gen_mulu2_i32(cpu_tmp2_i32, cpu_tmp3_i32, + cpu_tmp2_i32, cpu_tmp3_i32); + tcg_gen_extu_i32_tl(cpu_regs[s->vex_v], cpu_tmp2_i32); + tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp3_i32); + break; +#ifdef TARGET_X86_64 + case OT_QUAD: + tcg_gen_mulu2_i64(cpu_regs[s->vex_v], cpu_regs[reg], + cpu_T[0], cpu_regs[R_EDX]); + break; +#endif + } + break; + + case 0x3f5: /* pdep Gy, By, Ey */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = s->dflag == 2 ? OT_QUAD : OT_LONG; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + /* Note that by zero-extending the mask operand, we + automatically handle zero-extending the result. */ + if (s->dflag == 2) { + tcg_gen_mov_tl(cpu_T[1], cpu_regs[s->vex_v]); + } else { + tcg_gen_ext32u_tl(cpu_T[1], cpu_regs[s->vex_v]); + } + gen_helper_pdep(cpu_regs[reg], cpu_T[0], cpu_T[1]); + break; + + case 0x2f5: /* pext Gy, By, Ey */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = s->dflag == 2 ? OT_QUAD : OT_LONG; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + /* Note that by zero-extending the mask operand, we + automatically handle zero-extending the result. */ + if (s->dflag == 2) { + tcg_gen_mov_tl(cpu_T[1], cpu_regs[s->vex_v]); + } else { + tcg_gen_ext32u_tl(cpu_T[1], cpu_regs[s->vex_v]); + } + gen_helper_pext(cpu_regs[reg], cpu_T[0], cpu_T[1]); + break; + + case 0x1f6: /* adcx Gy, Ey */ + case 0x2f6: /* adox Gy, Ey */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_ADX)) { + goto illegal_op; + } else { + TCGv carry_in, carry_out, zero; + int end_op; + + ot = (s->dflag == 2 ? OT_QUAD : OT_LONG); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + + /* Re-use the carry-out from a previous round. */ + TCGV_UNUSED(carry_in); + carry_out = (b == 0x1f6 ? cpu_cc_dst : cpu_cc_src2); + switch (s->cc_op) { + case CC_OP_ADCX: + if (b == 0x1f6) { + carry_in = cpu_cc_dst; + end_op = CC_OP_ADCX; + } else { + end_op = CC_OP_ADCOX; + } + break; + case CC_OP_ADOX: + if (b == 0x1f6) { + end_op = CC_OP_ADCOX; + } else { + carry_in = cpu_cc_src2; + end_op = CC_OP_ADOX; + } + break; + case CC_OP_ADCOX: + end_op = CC_OP_ADCOX; + carry_in = carry_out; + break; + default: + end_op = (b == 0x1f6 ? CC_OP_ADCX : CC_OP_ADCOX); + break; + } + /* If we can't reuse carry-out, get it out of EFLAGS. */ + if (TCGV_IS_UNUSED(carry_in)) { + if (s->cc_op != CC_OP_ADCX && s->cc_op != CC_OP_ADOX) { + gen_compute_eflags(s); + } + carry_in = cpu_tmp0; + tcg_gen_shri_tl(carry_in, cpu_cc_src, + ctz32(b == 0x1f6 ? CC_C : CC_O)); + tcg_gen_andi_tl(carry_in, carry_in, 1); + } + + switch (ot) { +#ifdef TARGET_X86_64 + case OT_LONG: + /* If we know TL is 64-bit, and we want a 32-bit + result, just do everything in 64-bit arithmetic. */ + tcg_gen_ext32u_i64(cpu_regs[reg], cpu_regs[reg]); + tcg_gen_ext32u_i64(cpu_T[0], cpu_T[0]); + tcg_gen_add_i64(cpu_T[0], cpu_T[0], cpu_regs[reg]); + tcg_gen_add_i64(cpu_T[0], cpu_T[0], carry_in); + tcg_gen_ext32u_i64(cpu_regs[reg], cpu_T[0]); + tcg_gen_shri_i64(carry_out, cpu_T[0], 32); + break; +#endif + default: + /* Otherwise compute the carry-out in two steps. */ + zero = tcg_const_tl(0); + tcg_gen_add2_tl(cpu_T[0], carry_out, + cpu_T[0], zero, + carry_in, zero); + tcg_gen_add2_tl(cpu_regs[reg], carry_out, + cpu_regs[reg], carry_out, + cpu_T[0], zero); + tcg_temp_free(zero); + break; + } + set_cc_op(s, end_op); + } + break; + + case 0x1f7: /* shlx Gy, Ey, By */ + case 0x2f7: /* sarx Gy, Ey, By */ + case 0x3f7: /* shrx Gy, Ey, By */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = (s->dflag == 2 ? OT_QUAD : OT_LONG); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + if (ot == OT_QUAD) { + tcg_gen_andi_tl(cpu_T[1], cpu_regs[s->vex_v], 63); + } else { + tcg_gen_andi_tl(cpu_T[1], cpu_regs[s->vex_v], 31); + } + if (b == 0x1f7) { + tcg_gen_shl_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + } else if (b == 0x2f7) { + if (ot != OT_QUAD) { + tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]); + } + tcg_gen_sar_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + } else { + if (ot != OT_QUAD) { + tcg_gen_ext32u_tl(cpu_T[0], cpu_T[0]); + } + tcg_gen_shr_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + } + gen_op_mov_reg_T0(ot, reg); + break; + + case 0x0f3: + case 0x1f3: + case 0x2f3: + case 0x3f3: /* Group 17 */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = s->dflag == 2 ? OT_QUAD : OT_LONG; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + + switch (reg & 7) { + case 1: /* blsr By,Ey */ + tcg_gen_neg_tl(cpu_T[1], cpu_T[0]); + tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + gen_op_mov_reg_T0(ot, s->vex_v); + gen_op_update2_cc(); + set_cc_op(s, CC_OP_BMILGB + ot); + break; + + case 2: /* blsmsk By,Ey */ + tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); + tcg_gen_subi_tl(cpu_T[0], cpu_T[0], 1); + tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_cc_src); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); + set_cc_op(s, CC_OP_BMILGB + ot); + break; + + case 3: /* blsi By, Ey */ + tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); + tcg_gen_subi_tl(cpu_T[0], cpu_T[0], 1); + tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_cc_src); + tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); + set_cc_op(s, CC_OP_BMILGB + ot); + break; + + default: + goto illegal_op; + } + break; + + default: goto illegal_op; - if (!(s->cpuid_ext_features & CPUID_EXT_SSE42)) - goto illegal_op; - - if (b == 0xf0) - ot = OT_BYTE; - else if (b == 0xf1 && s->dflag != 2) - if (s->prefix & PREFIX_DATA) - ot = OT_WORD; - else - ot = OT_LONG; - else - ot = OT_QUAD; - - gen_op_mov_TN_reg(OT_LONG, 0, reg); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_helper_crc32(cpu_T[0], cpu_tmp2_i32, - cpu_T[0], tcg_const_i32(8 << ot)); - - ot = (s->dflag == 2) ? OT_QUAD : OT_LONG; - gen_op_mov_reg_T0(ot, reg); + } break; + case 0x03a: case 0x13a: b = modrm; @@ -4070,7 +4491,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, val = cpu_ldub_code(env, s->pc++); if ((b & 0xfc) == 0x60) { /* pcmpXstrX */ - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); if (s->dflag == 2) /* The helper must use entire 64-bit gp registers */ @@ -4081,6 +4502,38 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); sse_fn_eppi(cpu_env, cpu_ptr0, cpu_ptr1, tcg_const_i32(val)); break; + + case 0x33a: + /* Various integer extensions at 0f 3a f[0-f]. */ + b = modrm | (b1 << 8); + modrm = cpu_ldub_code(env, s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + + switch (b) { + case 0x3f0: /* rorx Gy,Ey, Ib */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = s->dflag == 2 ? OT_QUAD : OT_LONG; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + b = cpu_ldub_code(env, s->pc++); + if (ot == OT_QUAD) { + tcg_gen_rotri_tl(cpu_T[0], cpu_T[0], b & 63); + } else { + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_rotri_i32(cpu_tmp2_i32, cpu_tmp2_i32, b & 31); + tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); + } + gen_op_mov_reg_T0(ot, reg); + break; + + default: + goto illegal_op; + } + break; + default: goto illegal_op; } @@ -4191,7 +4644,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, break; } if (b == 0x2e || b == 0x2f) { - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); } } } @@ -4223,47 +4676,49 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, x86_64_hregs = 0; #endif s->rip_offset = 0; /* for relative ip address */ + s->vex_l = 0; + s->vex_v = 0; next_byte: b = cpu_ldub_code(env, s->pc); s->pc++; - /* check prefixes */ + /* Collect prefixes. */ + switch (b) { + case 0xf3: + prefixes |= PREFIX_REPZ; + goto next_byte; + case 0xf2: + prefixes |= PREFIX_REPNZ; + goto next_byte; + case 0xf0: + prefixes |= PREFIX_LOCK; + goto next_byte; + case 0x2e: + s->override = R_CS; + goto next_byte; + case 0x36: + s->override = R_SS; + goto next_byte; + case 0x3e: + s->override = R_DS; + goto next_byte; + case 0x26: + s->override = R_ES; + goto next_byte; + case 0x64: + s->override = R_FS; + goto next_byte; + case 0x65: + s->override = R_GS; + goto next_byte; + case 0x66: + prefixes |= PREFIX_DATA; + goto next_byte; + case 0x67: + prefixes |= PREFIX_ADR; + goto next_byte; #ifdef TARGET_X86_64 - if (CODE64(s)) { - switch (b) { - case 0xf3: - prefixes |= PREFIX_REPZ; - goto next_byte; - case 0xf2: - prefixes |= PREFIX_REPNZ; - goto next_byte; - case 0xf0: - prefixes |= PREFIX_LOCK; - goto next_byte; - case 0x2e: - s->override = R_CS; - goto next_byte; - case 0x36: - s->override = R_SS; - goto next_byte; - case 0x3e: - s->override = R_DS; - goto next_byte; - case 0x26: - s->override = R_ES; - goto next_byte; - case 0x64: - s->override = R_FS; - goto next_byte; - case 0x65: - s->override = R_GS; - goto next_byte; - case 0x66: - prefixes |= PREFIX_DATA; - goto next_byte; - case 0x67: - prefixes |= PREFIX_ADR; - goto next_byte; - case 0x40 ... 0x4f: + case 0x40 ... 0x4f: + if (CODE64(s)) { /* REX prefix */ rex_w = (b >> 3) & 1; rex_r = (b & 0x4) << 1; @@ -4272,58 +4727,85 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, x86_64_hregs = 1; /* select uniform byte register addressing */ goto next_byte; } + break; +#endif + case 0xc5: /* 2-byte VEX */ + case 0xc4: /* 3-byte VEX */ + /* VEX prefixes cannot be used except in 32-bit mode. + Otherwise the instruction is LES or LDS. */ + if (s->code32 && !s->vm86) { + static const int pp_prefix[4] = { + 0, PREFIX_DATA, PREFIX_REPZ, PREFIX_REPNZ + }; + int vex3, vex2 = cpu_ldub_code(env, s->pc); + + if (!CODE64(s) && (vex2 & 0xc0) != 0xc0) { + /* 4.1.4.6: In 32-bit mode, bits [7:6] must be 11b, + otherwise the instruction is LES or LDS. */ + break; + } + s->pc++; + + /* 4.1.1-4.1.3: No preceeding lock, 66, f2, f3, or rex prefixes. */ + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ + | PREFIX_LOCK | PREFIX_DATA)) { + goto illegal_op; + } +#ifdef TARGET_X86_64 + if (x86_64_hregs) { + goto illegal_op; + } +#endif + rex_r = (~vex2 >> 4) & 8; + if (b == 0xc5) { + vex3 = vex2; + b = cpu_ldub_code(env, s->pc++); + } else { +#ifdef TARGET_X86_64 + s->rex_x = (~vex2 >> 3) & 8; + s->rex_b = (~vex2 >> 2) & 8; +#endif + vex3 = cpu_ldub_code(env, s->pc++); + rex_w = (vex3 >> 7) & 1; + switch (vex2 & 0x1f) { + case 0x01: /* Implied 0f leading opcode bytes. */ + b = cpu_ldub_code(env, s->pc++) | 0x100; + break; + case 0x02: /* Implied 0f 38 leading opcode bytes. */ + b = 0x138; + break; + case 0x03: /* Implied 0f 3a leading opcode bytes. */ + b = 0x13a; + break; + default: /* Reserved for future use. */ + goto illegal_op; + } + } + s->vex_v = (~vex3 >> 3) & 0xf; + s->vex_l = (vex3 >> 2) & 1; + prefixes |= pp_prefix[vex3 & 3] | PREFIX_VEX; + } + break; + } + + /* Post-process prefixes. */ + if (prefixes & PREFIX_DATA) { + dflag ^= 1; + } + if (prefixes & PREFIX_ADR) { + aflag ^= 1; + } +#ifdef TARGET_X86_64 + if (CODE64(s)) { if (rex_w == 1) { /* 0x66 is ignored if rex.w is set */ dflag = 2; - } else { - if (prefixes & PREFIX_DATA) - dflag ^= 1; } - if (!(prefixes & PREFIX_ADR)) + if (!(prefixes & PREFIX_ADR)) { aflag = 2; - } else -#endif - { - switch (b) { - case 0xf3: - prefixes |= PREFIX_REPZ; - goto next_byte; - case 0xf2: - prefixes |= PREFIX_REPNZ; - goto next_byte; - case 0xf0: - prefixes |= PREFIX_LOCK; - goto next_byte; - case 0x2e: - s->override = R_CS; - goto next_byte; - case 0x36: - s->override = R_SS; - goto next_byte; - case 0x3e: - s->override = R_DS; - goto next_byte; - case 0x26: - s->override = R_ES; - goto next_byte; - case 0x64: - s->override = R_FS; - goto next_byte; - case 0x65: - s->override = R_GS; - goto next_byte; - case 0x66: - prefixes |= PREFIX_DATA; - goto next_byte; - case 0x67: - prefixes |= PREFIX_ADR; - goto next_byte; } - if (prefixes & PREFIX_DATA) - dflag ^= 1; - if (prefixes & PREFIX_ADR) - aflag ^= 1; } +#endif s->prefix = prefixes; s->aflag = aflag; @@ -4374,10 +4856,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else if (op == OP_XORL && rm == reg) { xor_zero: /* xor reg, reg optimisation */ + set_cc_op(s, CC_OP_CLR); gen_op_movl_T0_0(); - s->cc_op = CC_OP_LOGICB + ot; gen_op_mov_reg_T0(ot, reg); - gen_op_update1_cc(); break; } else { opreg = rm; @@ -4490,7 +4971,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, val = insn_get(env, s, ot); gen_op_movl_T1_im(val); gen_op_testl_T0_T1_cc(); - s->cc_op = CC_OP_LOGICB + ot; + set_cc_op(s, CC_OP_LOGICB + ot); break; case 2: /* not */ tcg_gen_not_tl(cpu_T[0], cpu_T[0]); @@ -4508,7 +4989,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_op_mov_reg_T0(ot, rm); } gen_op_update_neg_cc(); - s->cc_op = CC_OP_SUBB + ot; + set_cc_op(s, CC_OP_SUBB + ot); break; case 4: /* mul */ switch(ot) { @@ -4521,7 +5002,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_op_mov_reg_T0(OT_WORD, R_EAX); tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); tcg_gen_andi_tl(cpu_cc_src, cpu_T[0], 0xff00); - s->cc_op = CC_OP_MULB; + set_cc_op(s, CC_OP_MULB); break; case OT_WORD: gen_op_mov_TN_reg(OT_WORD, 1, R_EAX); @@ -4534,44 +5015,27 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 16); gen_op_mov_reg_T0(OT_WORD, R_EDX); tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); - s->cc_op = CC_OP_MULW; + set_cc_op(s, CC_OP_MULW); break; default: case OT_LONG: -#ifdef TARGET_X86_64 - gen_op_mov_TN_reg(OT_LONG, 1, R_EAX); - tcg_gen_ext32u_tl(cpu_T[0], cpu_T[0]); - tcg_gen_ext32u_tl(cpu_T[1], cpu_T[1]); - tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]); - gen_op_mov_reg_T0(OT_LONG, R_EAX); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 32); - gen_op_mov_reg_T0(OT_LONG, R_EDX); - tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); -#else - { - TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - gen_op_mov_TN_reg(OT_LONG, 1, R_EAX); - tcg_gen_extu_i32_i64(t0, cpu_T[0]); - tcg_gen_extu_i32_i64(t1, cpu_T[1]); - tcg_gen_mul_i64(t0, t0, t1); - tcg_gen_trunc_i64_i32(cpu_T[0], t0); - gen_op_mov_reg_T0(OT_LONG, R_EAX); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_trunc_i64_i32(cpu_T[0], t0); - gen_op_mov_reg_T0(OT_LONG, R_EDX); - tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); - } -#endif - s->cc_op = CC_OP_MULL; + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_regs[R_EAX]); + tcg_gen_mulu2_i32(cpu_tmp2_i32, cpu_tmp3_i32, + cpu_tmp2_i32, cpu_tmp3_i32); + tcg_gen_extu_i32_tl(cpu_regs[R_EAX], cpu_tmp2_i32); + tcg_gen_extu_i32_tl(cpu_regs[R_EDX], cpu_tmp3_i32); + tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); + tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); + set_cc_op(s, CC_OP_MULL); break; #ifdef TARGET_X86_64 case OT_QUAD: - gen_helper_mulq_EAX_T0(cpu_env, cpu_T[0]); - s->cc_op = CC_OP_MULQ; + tcg_gen_mulu2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], + cpu_T[0], cpu_regs[R_EAX]); + tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); + tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); + set_cc_op(s, CC_OP_MULQ); break; #endif } @@ -4588,7 +5052,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); tcg_gen_ext8s_tl(cpu_tmp0, cpu_T[0]); tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0); - s->cc_op = CC_OP_MULB; + set_cc_op(s, CC_OP_MULB); break; case OT_WORD: gen_op_mov_TN_reg(OT_WORD, 1, R_EAX); @@ -4602,46 +5066,30 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0); tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 16); gen_op_mov_reg_T0(OT_WORD, R_EDX); - s->cc_op = CC_OP_MULW; + set_cc_op(s, CC_OP_MULW); break; default: case OT_LONG: -#ifdef TARGET_X86_64 - gen_op_mov_TN_reg(OT_LONG, 1, R_EAX); - tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]); - tcg_gen_ext32s_tl(cpu_T[1], cpu_T[1]); - tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]); - gen_op_mov_reg_T0(OT_LONG, R_EAX); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_ext32s_tl(cpu_tmp0, cpu_T[0]); - tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0); - tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 32); - gen_op_mov_reg_T0(OT_LONG, R_EDX); -#else - { - TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - gen_op_mov_TN_reg(OT_LONG, 1, R_EAX); - tcg_gen_ext_i32_i64(t0, cpu_T[0]); - tcg_gen_ext_i32_i64(t1, cpu_T[1]); - tcg_gen_mul_i64(t0, t0, t1); - tcg_gen_trunc_i64_i32(cpu_T[0], t0); - gen_op_mov_reg_T0(OT_LONG, R_EAX); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_sari_tl(cpu_tmp0, cpu_T[0], 31); - tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_trunc_i64_i32(cpu_T[0], t0); - gen_op_mov_reg_T0(OT_LONG, R_EDX); - tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0); - } -#endif - s->cc_op = CC_OP_MULL; + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_regs[R_EAX]); + tcg_gen_muls2_i32(cpu_tmp2_i32, cpu_tmp3_i32, + cpu_tmp2_i32, cpu_tmp3_i32); + tcg_gen_extu_i32_tl(cpu_regs[R_EAX], cpu_tmp2_i32); + tcg_gen_extu_i32_tl(cpu_regs[R_EDX], cpu_tmp3_i32); + tcg_gen_sari_i32(cpu_tmp2_i32, cpu_tmp2_i32, 31); + tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); + tcg_gen_sub_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); + tcg_gen_extu_i32_tl(cpu_cc_src, cpu_tmp2_i32); + set_cc_op(s, CC_OP_MULL); break; #ifdef TARGET_X86_64 case OT_QUAD: - gen_helper_imulq_EAX_T0(cpu_env, cpu_T[0]); - s->cc_op = CC_OP_MULQ; + tcg_gen_muls2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], + cpu_T[0], cpu_regs[R_EAX]); + tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); + tcg_gen_sari_tl(cpu_cc_src, cpu_regs[R_EAX], 63); + tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_regs[R_EDX]); + set_cc_op(s, CC_OP_MULQ); break; #endif } @@ -4761,8 +5209,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_op_ldu_T0_A0(OT_WORD + s->mem_index); do_lcall: if (s->pe && !s->vm86) { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_lcall_protected(cpu_env, cpu_tmp2_i32, cpu_T[1], @@ -4788,8 +5235,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_op_ldu_T0_A0(OT_WORD + s->mem_index); do_ljmp: if (s->pe && !s->vm86) { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); gen_helper_ljmp_protected(cpu_env, cpu_tmp2_i32, cpu_T[1], @@ -4822,7 +5268,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); gen_op_mov_TN_reg(ot, 1, reg); gen_op_testl_T0_T1_cc(); - s->cc_op = CC_OP_LOGICB + ot; + set_cc_op(s, CC_OP_LOGICB + ot); break; case 0xa8: /* test eAX, Iv */ @@ -4836,7 +5282,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_op_mov_TN_reg(ot, 0, OR_EAX); gen_op_movl_T1_im(val); gen_op_testl_T0_T1_cc(); - s->cc_op = CC_OP_LOGICB + ot; + set_cc_op(s, CC_OP_LOGICB + ot); break; case 0x98: /* CWDE/CBW */ @@ -4897,37 +5343,27 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else { gen_op_mov_TN_reg(ot, 1, reg); } - + switch (ot) { #ifdef TARGET_X86_64 - if (ot == OT_QUAD) { - gen_helper_imulq_T0_T1(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); - } else + case OT_QUAD: + tcg_gen_muls2_i64(cpu_regs[reg], cpu_T[1], cpu_T[0], cpu_T[1]); + tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); + tcg_gen_sari_tl(cpu_cc_src, cpu_cc_dst, 63); + tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_T[1]); + break; #endif - if (ot == OT_LONG) { -#ifdef TARGET_X86_64 - tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]); - tcg_gen_ext32s_tl(cpu_T[1], cpu_T[1]); - tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_ext32s_tl(cpu_tmp0, cpu_T[0]); - tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0); -#else - { - TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - tcg_gen_ext_i32_i64(t0, cpu_T[0]); - tcg_gen_ext_i32_i64(t1, cpu_T[1]); - tcg_gen_mul_i64(t0, t0, t1); - tcg_gen_trunc_i64_i32(cpu_T[0], t0); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); - tcg_gen_sari_tl(cpu_tmp0, cpu_T[0], 31); - tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_trunc_i64_i32(cpu_T[1], t0); - tcg_gen_sub_tl(cpu_cc_src, cpu_T[1], cpu_tmp0); - } -#endif - } else { + case OT_LONG: + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); + tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); + tcg_gen_muls2_i32(cpu_tmp2_i32, cpu_tmp3_i32, + cpu_tmp2_i32, cpu_tmp3_i32); + tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp2_i32); + tcg_gen_sari_i32(cpu_tmp2_i32, cpu_tmp2_i32, 31); + tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); + tcg_gen_sub_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); + tcg_gen_extu_i32_tl(cpu_cc_src, cpu_tmp2_i32); + break; + default: tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]); tcg_gen_ext16s_tl(cpu_T[1], cpu_T[1]); /* XXX: use 32 bit mul which could be faster */ @@ -4935,9 +5371,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); tcg_gen_ext16s_tl(cpu_tmp0, cpu_T[0]); tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0); + gen_op_mov_reg_T0(ot, reg); + break; } - gen_op_mov_reg_T0(ot, reg); - s->cc_op = CC_OP_MULB + ot; + set_cc_op(s, CC_OP_MULB + ot); break; case 0x1c0: case 0x1c1: /* xadd Ev, Gv */ @@ -4964,7 +5401,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_op_mov_reg_T1(ot, reg); } gen_op_update2_cc(); - s->cc_op = CC_OP_ADDB + ot; + set_cc_op(s, CC_OP_ADDB + ot); break; case 0x1b0: case 0x1b1: /* cmpxchg Ev, Gv */ @@ -4994,9 +5431,10 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, rm = 0; /* avoid warning */ } label1 = gen_new_label(); - tcg_gen_sub_tl(t2, cpu_regs[R_EAX], t0); + tcg_gen_mov_tl(t2, cpu_regs[R_EAX]); + gen_extu(ot, t0); gen_extu(ot, t2); - tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, label1); + tcg_gen_brcond_tl(TCG_COND_EQ, t2, t0, label1); label2 = gen_new_label(); if (mod == 3) { gen_op_mov_reg_v(ot, R_EAX, t0); @@ -5015,8 +5453,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } gen_set_label(label2); tcg_gen_mov_tl(cpu_cc_src, t0); - tcg_gen_mov_tl(cpu_cc_dst, t2); - s->cc_op = CC_OP_SUBB + ot; + tcg_gen_mov_tl(cpu_cc_srcT, t2); + tcg_gen_sub_tl(cpu_cc_dst, t2, t0); + set_cc_op(s, CC_OP_SUBB + ot); tcg_temp_free(t0); tcg_temp_free(t1); tcg_temp_free(t2); @@ -5033,8 +5472,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!(s->cpuid_ext_features & CPUID_EXT_CX16)) goto illegal_op; gen_jmp_im(pc_start - s->cs_base); - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_lea_modrm(env, s, modrm, ®_addr, &offset_addr); gen_helper_cmpxchg16b(cpu_env, cpu_A0); } else @@ -5043,12 +5481,11 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!(s->cpuid_features & CPUID_CX8)) goto illegal_op; gen_jmp_im(pc_start - s->cs_base); - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_lea_modrm(env, s, modrm, ®_addr, &offset_addr); gen_helper_cmpxchg8b(cpu_env, cpu_A0); } - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); break; /**************************/ @@ -5452,13 +5889,11 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } break; case 0xc4: /* les Gv */ - if (CODE64(s)) - goto illegal_op; + /* In CODE64 this is VEX3; see above. */ op = R_ES; goto do_lxx; case 0xc5: /* lds Gv */ - if (CODE64(s)) - goto illegal_op; + /* In CODE64 this is VEX2; see above. */ op = R_DS; goto do_lxx; case 0x1b2: /* lss Gv */ @@ -5569,12 +6004,12 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_op_mov_TN_reg(ot, 1, reg); if (shift) { - val = cpu_ldub_code(env, s->pc++); - tcg_gen_movi_tl(cpu_T3, val); + TCGv imm = tcg_const_tl(cpu_ldub_code(env, s->pc++)); + gen_shiftd_rm_T1(s, ot, opreg, op, imm); + tcg_temp_free(imm); } else { - tcg_gen_mov_tl(cpu_T3, cpu_regs[R_ECX]); + gen_shiftd_rm_T1(s, ot, opreg, op, cpu_regs[R_ECX]); } - gen_shiftd_rm_T1_T3(s, ot, opreg, op); break; /************************/ @@ -5717,8 +6152,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } break; case 0x0c: /* fldenv mem */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_fldenv(cpu_env, cpu_A0, tcg_const_i32(s->dflag)); break; @@ -5728,8 +6162,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_helper_fldcw(cpu_env, cpu_tmp2_i32); break; case 0x0e: /* fnstenv mem */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_fstenv(cpu_env, cpu_A0, tcg_const_i32(s->dflag)); break; @@ -5739,27 +6172,23 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_op_st_T0_A0(OT_WORD + s->mem_index); break; case 0x1d: /* fldt mem */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_fldt_ST0(cpu_env, cpu_A0); break; case 0x1f: /* fstpt mem */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_fstt_ST0(cpu_env, cpu_A0); gen_helper_fpop(cpu_env); break; case 0x2c: /* frstor mem */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_frstor(cpu_env, cpu_A0, tcg_const_i32(s->dflag)); break; case 0x2e: /* fnsave mem */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_fsave(cpu_env, cpu_A0, tcg_const_i32(s->dflag)); break; @@ -5769,14 +6198,12 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_op_st_T0_A0(OT_WORD + s->mem_index); break; case 0x3c: /* fbld */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_fbld_ST0(cpu_env, cpu_A0); break; case 0x3e: /* fbstp */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_fbst_ST0(cpu_env, cpu_A0); gen_helper_fpop(cpu_env); @@ -5814,8 +6241,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, switch(rm) { case 0: /* fnop */ /* check exceptions (FreeBSD FPU probe) */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_fwait(cpu_env); break; @@ -5996,18 +6422,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } break; case 0x1d: /* fucomi */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); gen_helper_fucomi_ST0_FT0(cpu_env); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); break; case 0x1e: /* fcomi */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); gen_helper_fcomi_ST0_FT0(cpu_env); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); break; case 0x28: /* ffree sti */ gen_helper_ffree_STN(cpu_env, tcg_const_i32(opreg)); @@ -6059,20 +6483,18 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } break; case 0x3d: /* fucomip */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); gen_helper_fucomi_ST0_FT0(cpu_env); gen_helper_fpop(cpu_env); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); break; case 0x3e: /* fcomip */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); gen_helper_fcomi_ST0_FT0(cpu_env); gen_helper_fpop(cpu_env); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); break; case 0x10 ... 0x13: /* fcmovxx */ case 0x18 ... 0x1b: @@ -6086,7 +6508,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, }; op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1); l1 = gen_new_label(); - gen_jcc1(s, s->cc_op, op1, l1); + gen_jcc1_noeob(s, op1, l1); gen_helper_fmov_ST0_STN(cpu_env, tcg_const_i32(opreg)); gen_set_label(l1); } @@ -6150,7 +6572,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0); } else { gen_scas(s, ot); - s->cc_op = CC_OP_SUBB + ot; } break; @@ -6166,7 +6587,6 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0); } else { gen_cmps(s, ot); - s->cc_op = CC_OP_SUBB + ot; } break; case 0x6c: /* insS */ @@ -6323,8 +6743,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, s->pc += 2; do_lret: if (s->pe && !s->vm86) { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_lret_protected(cpu_env, tcg_const_i32(s->dflag), tcg_const_i32(val)); @@ -6354,21 +6773,20 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!s->pe) { /* real mode */ gen_helper_iret_real(cpu_env, tcg_const_i32(s->dflag)); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); } else if (s->vm86) { if (s->iopl != 3) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { gen_helper_iret_real(cpu_env, tcg_const_i32(s->dflag)); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); } } else { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_iret_protected(cpu_env, tcg_const_i32(s->dflag), tcg_const_i32(s->pc - s->cs_base)); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); } gen_eob(s); break; @@ -6455,44 +6873,14 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0x190 ... 0x19f: /* setcc Gv */ modrm = cpu_ldub_code(env, s->pc++); - gen_setcc(s, b); + gen_setcc1(s, b, cpu_T[0]); gen_ldst_modrm(env, s, modrm, OT_BYTE, OR_TMP0, 1); break; case 0x140 ... 0x14f: /* cmov Gv, Ev */ - { - int l1; - TCGv t0; - - ot = dflag + OT_WORD; - modrm = cpu_ldub_code(env, s->pc++); - reg = ((modrm >> 3) & 7) | rex_r; - mod = (modrm >> 6) & 3; - t0 = tcg_temp_local_new(); - if (mod != 3) { - gen_lea_modrm(env, s, modrm, ®_addr, &offset_addr); - gen_op_ld_v(ot + s->mem_index, t0, cpu_A0); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(ot, t0, rm); - } -#ifdef TARGET_X86_64 - if (ot == OT_LONG) { - /* XXX: specific Intel behaviour ? */ - l1 = gen_new_label(); - gen_jcc1(s, s->cc_op, b ^ 1, l1); - tcg_gen_mov_tl(cpu_regs[reg], t0); - gen_set_label(l1); - tcg_gen_ext32u_tl(cpu_regs[reg], cpu_regs[reg]); - } else -#endif - { - l1 = gen_new_label(); - gen_jcc1(s, s->cc_op, b ^ 1, l1); - gen_op_mov_reg_v(ot, reg, t0); - gen_set_label(l1); - } - tcg_temp_free(t0); - } + ot = dflag + OT_WORD; + modrm = cpu_ldub_code(env, s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + gen_cmovcc1(env, s, ot, b, modrm, reg); break; /************************/ @@ -6502,8 +6890,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (s->vm86 && s->iopl != 3) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_helper_read_eflags(cpu_T[0], cpu_env); gen_push_T0(s); } @@ -6560,7 +6947,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } } gen_pop_update(s); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); /* abort translation because TF/AC flag may change */ gen_jmp_im(s->pc - s->cs_base); gen_eob(s); @@ -6570,44 +6957,30 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) goto illegal_op; gen_op_mov_TN_reg(OT_BYTE, 0, R_AH); - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); - gen_compute_eflags(cpu_cc_src); + gen_compute_eflags(s); tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O); tcg_gen_andi_tl(cpu_T[0], cpu_T[0], CC_S | CC_Z | CC_A | CC_P | CC_C); tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, cpu_T[0]); - s->cc_op = CC_OP_EFLAGS; break; case 0x9f: /* lahf */ if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) goto illegal_op; - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); - gen_compute_eflags(cpu_T[0]); + gen_compute_eflags(s); /* Note: gen_compute_eflags() only gives the condition codes */ - tcg_gen_ori_tl(cpu_T[0], cpu_T[0], 0x02); + tcg_gen_ori_tl(cpu_T[0], cpu_cc_src, 0x02); gen_op_mov_reg_T0(OT_BYTE, R_AH); break; case 0xf5: /* cmc */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); - gen_compute_eflags(cpu_cc_src); + gen_compute_eflags(s); tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C); - s->cc_op = CC_OP_EFLAGS; break; case 0xf8: /* clc */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); - gen_compute_eflags(cpu_cc_src); + gen_compute_eflags(s); tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_C); - s->cc_op = CC_OP_EFLAGS; break; case 0xf9: /* stc */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); - gen_compute_eflags(cpu_cc_src); + gen_compute_eflags(s); tcg_gen_ori_tl(cpu_cc_src, cpu_cc_src, CC_C); - s->cc_op = CC_OP_EFLAGS; break; case 0xfc: /* cld */ tcg_gen_movi_i32(cpu_tmp2_i32, 1); @@ -6697,7 +7070,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_tmp0); break; } - s->cc_op = CC_OP_SARB + ot; + set_cc_op(s, CC_OP_SARB + ot); if (op != 0) { if (mod != 3) gen_op_st_T0_A0(ot + s->mem_index); @@ -6707,81 +7080,88 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_movi_tl(cpu_cc_dst, 0); } break; - case 0x1bc: /* bsf */ - case 0x1bd: /* bsr */ - { - int label1; - TCGv t0; + case 0x1bc: /* bsf / tzcnt */ + case 0x1bd: /* bsr / lzcnt */ + ot = dflag + OT_WORD; + modrm = cpu_ldub_code(env, s->pc++); + reg = ((modrm >> 3) & 7) | rex_r; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + gen_extu(ot, cpu_T[0]); - ot = dflag + OT_WORD; - modrm = cpu_ldub_code(env, s->pc++); - reg = ((modrm >> 3) & 7) | rex_r; - gen_ldst_modrm(env, s,modrm, ot, OR_TMP0, 0); - gen_extu(ot, cpu_T[0]); - t0 = tcg_temp_local_new(); - tcg_gen_mov_tl(t0, cpu_T[0]); - if ((b & 1) && (prefixes & PREFIX_REPZ) && - (s->cpuid_ext3_features & CPUID_EXT3_ABM)) { - switch(ot) { - case OT_WORD: gen_helper_lzcnt(cpu_T[0], t0, - tcg_const_i32(16)); break; - case OT_LONG: gen_helper_lzcnt(cpu_T[0], t0, - tcg_const_i32(32)); break; - case OT_QUAD: gen_helper_lzcnt(cpu_T[0], t0, - tcg_const_i32(64)); break; - } - gen_op_mov_reg_T0(ot, reg); + /* Note that lzcnt and tzcnt are in different extensions. */ + if ((prefixes & PREFIX_REPZ) + && (b & 1 + ? s->cpuid_ext3_features & CPUID_EXT3_ABM + : s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1)) { + int size = 8 << ot; + tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); + if (b & 1) { + /* For lzcnt, reduce the target_ulong result by the + number of zeros that we expect to find at the top. */ + gen_helper_clz(cpu_T[0], cpu_T[0]); + tcg_gen_subi_tl(cpu_T[0], cpu_T[0], TARGET_LONG_BITS - size); } else { - label1 = gen_new_label(); - tcg_gen_movi_tl(cpu_cc_dst, 0); - tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, label1); - if (b & 1) { - gen_helper_bsr(cpu_T[0], t0); - } else { - gen_helper_bsf(cpu_T[0], t0); - } - gen_op_mov_reg_T0(ot, reg); - tcg_gen_movi_tl(cpu_cc_dst, 1); - gen_set_label(label1); - tcg_gen_discard_tl(cpu_cc_src); - s->cc_op = CC_OP_LOGICB + ot; + /* For tzcnt, a zero input must return the operand size: + force all bits outside the operand size to 1. */ + target_ulong mask = (target_ulong)-2 << (size - 1); + tcg_gen_ori_tl(cpu_T[0], cpu_T[0], mask); + gen_helper_ctz(cpu_T[0], cpu_T[0]); } - tcg_temp_free(t0); + /* For lzcnt/tzcnt, C and Z bits are defined and are + related to the result. */ + gen_op_update1_cc(); + set_cc_op(s, CC_OP_BMILGB + ot); + } else { + /* For bsr/bsf, only the Z bit is defined and it is related + to the input and not the result. */ + tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); + set_cc_op(s, CC_OP_LOGICB + ot); + if (b & 1) { + /* For bsr, return the bit index of the first 1 bit, + not the count of leading zeros. */ + gen_helper_clz(cpu_T[0], cpu_T[0]); + tcg_gen_xori_tl(cpu_T[0], cpu_T[0], TARGET_LONG_BITS - 1); + } else { + gen_helper_ctz(cpu_T[0], cpu_T[0]); + } + /* ??? The manual says that the output is undefined when the + input is zero, but real hardware leaves it unchanged, and + real programs appear to depend on that. */ + tcg_gen_movi_tl(cpu_tmp0, 0); + tcg_gen_movcond_tl(TCG_COND_EQ, cpu_T[0], cpu_cc_dst, cpu_tmp0, + cpu_regs[reg], cpu_T[0]); } + gen_op_mov_reg_T0(ot, reg); break; /************************/ /* bcd */ case 0x27: /* daa */ if (CODE64(s)) goto illegal_op; - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_helper_daa(cpu_env); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); break; case 0x2f: /* das */ if (CODE64(s)) goto illegal_op; - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_helper_das(cpu_env); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); break; case 0x37: /* aaa */ if (CODE64(s)) goto illegal_op; - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_helper_aaa(cpu_env); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); break; case 0x3f: /* aas */ if (CODE64(s)) goto illegal_op; - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_helper_aas(cpu_env); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); break; case 0xd4: /* aam */ if (CODE64(s)) @@ -6791,7 +7171,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_exception(s, EXCP00_DIVZ, pc_start - s->cs_base); } else { gen_helper_aam(cpu_env, tcg_const_i32(val)); - s->cc_op = CC_OP_LOGICB; + set_cc_op(s, CC_OP_LOGICB); } break; case 0xd5: /* aad */ @@ -6799,7 +7179,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, goto illegal_op; val = cpu_ldub_code(env, s->pc++); gen_helper_aad(cpu_env, tcg_const_i32(val)); - s->cc_op = CC_OP_LOGICB; + set_cc_op(s, CC_OP_LOGICB); break; /************************/ /* misc */ @@ -6821,8 +7201,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, (HF_MP_MASK | HF_TS_MASK)) { gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); } else { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_fwait(cpu_env); } @@ -6841,8 +7220,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0xce: /* into */ if (CODE64(s)) goto illegal_op; - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_into(cpu_env, tcg_const_i32(s->pc - pc_start)); break; @@ -6854,7 +7232,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, #else /* start debug */ tb_flush(env); - cpu_set_log(CPU_LOG_INT | CPU_LOG_TB_IN_ASM); + qemu_set_log(CPU_LOG_INT | CPU_LOG_TB_IN_ASM); #endif break; #endif @@ -6935,9 +7313,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 0xd6: /* salc */ if (CODE64(s)) goto illegal_op; - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); - gen_compute_eflags_c(cpu_T[0]); + gen_compute_eflags_c(s, cpu_T[0]); tcg_gen_neg_tl(cpu_T[0], cpu_T[0]); gen_op_mov_reg_T0(OT_BYTE, R_EAX); break; @@ -6961,17 +7337,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, switch(b) { case 0: /* loopnz */ case 1: /* loopz */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); gen_op_add_reg_im(s->aflag, R_ECX, -1); gen_op_jz_ecx(s->aflag, l3); - gen_compute_eflags(cpu_tmp0); - tcg_gen_andi_tl(cpu_tmp0, cpu_tmp0, CC_Z); - if (b == 0) { - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_tmp0, 0, l1); - } else { - tcg_gen_brcondi_tl(TCG_COND_NE, cpu_tmp0, 0, l1); - } + gen_jcc1(s, (JCC_Z << 1) | (b ^ 1), l1); break; case 2: /* loop */ gen_op_add_reg_im(s->aflag, R_ECX, -1); @@ -6998,8 +7366,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (s->cpl != 0) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); if (b & 2) { gen_helper_rdmsr(cpu_env); @@ -7009,8 +7376,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } break; case 0x131: /* rdtsc */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); if (use_icount) gen_io_start(); @@ -7021,8 +7387,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } break; case 0x133: /* rdpmc */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_rdpmc(cpu_env); break; @@ -7068,15 +7433,15 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_jmp_im(pc_start - s->cs_base); gen_helper_sysret(cpu_env, tcg_const_i32(s->dflag)); /* condition codes are modified only in long mode */ - if (s->lma) - s->cc_op = CC_OP_EFLAGS; + if (s->lma) { + set_cc_op(s, CC_OP_EFLAGS); + } gen_eob(s); } break; #endif case 0x1a2: /* cpuid */ - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_cpuid(cpu_env); break; @@ -7084,8 +7449,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (s->cpl != 0) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_hlt(cpu_env, tcg_const_i32(s->pc - pc_start)); s->is_jmp = DISAS_TB_JUMP; @@ -7147,14 +7511,13 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!s->pe || s->vm86) goto illegal_op; gen_ldst_modrm(env, s, modrm, OT_WORD, OR_TMP0, 0); - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); if (op == 4) { gen_helper_verr(cpu_env, cpu_T[0]); } else { gen_helper_verw(cpu_env, cpu_T[0]); } - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); break; default: goto illegal_op; @@ -7186,8 +7549,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || s->cpl != 0) goto illegal_op; - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); #ifdef TARGET_X86_64 if (s->aflag == 2) { @@ -7247,8 +7609,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 2: /* lgdt */ case 3: /* lidt */ if (mod == 3) { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); switch(rm) { case 0: /* VMRUN */ @@ -7376,8 +7737,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (s->cpl != 0) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_lea_modrm(env, s, modrm, ®_addr, &offset_addr); gen_helper_invlpg(cpu_env, cpu_A0); @@ -7410,8 +7770,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 1: /* rdtscp */ if (!(s->cpuid_ext2_features & CPUID_EXT2_RDTSCP)) goto illegal_op; - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); if (use_icount) gen_io_start(); @@ -7507,12 +7866,9 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else { gen_op_mov_reg_v(ot, rm, t0); } - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); - gen_compute_eflags(cpu_cc_src); + gen_compute_eflags(s); tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_Z); tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t2); - s->cc_op = CC_OP_EFLAGS; tcg_temp_free(t0); tcg_temp_free(t1); tcg_temp_free(t2); @@ -7530,8 +7886,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, reg = ((modrm >> 3) & 7) | rex_r; gen_ldst_modrm(env, s, modrm, OT_WORD, OR_TMP0, 0); t0 = tcg_temp_local_new(); - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); if (b == 0x102) { gen_helper_lar(t0, cpu_env, cpu_T[0]); } else { @@ -7542,7 +7897,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_tmp0, 0, label1); gen_op_mov_reg_v(ot, reg, t0); gen_set_label(label1); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); tcg_temp_free(t0); } break; @@ -7596,8 +7951,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, case 3: case 4: case 8: - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); if (b & 2) { gen_op_mov_TN_reg(ot, 0, rm); @@ -7686,8 +8040,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, break; } gen_lea_modrm(env, s, modrm, ®_addr, &offset_addr); - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_fxsave(cpu_env, cpu_A0, tcg_const_i32((s->dflag == 2))); break; @@ -7700,8 +8053,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, break; } gen_lea_modrm(env, s, modrm, ®_addr, &offset_addr); - if (s->cc_op != CC_OP_DYNAMIC) - gen_op_set_cc_op(s->cc_op); + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_fxrstor(cpu_env, cpu_A0, tcg_const_i32((s->dflag == 2))); @@ -7785,7 +8137,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_helper_popcnt(cpu_T[0], cpu_env, cpu_T[0], tcg_const_i32(ot)); gen_op_mov_reg_T0(ot, reg); - s->cc_op = CC_OP_EFLAGS; + set_cc_op(s, CC_OP_EFLAGS); break; case 0x10e ... 0x10f: /* 3DNow! instructions, ignore prefixes */ @@ -7820,12 +8172,12 @@ void optimize_flags_init(void) cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); cpu_cc_op = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUX86State, cc_op), "cc_op"); - cpu_cc_src = tcg_global_mem_new(TCG_AREG0, offsetof(CPUX86State, cc_src), - "cc_src"); cpu_cc_dst = tcg_global_mem_new(TCG_AREG0, offsetof(CPUX86State, cc_dst), "cc_dst"); - cpu_cc_tmp = tcg_global_mem_new(TCG_AREG0, offsetof(CPUX86State, cc_tmp), - "cc_tmp"); + cpu_cc_src = tcg_global_mem_new(TCG_AREG0, offsetof(CPUX86State, cc_src), + "cc_src"); + cpu_cc_src2 = tcg_global_mem_new(TCG_AREG0, offsetof(CPUX86State, cc_src2), + "cc_src2"); #ifdef TARGET_X86_64 cpu_regs[R_EAX] = tcg_global_mem_new_i64(TCG_AREG0, @@ -7918,6 +8270,7 @@ static inline void gen_intermediate_code_internal(CPUX86State *env, dc->tf = (flags >> TF_SHIFT) & 1; dc->singlestep_enabled = env->singlestep_enabled; dc->cc_op = CC_OP_DYNAMIC; + dc->cc_op_dirty = false; dc->cs_base = cs_base; dc->tb = tb; dc->popl_esp_hack = 0; @@ -7951,16 +8304,15 @@ static inline void gen_intermediate_code_internal(CPUX86State *env, cpu_T[0] = tcg_temp_new(); cpu_T[1] = tcg_temp_new(); cpu_A0 = tcg_temp_new(); - cpu_T3 = tcg_temp_new(); cpu_tmp0 = tcg_temp_new(); cpu_tmp1_i64 = tcg_temp_new_i64(); cpu_tmp2_i32 = tcg_temp_new_i32(); cpu_tmp3_i32 = tcg_temp_new_i32(); cpu_tmp4 = tcg_temp_new(); - cpu_tmp5 = tcg_temp_new(); cpu_ptr0 = tcg_temp_new_ptr(); cpu_ptr1 = tcg_temp_new_ptr(); + cpu_cc_srcT = tcg_temp_local_new(); gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; @@ -7972,7 +8324,7 @@ static inline void gen_intermediate_code_internal(CPUX86State *env, if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_icount_start(); + gen_tb_start(); for(;;) { if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { QTAILQ_FOREACH(bp, &env->breakpoints, entry) { @@ -8030,7 +8382,7 @@ static inline void gen_intermediate_code_internal(CPUX86State *env, } if (tb->cflags & CF_LAST_IO) gen_io_end(); - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; /* we don't forget to fill the last values */ if (search_pc) { diff --git a/target-lm32/cpu-qom.h b/target-lm32/cpu-qom.h index 400cdbd554..957186075b 100644 --- a/target-lm32/cpu-qom.h +++ b/target-lm32/cpu-qom.h @@ -34,6 +34,7 @@ /** * LM32CPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. * * A LatticeMico32 CPU model. @@ -43,6 +44,7 @@ typedef struct LM32CPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); } LM32CPUClass; @@ -67,5 +69,12 @@ static inline LM32CPU *lm32_env_get_cpu(CPULM32State *env) #define ENV_GET_CPU(e) CPU(lm32_env_get_cpu(e)) +#define ENV_OFFSET offsetof(LM32CPU, env) + +#ifndef CONFIG_USER_ONLY +extern const struct VMStateDescription vmstate_lm32_cpu; +#endif + +void lm32_cpu_do_interrupt(CPUState *cpu); #endif diff --git a/target-lm32/cpu.c b/target-lm32/cpu.c index caa4834075..bbb7fbf768 100644 --- a/target-lm32/cpu.c +++ b/target-lm32/cpu.c @@ -30,7 +30,7 @@ static void lm32_cpu_reset(CPUState *s) CPULM32State *env = &cpu->env; if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); + qemu_log("CPU Reset (CPU %d)\n", s->cpu_index); log_cpu_state(env, 0); } @@ -42,25 +42,50 @@ static void lm32_cpu_reset(CPUState *s) memset(env, 0, offsetof(CPULM32State, breakpoints)); } +static void lm32_cpu_realizefn(DeviceState *dev, Error **errp) +{ + LM32CPU *cpu = LM32_CPU(dev); + LM32CPUClass *lcc = LM32_CPU_GET_CLASS(dev); + + cpu_reset(CPU(cpu)); + + qemu_init_vcpu(&cpu->env); + + lcc->parent_realize(dev, errp); +} + static void lm32_cpu_initfn(Object *obj) { + CPUState *cs = CPU(obj); LM32CPU *cpu = LM32_CPU(obj); CPULM32State *env = &cpu->env; + static bool tcg_initialized; + cs->env_ptr = env; cpu_exec_init(env); env->flags = 0; - cpu_reset(CPU(cpu)); + if (tcg_enabled() && !tcg_initialized) { + tcg_initialized = true; + lm32_translate_init(); + } } static void lm32_cpu_class_init(ObjectClass *oc, void *data) { LM32CPUClass *lcc = LM32_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + lcc->parent_realize = dc->realize; + dc->realize = lm32_cpu_realizefn; lcc->parent_reset = cc->reset; cc->reset = lm32_cpu_reset; + + cc->do_interrupt = lm32_cpu_do_interrupt; + cpu_class_set_vmsd(cc, &vmstate_lm32_cpu); } static const TypeInfo lm32_cpu_type_info = { diff --git a/target-lm32/cpu.h b/target-lm32/cpu.h index 4e202db32c..fd50b534fc 100644 --- a/target-lm32/cpu.h +++ b/target-lm32/cpu.h @@ -189,8 +189,6 @@ struct CPULM32State { LM32CPU *cpu_lm32_init(const char *cpu_model); void cpu_lm32_list(FILE *f, fprintf_function cpu_fprintf); int cpu_lm32_exec(CPULM32State *s); -void cpu_lm32_close(CPULM32State *s); -void do_interrupt(CPULM32State *env); /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero is returned if the signal was handled by the virtual CPU. */ @@ -213,8 +211,6 @@ static inline CPULM32State *cpu_init(const char *cpu_model) #define cpu_gen_code cpu_lm32_gen_code #define cpu_signal_handler cpu_lm32_signal_handler -#define CPU_SAVE_VERSION 1 - int cpu_lm32_handle_mmu_fault(CPULM32State *env, target_ulong address, int rw, int mmu_idx); #define cpu_handle_mmu_fault cpu_lm32_handle_mmu_fault @@ -255,9 +251,7 @@ static inline void cpu_get_tb_cpu_state(CPULM32State *env, target_ulong *pc, static inline bool cpu_has_work(CPUState *cpu) { - CPULM32State *env = &LM32_CPU(cpu)->env; - - return env->interrupt_request & CPU_INTERRUPT_HARD; + return cpu->interrupt_request & CPU_INTERRUPT_HARD; } #include "exec/exec-all.h" diff --git a/target-lm32/helper.c b/target-lm32/helper.c index d76ea3fe09..a0a8399906 100644 --- a/target-lm32/helper.c +++ b/target-lm32/helper.c @@ -42,8 +42,11 @@ hwaddr cpu_get_phys_page_debug(CPULM32State *env, target_ulong addr) return addr & TARGET_PAGE_MASK; } -void do_interrupt(CPULM32State *env) +void lm32_cpu_do_interrupt(CPUState *cs) { + LM32CPU *cpu = LM32_CPU(cs); + CPULM32State *env = &cpu->env; + qemu_log_mask(CPU_LOG_INT, "exception at pc=%x type=%x\n", env->pc, env->exception_index); @@ -197,7 +200,6 @@ LM32CPU *cpu_lm32_init(const char *cpu_model) LM32CPU *cpu; CPULM32State *env; const LM32Def *def; - static int tcg_initialized; def = cpu_lm32_find_by_name(cpu_model); if (!def) { @@ -212,12 +214,7 @@ LM32CPU *cpu_lm32_init(const char *cpu_model) env->num_wps = def->num_watchpoints; env->cfg = cfg_by_def(def); - qemu_init_vcpu(env); - - if (tcg_enabled() && !tcg_initialized) { - tcg_initialized = 1; - lm32_translate_init(); - } + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); return cpu; } diff --git a/target-lm32/machine.c b/target-lm32/machine.c index 6802e818f8..9e0919c254 100644 --- a/target-lm32/machine.c +++ b/target-lm32/machine.c @@ -1,9 +1,9 @@ #include "hw/hw.h" #include "hw/boards.h" -static const VMStateDescription vmstate_cpu = { - .name = "cpu", - .version_id = CPU_SAVE_VERSION, +static const VMStateDescription vmstate_env = { + .name = "env", + .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField[]) { @@ -22,12 +22,13 @@ static const VMStateDescription vmstate_cpu = { } }; -void cpu_save(QEMUFile *f, void *opaque) -{ - vmstate_save_state(f, &vmstate_cpu, opaque); -} - -int cpu_load(QEMUFile *f, void *opaque, int version_id) -{ - return vmstate_load_state(f, &vmstate_cpu, opaque, version_id); -} +const VMStateDescription vmstate_lm32_cpu = { + .name = "cpu", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(env, LM32CPU, 1, vmstate_env, CPULM32State), + VMSTATE_END_OF_LIST() + } +}; diff --git a/target-lm32/op_helper.c b/target-lm32/op_helper.c index 53410b176e..ebc94a0681 100644 --- a/target-lm32/op_helper.c +++ b/target-lm32/op_helper.c @@ -25,7 +25,9 @@ void helper_raise_exception(CPULM32State *env, uint32_t index) void helper_hlt(CPULM32State *env) { - env->halted = 1; + CPUState *cs = CPU(lm32_env_get_cpu(env)); + + cs->halted = 1; env->exception_index = EXCP_HLT; cpu_loop_exit(env); } diff --git a/target-lm32/translate.c b/target-lm32/translate.c index 6b87340174..695d9c59b2 100644 --- a/target-lm32/translate.c +++ b/target-lm32/translate.c @@ -1012,8 +1012,6 @@ static void gen_intermediate_code_internal(CPULM32State *env, int num_insns; int max_insns; - qemu_log_try_set_file(stderr); - pc_start = tb->pc; dc->env = env; dc->tb = tb; @@ -1042,7 +1040,7 @@ static void gen_intermediate_code_internal(CPULM32State *env, max_insns = CF_COUNT_MASK; } - gen_icount_start(); + gen_tb_start(); do { check_breakpoint(env, dc); @@ -1104,7 +1102,7 @@ static void gen_intermediate_code_internal(CPULM32State *env, } } - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; if (search_pc) { j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; diff --git a/target-m68k/Makefile.objs b/target-m68k/Makefile.objs index 7eccfab0e4..2e2b85044d 100644 --- a/target-m68k/Makefile.objs +++ b/target-m68k/Makefile.objs @@ -1,3 +1,2 @@ obj-y += m68k-semi.o obj-y += translate.o op_helper.o helper.o cpu.o -obj-$(CONFIG_SOFTMMU) += machine.o diff --git a/target-m68k/cpu-qom.h b/target-m68k/cpu-qom.h index 170daa7c96..846aa7453e 100644 --- a/target-m68k/cpu-qom.h +++ b/target-m68k/cpu-qom.h @@ -33,6 +33,7 @@ /** * M68kCPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. * * A Motorola 68k CPU model. @@ -42,6 +43,7 @@ typedef struct M68kCPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); } M68kCPUClass; @@ -66,5 +68,8 @@ static inline M68kCPU *m68k_env_get_cpu(CPUM68KState *env) #define ENV_GET_CPU(e) CPU(m68k_env_get_cpu(e)) +#define ENV_OFFSET offsetof(M68kCPU, env) + +void m68k_cpu_do_interrupt(CPUState *cpu); #endif diff --git a/target-m68k/cpu.c b/target-m68k/cpu.c index 3e70bb0ead..3c65b4e44f 100644 --- a/target-m68k/cpu.c +++ b/target-m68k/cpu.c @@ -20,6 +20,7 @@ #include "cpu.h" #include "qemu-common.h" +#include "migration/vmstate.h" static void m68k_set_feature(CPUM68KState *env, int feature) @@ -35,7 +36,7 @@ static void m68k_cpu_reset(CPUState *s) CPUM68KState *env = &cpu->env; if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); + qemu_log("CPU Reset (CPU %d)\n", s->cpu_index); log_cpu_state(env, 0); } @@ -55,6 +56,25 @@ static void m68k_cpu_reset(CPUState *s) /* CPU models */ +static ObjectClass *m68k_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + + if (cpu_model == NULL) { + return NULL; + } + + typename = g_strdup_printf("%s-" TYPE_M68K_CPU, cpu_model); + oc = object_class_by_name(typename); + g_free(typename); + if (oc != NULL && (object_class_dynamic_cast(oc, TYPE_M68K_CPU) == NULL || + object_class_is_abstract(oc))) { + return NULL; + } + return oc; +} + static void m5206_cpu_initfn(Object *obj) { M68kCPU *cpu = M68K_CPU(obj); @@ -119,32 +139,67 @@ static const M68kCPUInfo m68k_cpus[] = { { .name = "any", .instance_init = any_cpu_initfn }, }; +static void m68k_cpu_realizefn(DeviceState *dev, Error **errp) +{ + M68kCPU *cpu = M68K_CPU(dev); + M68kCPUClass *mcc = M68K_CPU_GET_CLASS(dev); + + m68k_cpu_init_gdb(cpu); + + cpu_reset(CPU(cpu)); + qemu_init_vcpu(&cpu->env); + + mcc->parent_realize(dev, errp); +} + static void m68k_cpu_initfn(Object *obj) { + CPUState *cs = CPU(obj); M68kCPU *cpu = M68K_CPU(obj); CPUM68KState *env = &cpu->env; + static bool inited; + cs->env_ptr = env; cpu_exec_init(env); + + if (tcg_enabled() && !inited) { + inited = true; + m68k_tcg_init(); + } } +static const VMStateDescription vmstate_m68k_cpu = { + .name = "cpu", + .unmigratable = 1, +}; + static void m68k_cpu_class_init(ObjectClass *c, void *data) { M68kCPUClass *mcc = M68K_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); + DeviceClass *dc = DEVICE_CLASS(c); + + mcc->parent_realize = dc->realize; + dc->realize = m68k_cpu_realizefn; mcc->parent_reset = cc->reset; cc->reset = m68k_cpu_reset; + + cc->class_by_name = m68k_cpu_class_by_name; + cc->do_interrupt = m68k_cpu_do_interrupt; + dc->vmsd = &vmstate_m68k_cpu; } static void register_cpu_type(const M68kCPUInfo *info) { TypeInfo type_info = { - .name = info->name, .parent = TYPE_M68K_CPU, .instance_init = info->instance_init, }; - type_register_static(&type_info); + type_info.name = g_strdup_printf("%s-" TYPE_M68K_CPU, info->name); + type_register(&type_info); + g_free((void *)type_info.name); } static const TypeInfo m68k_cpu_type_info = { diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h index adaf56c471..c90c40c370 100644 --- a/target-m68k/cpu.h +++ b/target-m68k/cpu.h @@ -116,9 +116,9 @@ typedef struct CPUM68KState { #include "cpu-qom.h" void m68k_tcg_init(void); -CPUM68KState *cpu_m68k_init(const char *cpu_model); +void m68k_cpu_init_gdb(M68kCPU *cpu); +M68kCPU *cpu_m68k_init(const char *cpu_model); int cpu_m68k_exec(CPUM68KState *s); -void do_interrupt(CPUM68KState *env1); void do_interrupt_m68k_hardirq(CPUM68KState *env1); /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero @@ -168,7 +168,7 @@ enum { #define MACSR_V 0x002 #define MACSR_EV 0x001 -void m68k_set_irq_level(CPUM68KState *env, int level, uint8_t vector); +void m68k_set_irq_level(M68kCPU *cpu, int level, uint8_t vector); void m68k_set_macsr(CPUM68KState *env, uint32_t val); void m68k_switch_sp(CPUM68KState *env); @@ -214,7 +214,15 @@ void register_m68k_insns (CPUM68KState *env); #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -#define cpu_init cpu_m68k_init +static inline CPUM68KState *cpu_init(const char *cpu_model) +{ + M68kCPU *cpu = cpu_m68k_init(cpu_model); + if (cpu == NULL) { + return NULL; + } + return &cpu->env; +} + #define cpu_exec cpu_m68k_exec #define cpu_gen_code cpu_m68k_gen_code #define cpu_signal_handler cpu_m68k_signal_handler @@ -256,9 +264,7 @@ static inline void cpu_get_tb_cpu_state(CPUM68KState *env, target_ulong *pc, static inline bool cpu_has_work(CPUState *cpu) { - CPUM68KState *env = &M68K_CPU(cpu)->env; - - return env->interrupt_request & CPU_INTERRUPT_HARD; + return cpu->interrupt_request & CPU_INTERRUPT_HARD; } #include "exec/exec-all.h" diff --git a/target-m68k/helper.c b/target-m68k/helper.c index a9a277865f..54fa419ace 100644 --- a/target-m68k/helper.c +++ b/target-m68k/helper.c @@ -25,11 +25,6 @@ #define SIGNBIT (1u << 31) -typedef struct M68kCPUListState { - fprintf_function cpu_fprintf; - FILE *file; -} M68kCPUListState; - /* Sort alphabetically, except for "any". */ static gint m68k_cpu_list_compare(gconstpointer a, gconstpointer b) { @@ -39,9 +34,9 @@ static gint m68k_cpu_list_compare(gconstpointer a, gconstpointer b) name_a = object_class_get_name(class_a); name_b = object_class_get_name(class_b); - if (strcmp(name_a, "any") == 0) { + if (strcmp(name_a, "any-" TYPE_M68K_CPU) == 0) { return 1; - } else if (strcmp(name_b, "any") == 0) { + } else if (strcmp(name_b, "any-" TYPE_M68K_CPU) == 0) { return -1; } else { return strcasecmp(name_a, name_b); @@ -51,15 +46,20 @@ static gint m68k_cpu_list_compare(gconstpointer a, gconstpointer b) static void m68k_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *c = data; - M68kCPUListState *s = user_data; + CPUListState *s = user_data; + const char *typename; + char *name; + typename = object_class_get_name(c); + name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_M68K_CPU)); (*s->cpu_fprintf)(s->file, "%s\n", - object_class_get_name(c)); + name); + g_free(name); } void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf) { - M68kCPUListState s = { + CPUListState s = { .file = f, .cpu_fprintf = cpu_fprintf, }; @@ -98,35 +98,36 @@ static int fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) return 0; } -CPUM68KState *cpu_m68k_init(const char *cpu_model) +M68kCPU *cpu_m68k_init(const char *cpu_model) { M68kCPU *cpu; CPUM68KState *env; - static int inited; + ObjectClass *oc; - if (object_class_by_name(cpu_model) == NULL) { + oc = cpu_class_by_name(TYPE_M68K_CPU, cpu_model); + if (oc == NULL) { return NULL; } - cpu = M68K_CPU(object_new(cpu_model)); + cpu = M68K_CPU(object_new(object_class_get_name(oc))); env = &cpu->env; - - if (!inited) { - inited = 1; - m68k_tcg_init(); - } - env->cpu_model_str = cpu_model; register_m68k_insns(env); + + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + + return cpu; +} + +void m68k_cpu_init_gdb(M68kCPU *cpu) +{ + CPUM68KState *env = &cpu->env; + if (m68k_feature(env, M68K_FEATURE_CF_FPU)) { gdb_register_coprocessor(env, fpu_gdb_get_reg, fpu_gdb_set_reg, 11, "cf-fp.xml", 18); } /* TODO: Add [E]MAC registers. */ - - cpu_reset(ENV_GET_CPU(env)); - qemu_init_vcpu(env); - return env; } void cpu_m68k_flush_flags(CPUM68KState *env, int cc_op) @@ -309,14 +310,18 @@ int cpu_m68k_handle_mmu_fault (CPUM68KState *env, target_ulong address, int rw, be handled by the interrupt controller. Real hardware only requests the vector when the interrupt is acknowledged by the CPU. For simplicitly we calculate it when the interrupt is signalled. */ -void m68k_set_irq_level(CPUM68KState *env, int level, uint8_t vector) +void m68k_set_irq_level(M68kCPU *cpu, int level, uint8_t vector) { + CPUState *cs = CPU(cpu); + CPUM68KState *env = &cpu->env; + env->pending_level = level; env->pending_vector = vector; - if (level) - cpu_interrupt(env, CPU_INTERRUPT_HARD); - else - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + if (level) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } } #endif diff --git a/target-m68k/machine.c b/target-m68k/machine.c deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/target-m68k/op_helper.c b/target-m68k/op_helper.c index 16df24c0ca..30f7d8b1ab 100644 --- a/target-m68k/op_helper.c +++ b/target-m68k/op_helper.c @@ -21,8 +21,11 @@ #if defined(CONFIG_USER_ONLY) -void do_interrupt(CPUM68KState *env) +void m68k_cpu_do_interrupt(CPUState *cs) { + M68kCPU *cpu = M68K_CPU(cs); + CPUM68KState *env = &cpu->env; + env->exception_index = -1; } @@ -84,6 +87,7 @@ static void do_rte(CPUM68KState *env) static void do_interrupt_all(CPUM68KState *env, int is_hw) { + CPUState *cs; uint32_t sp; uint32_t fmt; uint32_t retaddr; @@ -108,7 +112,8 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw) do_m68k_semihosting(env, env->dregs[0]); return; } - env->halted = 1; + cs = CPU(m68k_env_get_cpu(env)); + cs->halted = 1; env->exception_index = EXCP_HLT; cpu_loop_exit(env); return; @@ -147,8 +152,11 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw) env->pc = cpu_ldl_kernel(env, env->vbr + vector); } -void do_interrupt(CPUM68KState *env) +void m68k_cpu_do_interrupt(CPUState *cs) { + M68kCPU *cpu = M68K_CPU(cs); + CPUM68KState *env = &cpu->env; + do_interrupt_all(env, 0); } diff --git a/target-m68k/qregs.def b/target-m68k/qregs.def index 49400c4475..4235b02764 100644 --- a/target-m68k/qregs.def +++ b/target-m68k/qregs.def @@ -8,6 +8,5 @@ DEFO32(CC_X, cc_x) DEFO32(DIV1, div1) DEFO32(DIV2, div2) DEFO32(EXCEPTION, exception_index) -DEFO32(HALTED, halted) DEFO32(MACSR, macsr) DEFO32(MAC_MASK, mac_mask) diff --git a/target-m68k/translate.c b/target-m68k/translate.c index e763195f86..32b8132da6 100644 --- a/target-m68k/translate.c +++ b/target-m68k/translate.c @@ -42,6 +42,8 @@ #undef DEFO64 #undef DEFF64 +static TCGv_i32 cpu_halted; + static TCGv_ptr cpu_env; static char cpu_reg_names[3*8*3 + 5*4]; @@ -76,6 +78,10 @@ void m68k_tcg_init(void) #undef DEFO64 #undef DEFF64 + cpu_halted = tcg_global_mem_new_i32(TCG_AREG0, + -offsetof(M68kCPU, env) + + offsetof(CPUState, halted), "HALTED"); + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); p = cpu_reg_names; @@ -574,7 +580,7 @@ static inline TCGv gen_ea_once(CPUM68KState *env, DisasContext *s, return gen_ldst(s, opsize, tmp, val, what); } -/* Generate code to load/store a value ito/from an EA. If VAL > 0 this is +/* Generate code to load/store a value from/into an EA. If VAL > 0 this is a write otherwise it is a read (0 == sign extend, -1 == zero extend). ADDRP is non-null for readwrite operands. */ static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn, @@ -2024,7 +2030,7 @@ DISAS_INSN(stop) s->pc += 2; gen_set_sr_im(s, ext, 0); - tcg_gen_movi_i32(QREG_HALTED, 1); + tcg_gen_movi_i32(cpu_halted, 1); gen_exception(s, s->pc, EXCP_HLT); } @@ -2999,7 +3005,7 @@ gen_intermediate_code_internal(CPUM68KState *env, TranslationBlock *tb, if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_icount_start(); + gen_tb_start(); do { pc_offset = dc->pc - pc_start; gen_throws_exception = NULL; @@ -3063,7 +3069,7 @@ gen_intermediate_code_internal(CPUM68KState *env, TranslationBlock *tb, break; } } - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; #ifdef DEBUG_DISAS diff --git a/target-microblaze/Makefile.objs b/target-microblaze/Makefile.objs index afb87bcc80..985330eac5 100644 --- a/target-microblaze/Makefile.objs +++ b/target-microblaze/Makefile.objs @@ -1,2 +1,2 @@ obj-y += translate.o op_helper.o helper.o cpu.o -obj-$(CONFIG_SOFTMMU) += mmu.o machine.o +obj-$(CONFIG_SOFTMMU) += mmu.o diff --git a/target-microblaze/cpu-qom.h b/target-microblaze/cpu-qom.h index f75549dc22..aa51cf6406 100644 --- a/target-microblaze/cpu-qom.h +++ b/target-microblaze/cpu-qom.h @@ -33,6 +33,7 @@ /** * MicroBlazeCPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. * * A MicroBlaze CPU model. @@ -42,6 +43,7 @@ typedef struct MicroBlazeCPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); } MicroBlazeCPUClass; @@ -66,5 +68,8 @@ static inline MicroBlazeCPU *mb_env_get_cpu(CPUMBState *env) #define ENV_GET_CPU(e) CPU(mb_env_get_cpu(e)) +#define ENV_OFFSET offsetof(MicroBlazeCPU, env) + +void mb_cpu_do_interrupt(CPUState *cs); #endif diff --git a/target-microblaze/cpu.c b/target-microblaze/cpu.c index 34b3a9bfdc..0f4293dbd5 100644 --- a/target-microblaze/cpu.c +++ b/target-microblaze/cpu.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "qemu-common.h" +#include "migration/vmstate.h" /* CPUClass::reset() */ @@ -32,7 +33,7 @@ static void mb_cpu_reset(CPUState *s) CPUMBState *env = &cpu->env; if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); + qemu_log("CPU Reset (CPU %d)\n", s->cpu_index); log_cpu_state(env, 0); } @@ -84,23 +85,54 @@ static void mb_cpu_reset(CPUState *s) #endif } +static void mb_cpu_realizefn(DeviceState *dev, Error **errp) +{ + MicroBlazeCPU *cpu = MICROBLAZE_CPU(dev); + MicroBlazeCPUClass *mcc = MICROBLAZE_CPU_GET_CLASS(dev); + + cpu_reset(CPU(cpu)); + qemu_init_vcpu(&cpu->env); + + mcc->parent_realize(dev, errp); +} + static void mb_cpu_initfn(Object *obj) { + CPUState *cs = CPU(obj); MicroBlazeCPU *cpu = MICROBLAZE_CPU(obj); CPUMBState *env = &cpu->env; + static bool tcg_initialized; + cs->env_ptr = env; cpu_exec_init(env); set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + + if (tcg_enabled() && !tcg_initialized) { + tcg_initialized = true; + mb_tcg_init(); + } } +static const VMStateDescription vmstate_mb_cpu = { + .name = "cpu", + .unmigratable = 1, +}; + static void mb_cpu_class_init(ObjectClass *oc, void *data) { + DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); MicroBlazeCPUClass *mcc = MICROBLAZE_CPU_CLASS(oc); + mcc->parent_realize = dc->realize; + dc->realize = mb_cpu_realizefn; + mcc->parent_reset = cc->reset; cc->reset = mb_cpu_reset; + + cc->do_interrupt = mb_cpu_do_interrupt; + dc->vmsd = &vmstate_mb_cpu; } static const TypeInfo mb_cpu_type_info = { diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h index 4de22266ef..1813939fc9 100644 --- a/target-microblaze/cpu.h +++ b/target-microblaze/cpu.h @@ -272,10 +272,9 @@ struct CPUMBState { #include "cpu-qom.h" +void mb_tcg_init(void); MicroBlazeCPU *cpu_mb_init(const char *cpu_model); int cpu_mb_exec(CPUMBState *s); -void cpu_mb_close(CPUMBState *s); -void do_interrupt(CPUMBState *env); /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero is returned if the signal was handled by the virtual CPU. */ @@ -308,8 +307,6 @@ static inline CPUMBState *cpu_init(const char *cpu_model) #define cpu_gen_code cpu_mb_gen_code #define cpu_signal_handler cpu_mb_signal_handler -#define CPU_SAVE_VERSION 1 - /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _nommu #define MMU_MODE1_SUFFIX _kernel @@ -376,9 +373,7 @@ void cpu_unassigned_access(CPUMBState *env1, hwaddr addr, static inline bool cpu_has_work(CPUState *cpu) { - CPUMBState *env = &MICROBLAZE_CPU(cpu)->env; - - return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); + return cpu->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } #include "exec/exec-all.h" diff --git a/target-microblaze/helper.c b/target-microblaze/helper.c index 97aedc52bb..a0416d0b72 100644 --- a/target-microblaze/helper.c +++ b/target-microblaze/helper.c @@ -26,8 +26,11 @@ #if defined(CONFIG_USER_ONLY) -void do_interrupt (CPUMBState *env) +void mb_cpu_do_interrupt(CPUState *cs) { + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + CPUMBState *env = &cpu->env; + env->exception_index = -1; env->res_addr = RES_ADDR_NONE; env->regs[14] = env->sregs[SR_PC]; @@ -109,8 +112,10 @@ int cpu_mb_handle_mmu_fault (CPUMBState *env, target_ulong address, int rw, return r; } -void do_interrupt(CPUMBState *env) +void mb_cpu_do_interrupt(CPUState *cs) { + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + CPUMBState *env = &cpu->env; uint32_t t; /* IMM flag cannot propagate across a branch and into the dslot. */ diff --git a/target-microblaze/machine.c b/target-microblaze/machine.c deleted file mode 100644 index 1be1c351b2..0000000000 --- a/target-microblaze/machine.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "hw/hw.h" -#include "hw/boards.h" - -void cpu_save(QEMUFile *f, void *opaque) -{ -} - -int cpu_load(QEMUFile *f, void *opaque, int version_id) -{ - return 0; -} diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c index 58ce71267d..a74da8e1a5 100644 --- a/target-microblaze/translate.c +++ b/target-microblaze/translate.c @@ -1734,8 +1734,6 @@ gen_intermediate_code_internal(CPUMBState *env, TranslationBlock *tb, int num_insns; int max_insns; - qemu_log_try_set_file(stderr); - pc_start = tb->pc; dc->env = env; dc->tb = tb; @@ -1772,7 +1770,7 @@ gen_intermediate_code_internal(CPUMBState *env, TranslationBlock *tb, if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_icount_start(); + gen_tb_start(); do { #if SIM_COMPAT @@ -1896,7 +1894,7 @@ gen_intermediate_code_internal(CPUMBState *env, TranslationBlock *tb, break; } } - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; if (search_pc) { j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; @@ -1965,19 +1963,17 @@ void cpu_dump_state (CPUMBState *env, FILE *f, fprintf_function cpu_fprintf, MicroBlazeCPU *cpu_mb_init(const char *cpu_model) { MicroBlazeCPU *cpu; - static int tcg_initialized = 0; - int i; cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU)); - cpu_reset(CPU(cpu)); - qemu_init_vcpu(&cpu->env); + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); - if (tcg_initialized) { - return cpu; - } + return cpu; +} - tcg_initialized = 1; +void mb_tcg_init(void) +{ + int i; cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); @@ -2008,8 +2004,6 @@ MicroBlazeCPU *cpu_mb_init(const char *cpu_model) } #define GEN_HELPER 2 #include "helper.h" - - return cpu; } void restore_state_to_opc(CPUMBState *env, TranslationBlock *tb, int pc_pos) diff --git a/target-mips/cpu-qom.h b/target-mips/cpu-qom.h index 2a4b812402..32e3cad7bf 100644 --- a/target-mips/cpu-qom.h +++ b/target-mips/cpu-qom.h @@ -37,6 +37,7 @@ /** * MIPSCPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. * * A MIPS CPU model. @@ -46,6 +47,7 @@ typedef struct MIPSCPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); } MIPSCPUClass; @@ -70,5 +72,8 @@ static inline MIPSCPU *mips_env_get_cpu(CPUMIPSState *env) #define ENV_GET_CPU(e) CPU(mips_env_get_cpu(e)) +#define ENV_OFFSET offsetof(MIPSCPU, env) + +void mips_cpu_do_interrupt(CPUState *cpu); #endif diff --git a/target-mips/cpu.c b/target-mips/cpu.c index 004406232b..5315f7bda0 100644 --- a/target-mips/cpu.c +++ b/target-mips/cpu.c @@ -29,26 +29,57 @@ static void mips_cpu_reset(CPUState *s) MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cpu); CPUMIPSState *env = &cpu->env; + if (qemu_loglevel_mask(CPU_LOG_RESET)) { + qemu_log("CPU Reset (CPU %d)\n", s->cpu_index); + log_cpu_state(env, 0); + } + mcc->parent_reset(s); + memset(env, 0, offsetof(CPUMIPSState, breakpoints)); + tlb_flush(env, 1); + cpu_state_reset(env); } +static void mips_cpu_realizefn(DeviceState *dev, Error **errp) +{ + MIPSCPU *cpu = MIPS_CPU(dev); + MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(dev); + + cpu_reset(CPU(cpu)); + qemu_init_vcpu(&cpu->env); + + mcc->parent_realize(dev, errp); +} + static void mips_cpu_initfn(Object *obj) { + CPUState *cs = CPU(obj); MIPSCPU *cpu = MIPS_CPU(obj); CPUMIPSState *env = &cpu->env; + cs->env_ptr = env; cpu_exec_init(env); + + if (tcg_enabled()) { + mips_tcg_init(); + } } static void mips_cpu_class_init(ObjectClass *c, void *data) { MIPSCPUClass *mcc = MIPS_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); + DeviceClass *dc = DEVICE_CLASS(c); + + mcc->parent_realize = dc->realize; + dc->realize = mips_cpu_realizefn; mcc->parent_reset = cc->reset; cc->reset = mips_cpu_reset; + + cc->do_interrupt = mips_cpu_do_interrupt; } static const TypeInfo mips_cpu_type_info = { diff --git a/target-mips/cpu.h b/target-mips/cpu.h index 31602ac8ed..cedf03df43 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -504,6 +504,9 @@ void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf); #define cpu_signal_handler cpu_mips_signal_handler #define cpu_list mips_cpu_list +extern void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env); +extern uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env); + #define CPU_SAVE_VERSION 3 /* MMU modes definitions. We carefully match the indices with our @@ -629,6 +632,7 @@ enum { #define CPU_INTERRUPT_WAKE CPU_INTERRUPT_TGT_INT_0 int cpu_mips_exec(CPUMIPSState *s); +void mips_tcg_init(void); MIPSCPU *cpu_mips_init(const char *cpu_model); int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc); @@ -659,7 +663,6 @@ void cpu_mips_soft_irq(CPUMIPSState *env, int irq, int level); int cpu_mips_handle_mmu_fault (CPUMIPSState *env, target_ulong address, int rw, int mmu_idx); #define cpu_handle_mmu_fault cpu_mips_handle_mmu_fault -void do_interrupt (CPUMIPSState *env); #if !defined(CONFIG_USER_ONLY) void r4k_invalidate_tlb (CPUMIPSState *env, int idx, int use_extra); hwaddr cpu_mips_translate_address (CPUMIPSState *env, target_ulong address, @@ -718,7 +721,7 @@ static inline bool cpu_has_work(CPUState *cpu) /* It is implementation dependent if non-enabled interrupts wake-up the CPU, however most of the implementations only check for interrupts that can be taken. */ - if ((env->interrupt_request & CPU_INTERRUPT_HARD) && + if ((cpu->interrupt_request & CPU_INTERRUPT_HARD) && cpu_mips_hw_interrupts_pending(env)) { has_work = true; } @@ -727,7 +730,7 @@ static inline bool cpu_has_work(CPUState *cpu) if (env->CP0_Config3 & (1 << CP0C3_MT)) { /* The QEMU model will issue an _WAKE request whenever the CPUs should be woken up. */ - if (env->interrupt_request & CPU_INTERRUPT_WAKE) { + if (cpu->interrupt_request & CPU_INTERRUPT_WAKE) { has_work = true; } @@ -751,7 +754,7 @@ static inline void compute_hflags(CPUMIPSState *env) { env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU | - MIPS_HFLAG_UX); + MIPS_HFLAG_UX | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2); if (!(env->CP0_Status & (1 << CP0St_EXL)) && !(env->CP0_Status & (1 << CP0St_ERL)) && !(env->hflags & MIPS_HFLAG_DM)) { diff --git a/target-mips/dsp_helper.c b/target-mips/dsp_helper.c index 14daf91950..472be35bbf 100644 --- a/target-mips/dsp_helper.c +++ b/target-mips/dsp_helper.c @@ -2,7 +2,7 @@ * MIPS ASE DSP Instruction emulation helpers for QEMU. * * Copyright (c) 2012 Jia Liu - * Dongxue Zhang + * Dongxue Zhang * 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 @@ -20,9 +20,32 @@ #include "cpu.h" #include "helper.h" +/* As the byte ordering doesn't matter, i.e. all columns are treated + identically, these unions can be used directly. */ +typedef union { + uint8_t ub[4]; + int8_t sb[4]; + uint16_t uh[2]; + int16_t sh[2]; + uint32_t uw[1]; + int32_t sw[1]; +} DSP32Value; + +typedef union { + uint8_t ub[8]; + int8_t sb[8]; + uint16_t uh[4]; + int16_t sh[4]; + uint32_t uw[2]; + int32_t sw[2]; + uint64_t ul[1]; + int64_t sl[1]; +} DSP64Value; + /*** MIPS DSP internal functions begin ***/ #define MIPSDSP_ABS(x) (((x) >= 0) ? x : -x) -#define MIPSDSP_OVERFLOW(a, b, c, d) (!(!((a ^ b ^ -1) & (a ^ c) & d))) +#define MIPSDSP_OVERFLOW_ADD(a, b, c, d) (~(a ^ b) & (a ^ c) & d) +#define MIPSDSP_OVERFLOW_SUB(a, b, c, d) ((a ^ b) & (a ^ c) & d) static inline void set_DSPControl_overflow_flag(uint32_t flag, int position, CPUMIPSState *env) @@ -120,7 +143,7 @@ static inline int16_t mipsdsp_add_i16(int16_t a, int16_t b, CPUMIPSState *env) tempI = a + b; - if (MIPSDSP_OVERFLOW(a, b, tempI, 0x8000)) { + if (MIPSDSP_OVERFLOW_ADD(a, b, tempI, 0x8000)) { set_DSPControl_overflow_flag(1, 20, env); } @@ -134,7 +157,7 @@ static inline int16_t mipsdsp_sat_add_i16(int16_t a, int16_t b, tempS = a + b; - if (MIPSDSP_OVERFLOW(a, b, tempS, 0x8000)) { + if (MIPSDSP_OVERFLOW_ADD(a, b, tempS, 0x8000)) { if (a > 0) { tempS = 0x7FFF; } else { @@ -153,7 +176,7 @@ static inline int32_t mipsdsp_sat_add_i32(int32_t a, int32_t b, tempI = a + b; - if (MIPSDSP_OVERFLOW(a, b, tempI, 0x80000000)) { + if (MIPSDSP_OVERFLOW_ADD(a, b, tempI, 0x80000000)) { if (a > 0) { tempI = 0x7FFFFFFF; } else { @@ -484,35 +507,6 @@ static inline uint8_t mipsdsp_rrshift1_sub_u8(uint8_t a, uint8_t b) return (temp >> 1) & 0x00FF; } -static inline int64_t mipsdsp_rashift_short_acc(int32_t ac, - int32_t shift, - CPUMIPSState *env) -{ - int32_t sign, temp31; - int64_t temp, acc; - - sign = (env->active_tc.HI[ac] >> 31) & 0x01; - acc = ((int64_t)env->active_tc.HI[ac] << 32) | - ((int64_t)env->active_tc.LO[ac] & 0xFFFFFFFF); - if (shift == 0) { - temp = acc; - } else { - if (sign == 0) { - temp = (((int64_t)0x01 << (32 - shift + 1)) - 1) & (acc >> shift); - } else { - temp = ((((int64_t)0x01 << (shift + 1)) - 1) << (32 - shift)) | - (acc >> shift); - } - } - - temp31 = (temp >> 31) & 0x01; - if (sign != temp31) { - set_DSPControl_overflow_flag(1, 23, env); - } - - return temp; -} - /* 128 bits long. p[0] is LO, p[1] is HI. */ static inline void mipsdsp_rndrashift_short_acc(int64_t *p, int32_t ac, @@ -659,7 +653,7 @@ static inline int32_t mipsdsp_sat16_mul_q15_q15(uint16_t a, uint16_t b, temp = 0x7FFF0000; set_DSPControl_overflow_flag(1, 21, env); } else { - temp = ((uint32_t)a * (uint32_t)b); + temp = (int16_t)a * (int16_t)b; temp = temp << 1; } @@ -865,7 +859,7 @@ static inline uint16_t mipsdsp_sub_i16(int16_t a, int16_t b, CPUMIPSState *env) int16_t temp; temp = a - b; - if (MIPSDSP_OVERFLOW(a, -b, temp, 0x8000)) { + if (MIPSDSP_OVERFLOW_SUB(a, b, temp, 0x8000)) { set_DSPControl_overflow_flag(1, 20, env); } @@ -878,8 +872,8 @@ static inline uint16_t mipsdsp_sat16_sub(int16_t a, int16_t b, int16_t temp; temp = a - b; - if (MIPSDSP_OVERFLOW(a, -b, temp, 0x8000)) { - if (a > 0) { + if (MIPSDSP_OVERFLOW_SUB(a, b, temp, 0x8000)) { + if (a >= 0) { temp = 0x7FFF; } else { temp = 0x8000; @@ -896,8 +890,8 @@ static inline uint32_t mipsdsp_sat32_sub(int32_t a, int32_t b, int32_t temp; temp = a - b; - if (MIPSDSP_OVERFLOW(a, -b, temp, 0x80000000)) { - if (a > 0) { + if (MIPSDSP_OVERFLOW_SUB(a, b, temp, 0x80000000)) { + if (a >= 0) { temp = 0x7FFFFFFF; } else { temp = 0x80000000; @@ -1011,7 +1005,7 @@ static inline uint32_t mipsdsp_sub32(int32_t a, int32_t b, CPUMIPSState *env) int32_t temp; temp = a - b; - if (MIPSDSP_OVERFLOW(a, -b, temp, 0x80000000)) { + if (MIPSDSP_OVERFLOW_SUB(a, b, temp, 0x80000000)) { set_DSPControl_overflow_flag(1, 20, env); } @@ -1024,7 +1018,7 @@ static inline int32_t mipsdsp_add_i32(int32_t a, int32_t b, CPUMIPSState *env) temp = a + b; - if (MIPSDSP_OVERFLOW(a, b, temp, 0x80000000)) { + if (MIPSDSP_OVERFLOW_ADD(a, b, temp, 0x80000000)) { set_DSPControl_overflow_flag(1, 20, env); } @@ -1085,7 +1079,6 @@ static inline int32_t mipsdsp_cmpu_lt(uint32_t a, uint32_t b) b = num & MIPSDSP_LO; \ } while (0) -#define MIPSDSP_RETURN32(a) ((target_long)(int32_t)a) #define MIPSDSP_RETURN32_8(a, b, c, d) ((target_long)(int32_t) \ (((uint32_t)a << 24) | \ (((uint32_t)b << 16) | \ @@ -1118,287 +1111,169 @@ static inline int32_t mipsdsp_cmpu_lt(uint32_t a, uint32_t b) #endif /** DSP Arithmetic Sub-class insns **/ -#define ARITH_PH(name, func) \ -target_ulong helper_##name##_ph(target_ulong rs, target_ulong rt) \ -{ \ - uint16_t rsh, rsl, rth, rtl, temph, templ; \ - \ - MIPSDSP_SPLIT32_16(rs, rsh, rsl); \ - MIPSDSP_SPLIT32_16(rt, rth, rtl); \ - \ - temph = mipsdsp_##func(rsh, rth); \ - templ = mipsdsp_##func(rsl, rtl); \ - \ - return MIPSDSP_RETURN32_16(temph, templ); \ +#define MIPSDSP32_UNOP_ENV(name, func, element) \ +target_ulong helper_##name(target_ulong rt, CPUMIPSState *env) \ +{ \ + DSP32Value dt; \ + unsigned int i, n; \ + \ + n = sizeof(DSP32Value) / sizeof(dt.element[0]); \ + dt.sw[0] = rt; \ + \ + for (i = 0; i < n; i++) { \ + dt.element[i] = mipsdsp_##func(dt.element[i], env); \ + } \ + \ + return (target_long)dt.sw[0]; \ } +MIPSDSP32_UNOP_ENV(absq_s_ph, sat_abs16, sh) +MIPSDSP32_UNOP_ENV(absq_s_qb, sat_abs8, sb) +MIPSDSP32_UNOP_ENV(absq_s_w, sat_abs32, sw) +#undef MIPSDSP32_UNOP_ENV -#define ARITH_PH_ENV(name, func) \ -target_ulong helper_##name##_ph(target_ulong rs, target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - uint16_t rsh, rsl, rth, rtl, temph, templ; \ - \ - MIPSDSP_SPLIT32_16(rs, rsh, rsl); \ - MIPSDSP_SPLIT32_16(rt, rth, rtl); \ - \ - temph = mipsdsp_##func(rsh, rth, env); \ - templ = mipsdsp_##func(rsl, rtl, env); \ - \ - return MIPSDSP_RETURN32_16(temph, templ); \ +#if defined(TARGET_MIPS64) +#define MIPSDSP64_UNOP_ENV(name, func, element) \ +target_ulong helper_##name(target_ulong rt, CPUMIPSState *env) \ +{ \ + DSP64Value dt; \ + unsigned int i, n; \ + \ + n = sizeof(DSP64Value) / sizeof(dt.element[0]); \ + dt.sl[0] = rt; \ + \ + for (i = 0; i < n; i++) { \ + dt.element[i] = mipsdsp_##func(dt.element[i], env); \ + } \ + \ + return dt.sl[0]; \ } +MIPSDSP64_UNOP_ENV(absq_s_ob, sat_abs8, sb) +MIPSDSP64_UNOP_ENV(absq_s_qh, sat_abs16, sh) +MIPSDSP64_UNOP_ENV(absq_s_pw, sat_abs32, sw) +#undef MIPSDSP64_UNOP_ENV +#endif +#define MIPSDSP32_BINOP(name, func, element) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt) \ +{ \ + DSP32Value ds, dt; \ + unsigned int i, n; \ + \ + n = sizeof(DSP32Value) / sizeof(ds.element[0]); \ + ds.sw[0] = rs; \ + dt.sw[0] = rt; \ + \ + for (i = 0; i < n; i++) { \ + ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i]); \ + } \ + \ + return (target_long)ds.sw[0]; \ +} +MIPSDSP32_BINOP(addqh_ph, rshift1_add_q16, sh); +MIPSDSP32_BINOP(addqh_r_ph, rrshift1_add_q16, sh); +MIPSDSP32_BINOP(addqh_r_w, rrshift1_add_q32, sw); +MIPSDSP32_BINOP(addqh_w, rshift1_add_q32, sw); +MIPSDSP32_BINOP(adduh_qb, rshift1_add_u8, ub); +MIPSDSP32_BINOP(adduh_r_qb, rrshift1_add_u8, ub); +MIPSDSP32_BINOP(subqh_ph, rshift1_sub_q16, sh); +MIPSDSP32_BINOP(subqh_r_ph, rrshift1_sub_q16, sh); +MIPSDSP32_BINOP(subqh_r_w, rrshift1_sub_q32, sw); +MIPSDSP32_BINOP(subqh_w, rshift1_sub_q32, sw); +#undef MIPSDSP32_BINOP -ARITH_PH_ENV(addq, add_i16); -ARITH_PH_ENV(addq_s, sat_add_i16); -ARITH_PH_ENV(addu, add_u16); -ARITH_PH_ENV(addu_s, sat_add_u16); - -ARITH_PH(addqh, rshift1_add_q16); -ARITH_PH(addqh_r, rrshift1_add_q16); - -ARITH_PH_ENV(subq, sub_i16); -ARITH_PH_ENV(subq_s, sat16_sub); -ARITH_PH_ENV(subu, sub_u16_u16); -ARITH_PH_ENV(subu_s, satu16_sub_u16_u16); - -ARITH_PH(subqh, rshift1_sub_q16); -ARITH_PH(subqh_r, rrshift1_sub_q16); - -#undef ARITH_PH -#undef ARITH_PH_ENV +#define MIPSDSP32_BINOP_ENV(name, func, element) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt, \ + CPUMIPSState *env) \ +{ \ + DSP32Value ds, dt; \ + unsigned int i, n; \ + \ + n = sizeof(DSP32Value) / sizeof(ds.element[0]); \ + ds.sw[0] = rs; \ + dt.sw[0] = rt; \ + \ + for (i = 0 ; i < n ; i++) { \ + ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i], env); \ + } \ + \ + return (target_long)ds.sw[0]; \ +} +MIPSDSP32_BINOP_ENV(addq_ph, add_i16, sh) +MIPSDSP32_BINOP_ENV(addq_s_ph, sat_add_i16, sh) +MIPSDSP32_BINOP_ENV(addq_s_w, sat_add_i32, sw); +MIPSDSP32_BINOP_ENV(addu_ph, add_u16, sh) +MIPSDSP32_BINOP_ENV(addu_qb, add_u8, ub); +MIPSDSP32_BINOP_ENV(addu_s_ph, sat_add_u16, sh) +MIPSDSP32_BINOP_ENV(addu_s_qb, sat_add_u8, ub); +MIPSDSP32_BINOP_ENV(subq_ph, sub_i16, sh); +MIPSDSP32_BINOP_ENV(subq_s_ph, sat16_sub, sh); +MIPSDSP32_BINOP_ENV(subq_s_w, sat32_sub, sw); +MIPSDSP32_BINOP_ENV(subu_ph, sub_u16_u16, sh); +MIPSDSP32_BINOP_ENV(subu_qb, sub_u8, ub); +MIPSDSP32_BINOP_ENV(subu_s_ph, satu16_sub_u16_u16, sh); +MIPSDSP32_BINOP_ENV(subu_s_qb, satu8_sub, ub); +#undef MIPSDSP32_BINOP_ENV #ifdef TARGET_MIPS64 -#define ARITH_QH_ENV(name, func) \ -target_ulong helper_##name##_qh(target_ulong rs, target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - uint16_t rs3, rs2, rs1, rs0; \ - uint16_t rt3, rt2, rt1, rt0; \ - uint16_t tempD, tempC, tempB, tempA; \ - \ - MIPSDSP_SPLIT64_16(rs, rs3, rs2, rs1, rs0); \ - MIPSDSP_SPLIT64_16(rt, rt3, rt2, rt1, rt0); \ - \ - tempD = mipsdsp_##func(rs3, rt3, env); \ - tempC = mipsdsp_##func(rs2, rt2, env); \ - tempB = mipsdsp_##func(rs1, rt1, env); \ - tempA = mipsdsp_##func(rs0, rt0, env); \ - \ - return MIPSDSP_RETURN64_16(tempD, tempC, tempB, tempA); \ +#define MIPSDSP64_BINOP(name, func, element) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt) \ +{ \ + DSP64Value ds, dt; \ + unsigned int i, n; \ + \ + n = sizeof(DSP64Value) / sizeof(ds.element[0]); \ + ds.sl[0] = rs; \ + dt.sl[0] = rt; \ + \ + for (i = 0 ; i < n ; i++) { \ + ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i]); \ + } \ + \ + return ds.sl[0]; \ } +MIPSDSP64_BINOP(adduh_ob, rshift1_add_u8, ub); +MIPSDSP64_BINOP(adduh_r_ob, rrshift1_add_u8, ub); +MIPSDSP64_BINOP(subuh_ob, rshift1_sub_u8, ub); +MIPSDSP64_BINOP(subuh_r_ob, rrshift1_sub_u8, ub); +#undef MIPSDSP64_BINOP -ARITH_QH_ENV(addq, add_i16); -ARITH_QH_ENV(addq_s, sat_add_i16); -ARITH_QH_ENV(addu, add_u16); -ARITH_QH_ENV(addu_s, sat_add_u16); - -ARITH_QH_ENV(subq, sub_i16); -ARITH_QH_ENV(subq_s, sat16_sub); -ARITH_QH_ENV(subu, sub_u16_u16); -ARITH_QH_ENV(subu_s, satu16_sub_u16_u16); - -#undef ARITH_QH_ENV +#define MIPSDSP64_BINOP_ENV(name, func, element) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt, \ + CPUMIPSState *env) \ +{ \ + DSP64Value ds, dt; \ + unsigned int i, n; \ + \ + n = sizeof(DSP64Value) / sizeof(ds.element[0]); \ + ds.sl[0] = rs; \ + dt.sl[0] = rt; \ + \ + for (i = 0 ; i < n ; i++) { \ + ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i], env); \ + } \ + \ + return ds.sl[0]; \ +} +MIPSDSP64_BINOP_ENV(addq_pw, add_i32, sw); +MIPSDSP64_BINOP_ENV(addq_qh, add_i16, sh); +MIPSDSP64_BINOP_ENV(addq_s_pw, sat_add_i32, sw); +MIPSDSP64_BINOP_ENV(addq_s_qh, sat_add_i16, sh); +MIPSDSP64_BINOP_ENV(addu_ob, add_u8, uh); +MIPSDSP64_BINOP_ENV(addu_qh, add_u16, uh); +MIPSDSP64_BINOP_ENV(addu_s_ob, sat_add_u8, uh); +MIPSDSP64_BINOP_ENV(addu_s_qh, sat_add_u16, uh); +MIPSDSP64_BINOP_ENV(subq_pw, sub32, sw); +MIPSDSP64_BINOP_ENV(subq_qh, sub_i16, sh); +MIPSDSP64_BINOP_ENV(subq_s_pw, sat32_sub, sw); +MIPSDSP64_BINOP_ENV(subq_s_qh, sat16_sub, sh); +MIPSDSP64_BINOP_ENV(subu_ob, sub_u8, uh); +MIPSDSP64_BINOP_ENV(subu_qh, sub_u16_u16, uh); +MIPSDSP64_BINOP_ENV(subu_s_ob, satu8_sub, uh); +MIPSDSP64_BINOP_ENV(subu_s_qh, satu16_sub_u16_u16, uh); +#undef MIPSDSP64_BINOP_ENV #endif -#define ARITH_W(name, func) \ -target_ulong helper_##name##_w(target_ulong rs, target_ulong rt) \ -{ \ - uint32_t rd; \ - rd = mipsdsp_##func(rs, rt); \ - return MIPSDSP_RETURN32(rd); \ -} - -#define ARITH_W_ENV(name, func) \ -target_ulong helper_##name##_w(target_ulong rs, target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - uint32_t rd; \ - rd = mipsdsp_##func(rs, rt, env); \ - return MIPSDSP_RETURN32(rd); \ -} - -ARITH_W_ENV(addq_s, sat_add_i32); - -ARITH_W(addqh, rshift1_add_q32); -ARITH_W(addqh_r, rrshift1_add_q32); - -ARITH_W_ENV(subq_s, sat32_sub); - -ARITH_W(subqh, rshift1_sub_q32); -ARITH_W(subqh_r, rrshift1_sub_q32); - -#undef ARITH_W -#undef ARITH_W_ENV - -target_ulong helper_absq_s_w(target_ulong rt, CPUMIPSState *env) -{ - uint32_t rd; - - rd = mipsdsp_sat_abs32(rt, env); - - return (target_ulong)rd; -} - - -#if defined(TARGET_MIPS64) - -#define ARITH_PW_ENV(name, func) \ -target_ulong helper_##name##_pw(target_ulong rs, target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - uint32_t rs1, rs0; \ - uint32_t rt1, rt0; \ - uint32_t tempB, tempA; \ - \ - MIPSDSP_SPLIT64_32(rs, rs1, rs0); \ - MIPSDSP_SPLIT64_32(rt, rt1, rt0); \ - \ - tempB = mipsdsp_##func(rs1, rt1, env); \ - tempA = mipsdsp_##func(rs0, rt0, env); \ - \ - return MIPSDSP_RETURN64_32(tempB, tempA); \ -} - -ARITH_PW_ENV(addq, add_i32); -ARITH_PW_ENV(addq_s, sat_add_i32); -ARITH_PW_ENV(subq, sub32); -ARITH_PW_ENV(subq_s, sat32_sub); - -#undef ARITH_PW_ENV - -#endif - -#define ARITH_QB(name, func) \ -target_ulong helper_##name##_qb(target_ulong rs, target_ulong rt) \ -{ \ - uint8_t rs0, rs1, rs2, rs3; \ - uint8_t rt0, rt1, rt2, rt3; \ - uint8_t temp0, temp1, temp2, temp3; \ - \ - MIPSDSP_SPLIT32_8(rs, rs3, rs2, rs1, rs0); \ - MIPSDSP_SPLIT32_8(rt, rt3, rt2, rt1, rt0); \ - \ - temp0 = mipsdsp_##func(rs0, rt0); \ - temp1 = mipsdsp_##func(rs1, rt1); \ - temp2 = mipsdsp_##func(rs2, rt2); \ - temp3 = mipsdsp_##func(rs3, rt3); \ - \ - return MIPSDSP_RETURN32_8(temp3, temp2, temp1, temp0); \ -} - -#define ARITH_QB_ENV(name, func) \ -target_ulong helper_##name##_qb(target_ulong rs, target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - uint8_t rs0, rs1, rs2, rs3; \ - uint8_t rt0, rt1, rt2, rt3; \ - uint8_t temp0, temp1, temp2, temp3; \ - \ - MIPSDSP_SPLIT32_8(rs, rs3, rs2, rs1, rs0); \ - MIPSDSP_SPLIT32_8(rt, rt3, rt2, rt1, rt0); \ - \ - temp0 = mipsdsp_##func(rs0, rt0, env); \ - temp1 = mipsdsp_##func(rs1, rt1, env); \ - temp2 = mipsdsp_##func(rs2, rt2, env); \ - temp3 = mipsdsp_##func(rs3, rt3, env); \ - \ - return MIPSDSP_RETURN32_8(temp3, temp2, temp1, temp0); \ -} - -ARITH_QB(adduh, rshift1_add_u8); -ARITH_QB(adduh_r, rrshift1_add_u8); - -ARITH_QB_ENV(addu, add_u8); -ARITH_QB_ENV(addu_s, sat_add_u8); - -#undef ADDU_QB -#undef ADDU_QB_ENV - -#if defined(TARGET_MIPS64) -#define ARITH_OB(name, func) \ -target_ulong helper_##name##_ob(target_ulong rs, target_ulong rt) \ -{ \ - int i; \ - uint8_t rs_t[8], rt_t[8]; \ - uint8_t temp[8]; \ - uint64_t result; \ - \ - result = 0; \ - \ - for (i = 0; i < 8; i++) { \ - rs_t[i] = (rs >> (8 * i)) & MIPSDSP_Q0; \ - rt_t[i] = (rt >> (8 * i)) & MIPSDSP_Q0; \ - temp[i] = mipsdsp_##func(rs_t[i], rt_t[i]); \ - result |= (uint64_t)temp[i] << (8 * i); \ - } \ - \ - return result; \ -} - -#define ARITH_OB_ENV(name, func) \ -target_ulong helper_##name##_ob(target_ulong rs, target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - int i; \ - uint8_t rs_t[8], rt_t[8]; \ - uint8_t temp[8]; \ - uint64_t result; \ - \ - result = 0; \ - \ - for (i = 0; i < 8; i++) { \ - rs_t[i] = (rs >> (8 * i)) & MIPSDSP_Q0; \ - rt_t[i] = (rt >> (8 * i)) & MIPSDSP_Q0; \ - temp[i] = mipsdsp_##func(rs_t[i], rt_t[i], env); \ - result |= (uint64_t)temp[i] << (8 * i); \ - } \ - \ - return result; \ -} - -ARITH_OB_ENV(addu, add_u8); -ARITH_OB_ENV(addu_s, sat_add_u8); - -ARITH_OB(adduh, rshift1_add_u8); -ARITH_OB(adduh_r, rrshift1_add_u8); - -ARITH_OB_ENV(subu, sub_u8); -ARITH_OB_ENV(subu_s, satu8_sub); - -ARITH_OB(subuh, rshift1_sub_u8); -ARITH_OB(subuh_r, rrshift1_sub_u8); - -#undef ARITH_OB -#undef ARITH_OB_ENV - -#endif - -#define SUBU_QB(name, func) \ -target_ulong helper_##name##_qb(target_ulong rs, \ - target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - uint8_t rs3, rs2, rs1, rs0; \ - uint8_t rt3, rt2, rt1, rt0; \ - uint8_t tempD, tempC, tempB, tempA; \ - \ - MIPSDSP_SPLIT32_8(rs, rs3, rs2, rs1, rs0); \ - MIPSDSP_SPLIT32_8(rt, rt3, rt2, rt1, rt0); \ - \ - tempD = mipsdsp_##func(rs3, rt3, env); \ - tempC = mipsdsp_##func(rs2, rt2, env); \ - tempB = mipsdsp_##func(rs1, rt1, env); \ - tempA = mipsdsp_##func(rs0, rt0, env); \ - \ - return MIPSDSP_RETURN32_8(tempD, tempC, tempB, tempA); \ -} - -SUBU_QB(subu, sub_u8); -SUBU_QB(subu_s, satu8_sub); - -#undef SUBU_QB - #define SUBUH_QB(name, var) \ target_ulong helper_##name##_qb(target_ulong rs, target_ulong rt) \ { \ @@ -1478,103 +1353,29 @@ target_ulong helper_modsub(target_ulong rs, target_ulong rt) target_ulong helper_raddu_w_qb(target_ulong rs) { - uint8_t rs3, rs2, rs1, rs0; - uint16_t temp; + target_ulong ret = 0; + DSP32Value ds; + unsigned int i; - MIPSDSP_SPLIT32_8(rs, rs3, rs2, rs1, rs0); - - temp = (uint16_t)rs3 + (uint16_t)rs2 + (uint16_t)rs1 + (uint16_t)rs0; - - return (target_ulong)temp; + ds.uw[0] = rs; + for (i = 0; i < 4; i++) { + ret += ds.ub[i]; + } + return ret; } #if defined(TARGET_MIPS64) target_ulong helper_raddu_l_ob(target_ulong rs) { - int i; - uint16_t rs_t[8]; - uint64_t temp; - - temp = 0; + target_ulong ret = 0; + DSP64Value ds; + unsigned int i; + ds.ul[0] = rs; for (i = 0; i < 8; i++) { - rs_t[i] = (rs >> (8 * i)) & MIPSDSP_Q0; - temp += (uint64_t)rs_t[i]; + ret += ds.ub[i]; } - - return temp; -} -#endif - -target_ulong helper_absq_s_qb(target_ulong rt, CPUMIPSState *env) -{ - uint8_t tempD, tempC, tempB, tempA; - - MIPSDSP_SPLIT32_8(rt, tempD, tempC, tempB, tempA); - - tempD = mipsdsp_sat_abs8(tempD, env); - tempC = mipsdsp_sat_abs8(tempC, env); - tempB = mipsdsp_sat_abs8(tempB, env); - tempA = mipsdsp_sat_abs8(tempA, env); - - return MIPSDSP_RETURN32_8(tempD, tempC, tempB, tempA); -} - -target_ulong helper_absq_s_ph(target_ulong rt, CPUMIPSState *env) -{ - uint16_t tempB, tempA; - - MIPSDSP_SPLIT32_16(rt, tempB, tempA); - - tempB = mipsdsp_sat_abs16 (tempB, env); - tempA = mipsdsp_sat_abs16 (tempA, env); - - return MIPSDSP_RETURN32_16(tempB, tempA); -} - -#if defined(TARGET_MIPS64) -target_ulong helper_absq_s_ob(target_ulong rt, CPUMIPSState *env) -{ - int i; - int8_t temp[8]; - uint64_t result; - - for (i = 0; i < 8; i++) { - temp[i] = (rt >> (8 * i)) & MIPSDSP_Q0; - temp[i] = mipsdsp_sat_abs8(temp[i], env); - } - - for (i = 0; i < 8; i++) { - result = (uint64_t)(uint8_t)temp[i] << (8 * i); - } - - return result; -} - -target_ulong helper_absq_s_qh(target_ulong rt, CPUMIPSState *env) -{ - int16_t tempD, tempC, tempB, tempA; - - MIPSDSP_SPLIT64_16(rt, tempD, tempC, tempB, tempA); - - tempD = mipsdsp_sat_abs16(tempD, env); - tempC = mipsdsp_sat_abs16(tempC, env); - tempB = mipsdsp_sat_abs16(tempB, env); - tempA = mipsdsp_sat_abs16(tempA, env); - - return MIPSDSP_RETURN64_16(tempD, tempC, tempB, tempA); -} - -target_ulong helper_absq_s_pw(target_ulong rt, CPUMIPSState *env) -{ - int32_t tempB, tempA; - - MIPSDSP_SPLIT64_32(rt, tempB, tempA); - - tempB = mipsdsp_sat_abs32(tempB, env); - tempA = mipsdsp_sat_abs32(tempA, env); - - return MIPSDSP_RETURN64_32(tempB, tempA); + return ret; } #endif @@ -2502,7 +2303,7 @@ DP_OB(dpsu_h_obr, 0, 24, 16, 8, 0, 24, 16, 8, 0); void helper_##name(uint32_t ac, target_ulong rs, target_ulong rt, \ CPUMIPSState *env) \ { \ - uint16_t rsB, rsA, rtB, rtA; \ + int16_t rsB, rsA, rtB, rtA; \ int32_t tempA, tempB; \ int64_t acc; \ \ @@ -2688,37 +2489,42 @@ DP_QH(dpsq_s_w_qh, 0, 1); #endif #define DP_L_W(name, is_add) \ -void helper_##name(uint32_t ac, target_ulong rs, target_ulong rt, \ - CPUMIPSState *env) \ -{ \ - int32_t temp63; \ - int64_t dotp, acc; \ - uint64_t temp; \ - \ - dotp = mipsdsp_mul_q31_q31(ac, rs, rt, env); \ - acc = ((uint64_t)env->active_tc.HI[ac] << 32) | \ - ((uint64_t)env->active_tc.LO[ac] & MIPSDSP_LLO); \ - if (!is_add) { \ - dotp = -dotp; \ - } \ - \ - temp = acc + dotp; \ - if (MIPSDSP_OVERFLOW((uint64_t)acc, (uint64_t)dotp, temp, \ - (0x01ull << 63))) { \ - temp63 = (temp >> 63) & 0x01; \ - if (temp63 == 1) { \ - temp = (0x01ull << 63) - 1; \ - } else { \ - temp = 0x01ull << 63; \ - } \ - \ - set_DSPControl_overflow_flag(1, 16 + ac, env); \ - } \ - \ - env->active_tc.HI[ac] = (target_long)(int32_t) \ - ((temp & MIPSDSP_LHI) >> 32); \ - env->active_tc.LO[ac] = (target_long)(int32_t) \ - (temp & MIPSDSP_LLO); \ +void helper_##name(uint32_t ac, target_ulong rs, target_ulong rt, \ + CPUMIPSState *env) \ +{ \ + int32_t temp63; \ + int64_t dotp, acc; \ + uint64_t temp; \ + bool overflow; \ + \ + dotp = mipsdsp_mul_q31_q31(ac, rs, rt, env); \ + acc = ((uint64_t)env->active_tc.HI[ac] << 32) | \ + ((uint64_t)env->active_tc.LO[ac] & MIPSDSP_LLO); \ + if (is_add) { \ + temp = acc + dotp; \ + overflow = MIPSDSP_OVERFLOW_ADD((uint64_t)acc, (uint64_t)dotp, \ + temp, (0x01ull << 63)); \ + } else { \ + temp = acc - dotp; \ + overflow = MIPSDSP_OVERFLOW_SUB((uint64_t)acc, (uint64_t)dotp, \ + temp, (0x01ull << 63)); \ + } \ + \ + if (overflow) { \ + temp63 = (temp >> 63) & 0x01; \ + if (temp63 == 1) { \ + temp = (0x01ull << 63) - 1; \ + } else { \ + temp = 0x01ull << 63; \ + } \ + \ + set_DSPControl_overflow_flag(1, 16 + ac, env); \ + } \ + \ + env->active_tc.HI[ac] = (target_long)(int32_t) \ + ((temp & MIPSDSP_LHI) >> 32); \ + env->active_tc.LO[ac] = (target_long)(int32_t) \ + (temp & MIPSDSP_LLO); \ } DP_L_W(dpaq_sa_l_w, 1); @@ -2889,7 +2695,7 @@ MAQ_SA_W(maq_sa_w_phr, 0); target_ulong helper_##name(target_ulong rs, target_ulong rt, \ CPUMIPSState *env) \ { \ - uint32_t rs_t, rt_t; \ + int32_t rs_t, rt_t; \ int32_t tempI; \ int64_t tempL; \ \ @@ -3311,73 +3117,6 @@ PICK_INSN(pick_pw, 2, MIPSDSP_LLO, 32, 0); #endif #undef PICK_INSN -#define APPEND_INSN(name, ret_32) \ -target_ulong helper_##name(target_ulong rt, target_ulong rs, uint32_t sa) \ -{ \ - target_ulong temp; \ - \ - if (ret_32) { \ - temp = ((rt & MIPSDSP_LLO) << sa) | \ - ((rs & MIPSDSP_LLO) & ((0x01 << sa) - 1)); \ - temp = (target_long)(int32_t)(temp & MIPSDSP_LLO); \ - } else { \ - temp = (rt << sa) | (rs & ((0x01 << sa) - 1)); \ - } \ - \ - return temp; \ -} - -APPEND_INSN(append, 1); -#ifdef TARGET_MIPS64 -APPEND_INSN(dappend, 0); -#endif -#undef APPEND_INSN - -#define PREPEND_INSN(name, or_val, ret_32) \ -target_ulong helper_##name(target_ulong rs, target_ulong rt, \ - uint32_t sa) \ -{ \ - sa |= or_val; \ - \ - if (1) { \ - return (target_long)(int32_t)(uint32_t) \ - (((rs & MIPSDSP_LLO) << (32 - sa)) | \ - ((rt & MIPSDSP_LLO) >> sa)); \ - } else { \ - return (rs << (64 - sa)) | (rt >> sa); \ - } \ -} - -PREPEND_INSN(prepend, 0, 1); -#ifdef TARGET_MIPS64 -PREPEND_INSN(prependw, 0, 0); -PREPEND_INSN(prependd, 0x20, 0); -#endif -#undef PREPEND_INSN - -#define BALIGN_INSN(name, filter, ret32) \ -target_ulong helper_##name(target_ulong rs, target_ulong rt, uint32_t bp) \ -{ \ - bp = bp & 0x03; \ - \ - if ((bp & 1) == 0) { \ - return rt; \ - } else { \ - if (ret32) { \ - return (target_long)(int32_t)((rt << (8 * bp)) | \ - (rs >> (8 * (4 - bp)))); \ - } else { \ - return (rt << (8 * bp)) | (rs >> (8 * (8 - bp))); \ - } \ - } \ -} - -BALIGN_INSN(balign, 0x03, 1); -#if defined(TARGET_MIPS64) -BALIGN_INSN(dbalign, 0x07, 0); -#endif -#undef BALIGN_INSN - target_ulong helper_packrl_ph(target_ulong rs, target_ulong rt) { uint32_t rsl, rth; @@ -3407,7 +3146,7 @@ target_ulong helper_extr_w(target_ulong ac, target_ulong shift, int32_t tempI; int64_t tempDL[2]; - shift = shift & 0x0F; + shift = shift & 0x1F; mipsdsp_rndrashift_short_acc(tempDL, ac, shift, env); if ((tempDL[1] != 0 || (tempDL[0] & MIPSDSP_LHI) != 0) && @@ -3435,7 +3174,7 @@ target_ulong helper_extr_r_w(target_ulong ac, target_ulong shift, { int64_t tempDL[2]; - shift = shift & 0x0F; + shift = shift & 0x1F; mipsdsp_rndrashift_short_acc(tempDL, ac, shift, env); if ((tempDL[1] != 0 || (tempDL[0] & MIPSDSP_LHI) != 0) && @@ -3462,7 +3201,7 @@ target_ulong helper_extr_rs_w(target_ulong ac, target_ulong shift, int32_t tempI, temp64; int64_t tempDL[2]; - shift = shift & 0x0F; + shift = shift & 0x1F; mipsdsp_rndrashift_short_acc(tempDL, ac, shift, env); if ((tempDL[1] != 0 || (tempDL[0] & MIPSDSP_LHI) != 0) && @@ -3645,11 +3384,15 @@ target_ulong helper_dextr_rs_l(target_ulong ac, target_ulong shift, target_ulong helper_extr_s_h(target_ulong ac, target_ulong shift, CPUMIPSState *env) { - int64_t temp; + int64_t temp, acc; - shift = shift & 0x0F; + shift = shift & 0x1F; + + acc = ((int64_t)env->active_tc.HI[ac] << 32) | + ((int64_t)env->active_tc.LO[ac] & 0xFFFFFFFF); + + temp = acc >> shift; - temp = mipsdsp_rashift_short_acc(ac, shift, env); if (temp > (int64_t)0x7FFF) { temp = 0x00007FFF; set_DSPControl_overflow_flag(1, 23, env); @@ -3900,7 +3643,7 @@ void helper_dmthlip(target_ulong rs, target_ulong ac, CPUMIPSState *env) } #endif -void helper_wrdsp(target_ulong rs, target_ulong mask_num, CPUMIPSState *env) +void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env) { uint8_t mask[6]; uint8_t i; @@ -3948,7 +3691,11 @@ void helper_wrdsp(target_ulong rs, target_ulong mask_num, CPUMIPSState *env) if (mask[4] == 1) { overwrite &= 0x00FFFFFF; newbits &= 0x00FFFFFF; +#if defined(TARGET_MIPS64) newbits |= 0xFF000000 & rs; +#else + newbits |= 0x0F000000 & rs; +#endif } if (mask[5] == 1) { @@ -3962,7 +3709,12 @@ void helper_wrdsp(target_ulong rs, target_ulong mask_num, CPUMIPSState *env) env->active_tc.DSPControl = dsp; } -target_ulong helper_rddsp(target_ulong masknum, CPUMIPSState *env) +void helper_wrdsp(target_ulong rs, target_ulong mask_num, CPUMIPSState *env) +{ + return cpu_wrdsp(rs, mask_num, env); +} + +uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env) { uint8_t mask[6]; uint32_t ruler, i; @@ -3971,7 +3723,7 @@ target_ulong helper_rddsp(target_ulong masknum, CPUMIPSState *env) ruler = 0x01; for (i = 0; i < 6; i++) { - mask[i] = (masknum & ruler) >> i ; + mask[i] = (mask_num & ruler) >> i ; ruler = ruler << 1; } @@ -3999,7 +3751,11 @@ target_ulong helper_rddsp(target_ulong masknum, CPUMIPSState *env) } if (mask[4] == 1) { +#if defined(TARGET_MIPS64) temp |= dsp & 0xFF000000; +#else + temp |= dsp & 0x0F000000; +#endif } if (mask[5] == 1) { @@ -4009,6 +3765,11 @@ target_ulong helper_rddsp(target_ulong masknum, CPUMIPSState *env) return temp; } +target_ulong helper_rddsp(target_ulong mask_num, CPUMIPSState *env) +{ + return cpu_rddsp(mask_num, env); +} + #undef MIPSDSP_LHI #undef MIPSDSP_LLO @@ -4022,7 +3783,6 @@ target_ulong helper_rddsp(target_ulong masknum, CPUMIPSState *env) #undef MIPSDSP_SPLIT32_8 #undef MIPSDSP_SPLIT32_16 -#undef MIPSDSP_RETURN32 #undef MIPSDSP_RETURN32_8 #undef MIPSDSP_RETURN32_16 diff --git a/target-mips/helper.c b/target-mips/helper.c index e877b8db78..3a54acf706 100644 --- a/target-mips/helper.c +++ b/target-mips/helper.c @@ -396,10 +396,11 @@ static void set_hflags_for_handler (CPUMIPSState *env) } #endif -void do_interrupt (CPUMIPSState *env) +void mips_cpu_do_interrupt(CPUState *cs) { + MIPSCPU *cpu = MIPS_CPU(cs); + CPUMIPSState *env = &cpu->env; #if !defined(CONFIG_USER_ONLY) - MIPSCPU *cpu = mips_env_get_cpu(env); target_ulong offset; int cause = -1; const char *name; diff --git a/target-mips/helper.h b/target-mips/helper.h index 9ea60ec1bb..ed75e2c9f2 100644 --- a/target-mips/helper.h +++ b/target-mips/helper.h @@ -24,8 +24,6 @@ DEF_HELPER_FLAGS_1(clz, TCG_CALL_NO_RWG_SE, tl, tl) #ifdef TARGET_MIPS64 DEF_HELPER_FLAGS_1(dclo, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(dclz, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_3(dmult, void, env, tl, tl) -DEF_HELPER_3(dmultu, void, env, tl, tl) #endif DEF_HELPER_3(muls, tl, env, tl, tl) @@ -654,19 +652,6 @@ DEF_HELPER_FLAGS_3(pick_ob, 0, tl, tl, tl, env) DEF_HELPER_FLAGS_3(pick_qh, 0, tl, tl, tl, env) DEF_HELPER_FLAGS_3(pick_pw, 0, tl, tl, tl, env) #endif -DEF_HELPER_FLAGS_3(append, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -#if defined(TARGET_MIPS64) -DEF_HELPER_FLAGS_3(dappend, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -#endif -DEF_HELPER_FLAGS_3(prepend, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -#if defined(TARGET_MIPS64) -DEF_HELPER_FLAGS_3(prependd, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -DEF_HELPER_FLAGS_3(prependw, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -#endif -DEF_HELPER_FLAGS_3(balign, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -#if defined(TARGET_MIPS64) -DEF_HELPER_FLAGS_3(dbalign, TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) -#endif DEF_HELPER_FLAGS_2(packrl_ph, TCG_CALL_NO_RWG_SE, tl, tl, tl) #if defined(TARGET_MIPS64) DEF_HELPER_FLAGS_2(packrl_pw, TCG_CALL_NO_RWG_SE, tl, tl, tl) diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index e85edce6fa..3fa0d00cf9 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -38,10 +38,9 @@ static inline void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env, int error_code, uintptr_t pc) { -#if 1 - if (exception < 0x100) + if (exception < EXCP_SC) { qemu_log("%s: %d %d\n", __func__, exception, error_code); -#endif + } env->exception_index = exception; env->error_code = error_code; @@ -268,18 +267,6 @@ target_ulong helper_mulshiu(CPUMIPSState *env, target_ulong arg1, (uint64_t)(uint32_t)arg2); } -#ifdef TARGET_MIPS64 -void helper_dmult(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) -{ - muls64(&(env->active_tc.LO[0]), &(env->active_tc.HI[0]), arg1, arg2); -} - -void helper_dmultu(CPUMIPSState *env, target_ulong arg1, target_ulong arg2) -{ - mulu64(&(env->active_tc.LO[0]), &(env->active_tc.HI[0]), arg1, arg2); -} -#endif - #ifndef CONFIG_USER_ONLY static inline hwaddr do_translate_address(CPUMIPSState *env, @@ -528,29 +515,30 @@ void helper_sdm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, /* SMP helpers. */ static bool mips_vpe_is_wfi(MIPSCPU *c) { + CPUState *cpu = CPU(c); CPUMIPSState *env = &c->env; /* If the VPE is halted but otherwise active, it means it's waiting for an interrupt. */ - return env->halted && mips_vpe_active(env); + return cpu->halted && mips_vpe_active(env); } -static inline void mips_vpe_wake(CPUMIPSState *c) +static inline void mips_vpe_wake(MIPSCPU *c) { /* Dont set ->halted = 0 directly, let it be done via cpu_has_work because there might be other conditions that state that c should be sleeping. */ - cpu_interrupt(c, CPU_INTERRUPT_WAKE); + cpu_interrupt(CPU(c), CPU_INTERRUPT_WAKE); } static inline void mips_vpe_sleep(MIPSCPU *cpu) { - CPUMIPSState *c = &cpu->env; + CPUState *cs = CPU(cpu); /* The VPE was shut off, really go to bed. Reset any old _WAKE requests. */ - c->halted = 1; - cpu_reset_interrupt(c, CPU_INTERRUPT_WAKE); + cs->halted = 1; + cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); } static inline void mips_tc_wake(MIPSCPU *cpu, int tc) @@ -559,7 +547,7 @@ static inline void mips_tc_wake(MIPSCPU *cpu, int tc) /* FIXME: TC reschedule. */ if (mips_vpe_active(c) && !mips_vpe_is_wfi(cpu)) { - mips_vpe_wake(c); + mips_vpe_wake(cpu); } } @@ -573,17 +561,23 @@ static inline void mips_tc_sleep(MIPSCPU *cpu, int tc) } } -/* tc should point to an int with the value of the global TC index. - This function will transform it into a local index within the - returned CPUMIPSState. - - FIXME: This code assumes that all VPEs have the same number of TCs, +/** + * mips_cpu_map_tc: + * @env: CPU from which mapping is performed. + * @tc: Should point to an int with the value of the global TC index. + * + * This function will transform @tc into a local index within the + * returned #CPUMIPSState. + */ +/* FIXME: This code assumes that all VPEs have the same number of TCs, which depends on runtime setup. Can probably be fixed by walking the list of CPUMIPSStates. */ static CPUMIPSState *mips_cpu_map_tc(CPUMIPSState *env, int *tc) { - CPUMIPSState *other; - int vpe_idx, nr_threads = env->nr_threads; + MIPSCPU *cpu; + CPUState *cs; + CPUState *other_cs; + int vpe_idx; int tc_idx = *tc; if (!(env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP))) { @@ -592,10 +586,15 @@ static CPUMIPSState *mips_cpu_map_tc(CPUMIPSState *env, int *tc) return env; } - vpe_idx = tc_idx / nr_threads; - *tc = tc_idx % nr_threads; - other = qemu_get_cpu(vpe_idx); - return other ? other : env; + cs = CPU(mips_env_get_cpu(env)); + vpe_idx = tc_idx / cs->nr_threads; + *tc = tc_idx % cs->nr_threads; + other_cs = qemu_get_cpu(vpe_idx); + if (other_cs == NULL) { + return env; + } + cpu = MIPS_CPU(other_cs); + return &cpu->env; } /* The per VPE CP0_Status register shares some fields with the per TC @@ -1726,7 +1725,7 @@ target_ulong helper_evpe(CPUMIPSState *env) && !mips_vpe_is_wfi(other_cpu)) { /* Enable the VPE. */ other_cpu_env->mvp->CP0_MVPControl |= (1 << CP0MVPCo_EVP); - mips_vpe_wake(other_cpu_env); /* And wake it up. */ + mips_vpe_wake(other_cpu); /* And wake it up. */ } other_cpu_env = other_cpu_env->next_cpu; } while (other_cpu_env); @@ -2101,8 +2100,10 @@ void helper_pmon(CPUMIPSState *env, int function) void helper_wait(CPUMIPSState *env) { - env->halted = 1; - cpu_reset_interrupt(env, CPU_INTERRUPT_WAKE); + CPUState *cs = CPU(mips_env_get_cpu(env)); + + cs->halted = 1; + cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); helper_raise_exception(env, EXCP_HLT); } @@ -2171,11 +2172,17 @@ static unsigned int ieee_rm[] = { float_round_down }; -#define RESTORE_ROUNDING_MODE \ - set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3], &env->active_fpu.fp_status) +static inline void restore_rounding_mode(CPUMIPSState *env) +{ + set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3], + &env->active_fpu.fp_status); +} -#define RESTORE_FLUSH_MODE \ - set_flush_to_zero((env->active_fpu.fcr31 & (1 << 24)) != 0, &env->active_fpu.fp_status); +static inline void restore_flush_mode(CPUMIPSState *env) +{ + set_flush_to_zero((env->active_fpu.fcr31 & (1 << 24)) != 0, + &env->active_fpu.fp_status); +} target_ulong helper_cfc1(CPUMIPSState *env, uint32_t reg) { @@ -2231,9 +2238,9 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t reg) return; } /* set rounding mode */ - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); /* set flush-to-zero mode */ - RESTORE_FLUSH_MODE; + restore_flush_mode(env); set_float_exception_flags(0, &env->active_fpu.fp_status); if ((GET_FP_ENABLE(env->active_fpu.fcr31) | 0x20) & GET_FP_CAUSE(env->active_fpu.fcr31)) do_raise_exception(env, EXCP_FPE, GETPC()); @@ -2465,7 +2472,7 @@ uint64_t helper_float_roundl_d(CPUMIPSState *env, uint64_t fdt0) set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); if (get_float_exception_flags(&env->active_fpu.fp_status) & (float_flag_invalid | float_flag_overflow)) { dt2 = FP_TO_INT64_OVERFLOW; @@ -2480,7 +2487,7 @@ uint64_t helper_float_roundl_s(CPUMIPSState *env, uint32_t fst0) set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); if (get_float_exception_flags(&env->active_fpu.fp_status) & (float_flag_invalid | float_flag_overflow)) { dt2 = FP_TO_INT64_OVERFLOW; @@ -2495,7 +2502,7 @@ uint32_t helper_float_roundw_d(CPUMIPSState *env, uint64_t fdt0) set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); if (get_float_exception_flags(&env->active_fpu.fp_status) & (float_flag_invalid | float_flag_overflow)) { wt2 = FP_TO_INT32_OVERFLOW; @@ -2510,7 +2517,7 @@ uint32_t helper_float_roundw_s(CPUMIPSState *env, uint32_t fst0) set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); if (get_float_exception_flags(&env->active_fpu.fp_status) & (float_flag_invalid | float_flag_overflow)) { wt2 = FP_TO_INT32_OVERFLOW; @@ -2577,7 +2584,7 @@ uint64_t helper_float_ceill_d(CPUMIPSState *env, uint64_t fdt0) set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); if (get_float_exception_flags(&env->active_fpu.fp_status) & (float_flag_invalid | float_flag_overflow)) { dt2 = FP_TO_INT64_OVERFLOW; @@ -2592,7 +2599,7 @@ uint64_t helper_float_ceill_s(CPUMIPSState *env, uint32_t fst0) set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); if (get_float_exception_flags(&env->active_fpu.fp_status) & (float_flag_invalid | float_flag_overflow)) { dt2 = FP_TO_INT64_OVERFLOW; @@ -2607,7 +2614,7 @@ uint32_t helper_float_ceilw_d(CPUMIPSState *env, uint64_t fdt0) set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); if (get_float_exception_flags(&env->active_fpu.fp_status) & (float_flag_invalid | float_flag_overflow)) { wt2 = FP_TO_INT32_OVERFLOW; @@ -2622,7 +2629,7 @@ uint32_t helper_float_ceilw_s(CPUMIPSState *env, uint32_t fst0) set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); if (get_float_exception_flags(&env->active_fpu.fp_status) & (float_flag_invalid | float_flag_overflow)) { wt2 = FP_TO_INT32_OVERFLOW; @@ -2637,7 +2644,7 @@ uint64_t helper_float_floorl_d(CPUMIPSState *env, uint64_t fdt0) set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); if (get_float_exception_flags(&env->active_fpu.fp_status) & (float_flag_invalid | float_flag_overflow)) { dt2 = FP_TO_INT64_OVERFLOW; @@ -2652,7 +2659,7 @@ uint64_t helper_float_floorl_s(CPUMIPSState *env, uint32_t fst0) set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); if (get_float_exception_flags(&env->active_fpu.fp_status) & (float_flag_invalid | float_flag_overflow)) { dt2 = FP_TO_INT64_OVERFLOW; @@ -2667,7 +2674,7 @@ uint32_t helper_float_floorw_d(CPUMIPSState *env, uint64_t fdt0) set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); if (get_float_exception_flags(&env->active_fpu.fp_status) & (float_flag_invalid | float_flag_overflow)) { wt2 = FP_TO_INT32_OVERFLOW; @@ -2682,7 +2689,7 @@ uint32_t helper_float_floorw_s(CPUMIPSState *env, uint32_t fst0) set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); - RESTORE_ROUNDING_MODE; + restore_rounding_mode(env); if (get_float_exception_flags(&env->active_fpu.fp_status) & (float_flag_invalid | float_flag_overflow)) { wt2 = FP_TO_INT32_OVERFLOW; @@ -2862,14 +2869,26 @@ FLOAT_BINOP(mul) FLOAT_BINOP(div) #undef FLOAT_BINOP +#define UNFUSED_FMA(prefix, a, b, c, flags) \ +{ \ + a = prefix##_mul(a, b, &env->active_fpu.fp_status); \ + if ((flags) & float_muladd_negate_c) { \ + a = prefix##_sub(a, c, &env->active_fpu.fp_status); \ + } else { \ + a = prefix##_add(a, c, &env->active_fpu.fp_status); \ + } \ + if ((flags) & float_muladd_negate_result) { \ + a = prefix##_chs(a); \ + } \ +} + /* FMA based operations */ #define FLOAT_FMA(name, type) \ uint64_t helper_float_ ## name ## _d(CPUMIPSState *env, \ uint64_t fdt0, uint64_t fdt1, \ uint64_t fdt2) \ { \ - fdt0 = float64_muladd(fdt0, fdt1, fdt2, type, \ - &env->active_fpu.fp_status); \ + UNFUSED_FMA(float64, fdt0, fdt1, fdt2, type); \ update_fcr31(env, GETPC()); \ return fdt0; \ } \ @@ -2878,8 +2897,7 @@ uint32_t helper_float_ ## name ## _s(CPUMIPSState *env, \ uint32_t fst0, uint32_t fst1, \ uint32_t fst2) \ { \ - fst0 = float32_muladd(fst0, fst1, fst2, type, \ - &env->active_fpu.fp_status); \ + UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ update_fcr31(env, GETPC()); \ return fst0; \ } \ @@ -2895,10 +2913,8 @@ uint64_t helper_float_ ## name ## _ps(CPUMIPSState *env, \ uint32_t fst2 = fdt2 & 0XFFFFFFFF; \ uint32_t fsth2 = fdt2 >> 32; \ \ - fst0 = float32_muladd(fst0, fst1, fst2, type, \ - &env->active_fpu.fp_status); \ - fsth0 = float32_muladd(fsth0, fsth1, fsth2, type, \ - &env->active_fpu.fp_status); \ + UNFUSED_FMA(float32, fst0, fst1, fst2, type); \ + UNFUSED_FMA(float32, fsth0, fsth1, fsth2, type); \ update_fcr31(env, GETPC()); \ return ((uint64_t)fsth0 << 32) | fst0; \ } diff --git a/target-mips/translate.c b/target-mips/translate.c index e81ff38476..b7f8203e57 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -336,7 +336,7 @@ enum { /* DSP Bit/Manipulation Sub-class */ OPC_INSV_DSP = 0x0C | OPC_SPECIAL3, OPC_DINSV_DSP = 0x0D | OPC_SPECIAL3, - /* MIPS DSP Compare-Pick Sub-class */ + /* MIPS DSP Append Sub-class */ OPC_APPEND_DSP = 0x31 | OPC_SPECIAL3, OPC_DAPPEND_DSP = 0x35 | OPC_SPECIAL3, /* MIPS DSP Accumulator and DSPControl Access Sub-class */ @@ -543,7 +543,7 @@ enum { #define MASK_APPEND(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) enum { - /* MIPS DSP Compare-Pick Sub-class */ + /* MIPS DSP Append Sub-class */ OPC_APPEND = (0x00 << 6) | OPC_APPEND_DSP, OPC_PREPEND = (0x01 << 6) | OPC_APPEND_DSP, OPC_BALIGN = (0x10 << 6) | OPC_APPEND_DSP, @@ -667,7 +667,7 @@ enum { #define MASK_DAPPEND(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) enum { - /* DSP Compare-Pick Sub-class */ + /* DSP Append Sub-class */ OPC_DAPPEND = (0x00 << 6) | OPC_DAPPEND_DSP, OPC_PREPENDD = (0x03 << 6) | OPC_DAPPEND_DSP, OPC_PREPENDW = (0x01 << 6) | OPC_DAPPEND_DSP, @@ -1066,6 +1066,7 @@ typedef struct DisasContext { target_ulong pc, saved_pc; uint32_t opcode; int singlestep_enabled; + int insn_flags; /* Routine used to access memory */ int mem_idx; uint32_t hflags, saved_hflags; @@ -1393,23 +1394,32 @@ static inline void check_cp1_registers(DisasContext *ctx, int regs) static inline void check_dsp(DisasContext *ctx) { if (unlikely(!(ctx->hflags & MIPS_HFLAG_DSP))) { - generate_exception(ctx, EXCP_DSPDIS); + if (ctx->insn_flags & ASE_DSP) { + generate_exception(ctx, EXCP_DSPDIS); + } else { + generate_exception(ctx, EXCP_RI); + } } } static inline void check_dspr2(DisasContext *ctx) { if (unlikely(!(ctx->hflags & MIPS_HFLAG_DSPR2))) { - generate_exception(ctx, EXCP_DSPDIS); + if (ctx->insn_flags & ASE_DSP) { + generate_exception(ctx, EXCP_DSPDIS); + } else { + generate_exception(ctx, EXCP_RI); + } } } /* This code generates a "reserved instruction" exception if the CPU does not support the instruction set corresponding to flags. */ -static inline void check_insn(CPUMIPSState *env, DisasContext *ctx, int flags) +static inline void check_insn(DisasContext *ctx, int flags) { - if (unlikely(!(env->insn_flags & flags))) + if (unlikely(!(ctx->insn_flags & flags))) { generate_exception(ctx, EXCP_RI); + } } /* This code generates a "reserved instruction" exception if 64-bit @@ -1576,13 +1586,13 @@ static target_ulong pc_relative_pc (DisasContext *ctx) } /* Load */ -static void gen_ld (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, - int rt, int base, int16_t offset) +static void gen_ld(DisasContext *ctx, uint32_t opc, + int rt, int base, int16_t offset) { const char *opn = "ld"; TCGv t0, t1, t2; - if (rt == 0 && env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)) { + if (rt == 0 && ctx->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)) { /* Loongson CPU uses a load to zero register for prefetch. We emulate it as a NOP. On other CPU we must perform the actual memory access. */ @@ -1735,6 +1745,7 @@ static void gen_ld (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, tcg_temp_free(t2); tcg_gen_or_tl(t0, t0, t1); tcg_temp_free(t1); + tcg_gen_ext32s_tl(t0, t0); gen_store_gpr(t0, rt); opn = "lwr"; break; @@ -1921,8 +1932,8 @@ static void gen_cop1_ldst(CPUMIPSState *env, DisasContext *ctx, } /* Arithmetic with immediate operand */ -static void gen_arith_imm (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, - int rt, int rs, int16_t imm) +static void gen_arith_imm(DisasContext *ctx, uint32_t opc, + int rt, int rs, int16_t imm) { target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */ const char *opn = "imm arith"; @@ -2009,7 +2020,7 @@ static void gen_arith_imm (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Logic with immediate operand */ -static void gen_logic_imm(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_logic_imm(DisasContext *ctx, uint32_t opc, int rt, int rs, int16_t imm) { target_ulong uimm; @@ -2057,7 +2068,7 @@ static void gen_logic_imm(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Set on less than with immediate operand */ -static void gen_slt_imm(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_slt_imm(DisasContext *ctx, uint32_t opc, int rt, int rs, int16_t imm) { target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */ @@ -2087,7 +2098,7 @@ static void gen_slt_imm(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Shifts with immediate operand */ -static void gen_shift_imm(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_shift_imm(DisasContext *ctx, uint32_t opc, int rt, int rs, int16_t imm) { target_ulong uimm = ((uint16_t)imm) & 0x1f; @@ -2179,8 +2190,8 @@ static void gen_shift_imm(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Arithmetic */ -static void gen_arith (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, - int rd, int rs, int rt) +static void gen_arith(DisasContext *ctx, uint32_t opc, + int rd, int rs, int rt) { const char *opn = "arith"; @@ -2359,7 +2370,7 @@ static void gen_arith (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Conditional move */ -static void gen_cond_move(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_cond_move(DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { const char *opn = "cond move"; @@ -2395,7 +2406,7 @@ static void gen_cond_move(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Logic */ -static void gen_logic(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_logic(DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { const char *opn = "logic"; @@ -2457,7 +2468,7 @@ static void gen_logic(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Set on lower than */ -static void gen_slt(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_slt(DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { const char *opn = "slt"; @@ -2490,8 +2501,8 @@ static void gen_slt(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Shifts */ -static void gen_shift (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, - int rd, int rs, int rt) +static void gen_shift(DisasContext *ctx, uint32_t opc, + int rd, int rs, int rt) { const char *opn = "shifts"; TCGv t0, t1; @@ -2571,10 +2582,9 @@ static void gen_shift (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, } /* Arithmetic on HI/LO registers */ -static void gen_HILO (DisasContext *ctx, uint32_t opc, int reg) +static void gen_HILO(DisasContext *ctx, uint32_t opc, int acc, int reg) { const char *opn = "hilo"; - unsigned int acc; if (reg == 0 && (opc == OPC_MFHI || opc == OPC_MFLO)) { /* Treat as NOP. */ @@ -2582,12 +2592,6 @@ static void gen_HILO (DisasContext *ctx, uint32_t opc, int reg) return; } - if (opc == OPC_MFHI || opc == OPC_MFLO) { - acc = ((ctx->opcode) >> 21) & 0x03; - } else { - acc = ((ctx->opcode) >> 11) & 0x03; - } - if (acc != 0) { check_dsp(ctx); } @@ -2650,12 +2654,11 @@ static void gen_HILO (DisasContext *ctx, uint32_t opc, int reg) MIPS_DEBUG("%s %s", opn, regnames[reg]); } -static void gen_muldiv (DisasContext *ctx, uint32_t opc, - int rs, int rt) +static void gen_muldiv(DisasContext *ctx, uint32_t opc, + int acc, int rs, int rt) { const char *opn = "mul/div"; TCGv t0, t1; - unsigned int acc; t0 = tcg_temp_new(); t1 = tcg_temp_new(); @@ -2663,6 +2666,10 @@ static void gen_muldiv (DisasContext *ctx, uint32_t opc, gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); + if (acc != 0) { + check_dsp(ctx); + } + switch (opc) { case OPC_DIV: { @@ -2677,10 +2684,10 @@ static void gen_muldiv (DisasContext *ctx, uint32_t opc, tcg_gen_or_tl(t2, t2, t3); tcg_gen_movi_tl(t3, 0); tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); - tcg_gen_div_tl(cpu_LO[0], t0, t1); - tcg_gen_rem_tl(cpu_HI[0], t0, t1); - tcg_gen_ext32s_tl(cpu_LO[0], cpu_LO[0]); - tcg_gen_ext32s_tl(cpu_HI[0], cpu_HI[0]); + tcg_gen_div_tl(cpu_LO[acc], t0, t1); + tcg_gen_rem_tl(cpu_HI[acc], t0, t1); + tcg_gen_ext32s_tl(cpu_LO[acc], cpu_LO[acc]); + tcg_gen_ext32s_tl(cpu_HI[acc], cpu_HI[acc]); tcg_temp_free(t3); tcg_temp_free(t2); } @@ -2693,10 +2700,10 @@ static void gen_muldiv (DisasContext *ctx, uint32_t opc, tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); - tcg_gen_divu_tl(cpu_LO[0], t0, t1); - tcg_gen_remu_tl(cpu_HI[0], t0, t1); - tcg_gen_ext32s_tl(cpu_LO[0], cpu_LO[0]); - tcg_gen_ext32s_tl(cpu_HI[0], cpu_HI[0]); + tcg_gen_divu_tl(cpu_LO[acc], t0, t1); + tcg_gen_remu_tl(cpu_HI[acc], t0, t1); + tcg_gen_ext32s_tl(cpu_LO[acc], cpu_LO[acc]); + tcg_gen_ext32s_tl(cpu_HI[acc], cpu_HI[acc]); tcg_temp_free(t3); tcg_temp_free(t2); } @@ -2704,47 +2711,29 @@ static void gen_muldiv (DisasContext *ctx, uint32_t opc, break; case OPC_MULT: { - TCGv_i64 t2 = tcg_temp_new_i64(); - TCGv_i64 t3 = tcg_temp_new_i64(); - acc = ((ctx->opcode) >> 11) & 0x03; - if (acc != 0) { - check_dsp(ctx); - } - - tcg_gen_ext_tl_i64(t2, t0); - tcg_gen_ext_tl_i64(t3, t1); - tcg_gen_mul_i64(t2, t2, t3); - tcg_temp_free_i64(t3); - tcg_gen_trunc_i64_tl(t0, t2); - tcg_gen_shri_i64(t2, t2, 32); - tcg_gen_trunc_i64_tl(t1, t2); - tcg_temp_free_i64(t2); - tcg_gen_ext32s_tl(cpu_LO[acc], t0); - tcg_gen_ext32s_tl(cpu_HI[acc], t1); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t2, t0); + tcg_gen_trunc_tl_i32(t3, t1); + tcg_gen_muls2_i32(t2, t3, t2, t3); + tcg_gen_ext_i32_tl(cpu_LO[acc], t2); + tcg_gen_ext_i32_tl(cpu_HI[acc], t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); } opn = "mult"; break; case OPC_MULTU: { - TCGv_i64 t2 = tcg_temp_new_i64(); - TCGv_i64 t3 = tcg_temp_new_i64(); - acc = ((ctx->opcode) >> 11) & 0x03; - if (acc != 0) { - check_dsp(ctx); - } - - tcg_gen_ext32u_tl(t0, t0); - tcg_gen_ext32u_tl(t1, t1); - tcg_gen_extu_tl_i64(t2, t0); - tcg_gen_extu_tl_i64(t3, t1); - tcg_gen_mul_i64(t2, t2, t3); - tcg_temp_free_i64(t3); - tcg_gen_trunc_i64_tl(t0, t2); - tcg_gen_shri_i64(t2, t2, 32); - tcg_gen_trunc_i64_tl(t1, t2); - tcg_temp_free_i64(t2); - tcg_gen_ext32s_tl(cpu_LO[acc], t0); - tcg_gen_ext32s_tl(cpu_HI[acc], t1); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t2, t0); + tcg_gen_trunc_tl_i32(t3, t1); + tcg_gen_mulu2_i32(t2, t3, t2, t3); + tcg_gen_ext_i32_tl(cpu_LO[acc], t2); + tcg_gen_ext_i32_tl(cpu_HI[acc], t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); } opn = "multu"; break; @@ -2760,8 +2749,8 @@ static void gen_muldiv (DisasContext *ctx, uint32_t opc, tcg_gen_or_tl(t2, t2, t3); tcg_gen_movi_tl(t3, 0); tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); - tcg_gen_div_tl(cpu_LO[0], t0, t1); - tcg_gen_rem_tl(cpu_HI[0], t0, t1); + tcg_gen_div_tl(cpu_LO[acc], t0, t1); + tcg_gen_rem_tl(cpu_HI[acc], t0, t1); tcg_temp_free(t3); tcg_temp_free(t2); } @@ -2772,19 +2761,19 @@ static void gen_muldiv (DisasContext *ctx, uint32_t opc, TCGv t2 = tcg_const_tl(0); TCGv t3 = tcg_const_tl(1); tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); - tcg_gen_divu_i64(cpu_LO[0], t0, t1); - tcg_gen_remu_i64(cpu_HI[0], t0, t1); + tcg_gen_divu_i64(cpu_LO[acc], t0, t1); + tcg_gen_remu_i64(cpu_HI[acc], t0, t1); tcg_temp_free(t3); tcg_temp_free(t2); } opn = "ddivu"; break; case OPC_DMULT: - gen_helper_dmult(cpu_env, t0, t1); + tcg_gen_muls2_i64(cpu_LO[acc], cpu_HI[acc], t0, t1); opn = "dmult"; break; case OPC_DMULTU: - gen_helper_dmultu(cpu_env, t0, t1); + tcg_gen_mulu2_i64(cpu_LO[acc], cpu_HI[acc], t0, t1); opn = "dmultu"; break; #endif @@ -2792,10 +2781,6 @@ static void gen_muldiv (DisasContext *ctx, uint32_t opc, { TCGv_i64 t2 = tcg_temp_new_i64(); TCGv_i64 t3 = tcg_temp_new_i64(); - acc = ((ctx->opcode) >> 11) & 0x03; - if (acc != 0) { - check_dsp(ctx); - } tcg_gen_ext_tl_i64(t2, t0); tcg_gen_ext_tl_i64(t3, t1); @@ -2816,10 +2801,6 @@ static void gen_muldiv (DisasContext *ctx, uint32_t opc, { TCGv_i64 t2 = tcg_temp_new_i64(); TCGv_i64 t3 = tcg_temp_new_i64(); - acc = ((ctx->opcode) >> 11) & 0x03; - if (acc != 0) { - check_dsp(ctx); - } tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); @@ -2842,10 +2823,6 @@ static void gen_muldiv (DisasContext *ctx, uint32_t opc, { TCGv_i64 t2 = tcg_temp_new_i64(); TCGv_i64 t3 = tcg_temp_new_i64(); - acc = ((ctx->opcode) >> 11) & 0x03; - if (acc != 0) { - check_dsp(ctx); - } tcg_gen_ext_tl_i64(t2, t0); tcg_gen_ext_tl_i64(t3, t1); @@ -2866,10 +2843,6 @@ static void gen_muldiv (DisasContext *ctx, uint32_t opc, { TCGv_i64 t2 = tcg_temp_new_i64(); TCGv_i64 t3 = tcg_temp_new_i64(); - acc = ((ctx->opcode) >> 11) & 0x03; - if (acc != 0) { - check_dsp(ctx); - } tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); @@ -4097,12 +4070,12 @@ static inline void gen_mtc0_store64 (TCGv arg, target_ulong off) tcg_gen_st_tl(arg, cpu_env, off); } -static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, int sel) +static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *rn = "invalid"; if (sel != 0) - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); switch (reg) { case 0: @@ -4112,17 +4085,17 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Index"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_mvpcontrol(arg, cpu_env); rn = "MVPControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_mvpconf0(arg, cpu_env); rn = "MVPConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_mvpconf1(arg, cpu_env); rn = "MVPConf1"; break; @@ -4137,37 +4110,37 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Random"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl)); rn = "VPEControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0)); rn = "VPEConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1)); rn = "VPEConf1"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_YQMask)); rn = "YQMask"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPESchedule)); rn = "VPESchedule"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPEScheFBack)); rn = "VPEScheFBack"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt)); rn = "VPEOpt"; break; @@ -4183,37 +4156,37 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "EntryLo0"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcstatus(arg, cpu_env); rn = "TCStatus"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcbind(arg, cpu_env); rn = "TCBind"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcrestart(arg, cpu_env); rn = "TCRestart"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tchalt(arg, cpu_env); rn = "TCHalt"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tccontext(arg, cpu_env); rn = "TCContext"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcschedule(arg, cpu_env); rn = "TCSchedule"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcschefback(arg, cpu_env); rn = "TCScheFBack"; break; @@ -4254,7 +4227,7 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "PageMask"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageGrain)); rn = "PageGrain"; break; @@ -4269,27 +4242,27 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Wired"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf0)); rn = "SRSConf0"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf1)); rn = "SRSConf1"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf2)); rn = "SRSConf2"; break; case 4: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf3)); rn = "SRSConf3"; break; case 5: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf4)); rn = "SRSConf4"; break; @@ -4300,7 +4273,7 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i case 7: switch (sel) { case 0: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_HWREna)); rn = "HWREna"; break; @@ -4368,17 +4341,17 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Status"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_IntCtl)); rn = "IntCtl"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSCtl)); rn = "SRSCtl"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); rn = "SRSMap"; break; @@ -4414,7 +4387,7 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "PRid"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase)); rn = "EBase"; break; @@ -4488,7 +4461,7 @@ static void gen_mfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i switch (sel) { case 0: #if defined(TARGET_MIPS64) - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_XContext)); tcg_gen_ext32s_tl(arg, arg); rn = "XContext"; @@ -4677,12 +4650,12 @@ die: generate_exception(ctx, EXCP_RI); } -static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, int sel) +static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *rn = "invalid"; if (sel != 0) - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); if (use_icount) gen_io_start(); @@ -4695,17 +4668,17 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Index"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_mvpcontrol(cpu_env, arg); rn = "MVPControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); /* ignored */ rn = "MVPConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); /* ignored */ rn = "MVPConf1"; break; @@ -4720,37 +4693,37 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Random"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpecontrol(cpu_env, arg); rn = "VPEControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpeconf0(cpu_env, arg); rn = "VPEConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpeconf1(cpu_env, arg); rn = "VPEConf1"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_yqmask(cpu_env, arg); rn = "YQMask"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_VPESchedule)); rn = "VPESchedule"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_VPEScheFBack)); rn = "VPEScheFBack"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpeopt(cpu_env, arg); rn = "VPEOpt"; break; @@ -4765,37 +4738,37 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "EntryLo0"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcstatus(cpu_env, arg); rn = "TCStatus"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcbind(cpu_env, arg); rn = "TCBind"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcrestart(cpu_env, arg); rn = "TCRestart"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tchalt(cpu_env, arg); rn = "TCHalt"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tccontext(cpu_env, arg); rn = "TCContext"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcschedule(cpu_env, arg); rn = "TCSchedule"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcschefback(cpu_env, arg); rn = "TCScheFBack"; break; @@ -4834,7 +4807,7 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "PageMask"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_pagegrain(cpu_env, arg); rn = "PageGrain"; break; @@ -4849,27 +4822,27 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Wired"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf0(cpu_env, arg); rn = "SRSConf0"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf1(cpu_env, arg); rn = "SRSConf1"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf2(cpu_env, arg); rn = "SRSConf2"; break; case 4: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf3(cpu_env, arg); rn = "SRSConf3"; break; case 5: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf4(cpu_env, arg); rn = "SRSConf4"; break; @@ -4880,7 +4853,7 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i case 7: switch (sel) { case 0: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); rn = "HWREna"; break; @@ -4935,21 +4908,21 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "Status"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_intctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; rn = "IntCtl"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; rn = "SRSCtl"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; @@ -4987,7 +4960,7 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i rn = "PRid"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_ebase(cpu_env, arg); rn = "EBase"; break; @@ -5066,7 +5039,7 @@ static void gen_mtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, i switch (sel) { case 0: #if defined(TARGET_MIPS64) - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); gen_helper_mtc0_xcontext(cpu_env, arg); rn = "XContext"; break; @@ -5274,12 +5247,12 @@ die: } #if defined(TARGET_MIPS64) -static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, int sel) +static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *rn = "invalid"; if (sel != 0) - check_insn(env, ctx, ISA_MIPS64); + check_insn(ctx, ISA_MIPS64); switch (reg) { case 0: @@ -5289,17 +5262,17 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Index"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_mvpcontrol(arg, cpu_env); rn = "MVPControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_mvpconf0(arg, cpu_env); rn = "MVPConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_mvpconf1(arg, cpu_env); rn = "MVPConf1"; break; @@ -5314,37 +5287,37 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Random"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl)); rn = "VPEControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0)); rn = "VPEConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1)); rn = "VPEConf1"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_YQMask)); rn = "YQMask"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPESchedule)); rn = "VPESchedule"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); rn = "VPEScheFBack"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt)); rn = "VPEOpt"; break; @@ -5359,37 +5332,37 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "EntryLo0"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcstatus(arg, cpu_env); rn = "TCStatus"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mfc0_tcbind(arg, cpu_env); rn = "TCBind"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dmfc0_tcrestart(arg, cpu_env); rn = "TCRestart"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dmfc0_tchalt(arg, cpu_env); rn = "TCHalt"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dmfc0_tccontext(arg, cpu_env); rn = "TCContext"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dmfc0_tcschedule(arg, cpu_env); rn = "TCSchedule"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dmfc0_tcschefback(arg, cpu_env); rn = "TCScheFBack"; break; @@ -5428,7 +5401,7 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "PageMask"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageGrain)); rn = "PageGrain"; break; @@ -5443,27 +5416,27 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Wired"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf0)); rn = "SRSConf0"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf1)); rn = "SRSConf1"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf2)); rn = "SRSConf2"; break; case 4: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf3)); rn = "SRSConf3"; break; case 5: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf4)); rn = "SRSConf4"; break; @@ -5474,7 +5447,7 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, case 7: switch (sel) { case 0: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_HWREna)); rn = "HWREna"; break; @@ -5540,17 +5513,17 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Status"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_IntCtl)); rn = "IntCtl"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSCtl)); rn = "SRSCtl"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); rn = "SRSMap"; break; @@ -5585,7 +5558,7 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "PRid"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase)); rn = "EBase"; break; @@ -5657,7 +5630,7 @@ static void gen_dmfc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, case 20: switch (sel) { case 0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_XContext)); rn = "XContext"; break; @@ -5843,12 +5816,12 @@ die: generate_exception(ctx, EXCP_RI); } -static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, int sel) +static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) { const char *rn = "invalid"; if (sel != 0) - check_insn(env, ctx, ISA_MIPS64); + check_insn(ctx, ISA_MIPS64); if (use_icount) gen_io_start(); @@ -5861,17 +5834,17 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Index"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_mvpcontrol(cpu_env, arg); rn = "MVPControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); /* ignored */ rn = "MVPConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); /* ignored */ rn = "MVPConf1"; break; @@ -5886,37 +5859,37 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Random"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpecontrol(cpu_env, arg); rn = "VPEControl"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpeconf0(cpu_env, arg); rn = "VPEConf0"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpeconf1(cpu_env, arg); rn = "VPEConf1"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_yqmask(cpu_env, arg); rn = "YQMask"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPESchedule)); rn = "VPESchedule"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); rn = "VPEScheFBack"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_vpeopt(cpu_env, arg); rn = "VPEOpt"; break; @@ -5931,37 +5904,37 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "EntryLo0"; break; case 1: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcstatus(cpu_env, arg); rn = "TCStatus"; break; case 2: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcbind(cpu_env, arg); rn = "TCBind"; break; case 3: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcrestart(cpu_env, arg); rn = "TCRestart"; break; case 4: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tchalt(cpu_env, arg); rn = "TCHalt"; break; case 5: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tccontext(cpu_env, arg); rn = "TCContext"; break; case 6: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcschedule(cpu_env, arg); rn = "TCSchedule"; break; case 7: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_mtc0_tcschefback(cpu_env, arg); rn = "TCScheFBack"; break; @@ -6000,7 +5973,7 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "PageMask"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_pagegrain(cpu_env, arg); rn = "PageGrain"; break; @@ -6015,27 +5988,27 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Wired"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf0(cpu_env, arg); rn = "SRSConf0"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf1(cpu_env, arg); rn = "SRSConf1"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf2(cpu_env, arg); rn = "SRSConf2"; break; case 4: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf3(cpu_env, arg); rn = "SRSConf3"; break; case 5: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf4(cpu_env, arg); rn = "SRSConf4"; break; @@ -6046,7 +6019,7 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, case 7: switch (sel) { case 0: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); rn = "HWREna"; break; @@ -6105,21 +6078,21 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "Status"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_intctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; rn = "IntCtl"; break; case 2: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; rn = "SRSCtl"; break; case 3: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); /* Stop translation as we may have switched the execution mode */ ctx->bstate = BS_STOP; @@ -6167,7 +6140,7 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, rn = "PRid"; break; case 1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_ebase(cpu_env, arg); rn = "EBase"; break; @@ -6236,7 +6209,7 @@ static void gen_dmtc0 (CPUMIPSState *env, DisasContext *ctx, TCGv arg, int reg, case 20: switch (sel) { case 0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); gen_helper_mtc0_xcontext(cpu_env, arg); rn = "XContext"; break; @@ -6493,7 +6466,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_helper_mftc0_tcschefback(t0, cpu_env); break; default: - gen_mfc0(env, ctx, t0, rt, sel); + gen_mfc0(ctx, t0, rt, sel); break; } break; @@ -6503,7 +6476,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_helper_mftc0_entryhi(t0, cpu_env); break; default: - gen_mfc0(env, ctx, t0, rt, sel); + gen_mfc0(ctx, t0, rt, sel); break; } case 12: @@ -6512,7 +6485,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_helper_mftc0_status(t0, cpu_env); break; default: - gen_mfc0(env, ctx, t0, rt, sel); + gen_mfc0(ctx, t0, rt, sel); break; } case 13: @@ -6561,12 +6534,12 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, gen_helper_mftc0_debug(t0, cpu_env); break; default: - gen_mfc0(env, ctx, t0, rt, sel); + gen_mfc0(ctx, t0, rt, sel); break; } break; default: - gen_mfc0(env, ctx, t0, rt, sel); + gen_mfc0(ctx, t0, rt, sel); } } else switch (sel) { /* GPR registers. */ @@ -6711,7 +6684,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_helper_mttc0_tcschefback(cpu_env, t0); break; default: - gen_mtc0(env, ctx, t0, rd, sel); + gen_mtc0(ctx, t0, rd, sel); break; } break; @@ -6721,7 +6694,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_helper_mttc0_entryhi(cpu_env, t0); break; default: - gen_mtc0(env, ctx, t0, rd, sel); + gen_mtc0(ctx, t0, rd, sel); break; } case 12: @@ -6730,7 +6703,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_helper_mttc0_status(cpu_env, t0); break; default: - gen_mtc0(env, ctx, t0, rd, sel); + gen_mtc0(ctx, t0, rd, sel); break; } case 13: @@ -6759,12 +6732,12 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, gen_helper_mttc0_debug(cpu_env, t0); break; default: - gen_mtc0(env, ctx, t0, rd, sel); + gen_mtc0(ctx, t0, rd, sel); break; } break; default: - gen_mtc0(env, ctx, t0, rd, sel); + gen_mtc0(ctx, t0, rd, sel); } } else switch (sel) { /* GPR registers. */ @@ -6866,7 +6839,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, /* Treat as NOP. */ return; } - gen_mfc0(env, ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7); + gen_mfc0(ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7); opn = "mfc0"; break; case OPC_MTC0: @@ -6874,35 +6847,35 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, TCGv t0 = tcg_temp_new(); gen_load_gpr(t0, rt); - gen_mtc0(env, ctx, t0, rd, ctx->opcode & 0x7); + gen_mtc0(ctx, t0, rd, ctx->opcode & 0x7); tcg_temp_free(t0); } opn = "mtc0"; break; #if defined(TARGET_MIPS64) case OPC_DMFC0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); if (rt == 0) { /* Treat as NOP. */ return; } - gen_dmfc0(env, ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7); + gen_dmfc0(ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7); opn = "dmfc0"; break; case OPC_DMTC0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); { TCGv t0 = tcg_temp_new(); gen_load_gpr(t0, rt); - gen_dmtc0(env, ctx, t0, rd, ctx->opcode & 0x7); + gen_dmtc0(ctx, t0, rd, ctx->opcode & 0x7); tcg_temp_free(t0); } opn = "dmtc0"; break; #endif case OPC_MFTR: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); if (rd == 0) { /* Treat as NOP. */ return; @@ -6912,7 +6885,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, opn = "mftr"; break; case OPC_MTTR: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_mttr(env, ctx, rd, rt, (ctx->opcode >> 5) & 1, ctx->opcode & 0x7, (ctx->opcode >> 4) & 1); opn = "mttr"; @@ -6943,13 +6916,13 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, break; case OPC_ERET: opn = "eret"; - check_insn(env, ctx, ISA_MIPS2); + check_insn(ctx, ISA_MIPS2); gen_helper_eret(cpu_env); ctx->bstate = BS_EXCP; break; case OPC_DERET: opn = "deret"; - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); if (!(ctx->hflags & MIPS_HFLAG_DM)) { MIPS_INVAL(opn); generate_exception(ctx, EXCP_RI); @@ -6960,7 +6933,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, break; case OPC_WAIT: opn = "wait"; - check_insn(env, ctx, ISA_MIPS3 | ISA_MIPS32); + check_insn(ctx, ISA_MIPS3 | ISA_MIPS32); /* If we get an exception, we want to restart at next instruction */ ctx->pc += 4; save_cpu_state(ctx, 1); @@ -6980,15 +6953,15 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, #endif /* !CONFIG_USER_ONLY */ /* CP1 Branches (before delay slot) */ -static void gen_compute_branch1 (CPUMIPSState *env, DisasContext *ctx, uint32_t op, - int32_t cc, int32_t offset) +static void gen_compute_branch1(DisasContext *ctx, uint32_t op, + int32_t cc, int32_t offset) { target_ulong btarget; const char *opn = "cp1 cond branch"; TCGv_i32 t0 = tcg_temp_new_i32(); if (cc != 0) - check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32); + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); btarget = ctx->pc + 4 + offset; @@ -9032,15 +9005,14 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, fregnames[fs], fregnames[ft]); } -static void -gen_rdhwr (CPUMIPSState *env, DisasContext *ctx, int rt, int rd) +static void gen_rdhwr(DisasContext *ctx, int rt, int rd) { TCGv t0; #if !defined(CONFIG_USER_ONLY) /* The Linux kernel will emulate rdhwr if it's not supported natively. Therefore only check the ISA in system mode. */ - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); #endif t0 = tcg_temp_new(); @@ -9082,8 +9054,7 @@ gen_rdhwr (CPUMIPSState *env, DisasContext *ctx, int rt, int rd) tcg_temp_free(t0); } -static void handle_delay_slot (CPUMIPSState *env, DisasContext *ctx, - int insn_bytes) +static void handle_delay_slot(DisasContext *ctx, int insn_bytes) { if (ctx->hflags & MIPS_HFLAG_BMASK) { int proc_hflags = ctx->hflags & MIPS_HFLAG_BMASK; @@ -9121,7 +9092,7 @@ static void handle_delay_slot (CPUMIPSState *env, DisasContext *ctx, case MIPS_HFLAG_BR: /* unconditional branch to register */ MIPS_DEBUG("branch to register"); - if (env->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) { + if (ctx->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) { TCGv t0 = tcg_temp_new(); TCGv_i32 t1 = tcg_temp_new_i32(); @@ -9438,7 +9409,7 @@ static void gen_mips16_restore (DisasContext *ctx, #define DECR_AND_LOAD(reg) do { \ tcg_gen_subi_tl(t0, t0, 4); \ - tcg_gen_qemu_ld32u(t1, t0, ctx->mem_idx); \ + tcg_gen_qemu_ld32s(t1, t0, ctx->mem_idx); \ gen_store_gpr(t1, reg); \ } while (0) @@ -9548,7 +9519,7 @@ static void gen_addiupc (DisasContext *ctx, int rx, int imm, } #if defined(TARGET_MIPS64) -static void decode_i64_mips16 (CPUMIPSState *env, DisasContext *ctx, +static void decode_i64_mips16 (DisasContext *ctx, int ry, int funct, int16_t offset, int extended) { @@ -9556,7 +9527,7 @@ static void decode_i64_mips16 (CPUMIPSState *env, DisasContext *ctx, case I64_LDSP: check_mips_64(ctx); offset = extended ? offset : offset << 3; - gen_ld(env, ctx, OPC_LD, ry, 29, offset); + gen_ld(ctx, OPC_LD, ry, 29, offset); break; case I64_SDSP: check_mips_64(ctx); @@ -9571,20 +9542,20 @@ static void decode_i64_mips16 (CPUMIPSState *env, DisasContext *ctx, case I64_DADJSP: check_mips_64(ctx); offset = extended ? offset : ((int8_t)ctx->opcode) << 3; - gen_arith_imm(env, ctx, OPC_DADDIU, 29, 29, offset); + gen_arith_imm(ctx, OPC_DADDIU, 29, 29, offset); break; case I64_LDPC: if (extended && (ctx->hflags & MIPS_HFLAG_BMASK)) { generate_exception(ctx, EXCP_RI); } else { offset = extended ? offset : offset << 3; - gen_ld(env, ctx, OPC_LDPC, ry, 0, offset); + gen_ld(ctx, OPC_LDPC, ry, 0, offset); } break; case I64_DADDIU5: check_mips_64(ctx); offset = extended ? offset : ((int8_t)(offset << 3)) >> 3; - gen_arith_imm(env, ctx, OPC_DADDIU, ry, ry, offset); + gen_arith_imm(ctx, OPC_DADDIU, ry, ry, offset); break; case I64_DADDIUPC: check_mips_64(ctx); @@ -9594,7 +9565,7 @@ static void decode_i64_mips16 (CPUMIPSState *env, DisasContext *ctx, case I64_DADDIUSP: check_mips_64(ctx); offset = extended ? offset : offset << 2; - gen_arith_imm(env, ctx, OPC_DADDIU, ry, 29, offset); + gen_arith_imm(ctx, OPC_DADDIU, ry, 29, offset); break; } } @@ -9621,7 +9592,7 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx, counterparts. */ switch (op) { case M16_OPC_ADDIUSP: - gen_arith_imm(env, ctx, OPC_ADDIU, rx, 29, imm); + gen_arith_imm(ctx, OPC_ADDIU, rx, 29, imm); break; case M16_OPC_ADDIUPC: gen_addiupc(ctx, rx, imm, 0, 1); @@ -9641,28 +9612,28 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx, case M16_OPC_SHIFT: switch (ctx->opcode & 0x3) { case 0x0: - gen_shift_imm(env, ctx, OPC_SLL, rx, ry, sa); + gen_shift_imm(ctx, OPC_SLL, rx, ry, sa); break; case 0x1: #if defined(TARGET_MIPS64) check_mips_64(ctx); - gen_shift_imm(env, ctx, OPC_DSLL, rx, ry, sa); + gen_shift_imm(ctx, OPC_DSLL, rx, ry, sa); #else generate_exception(ctx, EXCP_RI); #endif break; case 0x2: - gen_shift_imm(env, ctx, OPC_SRL, rx, ry, sa); + gen_shift_imm(ctx, OPC_SRL, rx, ry, sa); break; case 0x3: - gen_shift_imm(env, ctx, OPC_SRA, rx, ry, sa); + gen_shift_imm(ctx, OPC_SRA, rx, ry, sa); break; } break; #if defined(TARGET_MIPS64) case M16_OPC_LD: check_mips_64(ctx); - gen_ld(env, ctx, OPC_LD, ry, rx, offset); + gen_ld(ctx, OPC_LD, ry, rx, offset); break; #endif case M16_OPC_RRIA: @@ -9673,22 +9644,22 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx, if ((ctx->opcode >> 4) & 0x1) { #if defined(TARGET_MIPS64) check_mips_64(ctx); - gen_arith_imm(env, ctx, OPC_DADDIU, ry, rx, imm); + gen_arith_imm(ctx, OPC_DADDIU, ry, rx, imm); #else generate_exception(ctx, EXCP_RI); #endif } else { - gen_arith_imm(env, ctx, OPC_ADDIU, ry, rx, imm); + gen_arith_imm(ctx, OPC_ADDIU, ry, rx, imm); } break; case M16_OPC_ADDIU8: - gen_arith_imm(env, ctx, OPC_ADDIU, rx, rx, imm); + gen_arith_imm(ctx, OPC_ADDIU, rx, rx, imm); break; case M16_OPC_SLTI: - gen_slt_imm(env, ctx, OPC_SLTI, 24, rx, imm); + gen_slt_imm(ctx, OPC_SLTI, 24, rx, imm); break; case M16_OPC_SLTIU: - gen_slt_imm(env, ctx, OPC_SLTIU, 24, rx, imm); + gen_slt_imm(ctx, OPC_SLTIU, 24, rx, imm); break; case M16_OPC_I8: switch (funct) { @@ -9702,7 +9673,7 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx, gen_st(ctx, OPC_SW, 31, 29, imm); break; case I8_ADJSP: - gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, imm); + gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm); break; case I8_SVRS: { @@ -9742,29 +9713,29 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx, break; #endif case M16_OPC_LB: - gen_ld(env, ctx, OPC_LB, ry, rx, offset); + gen_ld(ctx, OPC_LB, ry, rx, offset); break; case M16_OPC_LH: - gen_ld(env, ctx, OPC_LH, ry, rx, offset); + gen_ld(ctx, OPC_LH, ry, rx, offset); break; case M16_OPC_LWSP: - gen_ld(env, ctx, OPC_LW, rx, 29, offset); + gen_ld(ctx, OPC_LW, rx, 29, offset); break; case M16_OPC_LW: - gen_ld(env, ctx, OPC_LW, ry, rx, offset); + gen_ld(ctx, OPC_LW, ry, rx, offset); break; case M16_OPC_LBU: - gen_ld(env, ctx, OPC_LBU, ry, rx, offset); + gen_ld(ctx, OPC_LBU, ry, rx, offset); break; case M16_OPC_LHU: - gen_ld(env, ctx, OPC_LHU, ry, rx, offset); + gen_ld(ctx, OPC_LHU, ry, rx, offset); break; case M16_OPC_LWPC: - gen_ld(env, ctx, OPC_LWPC, rx, 0, offset); + gen_ld(ctx, OPC_LWPC, rx, 0, offset); break; #if defined(TARGET_MIPS64) case M16_OPC_LWU: - gen_ld(env, ctx, OPC_LWU, ry, rx, offset); + gen_ld(ctx, OPC_LWU, ry, rx, offset); break; #endif case M16_OPC_SB: @@ -9781,7 +9752,7 @@ static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx, break; #if defined(TARGET_MIPS64) case M16_OPC_I64: - decode_i64_mips16(env, ctx, ry, funct, offset, 1); + decode_i64_mips16(ctx, ry, funct, offset, 1); break; #endif default: @@ -9816,7 +9787,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, { int16_t imm = ((uint8_t) ctx->opcode) << 2; - gen_arith_imm(env, ctx, OPC_ADDIU, rx, 29, imm); + gen_arith_imm(ctx, OPC_ADDIU, rx, 29, imm); } break; case M16_OPC_ADDIUPC: @@ -9849,28 +9820,28 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, case M16_OPC_SHIFT: switch (ctx->opcode & 0x3) { case 0x0: - gen_shift_imm(env, ctx, OPC_SLL, rx, ry, sa); + gen_shift_imm(ctx, OPC_SLL, rx, ry, sa); break; case 0x1: #if defined(TARGET_MIPS64) check_mips_64(ctx); - gen_shift_imm(env, ctx, OPC_DSLL, rx, ry, sa); + gen_shift_imm(ctx, OPC_DSLL, rx, ry, sa); #else generate_exception(ctx, EXCP_RI); #endif break; case 0x2: - gen_shift_imm(env, ctx, OPC_SRL, rx, ry, sa); + gen_shift_imm(ctx, OPC_SRL, rx, ry, sa); break; case 0x3: - gen_shift_imm(env, ctx, OPC_SRA, rx, ry, sa); + gen_shift_imm(ctx, OPC_SRA, rx, ry, sa); break; } break; #if defined(TARGET_MIPS64) case M16_OPC_LD: check_mips_64(ctx); - gen_ld(env, ctx, OPC_LD, ry, rx, offset << 3); + gen_ld(ctx, OPC_LD, ry, rx, offset << 3); break; #endif case M16_OPC_RRIA: @@ -9880,12 +9851,12 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, if ((ctx->opcode >> 4) & 1) { #if defined(TARGET_MIPS64) check_mips_64(ctx); - gen_arith_imm(env, ctx, OPC_DADDIU, ry, rx, imm); + gen_arith_imm(ctx, OPC_DADDIU, ry, rx, imm); #else generate_exception(ctx, EXCP_RI); #endif } else { - gen_arith_imm(env, ctx, OPC_ADDIU, ry, rx, imm); + gen_arith_imm(ctx, OPC_ADDIU, ry, rx, imm); } } break; @@ -9893,19 +9864,19 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, { int16_t imm = (int8_t) ctx->opcode; - gen_arith_imm(env, ctx, OPC_ADDIU, rx, rx, imm); + gen_arith_imm(ctx, OPC_ADDIU, rx, rx, imm); } break; case M16_OPC_SLTI: { int16_t imm = (uint8_t) ctx->opcode; - gen_slt_imm(env, ctx, OPC_SLTI, 24, rx, imm); + gen_slt_imm(ctx, OPC_SLTI, 24, rx, imm); } break; case M16_OPC_SLTIU: { int16_t imm = (uint8_t) ctx->opcode; - gen_slt_imm(env, ctx, OPC_SLTIU, 24, rx, imm); + gen_slt_imm(ctx, OPC_SLTIU, 24, rx, imm); } break; case M16_OPC_I8: @@ -9926,7 +9897,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, gen_st(ctx, OPC_SW, 31, 29, (ctx->opcode & 0xff) << 2); break; case I8_ADJSP: - gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, + gen_arith_imm(ctx, OPC_ADDIU, 29, 29, ((int8_t)ctx->opcode) << 3); break; case I8_SVRS: @@ -9957,12 +9928,12 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, reg32 = (((ctx->opcode >> 3) & 0x3) << 3) | ((ctx->opcode >> 5) & 0x7); - gen_arith(env, ctx, OPC_ADDU, reg32, rz, 0); + gen_arith(ctx, OPC_ADDU, reg32, rz, 0); } break; case I8_MOVR32: reg32 = ctx->opcode & 0x1f; - gen_arith(env, ctx, OPC_ADDU, ry, reg32, 0); + gen_arith(ctx, OPC_ADDU, ry, reg32, 0); break; default: generate_exception(ctx, EXCP_RI); @@ -9974,13 +9945,13 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, { int16_t imm = (uint8_t) ctx->opcode; - gen_arith_imm(env, ctx, OPC_ADDIU, rx, 0, imm); + gen_arith_imm(ctx, OPC_ADDIU, rx, 0, imm); } break; case M16_OPC_CMPI: { int16_t imm = (uint8_t) ctx->opcode; - gen_logic_imm(env, ctx, OPC_XORI, 24, rx, imm); + gen_logic_imm(ctx, OPC_XORI, 24, rx, imm); } break; #if defined(TARGET_MIPS64) @@ -9990,30 +9961,30 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, break; #endif case M16_OPC_LB: - gen_ld(env, ctx, OPC_LB, ry, rx, offset); + gen_ld(ctx, OPC_LB, ry, rx, offset); break; case M16_OPC_LH: - gen_ld(env, ctx, OPC_LH, ry, rx, offset << 1); + gen_ld(ctx, OPC_LH, ry, rx, offset << 1); break; case M16_OPC_LWSP: - gen_ld(env, ctx, OPC_LW, rx, 29, ((uint8_t)ctx->opcode) << 2); + gen_ld(ctx, OPC_LW, rx, 29, ((uint8_t)ctx->opcode) << 2); break; case M16_OPC_LW: - gen_ld(env, ctx, OPC_LW, ry, rx, offset << 2); + gen_ld(ctx, OPC_LW, ry, rx, offset << 2); break; case M16_OPC_LBU: - gen_ld(env, ctx, OPC_LBU, ry, rx, offset); + gen_ld(ctx, OPC_LBU, ry, rx, offset); break; case M16_OPC_LHU: - gen_ld(env, ctx, OPC_LHU, ry, rx, offset << 1); + gen_ld(ctx, OPC_LHU, ry, rx, offset << 1); break; case M16_OPC_LWPC: - gen_ld(env, ctx, OPC_LWPC, rx, 0, ((uint8_t)ctx->opcode) << 2); + gen_ld(ctx, OPC_LWPC, rx, 0, ((uint8_t)ctx->opcode) << 2); break; #if defined (TARGET_MIPS64) case M16_OPC_LWU: check_mips_64(ctx); - gen_ld(env, ctx, OPC_LWU, ry, rx, offset << 2); + gen_ld(ctx, OPC_LWU, ry, rx, offset << 2); break; #endif case M16_OPC_SB: @@ -10055,7 +10026,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, goto done; } - gen_arith(env, ctx, mips32_op, rz, rx, ry); + gen_arith(ctx, mips32_op, rz, rx, ry); done: ; } @@ -10084,7 +10055,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, /* XXX: not clear which exception should be raised * when in debug mode... */ - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); if (!(ctx->hflags & MIPS_HFLAG_DM)) { generate_exception(ctx, EXCP_DBp); } else { @@ -10092,49 +10063,49 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, } break; case RR_SLT: - gen_slt(env, ctx, OPC_SLT, 24, rx, ry); + gen_slt(ctx, OPC_SLT, 24, rx, ry); break; case RR_SLTU: - gen_slt(env, ctx, OPC_SLTU, 24, rx, ry); + gen_slt(ctx, OPC_SLTU, 24, rx, ry); break; case RR_BREAK: generate_exception(ctx, EXCP_BREAK); break; case RR_SLLV: - gen_shift(env, ctx, OPC_SLLV, ry, rx, ry); + gen_shift(ctx, OPC_SLLV, ry, rx, ry); break; case RR_SRLV: - gen_shift(env, ctx, OPC_SRLV, ry, rx, ry); + gen_shift(ctx, OPC_SRLV, ry, rx, ry); break; case RR_SRAV: - gen_shift(env, ctx, OPC_SRAV, ry, rx, ry); + gen_shift(ctx, OPC_SRAV, ry, rx, ry); break; #if defined (TARGET_MIPS64) case RR_DSRL: check_mips_64(ctx); - gen_shift_imm(env, ctx, OPC_DSRL, ry, ry, sa); + gen_shift_imm(ctx, OPC_DSRL, ry, ry, sa); break; #endif case RR_CMP: - gen_logic(env, ctx, OPC_XOR, 24, rx, ry); + gen_logic(ctx, OPC_XOR, 24, rx, ry); break; case RR_NEG: - gen_arith(env, ctx, OPC_SUBU, rx, 0, ry); + gen_arith(ctx, OPC_SUBU, rx, 0, ry); break; case RR_AND: - gen_logic(env, ctx, OPC_AND, rx, rx, ry); + gen_logic(ctx, OPC_AND, rx, rx, ry); break; case RR_OR: - gen_logic(env, ctx, OPC_OR, rx, rx, ry); + gen_logic(ctx, OPC_OR, rx, rx, ry); break; case RR_XOR: - gen_logic(env, ctx, OPC_XOR, rx, rx, ry); + gen_logic(ctx, OPC_XOR, rx, rx, ry); break; case RR_NOT: - gen_logic(env, ctx, OPC_NOR, rx, ry, 0); + gen_logic(ctx, OPC_NOR, rx, ry, 0); break; case RR_MFHI: - gen_HILO(ctx, OPC_MFHI, rx); + gen_HILO(ctx, OPC_MFHI, 0, rx); break; case RR_CNVT: switch (cnvt_op) { @@ -10166,54 +10137,54 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, } break; case RR_MFLO: - gen_HILO(ctx, OPC_MFLO, rx); + gen_HILO(ctx, OPC_MFLO, 0, rx); break; #if defined (TARGET_MIPS64) case RR_DSRA: check_mips_64(ctx); - gen_shift_imm(env, ctx, OPC_DSRA, ry, ry, sa); + gen_shift_imm(ctx, OPC_DSRA, ry, ry, sa); break; case RR_DSLLV: check_mips_64(ctx); - gen_shift(env, ctx, OPC_DSLLV, ry, rx, ry); + gen_shift(ctx, OPC_DSLLV, ry, rx, ry); break; case RR_DSRLV: check_mips_64(ctx); - gen_shift(env, ctx, OPC_DSRLV, ry, rx, ry); + gen_shift(ctx, OPC_DSRLV, ry, rx, ry); break; case RR_DSRAV: check_mips_64(ctx); - gen_shift(env, ctx, OPC_DSRAV, ry, rx, ry); + gen_shift(ctx, OPC_DSRAV, ry, rx, ry); break; #endif case RR_MULT: - gen_muldiv(ctx, OPC_MULT, rx, ry); + gen_muldiv(ctx, OPC_MULT, 0, rx, ry); break; case RR_MULTU: - gen_muldiv(ctx, OPC_MULTU, rx, ry); + gen_muldiv(ctx, OPC_MULTU, 0, rx, ry); break; case RR_DIV: - gen_muldiv(ctx, OPC_DIV, rx, ry); + gen_muldiv(ctx, OPC_DIV, 0, rx, ry); break; case RR_DIVU: - gen_muldiv(ctx, OPC_DIVU, rx, ry); + gen_muldiv(ctx, OPC_DIVU, 0, rx, ry); break; #if defined (TARGET_MIPS64) case RR_DMULT: check_mips_64(ctx); - gen_muldiv(ctx, OPC_DMULT, rx, ry); + gen_muldiv(ctx, OPC_DMULT, 0, rx, ry); break; case RR_DMULTU: check_mips_64(ctx); - gen_muldiv(ctx, OPC_DMULTU, rx, ry); + gen_muldiv(ctx, OPC_DMULTU, 0, rx, ry); break; case RR_DDIV: check_mips_64(ctx); - gen_muldiv(ctx, OPC_DDIV, rx, ry); + gen_muldiv(ctx, OPC_DDIV, 0, rx, ry); break; case RR_DDIVU: check_mips_64(ctx); - gen_muldiv(ctx, OPC_DDIVU, rx, ry); + gen_muldiv(ctx, OPC_DDIVU, 0, rx, ry); break; #endif default: @@ -10228,7 +10199,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx, #if defined(TARGET_MIPS64) case M16_OPC_I64: funct = (ctx->opcode >> 8) & 0x7; - decode_i64_mips16(env, ctx, ry, funct, offset, 0); + decode_i64_mips16(ctx, ry, funct, offset, 0); break; #endif default: @@ -10730,23 +10701,23 @@ static int mmreg2 (int r) /* Zero-extended immediate */ #define ZIMM(op, start, width) ((op >> start) & ((~0U) >> (32-width))) -static void gen_addiur1sp (CPUMIPSState *env, DisasContext *ctx) +static void gen_addiur1sp(DisasContext *ctx) { int rd = mmreg(uMIPS_RD(ctx->opcode)); - gen_arith_imm(env, ctx, OPC_ADDIU, rd, 29, ((ctx->opcode >> 1) & 0x3f) << 2); + gen_arith_imm(ctx, OPC_ADDIU, rd, 29, ((ctx->opcode >> 1) & 0x3f) << 2); } -static void gen_addiur2 (CPUMIPSState *env, DisasContext *ctx) +static void gen_addiur2(DisasContext *ctx) { static const int decoded_imm[] = { 1, 4, 8, 12, 16, 20, 24, -1 }; int rd = mmreg(uMIPS_RD(ctx->opcode)); int rs = mmreg(uMIPS_RS(ctx->opcode)); - gen_arith_imm(env, ctx, OPC_ADDIU, rd, rs, decoded_imm[ZIMM(ctx->opcode, 1, 3)]); + gen_arith_imm(ctx, OPC_ADDIU, rd, rs, decoded_imm[ZIMM(ctx->opcode, 1, 3)]); } -static void gen_addiusp (CPUMIPSState *env, DisasContext *ctx) +static void gen_addiusp(DisasContext *ctx) { int encoded = ZIMM(ctx->opcode, 1, 9); int decoded; @@ -10761,18 +10732,18 @@ static void gen_addiusp (CPUMIPSState *env, DisasContext *ctx) decoded = encoded - 768; } - gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, decoded << 2); + gen_arith_imm(ctx, OPC_ADDIU, 29, 29, decoded << 2); } -static void gen_addius5 (CPUMIPSState *env, DisasContext *ctx) +static void gen_addius5(DisasContext *ctx) { int imm = SIMM(ctx->opcode, 1, 4); int rd = (ctx->opcode >> 5) & 0x1f; - gen_arith_imm(env, ctx, OPC_ADDIU, rd, rd, imm); + gen_arith_imm(ctx, OPC_ADDIU, rd, rd, imm); } -static void gen_andi16 (CPUMIPSState *env, DisasContext *ctx) +static void gen_andi16(DisasContext *ctx) { static const int decoded_imm[] = { 128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535 }; @@ -10780,7 +10751,7 @@ static void gen_andi16 (CPUMIPSState *env, DisasContext *ctx) int rs = mmreg(uMIPS_RS(ctx->opcode)); int encoded = ZIMM(ctx->opcode, 0, 4); - gen_logic_imm(env, ctx, OPC_ANDI, rd, rs, decoded_imm[encoded]); + gen_logic_imm(ctx, OPC_ANDI, rd, rs, decoded_imm[encoded]); } static void gen_ldst_multiple (DisasContext *ctx, uint32_t opc, int reglist, @@ -10831,7 +10802,7 @@ static void gen_ldst_multiple (DisasContext *ctx, uint32_t opc, int reglist, } -static void gen_pool16c_insn (CPUMIPSState *env, DisasContext *ctx, int *is_branch) +static void gen_pool16c_insn(DisasContext *ctx, int *is_branch) { int rd = mmreg((ctx->opcode >> 3) & 0x7); int rs = mmreg(ctx->opcode & 0x7); @@ -10842,25 +10813,25 @@ static void gen_pool16c_insn (CPUMIPSState *env, DisasContext *ctx, int *is_bran case NOT16 + 1: case NOT16 + 2: case NOT16 + 3: - gen_logic(env, ctx, OPC_NOR, rd, rs, 0); + gen_logic(ctx, OPC_NOR, rd, rs, 0); break; case XOR16 + 0: case XOR16 + 1: case XOR16 + 2: case XOR16 + 3: - gen_logic(env, ctx, OPC_XOR, rd, rd, rs); + gen_logic(ctx, OPC_XOR, rd, rd, rs); break; case AND16 + 0: case AND16 + 1: case AND16 + 2: case AND16 + 3: - gen_logic(env, ctx, OPC_AND, rd, rd, rs); + gen_logic(ctx, OPC_AND, rd, rd, rs); break; case OR16 + 0: case OR16 + 1: case OR16 + 2: case OR16 + 3: - gen_logic(env, ctx, OPC_OR, rd, rd, rs); + gen_logic(ctx, OPC_OR, rd, rd, rs); break; case LWM16 + 0: case LWM16 + 1: @@ -10922,11 +10893,11 @@ static void gen_pool16c_insn (CPUMIPSState *env, DisasContext *ctx, int *is_bran break; case MFHI16 + 0: case MFHI16 + 1: - gen_HILO(ctx, OPC_MFHI, uMIPS_RS5(ctx->opcode)); + gen_HILO(ctx, OPC_MFHI, 0, uMIPS_RS5(ctx->opcode)); break; case MFLO16 + 0: case MFLO16 + 1: - gen_HILO(ctx, OPC_MFLO, uMIPS_RS5(ctx->opcode)); + gen_HILO(ctx, OPC_MFLO, 0, uMIPS_RS5(ctx->opcode)); break; case BREAK16: generate_exception(ctx, EXCP_BREAK); @@ -10935,7 +10906,7 @@ static void gen_pool16c_insn (CPUMIPSState *env, DisasContext *ctx, int *is_bran /* XXX: not clear which exception should be raised * when in debug mode... */ - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); if (!(ctx->hflags & MIPS_HFLAG_DM)) { generate_exception(ctx, EXCP_DBp); } else { @@ -10948,7 +10919,7 @@ static void gen_pool16c_insn (CPUMIPSState *env, DisasContext *ctx, int *is_bran int imm = ZIMM(ctx->opcode, 0, 5); gen_compute_branch(ctx, OPC_JR, 2, 31, 0, 0); - gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, imm << 2); + gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm << 2); /* Let normal delay slot handling in our caller take us to the branch target. */ } @@ -11085,7 +11056,7 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs, /* Treat as NOP. */ break; } - gen_mfc0(env, ctx, cpu_gpr[rt], rs, (ctx->opcode >> 11) & 0x7); + gen_mfc0(ctx, cpu_gpr[rt], rs, (ctx->opcode >> 11) & 0x7); break; case MTC0: case MTC0 + 32: @@ -11094,7 +11065,7 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs, TCGv t0 = tcg_temp_new(); gen_load_gpr(t0, rt); - gen_mtc0(env, ctx, t0, rs, (ctx->opcode >> 11) & 0x7); + gen_mtc0(ctx, t0, rs, (ctx->opcode >> 11) & 0x7); tcg_temp_free(t0); } break; @@ -11113,41 +11084,45 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs, case CLZ: mips32_op = OPC_CLZ; do_cl: - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); gen_cl(ctx, mips32_op, rt, rs); break; case RDHWR: - gen_rdhwr(env, ctx, rt, rs); + gen_rdhwr(ctx, rt, rs); break; case WSBH: gen_bshfl(ctx, OPC_WSBH, rs, rt); break; case MULT: mips32_op = OPC_MULT; - goto do_muldiv; + goto do_mul; case MULTU: mips32_op = OPC_MULTU; - goto do_muldiv; + goto do_mul; case DIV: mips32_op = OPC_DIV; - goto do_muldiv; + goto do_div; case DIVU: mips32_op = OPC_DIVU; - goto do_muldiv; + goto do_div; + do_div: + check_insn(ctx, ISA_MIPS32); + gen_muldiv(ctx, mips32_op, 0, rs, rt); + break; case MADD: mips32_op = OPC_MADD; - goto do_muldiv; + goto do_mul; case MADDU: mips32_op = OPC_MADDU; - goto do_muldiv; + goto do_mul; case MSUB: mips32_op = OPC_MSUB; - goto do_muldiv; + goto do_mul; case MSUBU: mips32_op = OPC_MSUBU; - do_muldiv: - check_insn(env, ctx, ISA_MIPS32); - gen_muldiv(ctx, mips32_op, rs, rt); + do_mul: + check_insn(ctx, ISA_MIPS32); + gen_muldiv(ctx, mips32_op, (ctx->opcode >> 14) & 3, rs, rt); break; default: goto pool32axf_invalid; @@ -11187,12 +11162,12 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs, switch (minor) { case RDPGPR: check_cp0_enabled(ctx); - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_load_srsgpr(rt, rs); break; case WRPGPR: check_cp0_enabled(ctx); - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_store_srsgpr(rt, rs); break; default: @@ -11272,7 +11247,7 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs, ctx->bstate = BS_STOP; break; case SDBBP: - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); if (!(ctx->hflags & MIPS_HFLAG_DM)) { generate_exception(ctx, EXCP_DBp); } else { @@ -11284,18 +11259,18 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs, } break; case 0x35: - switch (minor) { + switch (minor & 3) { case MFHI32: - gen_HILO(ctx, OPC_MFHI, rs); + gen_HILO(ctx, OPC_MFHI, minor >> 2, rs); break; case MFLO32: - gen_HILO(ctx, OPC_MFLO, rs); + gen_HILO(ctx, OPC_MFLO, minor >> 2, rs); break; case MTHI32: - gen_HILO(ctx, OPC_MTHI, rs); + gen_HILO(ctx, OPC_MTHI, minor >> 2, rs); break; case MTLO32: - gen_HILO(ctx, OPC_MTLO, rs); + gen_HILO(ctx, OPC_MTLO, minor >> 2, rs); break; default: goto pool32axf_invalid; @@ -11329,7 +11304,7 @@ enum { FMT_DWL_L = 2 }; -static void gen_pool32fxf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) +static void gen_pool32fxf(DisasContext *ctx, int rt, int rs) { int extension = (ctx->opcode >> 6) & 0x3ff; uint32_t mips32_op; @@ -11614,7 +11589,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case ROTR: mips32_op = OPC_ROTR; do_shifti: - gen_shift_imm(env, ctx, mips32_op, rt, rs, rd); + gen_shift_imm(ctx, mips32_op, rt, rs, rd); break; default: goto pool32a_invalid; @@ -11639,7 +11614,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case MUL: mips32_op = OPC_MUL; do_arith: - gen_arith(env, ctx, mips32_op, rd, rs, rt); + gen_arith(ctx, mips32_op, rd, rs, rt); break; /* Shifts */ case SLLV: @@ -11654,7 +11629,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case ROTRV: mips32_op = OPC_ROTRV; do_shift: - gen_shift(env, ctx, mips32_op, rd, rs, rt); + gen_shift(ctx, mips32_op, rd, rs, rt); break; /* Logical operations */ case AND: @@ -11669,7 +11644,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case XOR32: mips32_op = OPC_XOR; do_logic: - gen_logic(env, ctx, mips32_op, rd, rs, rt); + gen_logic(ctx, mips32_op, rd, rs, rt); break; /* Set less than */ case SLT: @@ -11678,7 +11653,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case SLTU: mips32_op = OPC_SLTU; do_slt: - gen_slt(env, ctx, mips32_op, rd, rs, rt); + gen_slt(ctx, mips32_op, rd, rs, rt); break; default: goto pool32a_invalid; @@ -11694,7 +11669,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case MOVZ: mips32_op = OPC_MOVZ; do_cmov: - gen_cond_move(env, ctx, mips32_op, rd, rs, rt); + gen_cond_move(ctx, mips32_op, rd, rs, rt); break; case LWXS: gen_ldxs(ctx, rs, rt, rd); @@ -11839,7 +11814,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, } break; case POOL32FXF: - gen_pool32fxf(env, ctx, rt, rs); + gen_pool32fxf(ctx, rt, rs); break; case 0x00: /* PLL foo */ @@ -12107,7 +12082,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, target. */ break; case LUI: - gen_logic_imm(env, ctx, OPC_LUI, rs, -1, imm); + gen_logic_imm(ctx, OPC_LUI, rs, -1, imm); break; case SYNCI: break; @@ -12129,10 +12104,10 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, mips32_op = OPC_BC1TANY4; do_cp1mips3d: check_cop1x(ctx); - check_insn(env, ctx, ASE_MIPS3D); + check_insn(ctx, ASE_MIPS3D); /* Fall through */ do_cp1branch: - gen_compute_branch1(env, ctx, mips32_op, + gen_compute_branch1(ctx, mips32_op, (ctx->opcode >> 18) & 0x7, imm << 1); *is_branch = 1; break; @@ -12185,7 +12160,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, mips32_op = OPC_LL; goto do_ld_lr; do_ld_lr: - gen_ld(env, ctx, mips32_op, rt, rs, SIMM(ctx->opcode, 0, 12)); + gen_ld(ctx, mips32_op, rt, rs, SIMM(ctx->opcode, 0, 12)); break; do_st_lr: gen_st(ctx, mips32_op, rt, rs, SIMM(ctx->opcode, 0, 12)); @@ -12213,7 +12188,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case ADDIU32: mips32_op = OPC_ADDIU; do_addi: - gen_arith_imm(env, ctx, mips32_op, rt, rs, imm); + gen_arith_imm(ctx, mips32_op, rt, rs, imm); break; /* Logical operations */ @@ -12226,7 +12201,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case ANDI32: mips32_op = OPC_ANDI; do_logici: - gen_logic_imm(env, ctx, mips32_op, rt, rs, imm); + gen_logic_imm(ctx, mips32_op, rt, rs, imm); break; /* Set less than immediate */ @@ -12236,7 +12211,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case SLTIU32: mips32_op = OPC_SLTIU; do_slti: - gen_slt_imm(env, ctx, mips32_op, rt, rs, imm); + gen_slt_imm(ctx, mips32_op, rt, rs, imm); break; case JALX32: offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; @@ -12323,7 +12298,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, mips32_op = OPC_SW; goto do_st; do_ld: - gen_ld(env, ctx, mips32_op, rt, rs, imm); + gen_ld(ctx, mips32_op, rt, rs, imm); break; do_st: gen_st(ctx, mips32_op, rt, rs, imm); @@ -12443,7 +12418,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b break; } - gen_arith(env, ctx, opc, rd, rs1, rs2); + gen_arith(ctx, opc, rd, rs1, rs2); } break; case POOL16B: @@ -12463,11 +12438,11 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b break; } - gen_shift_imm(env, ctx, opc, rd, rs, amount); + gen_shift_imm(ctx, opc, rd, rs, amount); } break; case POOL16C: - gen_pool16c_insn(env, ctx, is_branch); + gen_pool16c_insn(ctx, is_branch); break; case LWGP16: { @@ -12475,7 +12450,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b int rb = 28; /* GP */ int16_t offset = SIMM(ctx->opcode, 0, 7) << 2; - gen_ld(env, ctx, OPC_LW, rd, rb, offset); + gen_ld(ctx, OPC_LW, rd, rb, offset); } break; case POOL16F: @@ -12496,8 +12471,8 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b rs = rs_rt_enc[enc_rs]; rt = rs_rt_enc[enc_rt]; - gen_arith_imm(env, ctx, OPC_ADDIU, rd, rs, 0); - gen_arith_imm(env, ctx, OPC_ADDIU, re, rt, 0); + gen_arith_imm(ctx, OPC_ADDIU, rd, rs, 0); + gen_arith_imm(ctx, OPC_ADDIU, re, rt, 0); } break; case LBU16: @@ -12507,7 +12482,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b int16_t offset = ZIMM(ctx->opcode, 0, 4); offset = (offset == 0xf ? -1 : offset); - gen_ld(env, ctx, OPC_LBU, rd, rb, offset); + gen_ld(ctx, OPC_LBU, rd, rb, offset); } break; case LHU16: @@ -12516,7 +12491,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b int rb = mmreg(uMIPS_RS(ctx->opcode)); int16_t offset = ZIMM(ctx->opcode, 0, 4) << 1; - gen_ld(env, ctx, OPC_LHU, rd, rb, offset); + gen_ld(ctx, OPC_LHU, rd, rb, offset); } break; case LWSP16: @@ -12525,7 +12500,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b int rb = 29; /* SP */ int16_t offset = ZIMM(ctx->opcode, 0, 5) << 2; - gen_ld(env, ctx, OPC_LW, rd, rb, offset); + gen_ld(ctx, OPC_LW, rd, rb, offset); } break; case LW16: @@ -12534,7 +12509,7 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b int rb = mmreg(uMIPS_RS(ctx->opcode)); int16_t offset = ZIMM(ctx->opcode, 0, 4) << 2; - gen_ld(env, ctx, OPC_LW, rd, rb, offset); + gen_ld(ctx, OPC_LW, rd, rb, offset); } break; case SB16: @@ -12578,29 +12553,29 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b int rd = uMIPS_RD5(ctx->opcode); int rs = uMIPS_RS5(ctx->opcode); - gen_arith_imm(env, ctx, OPC_ADDIU, rd, rs, 0); + gen_arith_imm(ctx, OPC_ADDIU, rd, rs, 0); } break; case ANDI16: - gen_andi16(env, ctx); + gen_andi16(ctx); break; case POOL16D: switch (ctx->opcode & 0x1) { case ADDIUS5: - gen_addius5(env, ctx); + gen_addius5(ctx); break; case ADDIUSP: - gen_addiusp(env, ctx); + gen_addiusp(ctx); break; } break; case POOL16E: switch (ctx->opcode & 0x1) { case ADDIUR2: - gen_addiur2(env, ctx); + gen_addiur2(ctx); break; case ADDIUR1SP: - gen_addiur1sp(env, ctx); + gen_addiur1sp(ctx); break; } break; @@ -12651,17 +12626,12 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx, int *is_b #endif /* MIPSDSP functions. */ -static void gen_mipsdsp_ld(CPUMIPSState *env, DisasContext *ctx, uint32_t opc, +static void gen_mipsdsp_ld(DisasContext *ctx, uint32_t opc, int rd, int base, int offset) { const char *opn = "ldx"; TCGv t0; - if (rd == 0) { - MIPS_DEBUG("NOP"); - return; - } - check_dsp(ctx); t0 = tcg_temp_new(); @@ -13717,8 +13687,7 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, } -static void gen_mipsdsp_bitinsn(CPUMIPSState *env, DisasContext *ctx, - uint32_t op1, uint32_t op2, +static void gen_mipsdsp_bitinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, int ret, int val) { const char *opn = "mipsdsp Bit/ Manipulation"; @@ -13769,9 +13738,10 @@ static void gen_mipsdsp_bitinsn(CPUMIPSState *env, DisasContext *ctx, check_dsp(ctx); { imm = (ctx->opcode >> 16) & 0x03FF; + imm = (int16_t)(imm << 6) >> 6; tcg_gen_movi_tl(cpu_gpr[ret], \ (target_long)((int32_t)imm << 16 | \ - (uint32_t)(uint16_t)imm)); + (uint16_t)imm)); } break; case OPC_REPLV_PH: @@ -13865,7 +13835,6 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, int ret, int v1, int v2, int check_ret) { const char *opn = "mipsdsp add compare pick"; - TCGv_i32 t0; TCGv t1; TCGv v1_t; TCGv v2_t; @@ -13876,7 +13845,6 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, return; } - t0 = tcg_temp_new_i32(); t1 = tcg_temp_new(); v1_t = tcg_temp_new(); v2_t = tcg_temp_new(); @@ -13885,26 +13853,6 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, gen_load_gpr(v2_t, v2); switch (op1) { - case OPC_APPEND_DSP: - switch (op2) { - case OPC_APPEND: - tcg_gen_movi_i32(t0, v2); - gen_helper_append(cpu_gpr[ret], cpu_gpr[ret], v1_t, t0); - break; - case OPC_PREPEND: - tcg_gen_movi_i32(t0, v2); - gen_helper_prepend(cpu_gpr[ret], v1_t, cpu_gpr[ret], t0); - break; - case OPC_BALIGN: - tcg_gen_movi_i32(t0, v2); - gen_helper_balign(cpu_gpr[ret], v1_t, cpu_gpr[ret], t0); - break; - default: /* Invid */ - MIPS_INVAL("MASK APPEND"); - generate_exception(ctx, EXCP_RI); - break; - } - break; case OPC_CMPU_EQ_QB_DSP: switch (op2) { case OPC_CMPU_EQ_QB: @@ -14062,23 +14010,95 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, break; } break; +#endif + } + + tcg_temp_free(t1); + tcg_temp_free(v1_t); + tcg_temp_free(v2_t); + + (void)opn; /* avoid a compiler warning */ + MIPS_DEBUG("%s", opn); +} + +static void gen_mipsdsp_append(CPUMIPSState *env, DisasContext *ctx, + uint32_t op1, int rt, int rs, int sa) +{ + const char *opn = "mipsdsp append/dappend"; + TCGv t0; + + check_dspr2(ctx); + + if (rt == 0) { + /* Treat as NOP. */ + MIPS_DEBUG("NOP"); + return; + } + + t0 = tcg_temp_new(); + gen_load_gpr(t0, rs); + + switch (op1) { + case OPC_APPEND_DSP: + switch (MASK_APPEND(ctx->opcode)) { + case OPC_APPEND: + if (sa != 0) { + tcg_gen_deposit_tl(cpu_gpr[rt], t0, cpu_gpr[rt], sa, 32 - sa); + } + tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); + break; + case OPC_PREPEND: + if (sa != 0) { + tcg_gen_ext32u_tl(cpu_gpr[rt], cpu_gpr[rt]); + tcg_gen_shri_tl(cpu_gpr[rt], cpu_gpr[rt], sa); + tcg_gen_shli_tl(t0, t0, 32 - sa); + tcg_gen_or_tl(cpu_gpr[rt], cpu_gpr[rt], t0); + } + tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); + break; + case OPC_BALIGN: + sa &= 3; + if (sa != 0 && sa != 2) { + tcg_gen_shli_tl(cpu_gpr[rt], cpu_gpr[rt], 8 * sa); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_shri_tl(t0, t0, 8 * (4 - sa)); + tcg_gen_or_tl(cpu_gpr[rt], cpu_gpr[rt], t0); + } + tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); + break; + default: /* Invalid */ + MIPS_INVAL("MASK APPEND"); + generate_exception(ctx, EXCP_RI); + break; + } + break; +#ifdef TARGET_MIPS64 case OPC_DAPPEND_DSP: - switch (op2) { + switch (MASK_DAPPEND(ctx->opcode)) { case OPC_DAPPEND: - tcg_gen_movi_i32(t0, v2); - gen_helper_dappend(cpu_gpr[ret], v1_t, cpu_gpr[ret], t0); + if (sa != 0) { + tcg_gen_deposit_tl(cpu_gpr[rt], t0, cpu_gpr[rt], sa, 64 - sa); + } break; case OPC_PREPENDD: - tcg_gen_movi_i32(t0, v2); - gen_helper_prependd(cpu_gpr[ret], v1_t, cpu_gpr[ret], t0); + tcg_gen_shri_tl(cpu_gpr[rt], cpu_gpr[rt], 0x20 | sa); + tcg_gen_shli_tl(t0, t0, 64 - (0x20 | sa)); + tcg_gen_or_tl(cpu_gpr[rt], t0, t0); break; case OPC_PREPENDW: - tcg_gen_movi_i32(t0, v2); - gen_helper_prependw(cpu_gpr[ret], v1_t, cpu_gpr[ret], t0); + if (sa != 0) { + tcg_gen_shri_tl(cpu_gpr[rt], cpu_gpr[rt], sa); + tcg_gen_shli_tl(t0, t0, 64 - sa); + tcg_gen_or_tl(cpu_gpr[rt], cpu_gpr[rt], t0); + } break; case OPC_DBALIGN: - tcg_gen_movi_i32(t0, v2); - gen_helper_dbalign(cpu_gpr[ret], v1_t, cpu_gpr[ret], t0); + sa &= 7; + if (sa != 0 && sa != 2 && sa != 4) { + tcg_gen_shli_tl(cpu_gpr[rt], cpu_gpr[rt], 8 * sa); + tcg_gen_shri_tl(t0, t0, 8 * (8 - sa)); + tcg_gen_or_tl(cpu_gpr[rt], cpu_gpr[rt], t0); + } break; default: /* Invalid */ MIPS_INVAL("MASK DAPPEND"); @@ -14088,12 +14108,7 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, break; #endif } - - tcg_temp_free_i32(t0); - tcg_temp_free(t1); - tcg_temp_free(v1_t); - tcg_temp_free(v2_t); - + tcg_temp_free(t0); (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s", opn); } @@ -14371,18 +14386,18 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) switch (op1) { case OPC_SLL: /* Shift with immediate */ case OPC_SRA: - gen_shift_imm(env, ctx, op1, rd, rt, sa); + gen_shift_imm(ctx, op1, rd, rt, sa); break; case OPC_SRL: switch ((ctx->opcode >> 21) & 0x1f) { case 1: /* rotr is decoded as srl on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { + if (ctx->insn_flags & ISA_MIPS32R2) { op1 = OPC_ROTR; } /* Fallthrough */ case 0: - gen_shift_imm(env, ctx, op1, rd, rt, sa); + gen_shift_imm(ctx, op1, rd, rt, sa); break; default: generate_exception(ctx, EXCP_RI); @@ -14391,27 +14406,27 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) break; case OPC_MOVN: /* Conditional move */ case OPC_MOVZ: - check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32 | + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32 | INSN_LOONGSON2E | INSN_LOONGSON2F); - gen_cond_move(env, ctx, op1, rd, rs, rt); + gen_cond_move(ctx, op1, rd, rs, rt); break; case OPC_ADD ... OPC_SUBU: - gen_arith(env, ctx, op1, rd, rs, rt); + gen_arith(ctx, op1, rd, rs, rt); break; case OPC_SLLV: /* Shifts */ case OPC_SRAV: - gen_shift(env, ctx, op1, rd, rs, rt); + gen_shift(ctx, op1, rd, rs, rt); break; case OPC_SRLV: switch ((ctx->opcode >> 6) & 0x1f) { case 1: /* rotrv is decoded as srlv on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { + if (ctx->insn_flags & ISA_MIPS32R2) { op1 = OPC_ROTRV; } /* Fallthrough */ case 0: - gen_shift(env, ctx, op1, rd, rs, rt); + gen_shift(ctx, op1, rd, rs, rt); break; default: generate_exception(ctx, EXCP_RI); @@ -14420,21 +14435,27 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) break; case OPC_SLT: /* Set on less than */ case OPC_SLTU: - gen_slt(env, ctx, op1, rd, rs, rt); + gen_slt(ctx, op1, rd, rs, rt); break; case OPC_AND: /* Logic*/ case OPC_OR: case OPC_NOR: case OPC_XOR: - gen_logic(env, ctx, op1, rd, rs, rt); + gen_logic(ctx, op1, rd, rs, rt); break; - case OPC_MULT ... OPC_DIVU: + case OPC_MULT: + case OPC_MULTU: if (sa) { - check_insn(env, ctx, INSN_VR54XX); + check_insn(ctx, INSN_VR54XX); op1 = MASK_MUL_VR54XX(ctx->opcode); gen_mul_vr54xx(ctx, op1, rd, rs, rt); - } else - gen_muldiv(ctx, op1, rs, rt); + } else { + gen_muldiv(ctx, op1, rd & 3, rs, rt); + } + break; + case OPC_DIV: + case OPC_DIVU: + gen_muldiv(ctx, op1, 0, rs, rt); break; case OPC_JR ... OPC_JALR: gen_compute_branch(ctx, op1, 4, rs, rd, sa); @@ -14446,11 +14467,11 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) break; case OPC_MFHI: /* Move from HI/LO */ case OPC_MFLO: - gen_HILO(ctx, op1, rd); + gen_HILO(ctx, op1, rs & 3, rd); break; case OPC_MTHI: case OPC_MTLO: /* Move to HI/LO */ - gen_HILO(ctx, op1, rs); + gen_HILO(ctx, op1, rd & 3, rs); break; case OPC_PMON: /* Pmon entry point, also R4010 selsl */ #ifdef MIPS_STRICT_STANDARD @@ -14482,7 +14503,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) break; case OPC_MOVCI: - check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32); + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); if (env->CP0_Config1 & (1 << CP0C1_FP)) { check_cp1_enabled(ctx); gen_movci(ctx, rd, rs, (ctx->opcode >> 18) & 0x7, @@ -14498,22 +14519,22 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_DSRA: case OPC_DSLL32: case OPC_DSRA32: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_shift_imm(env, ctx, op1, rd, rt, sa); + gen_shift_imm(ctx, op1, rd, rt, sa); break; case OPC_DSRL: switch ((ctx->opcode >> 21) & 0x1f) { case 1: /* drotr is decoded as dsrl on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { + if (ctx->insn_flags & ISA_MIPS32R2) { op1 = OPC_DROTR; } /* Fallthrough */ case 0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_shift_imm(env, ctx, op1, rd, rt, sa); + gen_shift_imm(ctx, op1, rd, rt, sa); break; default: generate_exception(ctx, EXCP_RI); @@ -14524,14 +14545,14 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) switch ((ctx->opcode >> 21) & 0x1f) { case 1: /* drotr32 is decoded as dsrl32 on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { + if (ctx->insn_flags & ISA_MIPS32R2) { op1 = OPC_DROTR32; } /* Fallthrough */ case 0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_shift_imm(env, ctx, op1, rd, rt, sa); + gen_shift_imm(ctx, op1, rd, rt, sa); break; default: generate_exception(ctx, EXCP_RI); @@ -14539,28 +14560,28 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) } break; case OPC_DADD ... OPC_DSUBU: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_arith(env, ctx, op1, rd, rs, rt); + gen_arith(ctx, op1, rd, rs, rt); break; case OPC_DSLLV: case OPC_DSRAV: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_shift(env, ctx, op1, rd, rs, rt); + gen_shift(ctx, op1, rd, rs, rt); break; case OPC_DSRLV: switch ((ctx->opcode >> 6) & 0x1f) { case 1: /* drotrv is decoded as dsrlv on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { + if (ctx->insn_flags & ISA_MIPS32R2) { op1 = OPC_DROTRV; } /* Fallthrough */ case 0: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_shift(env, ctx, op1, rd, rs, rt); + gen_shift(ctx, op1, rd, rs, rt); break; default: generate_exception(ctx, EXCP_RI); @@ -14568,9 +14589,9 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) } break; case OPC_DMULT ... OPC_DDIVU: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_muldiv(ctx, op1, rs, rt); + gen_muldiv(ctx, op1, 0, rs, rt); break; #endif default: /* Invalid */ @@ -14584,22 +14605,22 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) switch (op1) { case OPC_MADD ... OPC_MADDU: /* Multiply and add/sub */ case OPC_MSUB ... OPC_MSUBU: - check_insn(env, ctx, ISA_MIPS32); - gen_muldiv(ctx, op1, rs, rt); + check_insn(ctx, ISA_MIPS32); + gen_muldiv(ctx, op1, rd & 3, rs, rt); break; case OPC_MUL: - gen_arith(env, ctx, op1, rd, rs, rt); + gen_arith(ctx, op1, rd, rs, rt); break; case OPC_CLO: case OPC_CLZ: - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); gen_cl(ctx, op1, rd, rs); break; case OPC_SDBBP: /* XXX: not clear which exception should be raised * when in debug mode... */ - check_insn(env, ctx, ISA_MIPS32); + check_insn(ctx, ISA_MIPS32); if (!(ctx->hflags & MIPS_HFLAG_DM)) { generate_exception(ctx, EXCP_DBp); } else { @@ -14613,13 +14634,13 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_MULTU_G_2F: case OPC_MOD_G_2F: case OPC_MODU_G_2F: - check_insn(env, ctx, INSN_LOONGSON2F); + check_insn(ctx, INSN_LOONGSON2F); gen_loongson_integer(ctx, op1, rd, rs, rt); break; #if defined(TARGET_MIPS64) case OPC_DCLO: case OPC_DCLZ: - check_insn(env, ctx, ISA_MIPS64); + check_insn(ctx, ISA_MIPS64); check_mips_64(ctx); gen_cl(ctx, op1, rd, rs); break; @@ -14629,7 +14650,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_DDIVU_G_2F: case OPC_DMOD_G_2F: case OPC_DMODU_G_2F: - check_insn(env, ctx, INSN_LOONGSON2F); + check_insn(ctx, INSN_LOONGSON2F); gen_loongson_integer(ctx, op1, rd, rs, rt); break; #endif @@ -14644,19 +14665,19 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) switch (op1) { case OPC_EXT: case OPC_INS: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_bitops(ctx, op1, rt, rs, sa, rd); break; case OPC_BSHFL: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); op2 = MASK_BSHFL(ctx->opcode); gen_bshfl(ctx, op2, rt, rd); break; case OPC_RDHWR: - gen_rdhwr(env, ctx, rt, rd); + gen_rdhwr(ctx, rt, rd); break; case OPC_FORK: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); { TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); @@ -14669,7 +14690,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) } break; case OPC_YIELD: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); { TCGv t0 = tcg_temp_new(); @@ -14685,7 +14706,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_MULT_G_2E ... OPC_MULTU_G_2E: /* OPC_MULT_G_2E, OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have * the same mask and op1. */ - if ((env->insn_flags & ASE_DSPR2) && (op1 == OPC_MULT_G_2E)) { + if ((ctx->insn_flags & ASE_DSPR2) && (op1 == OPC_MULT_G_2E)) { op2 = MASK_ADDUH_QB(ctx->opcode); switch (op2) { case OPC_ADDUH_QB: @@ -14713,7 +14734,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) generate_exception(ctx, EXCP_RI); break; } - } else if (env->insn_flags & INSN_LOONGSON2E) { + } else if (ctx->insn_flags & INSN_LOONGSON2E) { gen_loongson_integer(ctx, op1, rd, rs, rt); } else { generate_exception(ctx, EXCP_RI); @@ -14728,7 +14749,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_LBUX: case OPC_LHX: case OPC_LWX: - gen_mipsdsp_ld(env, ctx, op2, rd, rs, rt); + gen_mipsdsp_ld(ctx, op2, rd, rs, rt); break; default: /* Invalid */ MIPS_INVAL("MASK LX"); @@ -14759,7 +14780,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_REPLV_QB: case OPC_REPL_PH: case OPC_REPLV_PH: - gen_mipsdsp_bitinsn(env, ctx, op1, op2, rd, rt); + gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt); break; default: MIPS_INVAL("MASK ABSQ_S.PH"); @@ -14912,9 +14933,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) } break; case OPC_APPEND_DSP: - check_dspr2(ctx); - op2 = MASK_APPEND(ctx->opcode); - gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rt, rs, rd, 1); + gen_mipsdsp_append(env, ctx, op1, rt, rs, rd); break; case OPC_EXTR_W_DSP: op2 = MASK_EXTR_W(ctx->opcode); @@ -14951,12 +14970,12 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) #if defined(TARGET_MIPS64) case OPC_DEXTM ... OPC_DEXT: case OPC_DINSM ... OPC_DINS: - check_insn(env, ctx, ISA_MIPS64R2); + check_insn(ctx, ISA_MIPS64R2); check_mips_64(ctx); gen_bitops(ctx, op1, rt, rs, sa, rd); break; case OPC_DBSHFL: - check_insn(env, ctx, ISA_MIPS64R2); + check_insn(ctx, ISA_MIPS64R2); check_mips_64(ctx); op2 = MASK_DBSHFL(ctx->opcode); gen_bshfl(ctx, op2, rt, rd); @@ -14964,7 +14983,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_DDIV_G_2E ... OPC_DDIVU_G_2E: case OPC_DMULT_G_2E ... OPC_DMULTU_G_2E: case OPC_DMOD_G_2E ... OPC_DMODU_G_2E: - check_insn(env, ctx, INSN_LOONGSON2E); + check_insn(ctx, INSN_LOONGSON2E); gen_loongson_integer(ctx, op1, rd, rs, rt); break; case OPC_ABSQ_S_QH_DSP: @@ -14995,7 +15014,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_REPLV_OB: case OPC_REPLV_PW: case OPC_REPLV_QH: - gen_mipsdsp_bitinsn(env, ctx, op1, op2, rd, rt); + gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt); break; default: /* Invalid */ MIPS_INVAL("MASK ABSQ_S.QH"); @@ -15088,9 +15107,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) } break; case OPC_DAPPEND_DSP: - check_dspr2(ctx); - op2 = MASK_DAPPEND(ctx->opcode); - gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rt, rs, rd, 1); + gen_mipsdsp_append(env, ctx, op1, rt, rs, rd); break; case OPC_DEXTR_W_DSP: op2 = MASK_DEXTR_W(ctx->opcode); @@ -15216,7 +15233,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) gen_trap(ctx, op1, rs, -1, imm); break; case OPC_SYNCI: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); /* Treat as NOP. */ break; case OPC_BPOSGE32: /* MIPS DSP branch */ @@ -15262,27 +15279,27 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) op2 = MASK_MFMC0(ctx->opcode); switch (op2) { case OPC_DMT: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dmt(t0); gen_store_gpr(t0, rt); break; case OPC_EMT: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_emt(t0); gen_store_gpr(t0, rt); break; case OPC_DVPE: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_dvpe(t0, cpu_env); gen_store_gpr(t0, rt); break; case OPC_EVPE: - check_insn(env, ctx, ASE_MT); + check_insn(ctx, ASE_MT); gen_helper_evpe(t0, cpu_env); gen_store_gpr(t0, rt); break; case OPC_DI: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); save_cpu_state(ctx, 1); gen_helper_di(t0, cpu_env); gen_store_gpr(t0, rt); @@ -15290,7 +15307,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) ctx->bstate = BS_STOP; break; case OPC_EI: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); save_cpu_state(ctx, 1); gen_helper_ei(t0, cpu_env); gen_store_gpr(t0, rt); @@ -15307,11 +15324,11 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) #endif /* !CONFIG_USER_ONLY */ break; case OPC_RDPGPR: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_load_srsgpr(rt, rd); break; case OPC_WRPGPR: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); gen_store_srsgpr(rt, rd); break; default: @@ -15322,17 +15339,17 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) break; case OPC_ADDI: /* Arithmetic with immediate opcode */ case OPC_ADDIU: - gen_arith_imm(env, ctx, op, rt, rs, imm); + gen_arith_imm(ctx, op, rt, rs, imm); break; case OPC_SLTI: /* Set on less than with immediate opcode */ case OPC_SLTIU: - gen_slt_imm(env, ctx, op, rt, rs, imm); + gen_slt_imm(ctx, op, rt, rs, imm); break; case OPC_ANDI: /* Arithmetic with immediate opcode */ case OPC_LUI: case OPC_ORI: case OPC_XORI: - gen_logic_imm(env, ctx, op, rt, rs, imm); + gen_logic_imm(ctx, op, rt, rs, imm); break; case OPC_J ... OPC_JAL: /* Jump */ offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; @@ -15346,7 +15363,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) break; case OPC_LB ... OPC_LWR: /* Load and stores */ case OPC_LL: - gen_ld(env, ctx, op, rt, rs, imm); + gen_ld(ctx, op, rt, rs, imm); break; case OPC_SB ... OPC_SW: case OPC_SWR: @@ -15357,11 +15374,11 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) break; case OPC_CACHE: check_cp0_enabled(ctx); - check_insn(env, ctx, ISA_MIPS3 | ISA_MIPS32); + check_insn(ctx, ISA_MIPS3 | ISA_MIPS32); /* Treat as NOP. */ break; case OPC_PREF: - check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32); + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); /* Treat as NOP. */ break; @@ -15380,7 +15397,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) switch (op1) { case OPC_MFHC1: case OPC_MTHC1: - check_insn(env, ctx, ISA_MIPS32R2); + check_insn(ctx, ISA_MIPS32R2); case OPC_MFC1: case OPC_CFC1: case OPC_MTC1: @@ -15390,17 +15407,17 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) #if defined(TARGET_MIPS64) case OPC_DMFC1: case OPC_DMTC1: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); gen_cp1(ctx, op1, rt, rd); break; #endif case OPC_BC1ANY2: case OPC_BC1ANY4: check_cop1x(ctx); - check_insn(env, ctx, ASE_MIPS3D); + check_insn(ctx, ASE_MIPS3D); /* fall through */ case OPC_BC1: - gen_compute_branch1(env, ctx, MASK_BC1(ctx->opcode), + gen_compute_branch1(ctx, MASK_BC1(ctx->opcode), (rt >> 2) & 0x7, imm << 2); *is_branch = 1; break; @@ -15431,7 +15448,7 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) generate_exception_err(ctx, EXCP_CpU, 2); break; case OPC_CP2: - check_insn(env, ctx, INSN_LOONGSON2F); + check_insn(ctx, INSN_LOONGSON2F); /* Note that these instructions use different fields. */ gen_loongson_multimedia(ctx, sa, rd, rt); break; @@ -15483,36 +15500,36 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx, int *is_branch) case OPC_LDL ... OPC_LDR: case OPC_LLD: case OPC_LD: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_ld(env, ctx, op, rt, rs, imm); + gen_ld(ctx, op, rt, rs, imm); break; case OPC_SDL ... OPC_SDR: case OPC_SD: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_st(ctx, op, rt, rs, imm); break; case OPC_SCD: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_st_cond(ctx, op, rt, rs, imm); break; case OPC_DADDI: case OPC_DADDIU: - check_insn(env, ctx, ISA_MIPS3); + check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_arith_imm(env, ctx, op, rt, rs, imm); + gen_arith_imm(ctx, op, rt, rs, imm); break; #endif case OPC_JALX: - check_insn(env, ctx, ASE_MIPS16 | ASE_MICROMIPS); + check_insn(ctx, ASE_MIPS16 | ASE_MICROMIPS); offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; gen_compute_branch(ctx, op, 4, rs, rt, offset); *is_branch = 1; break; case OPC_MDMX: - check_insn(env, ctx, ASE_MDMX); + check_insn(ctx, ASE_MDMX); /* MDMX: Not implemented. */ default: /* Invalid */ MIPS_INVAL("major opcode"); @@ -15543,6 +15560,7 @@ gen_intermediate_code_internal (CPUMIPSState *env, TranslationBlock *tb, ctx.pc = pc_start; ctx.saved_pc = -1; ctx.singlestep_enabled = env->singlestep_enabled; + ctx.insn_flags = env->insn_flags; ctx.tb = tb; ctx.bstate = BS_NONE; /* Restore delay slot state from the tb context. */ @@ -15558,7 +15576,7 @@ gen_intermediate_code_internal (CPUMIPSState *env, TranslationBlock *tb, if (max_insns == 0) max_insns = CF_COUNT_MASK; LOG_DISAS("\ntb %p idx %d hflags %04x\n", tb, ctx.mem_idx, ctx.hflags); - gen_icount_start(); + gen_tb_start(); while (ctx.bstate == BS_NONE) { if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { QTAILQ_FOREACH(bp, &env->breakpoints, entry) { @@ -15595,10 +15613,10 @@ gen_intermediate_code_internal (CPUMIPSState *env, TranslationBlock *tb, ctx.opcode = cpu_ldl_code(env, ctx.pc); insn_bytes = 4; decode_opc(env, &ctx, &is_branch); - } else if (env->insn_flags & ASE_MICROMIPS) { + } else if (ctx.insn_flags & ASE_MICROMIPS) { ctx.opcode = cpu_lduw_code(env, ctx.pc); insn_bytes = decode_micromips_opc(env, &ctx, &is_branch); - } else if (env->insn_flags & ASE_MIPS16) { + } else if (ctx.insn_flags & ASE_MIPS16) { ctx.opcode = cpu_lduw_code(env, ctx.pc); insn_bytes = decode_mips16_opc(env, &ctx, &is_branch); } else { @@ -15607,7 +15625,7 @@ gen_intermediate_code_internal (CPUMIPSState *env, TranslationBlock *tb, break; } if (!is_branch) { - handle_delay_slot(env, &ctx, insn_bytes); + handle_delay_slot(&ctx, insn_bytes); } ctx.pc += insn_bytes; @@ -15656,7 +15674,7 @@ gen_intermediate_code_internal (CPUMIPSState *env, TranslationBlock *tb, } } done_generating: - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; if (search_pc) { j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; @@ -15790,7 +15808,7 @@ void cpu_dump_state (CPUMIPSState *env, FILE *f, fprintf_function cpu_fprintf, #endif } -static void mips_tcg_init(void) +void mips_tcg_init(void) { int i; static int inited; @@ -15869,21 +15887,18 @@ MIPSCPU *cpu_mips_init(const char *cpu_model) #endif fpu_init(env, def); mvp_init(env, def); - mips_tcg_init(); - cpu_reset(CPU(cpu)); - qemu_init_vcpu(env); + + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + return cpu; } void cpu_state_reset(CPUMIPSState *env) { - if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); - log_cpu_state(env, 0); - } - - memset(env, 0, offsetof(CPUMIPSState, breakpoints)); - tlb_flush(env, 1); +#ifndef CONFIG_USER_ONLY + MIPSCPU *cpu = mips_env_get_cpu(env); + CPUState *cs = CPU(cpu); +#endif /* Reset registers to their default values */ env->CP0_PRid = env->cpu_model->CP0_PRid; @@ -15929,16 +15944,26 @@ void cpu_state_reset(CPUMIPSState *env) #if defined(CONFIG_USER_ONLY) env->CP0_Status = (MIPS_HFLAG_UM << CP0St_KSU); +# ifdef TARGET_MIPS64 + /* Enable 64-bit register mode. */ + env->CP0_Status |= (1 << CP0St_PX); +# endif +# ifdef TARGET_ABI_MIPSN64 + /* Enable 64-bit address mode. */ + env->CP0_Status |= (1 << CP0St_UX); +# endif /* Enable access to the CPUNum, SYNCI_Step, CC, and CCRes RDHWR hardware registers. */ env->CP0_HWREna |= 0x0000000F; if (env->CP0_Config1 & (1 << CP0C1_FP)) { env->CP0_Status |= (1 << CP0St_CU1); } - if (env->cpu_model->insn_flags & ASE_DSPR2) { - env->hflags |= MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2; - } else if (env->cpu_model->insn_flags & ASE_DSP) { - env->hflags |= MIPS_HFLAG_DSP; + if (env->CP0_Config3 & (1 << CP0C3_DSPP)) { + env->CP0_Status |= (1 << CP0St_MX); + } + /* Enable 64-bit FPU if the target cpu supports it. */ + if (env->active_fpu.fcr0 & (1 << FCR0_F64)) { + env->CP0_Status |= (1 << CP0St_FR); } #else if (env->hflags & MIPS_HFLAG_BMASK) { @@ -15952,7 +15977,7 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_Random = env->tlb->nb_tlb - 1; env->tlb->tlb_in_use = env->tlb->nb_tlb; env->CP0_Wired = 0; - env->CP0_EBase = 0x80000000 | (env->cpu_index & 0x3FF); + env->CP0_EBase = 0x80000000 | (cs->cpu_index & 0x3FF); env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL); /* vectored interrupts not implemented, timer on int 7, no performance counters. */ @@ -15975,19 +16000,19 @@ void cpu_state_reset(CPUMIPSState *env) /* Only TC0 on VPE 0 starts as active. */ for (i = 0; i < ARRAY_SIZE(env->tcs); i++) { - env->tcs[i].CP0_TCBind = env->cpu_index << CP0TCBd_CurVPE; + env->tcs[i].CP0_TCBind = cs->cpu_index << CP0TCBd_CurVPE; env->tcs[i].CP0_TCHalt = 1; } env->active_tc.CP0_TCHalt = 1; - env->halted = 1; + cs->halted = 1; - if (!env->cpu_index) { + if (cs->cpu_index == 0) { /* VPE0 starts up enabled. */ env->mvp->CP0_MVPControl |= (1 << CP0MVPCo_EVP); env->CP0_VPEConf0 |= (1 << CP0VPEC0_MVP) | (1 << CP0VPEC0_VPA); /* TC0 starts up unhalted. */ - env->halted = 0; + cs->halted = 0; env->active_tc.CP0_TCHalt = 0; env->tcs[0].CP0_TCHalt = 0; /* With thread 0 active. */ diff --git a/target-openrisc/cpu.c b/target-openrisc/cpu.c index ba35b17581..ffe14f3c8d 100644 --- a/target-openrisc/cpu.c +++ b/target-openrisc/cpu.c @@ -27,7 +27,7 @@ static void openrisc_cpu_reset(CPUState *s) OpenRISCCPUClass *occ = OPENRISC_CPU_GET_CLASS(cpu); if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", cpu->env.cpu_index); + qemu_log("CPU Reset (CPU %d)\n", s->cpu_index); log_cpu_state(&cpu->env, 0); } @@ -62,19 +62,24 @@ static inline void set_feature(OpenRISCCPU *cpu, int feature) cpu->env.cpucfgr = cpu->feature; } -void openrisc_cpu_realize(Object *obj, Error **errp) +static void openrisc_cpu_realizefn(DeviceState *dev, Error **errp) { - OpenRISCCPU *cpu = OPENRISC_CPU(obj); + OpenRISCCPU *cpu = OPENRISC_CPU(dev); + OpenRISCCPUClass *occ = OPENRISC_CPU_GET_CLASS(dev); qemu_init_vcpu(&cpu->env); cpu_reset(CPU(cpu)); + + occ->parent_realize(dev, errp); } static void openrisc_cpu_initfn(Object *obj) { + CPUState *cs = CPU(obj); OpenRISCCPU *cpu = OPENRISC_CPU(obj); static int inited; + cs->env_ptr = &cpu->env; cpu_exec_init(&cpu->env); #ifndef CONFIG_USER_ONLY @@ -88,6 +93,23 @@ static void openrisc_cpu_initfn(Object *obj) } /* CPU models */ + +static ObjectClass *openrisc_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + + if (cpu_model == NULL) { + return NULL; + } + + oc = object_class_by_name(cpu_model); + if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_OPENRISC_CPU) || + object_class_is_abstract(oc))) { + return NULL; + } + return oc; +} + static void or1200_initfn(Object *obj) { OpenRISCCPU *cpu = OPENRISC_CPU(obj); @@ -117,22 +139,30 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) { OpenRISCCPUClass *occ = OPENRISC_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(occ); + DeviceClass *dc = DEVICE_CLASS(oc); + + occ->parent_realize = dc->realize; + dc->realize = openrisc_cpu_realizefn; occ->parent_reset = cc->reset; cc->reset = openrisc_cpu_reset; + + cc->class_by_name = openrisc_cpu_class_by_name; + cc->do_interrupt = openrisc_cpu_do_interrupt; } static void cpu_register(const OpenRISCCPUInfo *info) { TypeInfo type_info = { - .name = info->name, .parent = TYPE_OPENRISC_CPU, .instance_size = sizeof(OpenRISCCPU), .instance_init = info->initfn, .class_size = sizeof(OpenRISCCPUClass), }; - type_register_static(&type_info); + type_info.name = g_strdup_printf("%s-" TYPE_OPENRISC_CPU, info->name); + type_register(&type_info); + g_free((void *)type_info.name); } static const TypeInfo openrisc_cpu_type_info = { @@ -140,7 +170,7 @@ static const TypeInfo openrisc_cpu_type_info = { .parent = TYPE_CPU, .instance_size = sizeof(OpenRISCCPU), .instance_init = openrisc_cpu_initfn, - .abstract = false, + .abstract = true, .class_size = sizeof(OpenRISCCPUClass), .class_init = openrisc_cpu_class_init, }; @@ -158,23 +188,20 @@ static void openrisc_cpu_register_types(void) OpenRISCCPU *cpu_openrisc_init(const char *cpu_model) { OpenRISCCPU *cpu; + ObjectClass *oc; - if (!object_class_by_name(cpu_model)) { + oc = openrisc_cpu_class_by_name(cpu_model); + if (oc == NULL) { return NULL; } - cpu = OPENRISC_CPU(object_new(cpu_model)); + cpu = OPENRISC_CPU(object_new(object_class_get_name(oc))); cpu->env.cpu_model_str = cpu_model; - openrisc_cpu_realize(OBJECT(cpu), NULL); + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); return cpu; } -typedef struct OpenRISCCPUList { - fprintf_function cpu_fprintf; - FILE *file; -} OpenRISCCPUList; - /* Sort alphabetically by type name, except for "any". */ static gint openrisc_cpu_list_compare(gconstpointer a, gconstpointer b) { @@ -184,9 +211,9 @@ static gint openrisc_cpu_list_compare(gconstpointer a, gconstpointer b) name_a = object_class_get_name(class_a); name_b = object_class_get_name(class_b); - if (strcmp(name_a, "any") == 0) { + if (strcmp(name_a, "any-" TYPE_OPENRISC_CPU) == 0) { return 1; - } else if (strcmp(name_b, "any") == 0) { + } else if (strcmp(name_b, "any-" TYPE_OPENRISC_CPU) == 0) { return -1; } else { return strcmp(name_a, name_b); @@ -196,15 +223,21 @@ static gint openrisc_cpu_list_compare(gconstpointer a, gconstpointer b) static void openrisc_cpu_list_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; - OpenRISCCPUList *s = user_data; + CPUListState *s = user_data; + const char *typename; + char *name; + typename = object_class_get_name(oc); + name = g_strndup(typename, + strlen(typename) - strlen("-" TYPE_OPENRISC_CPU)); (*s->cpu_fprintf)(s->file, " %s\n", - object_class_get_name(oc)); + name); + g_free(name); } void cpu_openrisc_list(FILE *f, fprintf_function cpu_fprintf) { - OpenRISCCPUList s = { + CPUListState s = { .file = f, .cpu_fprintf = cpu_fprintf, }; diff --git a/target-openrisc/cpu.h b/target-openrisc/cpu.h index 3beab45c3c..b9c55ba83b 100644 --- a/target-openrisc/cpu.h +++ b/target-openrisc/cpu.h @@ -33,7 +33,6 @@ struct OpenRISCCPU; #include "exec/cpu-defs.h" #include "fpu/softfloat.h" #include "qom/cpu.h" -#include "qapi/error.h" #define TYPE_OPENRISC_CPU "or32-cpu" @@ -46,6 +45,7 @@ struct OpenRISCCPU; /** * OpenRISCCPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. * * A OpenRISC CPU model. @@ -55,6 +55,7 @@ typedef struct OpenRISCCPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); } OpenRISCCPUClass; @@ -339,12 +340,13 @@ static inline OpenRISCCPU *openrisc_env_get_cpu(CPUOpenRISCState *env) #define ENV_GET_CPU(e) CPU(openrisc_env_get_cpu(e)) +#define ENV_OFFSET offsetof(OpenRISCCPU, env) + OpenRISCCPU *cpu_openrisc_init(const char *cpu_model); -void openrisc_cpu_realize(Object *obj, Error **errp); void cpu_openrisc_list(FILE *f, fprintf_function cpu_fprintf); int cpu_openrisc_exec(CPUOpenRISCState *s); -void do_interrupt(CPUOpenRISCState *env); +void openrisc_cpu_do_interrupt(CPUState *cpu); void openrisc_translate_init(void); int cpu_openrisc_handle_mmu_fault(CPUOpenRISCState *env, target_ulong address, @@ -421,9 +423,7 @@ static inline int cpu_mmu_index(CPUOpenRISCState *env) #define CPU_INTERRUPT_TIMER CPU_INTERRUPT_TGT_INT_0 static inline bool cpu_has_work(CPUState *cpu) { - CPUOpenRISCState *env = &OPENRISC_CPU(cpu)->env; - - return env->interrupt_request & (CPU_INTERRUPT_HARD | + return cpu->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_TIMER); } diff --git a/target-openrisc/exception_helper.c b/target-openrisc/exception_helper.c index dab4148151..0c53b7755b 100644 --- a/target-openrisc/exception_helper.c +++ b/target-openrisc/exception_helper.c @@ -23,7 +23,7 @@ void HELPER(exception)(CPUOpenRISCState *env, uint32_t excp) { - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); raise_exception(cpu, excp); } diff --git a/target-openrisc/fpu_helper.c b/target-openrisc/fpu_helper.c index b184d5ef73..4615a366d1 100644 --- a/target-openrisc/fpu_helper.c +++ b/target-openrisc/fpu_helper.c @@ -68,7 +68,7 @@ static inline void update_fpcsr(OpenRISCCPU *cpu) uint64_t HELPER(itofd)(CPUOpenRISCState *env, uint64_t val) { uint64_t itofd; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); set_float_exception_flags(0, &cpu->env.fp_status); itofd = int32_to_float64(val, &cpu->env.fp_status); @@ -80,7 +80,7 @@ uint64_t HELPER(itofd)(CPUOpenRISCState *env, uint64_t val) uint32_t HELPER(itofs)(CPUOpenRISCState *env, uint32_t val) { uint32_t itofs; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); set_float_exception_flags(0, &cpu->env.fp_status); itofs = int32_to_float32(val, &cpu->env.fp_status); @@ -92,7 +92,7 @@ uint32_t HELPER(itofs)(CPUOpenRISCState *env, uint32_t val) uint64_t HELPER(ftoid)(CPUOpenRISCState *env, uint64_t val) { uint64_t ftoid; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); set_float_exception_flags(0, &cpu->env.fp_status); ftoid = float32_to_int64(val, &cpu->env.fp_status); @@ -104,7 +104,7 @@ uint64_t HELPER(ftoid)(CPUOpenRISCState *env, uint64_t val) uint32_t HELPER(ftois)(CPUOpenRISCState *env, uint32_t val) { uint32_t ftois; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); set_float_exception_flags(0, &cpu->env.fp_status); ftois = float32_to_int32(val, &cpu->env.fp_status); @@ -120,7 +120,7 @@ uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ { \ uint64_t result; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ result = float64_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -131,7 +131,7 @@ uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1) \ { \ uint32_t result; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ result = float32_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -152,7 +152,7 @@ uint64_t helper_float_ ## name1 ## name2 ## _d(CPUOpenRISCState *env, \ { \ uint64_t result, temp, hi, lo; \ uint32_t val1, val2; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ hi = env->fpmaddhi; \ lo = env->fpmaddlo; \ set_float_exception_flags(0, &cpu->env.fp_status); \ @@ -174,7 +174,7 @@ uint32_t helper_float_ ## name1 ## name2 ## _s(CPUOpenRISCState *env, \ { \ uint64_t result, temp, hi, lo; \ uint32_t val1, val2; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ hi = cpu->env.fpmaddhi; \ lo = cpu->env.fpmaddlo; \ set_float_exception_flags(0, &cpu->env.fp_status); \ @@ -198,7 +198,7 @@ uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = float64_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -209,7 +209,7 @@ uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1)\ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = float32_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -227,7 +227,7 @@ uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = !float64_eq_quiet(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -238,7 +238,7 @@ uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = !float32_eq_quiet(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -253,7 +253,7 @@ uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = !float64_le(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -264,7 +264,7 @@ uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = !float32_le(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -278,7 +278,7 @@ uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = !float64_lt(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ @@ -289,7 +289,7 @@ uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1) \ { \ int res; \ - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); \ + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ set_float_exception_flags(0, &cpu->env.fp_status); \ res = !float32_lt(fdt0, fdt1, &cpu->env.fp_status); \ update_fpcsr(cpu); \ diff --git a/target-openrisc/int_helper.c b/target-openrisc/int_helper.c index 20f9837e6a..16cb5abfcd 100644 --- a/target-openrisc/int_helper.c +++ b/target-openrisc/int_helper.c @@ -48,7 +48,7 @@ uint32_t HELPER(mul32)(CPUOpenRISCState *env, uint64_t result; uint32_t high, cy; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); result = (uint64_t)ra * rb; /* regisiers in or32 is 32bit, so 32 is NOT a magic number. diff --git a/target-openrisc/interrupt.c b/target-openrisc/interrupt.c index 7f2c025da2..16ef4b3e79 100644 --- a/target-openrisc/interrupt.c +++ b/target-openrisc/interrupt.c @@ -25,8 +25,10 @@ #include "hw/loader.h" #endif -void do_interrupt(CPUOpenRISCState *env) +void openrisc_cpu_do_interrupt(CPUState *cs) { + OpenRISCCPU *cpu = OPENRISC_CPU(cs); + CPUOpenRISCState *env = &cpu->env; #ifndef CONFIG_USER_ONLY if (env->flags & D_FLAG) { /* Delay Slot insn */ env->flags &= ~D_FLAG; diff --git a/target-openrisc/interrupt_helper.c b/target-openrisc/interrupt_helper.c index 79f5afed44..844648f780 100644 --- a/target-openrisc/interrupt_helper.c +++ b/target-openrisc/interrupt_helper.c @@ -23,7 +23,8 @@ void HELPER(rfe)(CPUOpenRISCState *env) { - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); + CPUState *cs = CPU(cpu); #ifndef CONFIG_USER_ONLY int need_flush_tlb = (cpu->env.sr & (SR_SM | SR_IME | SR_DME)) ^ (cpu->env.esr & (SR_SM | SR_IME | SR_DME)); @@ -53,5 +54,5 @@ void HELPER(rfe)(CPUOpenRISCState *env) tlb_flush(&cpu->env, 1); } #endif - cpu->env.interrupt_request |= CPU_INTERRUPT_EXITTB; + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } diff --git a/target-openrisc/mmu.c b/target-openrisc/mmu.c index 836465259a..d354e1f8b2 100644 --- a/target-openrisc/mmu.c +++ b/target-openrisc/mmu.c @@ -187,7 +187,7 @@ int cpu_openrisc_handle_mmu_fault(CPUOpenRISCState *env, int ret = 0; hwaddr physical = 0; int prot = 0; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot, address, rw); @@ -209,7 +209,7 @@ int cpu_openrisc_handle_mmu_fault(CPUOpenRISCState *env, target_ulong address, int rw, int mmu_idx) { int ret = 0; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); ret = 1; @@ -224,7 +224,7 @@ hwaddr cpu_get_phys_page_debug(CPUOpenRISCState *env, { hwaddr phys_addr; int prot; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); if (cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0)) { return -1; diff --git a/target-openrisc/sys_helper.c b/target-openrisc/sys_helper.c index f160dc397c..cccbc0e939 100644 --- a/target-openrisc/sys_helper.c +++ b/target-openrisc/sys_helper.c @@ -30,7 +30,8 @@ void HELPER(mtspr)(CPUOpenRISCState *env, int spr = (ra | offset); int idx; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); + CPUState *cs = CPU(cpu); switch (spr) { case TO_SPR(0, 0): /* VR */ @@ -132,7 +133,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, env->ttmr = (rb & ~TTMR_IP) + ip; } else { /* Clear IP bit. */ env->ttmr = rb & ~TTMR_IP; - env->interrupt_request &= ~CPU_INTERRUPT_TIMER; + cs->interrupt_request &= ~CPU_INTERRUPT_TIMER; } cpu_openrisc_count_update(cpu); @@ -177,7 +178,7 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, int spr = (ra | offset); int idx; - OpenRISCCPU *cpu = OPENRISC_CPU(ENV_GET_CPU(env)); + OpenRISCCPU *cpu = openrisc_env_get_cpu(env); switch (spr) { case TO_SPR(0, 0): /* VR */ diff --git a/target-openrisc/translate.c b/target-openrisc/translate.c index 1e1b30cdcb..0eafd0296c 100644 --- a/target-openrisc/translate.c +++ b/target-openrisc/translate.c @@ -1670,8 +1670,6 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, int num_insns; int max_insns; - qemu_log_try_set_file(stderr); - pc_start = tb->pc; dc->tb = tb; @@ -1698,7 +1696,7 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, max_insns = CF_COUNT_MASK; } - gen_icount_start(); + gen_tb_start(); do { check_breakpoint(cpu, dc); @@ -1781,7 +1779,7 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu, } } - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; if (search_pc) { j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; diff --git a/target-ppc/Makefile.objs b/target-ppc/Makefile.objs index 237a0ed4f7..00ac4adc51 100644 --- a/target-ppc/Makefile.objs +++ b/target-ppc/Makefile.objs @@ -1,7 +1,7 @@ -obj-y += translate.o helper.o +obj-y += cpu-models.o +obj-y += translate.o obj-$(CONFIG_SOFTMMU) += machine.o obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o -obj-y += helper.o obj-y += excp_helper.o obj-y += fpu_helper.o obj-y += int_helper.o @@ -9,4 +9,3 @@ obj-y += mmu_helper.o obj-y += timebase_helper.o obj-y += misc_helper.o obj-y += mem_helper.o -obj-y += mpic_helper.o diff --git a/target-ppc/cpu-models.c b/target-ppc/cpu-models.c new file mode 100644 index 0000000000..20ca84e614 --- /dev/null +++ b/target-ppc/cpu-models.c @@ -0,0 +1,1419 @@ +/* + * PowerPC CPU initialization for qemu. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright 2011 Freescale Semiconductor, Inc. + * Copyright 2013 SUSE LINUX Products GmbH + * + * 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 . + */ + +/* A lot of PowerPC definition have been included here. + * Most of them are not usable for now but have been kept + * inside "#if defined(TODO) ... #endif" statements to make tests easier. + */ + +#include "cpu.h" +#include "cpu-models.h" + +#if defined(CONFIG_USER_ONLY) +#define TODO_USER_ONLY 1 +#endif + +/***************************************************************************/ +/* PowerPC CPU definitions */ +#define POWERPC_DEF_PREFIX(pvr, svr, type) \ + glue(glue(glue(glue(pvr, _), svr), _), type) +#define POWERPC_DEF_SVR(_name, _desc, _pvr, _svr, _type) \ + static void \ + glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_class_init) \ + (ObjectClass *oc, void *data) \ + { \ + DeviceClass *dc = DEVICE_CLASS(oc); \ + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); \ + \ + pcc->pvr = _pvr; \ + pcc->svr = _svr; \ + dc->desc = _desc; \ + } \ + \ + static const TypeInfo \ + glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_type_info) = { \ + .name = _name "-" TYPE_POWERPC_CPU, \ + .parent = stringify(_type) "-family-" TYPE_POWERPC_CPU, \ + .class_init = \ + glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_class_init), \ + }; \ + \ + static void \ + glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_register_types)(void) \ + { \ + type_register_static( \ + &glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_type_info)); \ + } \ + \ + type_init( \ + glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_register_types)) + +#define POWERPC_DEF(_name, _pvr, _type, _desc) \ + POWERPC_DEF_SVR(_name, _desc, _pvr, POWERPC_SVR_NONE, _type) + + /* Embedded PowerPC */ + /* PowerPC 401 family */ + POWERPC_DEF("401", CPU_POWERPC_401, 401, + "Generic PowerPC 401") + /* PowerPC 401 cores */ + POWERPC_DEF("401A1", CPU_POWERPC_401A1, 401, + "PowerPC 401A1") + POWERPC_DEF("401B2", CPU_POWERPC_401B2, 401x2, + "PowerPC 401B2") +#if defined(TODO) + POWERPC_DEF("401B3", CPU_POWERPC_401B3, 401x3, + "PowerPC 401B3") +#endif + POWERPC_DEF("401C2", CPU_POWERPC_401C2, 401x2, + "PowerPC 401C2") + POWERPC_DEF("401D2", CPU_POWERPC_401D2, 401x2, + "PowerPC 401D2") + POWERPC_DEF("401E2", CPU_POWERPC_401E2, 401x2, + "PowerPC 401E2") + POWERPC_DEF("401F2", CPU_POWERPC_401F2, 401x2, + "PowerPC 401F2") + /* XXX: to be checked */ + POWERPC_DEF("401G2", CPU_POWERPC_401G2, 401x2, + "PowerPC 401G2") + /* PowerPC 401 microcontrollers */ +#if defined(TODO) + POWERPC_DEF("401GF", CPU_POWERPC_401GF, 401, + "PowerPC 401GF") +#endif + POWERPC_DEF("IOP480", CPU_POWERPC_IOP480, IOP480, + "IOP480 (401 microcontroller)") + POWERPC_DEF("Cobra", CPU_POWERPC_COBRA, 401, + "IBM Processor for Network Resources") +#if defined(TODO) + POWERPC_DEF("Xipchip", CPU_POWERPC_XIPCHIP, 401, + NULL) +#endif + /* PowerPC 403 family */ + /* PowerPC 403 microcontrollers */ + POWERPC_DEF("403GA", CPU_POWERPC_403GA, 403, + "PowerPC 403 GA") + POWERPC_DEF("403GB", CPU_POWERPC_403GB, 403, + "PowerPC 403 GB") + POWERPC_DEF("403GC", CPU_POWERPC_403GC, 403, + "PowerPC 403 GC") + POWERPC_DEF("403GCX", CPU_POWERPC_403GCX, 403GCX, + "PowerPC 403 GCX") +#if defined(TODO) + POWERPC_DEF("403GP", CPU_POWERPC_403GP, 403, + "PowerPC 403 GP") +#endif + /* PowerPC 405 family */ + /* PowerPC 405 cores */ +#if defined(TODO) + POWERPC_DEF("405A3", CPU_POWERPC_405A3, 405, + "PowerPC 405 A3") +#endif +#if defined(TODO) + POWERPC_DEF("405A4", CPU_POWERPC_405A4, 405, + "PowerPC 405 A4") +#endif +#if defined(TODO) + POWERPC_DEF("405B3", CPU_POWERPC_405B3, 405, + "PowerPC 405 B3") +#endif +#if defined(TODO) + POWERPC_DEF("405B4", CPU_POWERPC_405B4, 405, + "PowerPC 405 B4") +#endif +#if defined(TODO) + POWERPC_DEF("405C3", CPU_POWERPC_405C3, 405, + "PowerPC 405 C3") +#endif +#if defined(TODO) + POWERPC_DEF("405C4", CPU_POWERPC_405C4, 405, + "PowerPC 405 C4") +#endif + POWERPC_DEF("405D2", CPU_POWERPC_405D2, 405, + "PowerPC 405 D2") +#if defined(TODO) + POWERPC_DEF("405D3", CPU_POWERPC_405D3, 405, + "PowerPC 405 D3") +#endif + POWERPC_DEF("405D4", CPU_POWERPC_405D4, 405, + "PowerPC 405 D4") +#if defined(TODO) + POWERPC_DEF("405D5", CPU_POWERPC_405D5, 405, + "PowerPC 405 D5") +#endif +#if defined(TODO) + POWERPC_DEF("405E4", CPU_POWERPC_405E4, 405, + "PowerPC 405 E4") +#endif +#if defined(TODO) + POWERPC_DEF("405F4", CPU_POWERPC_405F4, 405, + "PowerPC 405 F4") +#endif +#if defined(TODO) + POWERPC_DEF("405F5", CPU_POWERPC_405F5, 405, + "PowerPC 405 F5") +#endif +#if defined(TODO) + POWERPC_DEF("405F6", CPU_POWERPC_405F6, 405, + "PowerPC 405 F6") +#endif + /* PowerPC 405 microcontrollers */ + POWERPC_DEF("405CRa", CPU_POWERPC_405CRa, 405, + "PowerPC 405 CRa") + POWERPC_DEF("405CRb", CPU_POWERPC_405CRb, 405, + "PowerPC 405 CRb") + POWERPC_DEF("405CRc", CPU_POWERPC_405CRc, 405, + "PowerPC 405 CRc") + POWERPC_DEF("405EP", CPU_POWERPC_405EP, 405, + "PowerPC 405 EP") +#if defined(TODO) + POWERPC_DEF("405EXr", CPU_POWERPC_405EXr, 405, + "PowerPC 405 EXr") +#endif + POWERPC_DEF("405EZ", CPU_POWERPC_405EZ, 405, + "PowerPC 405 EZ") +#if defined(TODO) + POWERPC_DEF("405FX", CPU_POWERPC_405FX, 405, + "PowerPC 405 FX") +#endif + POWERPC_DEF("405GPa", CPU_POWERPC_405GPa, 405, + "PowerPC 405 GPa") + POWERPC_DEF("405GPb", CPU_POWERPC_405GPb, 405, + "PowerPC 405 GPb") + POWERPC_DEF("405GPc", CPU_POWERPC_405GPc, 405, + "PowerPC 405 GPc") + POWERPC_DEF("405GPd", CPU_POWERPC_405GPd, 405, + "PowerPC 405 GPd") + POWERPC_DEF("405GPR", CPU_POWERPC_405GPR, 405, + "PowerPC 405 GPR") +#if defined(TODO) + POWERPC_DEF("405H", CPU_POWERPC_405H, 405, + "PowerPC 405 H") +#endif +#if defined(TODO) + POWERPC_DEF("405L", CPU_POWERPC_405L, 405, + "PowerPC 405 L") +#endif + POWERPC_DEF("405LP", CPU_POWERPC_405LP, 405, + "PowerPC 405 LP") +#if defined(TODO) + POWERPC_DEF("405PM", CPU_POWERPC_405PM, 405, + "PowerPC 405 PM") +#endif +#if defined(TODO) + POWERPC_DEF("405PS", CPU_POWERPC_405PS, 405, + "PowerPC 405 PS") +#endif +#if defined(TODO) + POWERPC_DEF("405S", CPU_POWERPC_405S, 405, + "PowerPC 405 S") +#endif + POWERPC_DEF("Npe405H", CPU_POWERPC_NPE405H, 405, + "Npe405 H") + POWERPC_DEF("Npe405H2", CPU_POWERPC_NPE405H2, 405, + "Npe405 H2") + POWERPC_DEF("Npe405L", CPU_POWERPC_NPE405L, 405, + "Npe405 L") + POWERPC_DEF("Npe4GS3", CPU_POWERPC_NPE4GS3, 405, + "Npe4GS3") +#if defined(TODO) + POWERPC_DEF("Npcxx1", CPU_POWERPC_NPCxx1, 405, + NULL) +#endif +#if defined(TODO) + POWERPC_DEF("Npr161", CPU_POWERPC_NPR161, 405, + NULL) +#endif +#if defined(TODO) + POWERPC_DEF("LC77700", CPU_POWERPC_LC77700, 405, + "PowerPC LC77700 (Sanyo)") +#endif + /* PowerPC 401/403/405 based set-top-box microcontrollers */ +#if defined(TODO) + POWERPC_DEF("STB01000", CPU_POWERPC_STB01000, 401x2, + "STB010000") +#endif +#if defined(TODO) + POWERPC_DEF("STB01010", CPU_POWERPC_STB01010, 401x2, + "STB01010") +#endif +#if defined(TODO) + POWERPC_DEF("STB0210", CPU_POWERPC_STB0210, 401x3, + "STB0210") +#endif + POWERPC_DEF("STB03", CPU_POWERPC_STB03, 405, + "STB03xx") +#if defined(TODO) + POWERPC_DEF("STB043", CPU_POWERPC_STB043, 405, + "STB043x") +#endif +#if defined(TODO) + POWERPC_DEF("STB045", CPU_POWERPC_STB045, 405, + "STB045x") +#endif + POWERPC_DEF("STB04", CPU_POWERPC_STB04, 405, + "STB04xx") + POWERPC_DEF("STB25", CPU_POWERPC_STB25, 405, + "STB25xx") +#if defined(TODO) + POWERPC_DEF("STB130", CPU_POWERPC_STB130, 405, + "STB130") +#endif + /* Xilinx PowerPC 405 cores */ + POWERPC_DEF("x2vp4", CPU_POWERPC_X2VP4, 405, + NULL) + POWERPC_DEF("x2vp20", CPU_POWERPC_X2VP20, 405, + NULL) +#if defined(TODO) + POWERPC_DEF("zl10310", CPU_POWERPC_ZL10310, 405, + "Zarlink ZL10310") +#endif +#if defined(TODO) + POWERPC_DEF("zl10311", CPU_POWERPC_ZL10311, 405, + "Zarlink ZL10311") +#endif +#if defined(TODO) + POWERPC_DEF("zl10320", CPU_POWERPC_ZL10320, 405, + "Zarlink ZL10320") +#endif +#if defined(TODO) + POWERPC_DEF("zl10321", CPU_POWERPC_ZL10321, 405, + "Zarlink ZL10321") +#endif + /* PowerPC 440 family */ +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440", CPU_POWERPC_440, 440GP, + "Generic PowerPC 440") +#endif + /* PowerPC 440 cores */ +#if defined(TODO) + POWERPC_DEF("440A4", CPU_POWERPC_440A4, 440x4, + "PowerPC 440 A4") +#endif + POWERPC_DEF("440-Xilinx", CPU_POWERPC_440_XILINX, 440x5, + "PowerPC 440 Xilinx 5") +#if defined(TODO) + POWERPC_DEF("440A5", CPU_POWERPC_440A5, 440x5, + "PowerPC 440 A5") +#endif +#if defined(TODO) + POWERPC_DEF("440B4", CPU_POWERPC_440B4, 440x4, + "PowerPC 440 B4") +#endif +#if defined(TODO) + POWERPC_DEF("440G4", CPU_POWERPC_440G4, 440x4, + "PowerPC 440 G4") +#endif +#if defined(TODO) + POWERPC_DEF("440F5", CPU_POWERPC_440F5, 440x5, + "PowerPC 440 F5") +#endif +#if defined(TODO) + POWERPC_DEF("440G5", CPU_POWERPC_440G5, 440x5, + "PowerPC 440 G5") +#endif +#if defined(TODO) + POWERPC_DEF("440H4", CPU_POWERPC_440H4, 440x4, + "PowerPC 440H4") +#endif +#if defined(TODO) + POWERPC_DEF("440H6", CPU_POWERPC_440H6, 440Gx5, + "PowerPC 440H6") +#endif + /* PowerPC 440 microcontrollers */ + POWERPC_DEF("440EPa", CPU_POWERPC_440EPa, 440EP, + "PowerPC 440 EPa") + POWERPC_DEF("440EPb", CPU_POWERPC_440EPb, 440EP, + "PowerPC 440 EPb") + POWERPC_DEF("440EPX", CPU_POWERPC_440EPX, 440EP, + "PowerPC 440 EPX") +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GPb", CPU_POWERPC_440GPb, 440GP, + "PowerPC 440 GPb") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GPc", CPU_POWERPC_440GPc, 440GP, + "PowerPC 440 GPc") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GRa", CPU_POWERPC_440GRa, 440x5, + "PowerPC 440 GRa") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GRX", CPU_POWERPC_440GRX, 440x5, + "PowerPC 440 GRX") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GXa", CPU_POWERPC_440GXa, 440EP, + "PowerPC 440 GXa") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GXb", CPU_POWERPC_440GXb, 440EP, + "PowerPC 440 GXb") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GXc", CPU_POWERPC_440GXc, 440EP, + "PowerPC 440 GXc") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440GXf", CPU_POWERPC_440GXf, 440EP, + "PowerPC 440 GXf") +#endif +#if defined(TODO) + POWERPC_DEF("440S", CPU_POWERPC_440S, 440, + "PowerPC 440 S") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440SP", CPU_POWERPC_440SP, 440EP, + "PowerPC 440 SP") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440SP2", CPU_POWERPC_440SP2, 440EP, + "PowerPC 440 SP2") +#endif +#if defined(TODO_USER_ONLY) + POWERPC_DEF("440SPE", CPU_POWERPC_440SPE, 440EP, + "PowerPC 440 SPE") +#endif + /* PowerPC 460 family */ +#if defined(TODO) + POWERPC_DEF("464", CPU_POWERPC_464, 460, + "Generic PowerPC 464") +#endif + /* PowerPC 464 microcontrollers */ +#if defined(TODO) + POWERPC_DEF("464H90", CPU_POWERPC_464H90, 460, + "PowerPC 464H90") +#endif +#if defined(TODO) + POWERPC_DEF("464H90F", CPU_POWERPC_464H90F, 460F, + "PowerPC 464H90F") +#endif + /* Freescale embedded PowerPC cores */ + /* MPC5xx family (aka RCPU) */ +#if defined(TODO_USER_ONLY) + POWERPC_DEF("MPC5xx", CPU_POWERPC_MPC5xx, MPC5xx, + "Generic MPC5xx core") +#endif + /* MPC8xx family (aka PowerQUICC) */ +#if defined(TODO_USER_ONLY) + POWERPC_DEF("MPC8xx", CPU_POWERPC_MPC8xx, MPC8xx, + "Generic MPC8xx core") +#endif + /* MPC82xx family (aka PowerQUICC-II) */ + POWERPC_DEF("G2", CPU_POWERPC_G2, G2, + "PowerPC G2 core") + POWERPC_DEF("G2H4", CPU_POWERPC_G2H4, G2, + "PowerPC G2 H4 core") + POWERPC_DEF("G2GP", CPU_POWERPC_G2gp, G2, + "PowerPC G2 GP core") + POWERPC_DEF("G2LS", CPU_POWERPC_G2ls, G2, + "PowerPC G2 LS core") + POWERPC_DEF("G2HiP3", CPU_POWERPC_G2_HIP3, G2, + "PowerPC G2 HiP3 core") + POWERPC_DEF("G2HiP4", CPU_POWERPC_G2_HIP4, G2, + "PowerPC G2 HiP4 core") + POWERPC_DEF("MPC603", CPU_POWERPC_MPC603, 603E, + "PowerPC MPC603 core") + POWERPC_DEF("G2le", CPU_POWERPC_G2LE, G2LE, + "PowerPC G2le core (same as G2 plus little-endian mode support)") + POWERPC_DEF("G2leGP", CPU_POWERPC_G2LEgp, G2LE, + "PowerPC G2LE GP core") + POWERPC_DEF("G2leLS", CPU_POWERPC_G2LEls, G2LE, + "PowerPC G2LE LS core") + POWERPC_DEF("G2leGP1", CPU_POWERPC_G2LEgp1, G2LE, + "PowerPC G2LE GP1 core") + POWERPC_DEF("G2leGP3", CPU_POWERPC_G2LEgp3, G2LE, + "PowerPC G2LE GP3 core") + /* PowerPC G2 microcontrollers */ +#if defined(TODO) + POWERPC_DEF_SVR("MPC5121", "MPC5121", + CPU_POWERPC_MPC5121, POWERPC_SVR_5121, G2LE) +#endif + POWERPC_DEF_SVR("MPC5200_v10", "MPC5200 v1.0", + CPU_POWERPC_MPC5200_v10, POWERPC_SVR_5200_v10, G2LE) + POWERPC_DEF_SVR("MPC5200_v11", "MPC5200 v1.1", + CPU_POWERPC_MPC5200_v11, POWERPC_SVR_5200_v11, G2LE) + POWERPC_DEF_SVR("MPC5200_v12", "MPC5200 v1.2", + CPU_POWERPC_MPC5200_v12, POWERPC_SVR_5200_v12, G2LE) + POWERPC_DEF_SVR("MPC5200B_v20", "MPC5200B v2.0", + CPU_POWERPC_MPC5200B_v20, POWERPC_SVR_5200B_v20, G2LE) + POWERPC_DEF_SVR("MPC5200B_v21", "MPC5200B v2.1", + CPU_POWERPC_MPC5200B_v21, POWERPC_SVR_5200B_v21, G2LE) + /* e200 family */ +#if defined(TODO) + POWERPC_DEF_SVR("MPC55xx", "Generic MPC55xx core", + CPU_POWERPC_MPC55xx, POWERPC_SVR_55xx, e200) +#endif +#if defined(TODO) + POWERPC_DEF("e200z0", CPU_POWERPC_e200z0, e200, + "PowerPC e200z0 core") +#endif +#if defined(TODO) + POWERPC_DEF("e200z1", CPU_POWERPC_e200z1, e200, + "PowerPC e200z1 core") +#endif +#if defined(TODO) + POWERPC_DEF("e200z3", CPU_POWERPC_e200z3, e200, + "PowerPC e200z3 core") +#endif + POWERPC_DEF("e200z5", CPU_POWERPC_e200z5, e200, + "PowerPC e200z5 core") + POWERPC_DEF("e200z6", CPU_POWERPC_e200z6, e200, + "PowerPC e200z6 core") + /* PowerPC e200 microcontrollers */ +#if defined(TODO) + POWERPC_DEF_SVR("MPC5514E", "MPC5514E", + CPU_POWERPC_MPC5514E, POWERPC_SVR_5514E, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5514E_v0", "MPC5514E v0", + CPU_POWERPC_MPC5514E_v0, POWERPC_SVR_5514E_v0, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5514E_v1", "MPC5514E v1", + CPU_POWERPC_MPC5514E_v1, POWERPC_SVR_5514E_v1, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5514G", "MPC5514G", + CPU_POWERPC_MPC5514G, POWERPC_SVR_5514G, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5514G_v0", "MPC5514G v0", + CPU_POWERPC_MPC5514G_v0, POWERPC_SVR_5514G_v0, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5514G_v1", "MPC5514G v1", + CPU_POWERPC_MPC5514G_v1, POWERPC_SVR_5514G_v1, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5515S", "MPC5515S", + CPU_POWERPC_MPC5515S, POWERPC_SVR_5515S, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516E", "MPC5516E", + CPU_POWERPC_MPC5516E, POWERPC_SVR_5516E, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516E_v0", "MPC5516E v0", + CPU_POWERPC_MPC5516E_v0, POWERPC_SVR_5516E_v0, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516E_v1", "MPC5516E v1", + CPU_POWERPC_MPC5516E_v1, POWERPC_SVR_5516E_v1, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516G", "MPC5516G", + CPU_POWERPC_MPC5516G, POWERPC_SVR_5516G, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516G_v0", "MPC5516G v0", + CPU_POWERPC_MPC5516G_v0, POWERPC_SVR_5516G_v0, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516G_v1", "MPC5516G v1", + CPU_POWERPC_MPC5516G_v1, POWERPC_SVR_5516G_v1, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5516S", "MPC5516S", + CPU_POWERPC_MPC5516S, POWERPC_SVR_5516S, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5533", "MPC5533", + CPU_POWERPC_MPC5533, POWERPC_SVR_5533, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5534", "MPC5534", + CPU_POWERPC_MPC5534, POWERPC_SVR_5534, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5553", "MPC5553", + CPU_POWERPC_MPC5553, POWERPC_SVR_5553, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5554", "MPC5554", + CPU_POWERPC_MPC5554, POWERPC_SVR_5554, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5561", "MPC5561", + CPU_POWERPC_MPC5561, POWERPC_SVR_5561, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5565", "MPC5565", + CPU_POWERPC_MPC5565, POWERPC_SVR_5565, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5566", "MPC5566", + CPU_POWERPC_MPC5566, POWERPC_SVR_5566, e200) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC5567", "MPC5567", + CPU_POWERPC_MPC5567, POWERPC_SVR_5567, e200) +#endif + /* e300 family */ + POWERPC_DEF("e300c1", CPU_POWERPC_e300c1, e300, + "PowerPC e300c1 core") + POWERPC_DEF("e300c2", CPU_POWERPC_e300c2, e300, + "PowerPC e300c2 core") + POWERPC_DEF("e300c3", CPU_POWERPC_e300c3, e300, + "PowerPC e300c3 core") + POWERPC_DEF("e300c4", CPU_POWERPC_e300c4, e300, + "PowerPC e300c4 core") + /* PowerPC e300 microcontrollers */ +#if defined(TODO) + POWERPC_DEF_SVR("MPC8313", "MPC8313", + CPU_POWERPC_MPC831x, POWERPC_SVR_8313, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8313E", "MPC8313E", + CPU_POWERPC_MPC831x, POWERPC_SVR_8313E, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8314", "MPC8314", + CPU_POWERPC_MPC831x, POWERPC_SVR_8314, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8314E", "MPC8314E", + CPU_POWERPC_MPC831x, POWERPC_SVR_8314E, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8315", "MPC8315", + CPU_POWERPC_MPC831x, POWERPC_SVR_8315, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8315E", "MPC8315E", + CPU_POWERPC_MPC831x, POWERPC_SVR_8315E, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8321", "MPC8321", + CPU_POWERPC_MPC832x, POWERPC_SVR_8321, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8321E", "MPC8321E", + CPU_POWERPC_MPC832x, POWERPC_SVR_8321E, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8323", "MPC8323", + CPU_POWERPC_MPC832x, POWERPC_SVR_8323, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8323E", "MPC8323E", + CPU_POWERPC_MPC832x, POWERPC_SVR_8323E, e300) +#endif + POWERPC_DEF_SVR("MPC8343", "MPC8343", + CPU_POWERPC_MPC834x, POWERPC_SVR_8343, e300) + POWERPC_DEF_SVR("MPC8343A", "MPC8343A", + CPU_POWERPC_MPC834x, POWERPC_SVR_8343A, e300) + POWERPC_DEF_SVR("MPC8343E", "MPC8343E", + CPU_POWERPC_MPC834x, POWERPC_SVR_8343E, e300) + POWERPC_DEF_SVR("MPC8343EA", "MPC8343EA", + CPU_POWERPC_MPC834x, POWERPC_SVR_8343EA, e300) + POWERPC_DEF_SVR("MPC8347T", "MPC8347T", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347T, e300) + POWERPC_DEF_SVR("MPC8347P", "MPC8347P", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347P, e300) + POWERPC_DEF_SVR("MPC8347AT", "MPC8347AT", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347AT, e300) + POWERPC_DEF_SVR("MPC8347AP", "MPC8347AP", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347AP, e300) + POWERPC_DEF_SVR("MPC8347ET", "MPC8347ET", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347ET, e300) + POWERPC_DEF_SVR("MPC8347EP", "MPC8343EP", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347EP, e300) + POWERPC_DEF_SVR("MPC8347EAT", "MPC8347EAT", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347EAT, e300) + POWERPC_DEF_SVR("MPC8347EAP", "MPC8343EAP", + CPU_POWERPC_MPC834x, POWERPC_SVR_8347EAP, e300) + POWERPC_DEF_SVR("MPC8349", "MPC8349", + CPU_POWERPC_MPC834x, POWERPC_SVR_8349, e300) + POWERPC_DEF_SVR("MPC8349A", "MPC8349A", + CPU_POWERPC_MPC834x, POWERPC_SVR_8349A, e300) + POWERPC_DEF_SVR("MPC8349E", "MPC8349E", + CPU_POWERPC_MPC834x, POWERPC_SVR_8349E, e300) + POWERPC_DEF_SVR("MPC8349EA", "MPC8349EA", + CPU_POWERPC_MPC834x, POWERPC_SVR_8349EA, e300) +#if defined(TODO) + POWERPC_DEF_SVR("MPC8358E", "MPC8358E", + CPU_POWERPC_MPC835x, POWERPC_SVR_8358E, e300) +#endif +#if defined(TODO) + POWERPC_DEF_SVR("MPC8360E", "MPC8360E", + CPU_POWERPC_MPC836x, POWERPC_SVR_8360E, e300) +#endif + POWERPC_DEF_SVR("MPC8377", "MPC8377", + CPU_POWERPC_MPC837x, POWERPC_SVR_8377, e300) + POWERPC_DEF_SVR("MPC8377E", "MPC8377E", + CPU_POWERPC_MPC837x, POWERPC_SVR_8377E, e300) + POWERPC_DEF_SVR("MPC8378", "MPC8378", + CPU_POWERPC_MPC837x, POWERPC_SVR_8378, e300) + POWERPC_DEF_SVR("MPC8378E", "MPC8378E", + CPU_POWERPC_MPC837x, POWERPC_SVR_8378E, e300) + POWERPC_DEF_SVR("MPC8379", "MPC8379", + CPU_POWERPC_MPC837x, POWERPC_SVR_8379, e300) + POWERPC_DEF_SVR("MPC8379E", "MPC8379E", + CPU_POWERPC_MPC837x, POWERPC_SVR_8379E, e300) + /* e500 family */ + POWERPC_DEF("e500_v10", CPU_POWERPC_e500v1_v10, e500v1, + "PowerPC e500 v1.0 core") + POWERPC_DEF("e500_v20", CPU_POWERPC_e500v1_v20, e500v1, + "PowerPC e500 v2.0 core") + POWERPC_DEF("e500v2_v10", CPU_POWERPC_e500v2_v10, e500v2, + "PowerPC e500v2 v1.0 core") + POWERPC_DEF("e500v2_v20", CPU_POWERPC_e500v2_v20, e500v2, + "PowerPC e500v2 v2.0 core") + POWERPC_DEF("e500v2_v21", CPU_POWERPC_e500v2_v21, e500v2, + "PowerPC e500v2 v2.1 core") + POWERPC_DEF("e500v2_v22", CPU_POWERPC_e500v2_v22, e500v2, + "PowerPC e500v2 v2.2 core") + POWERPC_DEF("e500v2_v30", CPU_POWERPC_e500v2_v30, e500v2, + "PowerPC e500v2 v3.0 core") + POWERPC_DEF_SVR("e500mc", "e500mc", + CPU_POWERPC_e500mc, POWERPC_SVR_E500, e500mc) +#ifdef TARGET_PPC64 + POWERPC_DEF_SVR("e5500", "e5500", + CPU_POWERPC_e5500, POWERPC_SVR_E500, e5500) +#endif + /* PowerPC e500 microcontrollers */ + POWERPC_DEF_SVR("MPC8533_v10", "MPC8533 v1.0", + CPU_POWERPC_MPC8533_v10, POWERPC_SVR_8533_v10, e500v2) + POWERPC_DEF_SVR("MPC8533_v11", "MPC8533 v1.1", + CPU_POWERPC_MPC8533_v11, POWERPC_SVR_8533_v11, e500v2) + POWERPC_DEF_SVR("MPC8533E_v10", "MPC8533E v1.0", + CPU_POWERPC_MPC8533E_v10, POWERPC_SVR_8533E_v10, e500v2) + POWERPC_DEF_SVR("MPC8533E_v11", "MPC8533E v1.1", + CPU_POWERPC_MPC8533E_v11, POWERPC_SVR_8533E_v11, e500v2) + POWERPC_DEF_SVR("MPC8540_v10", "MPC8540 v1.0", + CPU_POWERPC_MPC8540_v10, POWERPC_SVR_8540_v10, e500v1) + POWERPC_DEF_SVR("MPC8540_v20", "MPC8540 v2.0", + CPU_POWERPC_MPC8540_v20, POWERPC_SVR_8540_v20, e500v1) + POWERPC_DEF_SVR("MPC8540_v21", "MPC8540 v2.1", + CPU_POWERPC_MPC8540_v21, POWERPC_SVR_8540_v21, e500v1) + POWERPC_DEF_SVR("MPC8541_v10", "MPC8541 v1.0", + CPU_POWERPC_MPC8541_v10, POWERPC_SVR_8541_v10, e500v1) + POWERPC_DEF_SVR("MPC8541_v11", "MPC8541 v1.1", + CPU_POWERPC_MPC8541_v11, POWERPC_SVR_8541_v11, e500v1) + POWERPC_DEF_SVR("MPC8541E_v10", "MPC8541E v1.0", + CPU_POWERPC_MPC8541E_v10, POWERPC_SVR_8541E_v10, e500v1) + POWERPC_DEF_SVR("MPC8541E_v11", "MPC8541E v1.1", + CPU_POWERPC_MPC8541E_v11, POWERPC_SVR_8541E_v11, e500v1) + POWERPC_DEF_SVR("MPC8543_v10", "MPC8543 v1.0", + CPU_POWERPC_MPC8543_v10, POWERPC_SVR_8543_v10, e500v2) + POWERPC_DEF_SVR("MPC8543_v11", "MPC8543 v1.1", + CPU_POWERPC_MPC8543_v11, POWERPC_SVR_8543_v11, e500v2) + POWERPC_DEF_SVR("MPC8543_v20", "MPC8543 v2.0", + CPU_POWERPC_MPC8543_v20, POWERPC_SVR_8543_v20, e500v2) + POWERPC_DEF_SVR("MPC8543_v21", "MPC8543 v2.1", + CPU_POWERPC_MPC8543_v21, POWERPC_SVR_8543_v21, e500v2) + POWERPC_DEF_SVR("MPC8543E_v10", "MPC8543E v1.0", + CPU_POWERPC_MPC8543E_v10, POWERPC_SVR_8543E_v10, e500v2) + POWERPC_DEF_SVR("MPC8543E_v11", "MPC8543E v1.1", + CPU_POWERPC_MPC8543E_v11, POWERPC_SVR_8543E_v11, e500v2) + POWERPC_DEF_SVR("MPC8543E_v20", "MPC8543E v2.0", + CPU_POWERPC_MPC8543E_v20, POWERPC_SVR_8543E_v20, e500v2) + POWERPC_DEF_SVR("MPC8543E_v21", "MPC8543E v2.1", + CPU_POWERPC_MPC8543E_v21, POWERPC_SVR_8543E_v21, e500v2) + POWERPC_DEF_SVR("MPC8544_v10", "MPC8544 v1.0", + CPU_POWERPC_MPC8544_v10, POWERPC_SVR_8544_v10, e500v2) + POWERPC_DEF_SVR("MPC8544_v11", "MPC8544 v1.1", + CPU_POWERPC_MPC8544_v11, POWERPC_SVR_8544_v11, e500v2) + POWERPC_DEF_SVR("MPC8544E_v10", "MPC8544E v1.0", + CPU_POWERPC_MPC8544E_v10, POWERPC_SVR_8544E_v10, e500v2) + POWERPC_DEF_SVR("MPC8544E_v11", "MPC8544E v1.1", + CPU_POWERPC_MPC8544E_v11, POWERPC_SVR_8544E_v11, e500v2) + POWERPC_DEF_SVR("MPC8545_v20", "MPC8545 v2.0", + CPU_POWERPC_MPC8545_v20, POWERPC_SVR_8545_v20, e500v2) + POWERPC_DEF_SVR("MPC8545_v21", "MPC8545 v2.1", + CPU_POWERPC_MPC8545_v21, POWERPC_SVR_8545_v21, e500v2) + POWERPC_DEF_SVR("MPC8545E_v20", "MPC8545E v2.0", + CPU_POWERPC_MPC8545E_v20, POWERPC_SVR_8545E_v20, e500v2) + POWERPC_DEF_SVR("MPC8545E_v21", "MPC8545E v2.1", + CPU_POWERPC_MPC8545E_v21, POWERPC_SVR_8545E_v21, e500v2) + POWERPC_DEF_SVR("MPC8547E_v20", "MPC8547E v2.0", + CPU_POWERPC_MPC8547E_v20, POWERPC_SVR_8547E_v20, e500v2) + POWERPC_DEF_SVR("MPC8547E_v21", "MPC8547E v2.1", + CPU_POWERPC_MPC8547E_v21, POWERPC_SVR_8547E_v21, e500v2) + POWERPC_DEF_SVR("MPC8548_v10", "MPC8548 v1.0", + CPU_POWERPC_MPC8548_v10, POWERPC_SVR_8548_v10, e500v2) + POWERPC_DEF_SVR("MPC8548_v11", "MPC8548 v1.1", + CPU_POWERPC_MPC8548_v11, POWERPC_SVR_8548_v11, e500v2) + POWERPC_DEF_SVR("MPC8548_v20", "MPC8548 v2.0", + CPU_POWERPC_MPC8548_v20, POWERPC_SVR_8548_v20, e500v2) + POWERPC_DEF_SVR("MPC8548_v21", "MPC8548 v2.1", + CPU_POWERPC_MPC8548_v21, POWERPC_SVR_8548_v21, e500v2) + POWERPC_DEF_SVR("MPC8548E_v10", "MPC8548E v1.0", + CPU_POWERPC_MPC8548E_v10, POWERPC_SVR_8548E_v10, e500v2) + POWERPC_DEF_SVR("MPC8548E_v11", "MPC8548E v1.1", + CPU_POWERPC_MPC8548E_v11, POWERPC_SVR_8548E_v11, e500v2) + POWERPC_DEF_SVR("MPC8548E_v20", "MPC8548E v2.0", + CPU_POWERPC_MPC8548E_v20, POWERPC_SVR_8548E_v20, e500v2) + POWERPC_DEF_SVR("MPC8548E_v21", "MPC8548E v2.1", + CPU_POWERPC_MPC8548E_v21, POWERPC_SVR_8548E_v21, e500v2) + POWERPC_DEF_SVR("MPC8555_v10", "MPC8555 v1.0", + CPU_POWERPC_MPC8555_v10, POWERPC_SVR_8555_v10, e500v2) + POWERPC_DEF_SVR("MPC8555_v11", "MPC8555 v1.1", + CPU_POWERPC_MPC8555_v11, POWERPC_SVR_8555_v11, e500v2) + POWERPC_DEF_SVR("MPC8555E_v10", "MPC8555E v1.0", + CPU_POWERPC_MPC8555E_v10, POWERPC_SVR_8555E_v10, e500v2) + POWERPC_DEF_SVR("MPC8555E_v11", "MPC8555E v1.1", + CPU_POWERPC_MPC8555E_v11, POWERPC_SVR_8555E_v11, e500v2) + POWERPC_DEF_SVR("MPC8560_v10", "MPC8560 v1.0", + CPU_POWERPC_MPC8560_v10, POWERPC_SVR_8560_v10, e500v2) + POWERPC_DEF_SVR("MPC8560_v20", "MPC8560 v2.0", + CPU_POWERPC_MPC8560_v20, POWERPC_SVR_8560_v20, e500v2) + POWERPC_DEF_SVR("MPC8560_v21", "MPC8560 v2.1", + CPU_POWERPC_MPC8560_v21, POWERPC_SVR_8560_v21, e500v2) + POWERPC_DEF_SVR("MPC8567", "MPC8567", + CPU_POWERPC_MPC8567, POWERPC_SVR_8567, e500v2) + POWERPC_DEF_SVR("MPC8567E", "MPC8567E", + CPU_POWERPC_MPC8567E, POWERPC_SVR_8567E, e500v2) + POWERPC_DEF_SVR("MPC8568", "MPC8568", + CPU_POWERPC_MPC8568, POWERPC_SVR_8568, e500v2) + POWERPC_DEF_SVR("MPC8568E", "MPC8568E", + CPU_POWERPC_MPC8568E, POWERPC_SVR_8568E, e500v2) + POWERPC_DEF_SVR("MPC8572", "MPC8572", + CPU_POWERPC_MPC8572, POWERPC_SVR_8572, e500v2) + POWERPC_DEF_SVR("MPC8572E", "MPC8572E", + CPU_POWERPC_MPC8572E, POWERPC_SVR_8572E, e500v2) + /* e600 family */ + POWERPC_DEF("e600", CPU_POWERPC_e600, 7400, + "PowerPC e600 core") + /* PowerPC e600 microcontrollers */ +#if defined(TODO) + POWERPC_DEF_SVR("MPC8610", "MPC8610", + CPU_POWERPC_MPC8610, POWERPC_SVR_8610, 7400) +#endif + POWERPC_DEF_SVR("MPC8641", "MPC8641", + CPU_POWERPC_MPC8641, POWERPC_SVR_8641, 7400) + POWERPC_DEF_SVR("MPC8641D", "MPC8641D", + CPU_POWERPC_MPC8641D, POWERPC_SVR_8641D, 7400) + /* 32 bits "classic" PowerPC */ + /* PowerPC 6xx family */ + POWERPC_DEF("601_v0", CPU_POWERPC_601_v0, 601, + "PowerPC 601v0") + POWERPC_DEF("601_v1", CPU_POWERPC_601_v1, 601, + "PowerPC 601v1") + POWERPC_DEF("601_v2", CPU_POWERPC_601_v2, 601v, + "PowerPC 601v2") + POWERPC_DEF("602", CPU_POWERPC_602, 602, + "PowerPC 602") + POWERPC_DEF("603", CPU_POWERPC_603, 603, + "PowerPC 603") + POWERPC_DEF("603e_v1.1", CPU_POWERPC_603E_v11, 603E, + "PowerPC 603e v1.1") + POWERPC_DEF("603e_v1.2", CPU_POWERPC_603E_v12, 603E, + "PowerPC 603e v1.2") + POWERPC_DEF("603e_v1.3", CPU_POWERPC_603E_v13, 603E, + "PowerPC 603e v1.3") + POWERPC_DEF("603e_v1.4", CPU_POWERPC_603E_v14, 603E, + "PowerPC 603e v1.4") + POWERPC_DEF("603e_v2.2", CPU_POWERPC_603E_v22, 603E, + "PowerPC 603e v2.2") + POWERPC_DEF("603e_v3", CPU_POWERPC_603E_v3, 603E, + "PowerPC 603e v3") + POWERPC_DEF("603e_v4", CPU_POWERPC_603E_v4, 603E, + "PowerPC 603e v4") + POWERPC_DEF("603e_v4.1", CPU_POWERPC_603E_v41, 603E, + "PowerPC 603e v4.1") + POWERPC_DEF("603e7", CPU_POWERPC_603E7, 603E, + "PowerPC 603e (aka PID7)") + POWERPC_DEF("603e7t", CPU_POWERPC_603E7t, 603E, + "PowerPC 603e7t") + POWERPC_DEF("603e7v", CPU_POWERPC_603E7v, 603E, + "PowerPC 603e7v") + POWERPC_DEF("603e7v1", CPU_POWERPC_603E7v1, 603E, + "PowerPC 603e7v1") + POWERPC_DEF("603e7v2", CPU_POWERPC_603E7v2, 603E, + "PowerPC 603e7v2") + POWERPC_DEF("603p", CPU_POWERPC_603P, 603E, + "PowerPC 603p (aka PID7v)") + POWERPC_DEF("604", CPU_POWERPC_604, 604, + "PowerPC 604") + POWERPC_DEF("604e_v1.0", CPU_POWERPC_604E_v10, 604E, + "PowerPC 604e v1.0") + POWERPC_DEF("604e_v2.2", CPU_POWERPC_604E_v22, 604E, + "PowerPC 604e v2.2") + POWERPC_DEF("604e_v2.4", CPU_POWERPC_604E_v24, 604E, + "PowerPC 604e v2.4") + POWERPC_DEF("604r", CPU_POWERPC_604R, 604E, + "PowerPC 604r (aka PIDA)") +#if defined(TODO) + POWERPC_DEF("604ev", CPU_POWERPC_604EV, 604E, + "PowerPC 604ev") +#endif + /* PowerPC 7xx family */ + POWERPC_DEF("740_v1.0", CPU_POWERPC_7x0_v10, 740, + "PowerPC 740 v1.0 (G3)") + POWERPC_DEF("750_v1.0", CPU_POWERPC_7x0_v10, 750, + "PowerPC 750 v1.0 (G3)") + POWERPC_DEF("740_v2.0", CPU_POWERPC_7x0_v20, 740, + "PowerPC 740 v2.0 (G3)") + POWERPC_DEF("750_v2.0", CPU_POWERPC_7x0_v20, 750, + "PowerPC 750 v2.0 (G3)") + POWERPC_DEF("740_v2.1", CPU_POWERPC_7x0_v21, 740, + "PowerPC 740 v2.1 (G3)") + POWERPC_DEF("750_v2.1", CPU_POWERPC_7x0_v21, 750, + "PowerPC 750 v2.1 (G3)") + POWERPC_DEF("740_v2.2", CPU_POWERPC_7x0_v22, 740, + "PowerPC 740 v2.2 (G3)") + POWERPC_DEF("750_v2.2", CPU_POWERPC_7x0_v22, 750, + "PowerPC 750 v2.2 (G3)") + POWERPC_DEF("740_v3.0", CPU_POWERPC_7x0_v30, 740, + "PowerPC 740 v3.0 (G3)") + POWERPC_DEF("750_v3.0", CPU_POWERPC_7x0_v30, 750, + "PowerPC 750 v3.0 (G3)") + POWERPC_DEF("740_v3.1", CPU_POWERPC_7x0_v31, 740, + "PowerPC 740 v3.1 (G3)") + POWERPC_DEF("750_v3.1", CPU_POWERPC_7x0_v31, 750, + "PowerPC 750 v3.1 (G3)") + POWERPC_DEF("740e", CPU_POWERPC_740E, 740, + "PowerPC 740E (G3)") + POWERPC_DEF("750e", CPU_POWERPC_750E, 750, + "PowerPC 750E (G3)") + POWERPC_DEF("740p", CPU_POWERPC_7x0P, 740, + "PowerPC 740P (G3)") + POWERPC_DEF("750p", CPU_POWERPC_7x0P, 750, + "PowerPC 750P (G3)") + POWERPC_DEF("750cl_v1.0", CPU_POWERPC_750CL_v10, 750cl, + "PowerPC 750CL v1.0") + POWERPC_DEF("750cl_v2.0", CPU_POWERPC_750CL_v20, 750cl, + "PowerPC 750CL v2.0") + POWERPC_DEF("750cx_v1.0", CPU_POWERPC_750CX_v10, 750cx, + "PowerPC 750CX v1.0 (G3 embedded)") + POWERPC_DEF("750cx_v2.0", CPU_POWERPC_750CX_v20, 750cx, + "PowerPC 750CX v2.1 (G3 embedded)") + POWERPC_DEF("750cx_v2.1", CPU_POWERPC_750CX_v21, 750cx, + "PowerPC 750CX v2.1 (G3 embedded)") + POWERPC_DEF("750cx_v2.2", CPU_POWERPC_750CX_v22, 750cx, + "PowerPC 750CX v2.2 (G3 embedded)") + POWERPC_DEF("750cxe_v2.1", CPU_POWERPC_750CXE_v21, 750cx, + "PowerPC 750CXe v2.1 (G3 embedded)") + POWERPC_DEF("750cxe_v2.2", CPU_POWERPC_750CXE_v22, 750cx, + "PowerPC 750CXe v2.2 (G3 embedded)") + POWERPC_DEF("750cxe_v2.3", CPU_POWERPC_750CXE_v23, 750cx, + "PowerPC 750CXe v2.3 (G3 embedded)") + POWERPC_DEF("750cxe_v2.4", CPU_POWERPC_750CXE_v24, 750cx, + "PowerPC 750CXe v2.4 (G3 embedded)") + POWERPC_DEF("750cxe_v2.4b", CPU_POWERPC_750CXE_v24b, 750cx, + "PowerPC 750CXe v2.4b (G3 embedded)") + POWERPC_DEF("750cxe_v3.0", CPU_POWERPC_750CXE_v30, 750cx, + "PowerPC 750CXe v3.0 (G3 embedded)") + POWERPC_DEF("750cxe_v3.1", CPU_POWERPC_750CXE_v31, 750cx, + "PowerPC 750CXe v3.1 (G3 embedded)") + POWERPC_DEF("750cxe_v3.1b", CPU_POWERPC_750CXE_v31b, 750cx, + "PowerPC 750CXe v3.1b (G3 embedded)") + POWERPC_DEF("750cxr", CPU_POWERPC_750CXR, 750cx, + "PowerPC 750CXr (G3 embedded)") + POWERPC_DEF("750fl", CPU_POWERPC_750FL, 750fx, + "PowerPC 750FL (G3 embedded)") + POWERPC_DEF("750fx_v1.0", CPU_POWERPC_750FX_v10, 750fx, + "PowerPC 750FX v1.0 (G3 embedded)") + POWERPC_DEF("750fx_v2.0", CPU_POWERPC_750FX_v20, 750fx, + "PowerPC 750FX v2.0 (G3 embedded)") + POWERPC_DEF("750fx_v2.1", CPU_POWERPC_750FX_v21, 750fx, + "PowerPC 750FX v2.1 (G3 embedded)") + POWERPC_DEF("750fx_v2.2", CPU_POWERPC_750FX_v22, 750fx, + "PowerPC 750FX v2.2 (G3 embedded)") + POWERPC_DEF("750fx_v2.3", CPU_POWERPC_750FX_v23, 750fx, + "PowerPC 750FX v2.3 (G3 embedded)") + POWERPC_DEF("750gl", CPU_POWERPC_750GL, 750gx, + "PowerPC 750GL (G3 embedded)") + POWERPC_DEF("750gx_v1.0", CPU_POWERPC_750GX_v10, 750gx, + "PowerPC 750GX v1.0 (G3 embedded)") + POWERPC_DEF("750gx_v1.1", CPU_POWERPC_750GX_v11, 750gx, + "PowerPC 750GX v1.1 (G3 embedded)") + POWERPC_DEF("750gx_v1.2", CPU_POWERPC_750GX_v12, 750gx, + "PowerPC 750GX v1.2 (G3 embedded)") + POWERPC_DEF("750l_v2.0", CPU_POWERPC_750L_v20, 750, + "PowerPC 750L v2.0 (G3 embedded)") + POWERPC_DEF("750l_v2.1", CPU_POWERPC_750L_v21, 750, + "PowerPC 750L v2.1 (G3 embedded)") + POWERPC_DEF("750l_v2.2", CPU_POWERPC_750L_v22, 750, + "PowerPC 750L v2.2 (G3 embedded)") + POWERPC_DEF("750l_v3.0", CPU_POWERPC_750L_v30, 750, + "PowerPC 750L v3.0 (G3 embedded)") + POWERPC_DEF("750l_v3.2", CPU_POWERPC_750L_v32, 750, + "PowerPC 750L v3.2 (G3 embedded)") + POWERPC_DEF("745_v1.0", CPU_POWERPC_7x5_v10, 745, + "PowerPC 745 v1.0") + POWERPC_DEF("755_v1.0", CPU_POWERPC_7x5_v10, 755, + "PowerPC 755 v1.0") + POWERPC_DEF("745_v1.1", CPU_POWERPC_7x5_v11, 745, + "PowerPC 745 v1.1") + POWERPC_DEF("755_v1.1", CPU_POWERPC_7x5_v11, 755, + "PowerPC 755 v1.1") + POWERPC_DEF("745_v2.0", CPU_POWERPC_7x5_v20, 745, + "PowerPC 745 v2.0") + POWERPC_DEF("755_v2.0", CPU_POWERPC_7x5_v20, 755, + "PowerPC 755 v2.0") + POWERPC_DEF("745_v2.1", CPU_POWERPC_7x5_v21, 745, + "PowerPC 745 v2.1") + POWERPC_DEF("755_v2.1", CPU_POWERPC_7x5_v21, 755, + "PowerPC 755 v2.1") + POWERPC_DEF("745_v2.2", CPU_POWERPC_7x5_v22, 745, + "PowerPC 745 v2.2") + POWERPC_DEF("755_v2.2", CPU_POWERPC_7x5_v22, 755, + "PowerPC 755 v2.2") + POWERPC_DEF("745_v2.3", CPU_POWERPC_7x5_v23, 745, + "PowerPC 745 v2.3") + POWERPC_DEF("755_v2.3", CPU_POWERPC_7x5_v23, 755, + "PowerPC 755 v2.3") + POWERPC_DEF("745_v2.4", CPU_POWERPC_7x5_v24, 745, + "PowerPC 745 v2.4") + POWERPC_DEF("755_v2.4", CPU_POWERPC_7x5_v24, 755, + "PowerPC 755 v2.4") + POWERPC_DEF("745_v2.5", CPU_POWERPC_7x5_v25, 745, + "PowerPC 745 v2.5") + POWERPC_DEF("755_v2.5", CPU_POWERPC_7x5_v25, 755, + "PowerPC 755 v2.5") + POWERPC_DEF("745_v2.6", CPU_POWERPC_7x5_v26, 745, + "PowerPC 745 v2.6") + POWERPC_DEF("755_v2.6", CPU_POWERPC_7x5_v26, 755, + "PowerPC 755 v2.6") + POWERPC_DEF("745_v2.7", CPU_POWERPC_7x5_v27, 745, + "PowerPC 745 v2.7") + POWERPC_DEF("755_v2.7", CPU_POWERPC_7x5_v27, 755, + "PowerPC 755 v2.7") + POWERPC_DEF("745_v2.8", CPU_POWERPC_7x5_v28, 745, + "PowerPC 745 v2.8") + POWERPC_DEF("755_v2.8", CPU_POWERPC_7x5_v28, 755, + "PowerPC 755 v2.8") +#if defined(TODO) + POWERPC_DEF("745p", CPU_POWERPC_7x5P, 745, + "PowerPC 745P (G3)") + POWERPC_DEF("755p", CPU_POWERPC_7x5P, 755, + "PowerPC 755P (G3)") +#endif + /* PowerPC 74xx family */ + POWERPC_DEF("7400_v1.0", CPU_POWERPC_7400_v10, 7400, + "PowerPC 7400 v1.0 (G4)") + POWERPC_DEF("7400_v1.1", CPU_POWERPC_7400_v11, 7400, + "PowerPC 7400 v1.1 (G4)") + POWERPC_DEF("7400_v2.0", CPU_POWERPC_7400_v20, 7400, + "PowerPC 7400 v2.0 (G4)") + POWERPC_DEF("7400_v2.1", CPU_POWERPC_7400_v21, 7400, + "PowerPC 7400 v2.1 (G4)") + POWERPC_DEF("7400_v2.2", CPU_POWERPC_7400_v22, 7400, + "PowerPC 7400 v2.2 (G4)") + POWERPC_DEF("7400_v2.6", CPU_POWERPC_7400_v26, 7400, + "PowerPC 7400 v2.6 (G4)") + POWERPC_DEF("7400_v2.7", CPU_POWERPC_7400_v27, 7400, + "PowerPC 7400 v2.7 (G4)") + POWERPC_DEF("7400_v2.8", CPU_POWERPC_7400_v28, 7400, + "PowerPC 7400 v2.8 (G4)") + POWERPC_DEF("7400_v2.9", CPU_POWERPC_7400_v29, 7400, + "PowerPC 7400 v2.9 (G4)") + POWERPC_DEF("7410_v1.0", CPU_POWERPC_7410_v10, 7410, + "PowerPC 7410 v1.0 (G4)") + POWERPC_DEF("7410_v1.1", CPU_POWERPC_7410_v11, 7410, + "PowerPC 7410 v1.1 (G4)") + POWERPC_DEF("7410_v1.2", CPU_POWERPC_7410_v12, 7410, + "PowerPC 7410 v1.2 (G4)") + POWERPC_DEF("7410_v1.3", CPU_POWERPC_7410_v13, 7410, + "PowerPC 7410 v1.3 (G4)") + POWERPC_DEF("7410_v1.4", CPU_POWERPC_7410_v14, 7410, + "PowerPC 7410 v1.4 (G4)") + POWERPC_DEF("7448_v1.0", CPU_POWERPC_7448_v10, 7400, + "PowerPC 7448 v1.0 (G4)") + POWERPC_DEF("7448_v1.1", CPU_POWERPC_7448_v11, 7400, + "PowerPC 7448 v1.1 (G4)") + POWERPC_DEF("7448_v2.0", CPU_POWERPC_7448_v20, 7400, + "PowerPC 7448 v2.0 (G4)") + POWERPC_DEF("7448_v2.1", CPU_POWERPC_7448_v21, 7400, + "PowerPC 7448 v2.1 (G4)") + POWERPC_DEF("7450_v1.0", CPU_POWERPC_7450_v10, 7450, + "PowerPC 7450 v1.0 (G4)") + POWERPC_DEF("7450_v1.1", CPU_POWERPC_7450_v11, 7450, + "PowerPC 7450 v1.1 (G4)") + POWERPC_DEF("7450_v1.2", CPU_POWERPC_7450_v12, 7450, + "PowerPC 7450 v1.2 (G4)") + POWERPC_DEF("7450_v2.0", CPU_POWERPC_7450_v20, 7450, + "PowerPC 7450 v2.0 (G4)") + POWERPC_DEF("7450_v2.1", CPU_POWERPC_7450_v21, 7450, + "PowerPC 7450 v2.1 (G4)") + POWERPC_DEF("7441_v2.1", CPU_POWERPC_7450_v21, 7440, + "PowerPC 7441 v2.1 (G4)") + POWERPC_DEF("7441_v2.3", CPU_POWERPC_74x1_v23, 7440, + "PowerPC 7441 v2.3 (G4)") + POWERPC_DEF("7451_v2.3", CPU_POWERPC_74x1_v23, 7450, + "PowerPC 7451 v2.3 (G4)") + POWERPC_DEF("7441_v2.10", CPU_POWERPC_74x1_v210, 7440, + "PowerPC 7441 v2.10 (G4)") + POWERPC_DEF("7451_v2.10", CPU_POWERPC_74x1_v210, 7450, + "PowerPC 7451 v2.10 (G4)") + POWERPC_DEF("7445_v1.0", CPU_POWERPC_74x5_v10, 7445, + "PowerPC 7445 v1.0 (G4)") + POWERPC_DEF("7455_v1.0", CPU_POWERPC_74x5_v10, 7455, + "PowerPC 7455 v1.0 (G4)") + POWERPC_DEF("7445_v2.1", CPU_POWERPC_74x5_v21, 7445, + "PowerPC 7445 v2.1 (G4)") + POWERPC_DEF("7455_v2.1", CPU_POWERPC_74x5_v21, 7455, + "PowerPC 7455 v2.1 (G4)") + POWERPC_DEF("7445_v3.2", CPU_POWERPC_74x5_v32, 7445, + "PowerPC 7445 v3.2 (G4)") + POWERPC_DEF("7455_v3.2", CPU_POWERPC_74x5_v32, 7455, + "PowerPC 7455 v3.2 (G4)") + POWERPC_DEF("7445_v3.3", CPU_POWERPC_74x5_v33, 7445, + "PowerPC 7445 v3.3 (G4)") + POWERPC_DEF("7455_v3.3", CPU_POWERPC_74x5_v33, 7455, + "PowerPC 7455 v3.3 (G4)") + POWERPC_DEF("7445_v3.4", CPU_POWERPC_74x5_v34, 7445, + "PowerPC 7445 v3.4 (G4)") + POWERPC_DEF("7455_v3.4", CPU_POWERPC_74x5_v34, 7455, + "PowerPC 7455 v3.4 (G4)") + POWERPC_DEF("7447_v1.0", CPU_POWERPC_74x7_v10, 7445, + "PowerPC 7447 v1.0 (G4)") + POWERPC_DEF("7457_v1.0", CPU_POWERPC_74x7_v10, 7455, + "PowerPC 7457 v1.0 (G4)") + POWERPC_DEF("7447_v1.1", CPU_POWERPC_74x7_v11, 7445, + "PowerPC 7447 v1.1 (G4)") + POWERPC_DEF("7457_v1.1", CPU_POWERPC_74x7_v11, 7455, + "PowerPC 7457 v1.1 (G4)") + POWERPC_DEF("7457_v1.2", CPU_POWERPC_74x7_v12, 7455, + "PowerPC 7457 v1.2 (G4)") + POWERPC_DEF("7447A_v1.0", CPU_POWERPC_74x7A_v10, 7445, + "PowerPC 7447A v1.0 (G4)") + POWERPC_DEF("7457A_v1.0", CPU_POWERPC_74x7A_v10, 7455, + "PowerPC 7457A v1.0 (G4)") + POWERPC_DEF("7447A_v1.1", CPU_POWERPC_74x7A_v11, 7445, + "PowerPC 7447A v1.1 (G4)") + POWERPC_DEF("7457A_v1.1", CPU_POWERPC_74x7A_v11, 7455, + "PowerPC 7457A v1.1 (G4)") + POWERPC_DEF("7447A_v1.2", CPU_POWERPC_74x7A_v12, 7445, + "PowerPC 7447A v1.2 (G4)") + POWERPC_DEF("7457A_v1.2", CPU_POWERPC_74x7A_v12, 7455, + "PowerPC 7457A v1.2 (G4)") + /* 64 bits PowerPC */ +#if defined (TARGET_PPC64) + POWERPC_DEF("620", CPU_POWERPC_620, 620, + "PowerPC 620") +#if defined(TODO) + POWERPC_DEF("630", CPU_POWERPC_630, 630, + "PowerPC 630 (POWER3)") +#endif +#if defined(TODO) + POWERPC_DEF("631", CPU_POWERPC_631, 631, + "PowerPC 631 (Power 3+)") +#endif +#if defined(TODO) + POWERPC_DEF("POWER4", CPU_POWERPC_POWER4, POWER4, + "POWER4") +#endif +#if defined(TODO) + POWERPC_DEF("POWER4+", CPU_POWERPC_POWER4P, POWER4P, + "POWER4p") +#endif +#if defined(TODO) + POWERPC_DEF("POWER5", CPU_POWERPC_POWER5, POWER5, + "POWER5") + POWERPC_DEF("POWER5gr", CPU_POWERPC_POWER5GR, POWER5, + "POWER5GR") +#endif +#if defined(TODO) + POWERPC_DEF("POWER5+", CPU_POWERPC_POWER5P, POWER5P, + "POWER5+") + POWERPC_DEF("POWER5gs", CPU_POWERPC_POWER5GS, POWER5P, + "POWER5GS") +#endif +#if defined(TODO) + POWERPC_DEF("POWER6", CPU_POWERPC_POWER6, POWER6, + "POWER6") + POWERPC_DEF("POWER6_5", CPU_POWERPC_POWER6_5, POWER5, + "POWER6 running in POWER5 mode") + POWERPC_DEF("POWER6A", CPU_POWERPC_POWER6A, POWER6, + "POWER6A") +#endif + POWERPC_DEF("POWER7_v2.0", CPU_POWERPC_POWER7_v20, POWER7, + "POWER7 v2.0") + POWERPC_DEF("POWER7_v2.1", CPU_POWERPC_POWER7_v21, POWER7, + "POWER7 v2.1") + POWERPC_DEF("POWER7_v2.3", CPU_POWERPC_POWER7_v23, POWER7, + "POWER7 v2.3") + POWERPC_DEF("970", CPU_POWERPC_970, 970, + "PowerPC 970") + POWERPC_DEF("970fx_v1.0", CPU_POWERPC_970FX_v10, 970FX, + "PowerPC 970FX v1.0 (G5)") + POWERPC_DEF("970fx_v2.0", CPU_POWERPC_970FX_v20, 970FX, + "PowerPC 970FX v2.0 (G5)") + POWERPC_DEF("970fx_v2.1", CPU_POWERPC_970FX_v21, 970FX, + "PowerPC 970FX v2.1 (G5)") + POWERPC_DEF("970fx_v3.0", CPU_POWERPC_970FX_v30, 970FX, + "PowerPC 970FX v3.0 (G5)") + POWERPC_DEF("970fx_v3.1", CPU_POWERPC_970FX_v31, 970FX, + "PowerPC 970FX v3.1 (G5)") + POWERPC_DEF("970gx", CPU_POWERPC_970GX, 970GX, + "PowerPC 970GX (G5)") + POWERPC_DEF("970mp_v1.0", CPU_POWERPC_970MP_v10, 970MP, + "PowerPC 970MP v1.0") + POWERPC_DEF("970mp_v1.1", CPU_POWERPC_970MP_v11, 970MP, + "PowerPC 970MP v1.1") +#if defined(TODO) + POWERPC_DEF("Cell", CPU_POWERPC_CELL, 970, + "PowerPC Cell") +#endif +#if defined(TODO) + POWERPC_DEF("Cell_v1.0", CPU_POWERPC_CELL_v10, 970, + "PowerPC Cell v1.0") +#endif +#if defined(TODO) + POWERPC_DEF("Cell_v2.0", CPU_POWERPC_CELL_v20, 970, + "PowerPC Cell v2.0") +#endif +#if defined(TODO) + POWERPC_DEF("Cell_v3.0", CPU_POWERPC_CELL_v30, 970, + "PowerPC Cell v3.0") +#endif +#if defined(TODO) + POWERPC_DEF("Cell_v3.1", CPU_POWERPC_CELL_v31, 970, + "PowerPC Cell v3.1") +#endif +#if defined(TODO) + POWERPC_DEF("Cell_v3.2", CPU_POWERPC_CELL_v32, 970, + "PowerPC Cell v3.2") +#endif +#if defined(TODO) + /* This one seems to support the whole POWER2 instruction set + * and the PowerPC 64 one. + */ + /* What about A10 & A30 ? */ + POWERPC_DEF("RS64", CPU_POWERPC_RS64, RS64, + "RS64 (Apache/A35)") +#endif +#if defined(TODO) + POWERPC_DEF("RS64-II", CPU_POWERPC_RS64II, RS64, + "RS64-II (NorthStar/A50)") +#endif +#if defined(TODO) + POWERPC_DEF("RS64-III", CPU_POWERPC_RS64III, RS64, + "RS64-III (Pulsar)") +#endif +#if defined(TODO) + POWERPC_DEF("RS64-IV", CPU_POWERPC_RS64IV, RS64, + "RS64-IV (IceStar/IStar/SStar)") +#endif +#endif /* defined (TARGET_PPC64) */ + /* POWER */ +#if defined(TODO) + POWERPC_DEF("POWER", CPU_POWERPC_POWER, POWER, + "Original POWER") +#endif +#if defined(TODO) + POWERPC_DEF("POWER2", CPU_POWERPC_POWER2, POWER, + "POWER2") +#endif + /* PA semi cores */ +#if defined(TODO) + POWERPC_DEF("PA6T", CPU_POWERPC_PA6T, PA6T, + "PA PA6T") +#endif + + +/***************************************************************************/ +/* PowerPC CPU aliases */ + +const PowerPCCPUAlias ppc_cpu_aliases[] = { + { "403", "403GC" }, + { "405", "405D4" }, + { "405CR", "405CRc" }, + { "405GP", "405GPd" }, + { "405GPe", "405CRc" }, + { "x2vp7", "x2vp4" }, + { "x2vp50", "x2vp20" }, + + { "440EP", "440EPb" }, + { "440GP", "440GPc" }, + { "440GR", "440GRa" }, + { "440GX", "440GXf" }, + + { "RCPU", "MPC5xx" }, + /* MPC5xx microcontrollers */ + { "MGT560", "MPC5xx" }, + { "MPC509", "MPC5xx" }, + { "MPC533", "MPC5xx" }, + { "MPC534", "MPC5xx" }, + { "MPC555", "MPC5xx" }, + { "MPC556", "MPC5xx" }, + { "MPC560", "MPC5xx" }, + { "MPC561", "MPC5xx" }, + { "MPC562", "MPC5xx" }, + { "MPC563", "MPC5xx" }, + { "MPC564", "MPC5xx" }, + { "MPC565", "MPC5xx" }, + { "MPC566", "MPC5xx" }, + + { "PowerQUICC", "MPC8xx" }, + /* MPC8xx microcontrollers */ + { "MGT823", "MPC8xx" }, + { "MPC821", "MPC8xx" }, + { "MPC823", "MPC8xx" }, + { "MPC850", "MPC8xx" }, + { "MPC852T", "MPC8xx" }, + { "MPC855T", "MPC8xx" }, + { "MPC857", "MPC8xx" }, + { "MPC859", "MPC8xx" }, + { "MPC860", "MPC8xx" }, + { "MPC862", "MPC8xx" }, + { "MPC866", "MPC8xx" }, + { "MPC870", "MPC8xx" }, + { "MPC875", "MPC8xx" }, + { "MPC880", "MPC8xx" }, + { "MPC885", "MPC8xx" }, + + /* PowerPC MPC603 microcontrollers */ + { "MPC8240", "603" }, + + { "MPC52xx", "MPC5200" }, + { "MPC5200", "MPC5200_v12" }, + { "MPC5200B", "MPC5200B_v21" }, + + { "MPC82xx", "MPC8280" }, + { "PowerQUICC-II", "MPC82xx" }, + { "MPC8241", "G2HiP4" }, + { "MPC8245", "G2HiP4" }, + { "MPC8247", "G2leGP3" }, + { "MPC8248", "G2leGP3" }, + { "MPC8250", "MPC8250_HiP4" }, + { "MPC8250_HiP3", "G2HiP3" }, + { "MPC8250_HiP4", "G2HiP4" }, + { "MPC8255", "MPC8255_HiP4" }, + { "MPC8255_HiP3", "G2HiP3" }, + { "MPC8255_HiP4", "G2HiP4" }, + { "MPC8260", "MPC8260_HiP4" }, + { "MPC8260_HiP3", "G2HiP3" }, + { "MPC8260_HiP4", "G2HiP4" }, + { "MPC8264", "MPC8264_HiP4" }, + { "MPC8264_HiP3", "G2HiP3" }, + { "MPC8264_HiP4", "G2HiP4" }, + { "MPC8265", "MPC8265_HiP4" }, + { "MPC8265_HiP3", "G2HiP3" }, + { "MPC8265_HiP4", "G2HiP4" }, + { "MPC8266", "MPC8266_HiP4" }, + { "MPC8266_HiP3", "G2HiP3" }, + { "MPC8266_HiP4", "G2HiP4" }, + { "MPC8270", "G2leGP3" }, + { "MPC8271", "G2leGP3" }, + { "MPC8272", "G2leGP3" }, + { "MPC8275", "G2leGP3" }, + { "MPC8280", "G2leGP3" }, + { "e200", "e200z6" }, + { "e300", "e300c3" }, + { "MPC8347", "MPC8347T" }, + { "MPC8347A", "MPC8347AT" }, + { "MPC8347E", "MPC8347ET" }, + { "MPC8347EA", "MPC8347EAT" }, + { "e500", "e500v2_v22" }, + { "e500v1", "e500_v20" }, + { "e500v2", "e500v2_v22" }, + { "MPC8533", "MPC8533_v11" }, + { "MPC8533E", "MPC8533E_v11" }, + { "MPC8540", "MPC8540_v21" }, + { "MPC8541", "MPC8541_v11" }, + { "MPC8541E", "MPC8541E_v11" }, + { "MPC8543", "MPC8543_v21" }, + { "MPC8543E", "MPC8543E_v21" }, + { "MPC8544", "MPC8544_v11" }, + { "MPC8544E", "MPC8544E_v11" }, + { "MPC8545", "MPC8545_v21" }, + { "MPC8545E", "MPC8545E_v21" }, + { "MPC8547E", "MPC8547E_v21" }, + { "MPC8548", "MPC8548_v21" }, + { "MPC8548E", "MPC8548E_v21" }, + { "MPC8555", "MPC8555_v11" }, + { "MPC8555E", "MPC8555E_v11" }, + { "MPC8560", "MPC8560_v21" }, + { "601", "601_v2" }, + { "601v", "601_v2" }, + { "Vanilla", "603" }, + { "603e", "603e_v4.1" }, + { "Stretch", "603e" }, + { "Vaillant", "603e7v" }, + { "603r", "603e7t" }, + { "Goldeneye", "603r" }, + { "604e", "604e_v2.4" }, + { "Sirocco", "604e" }, + { "Mach5", "604r" }, + { "740", "740_v3.1" }, + { "Arthur", "740" }, + { "750", "750_v3.1" }, + { "Typhoon", "750" }, + { "G3", "750" }, + { "Conan/Doyle", "750p" }, + { "750cl", "750cl_v2.0" }, + { "750cx", "750cx_v2.2" }, + { "750cxe", "750cxe_v3.1b" }, + { "750fx", "750fx_v2.3" }, + { "750gx", "750gx_v1.2" }, + { "750l", "750l_v3.2" }, + { "LoneStar", "750l" }, + { "745", "745_v2.8" }, + { "755", "755_v2.8" }, + { "Goldfinger", "755" }, + { "7400", "7400_v2.9" }, + { "Max", "7400" }, + { "G4", "7400" }, + { "7410", "7410_v1.4" }, + { "Nitro", "7410" }, + { "7448", "7448_v2.1" }, + { "7450", "7450_v2.1" }, + { "Vger", "7450" }, + { "7441", "7441_v2.3" }, + { "7451", "7451_v2.3" }, + { "7445", "7445_v3.2" }, + { "7455", "7455_v3.2" }, + { "Apollo6", "7455" }, + { "7447", "7447_v1.2" }, + { "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" }, + { "POWER7", "POWER7_v2.3" }, + { "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" }, + + /* Generic PowerPCs */ +#if defined(TARGET_PPC64) + { "ppc64", "970fx" }, +#endif + { "ppc32", "604" }, + { "ppc", "ppc32" }, + { "default", "ppc" }, + { NULL, NULL } +}; diff --git a/target-ppc/cpu-models.h b/target-ppc/cpu-models.h new file mode 100644 index 0000000000..a94f835121 --- /dev/null +++ b/target-ppc/cpu-models.h @@ -0,0 +1,741 @@ +/* + * PowerPC CPU initialization for qemu. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * Copyright 2011 Freescale Semiconductor, Inc. + * Copyright 2013 SUSE LINUX Products GmbH + * + * 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 . + */ +#ifndef TARGET_PPC_CPU_MODELS_H +#define TARGET_PPC_CPU_MODELS_H + +/** + * PowerPCCPUAlias: + * @alias: The alias name. + * @model: The CPU model @alias refers to. + * + * A mapping entry from CPU @alias to CPU @model. + */ +typedef struct PowerPCCPUAlias { + const char *alias; + const char *model; +} PowerPCCPUAlias; + +extern const PowerPCCPUAlias ppc_cpu_aliases[]; + +/*****************************************************************************/ +/* PVR definitions for most known PowerPC */ +enum { + /* PowerPC 401 family */ + /* Generic PowerPC 401 */ +#define CPU_POWERPC_401 CPU_POWERPC_401G2 + /* PowerPC 401 cores */ + CPU_POWERPC_401A1 = 0x00210000, + CPU_POWERPC_401B2 = 0x00220000, +#if 0 + CPU_POWERPC_401B3 = xxx, +#endif + CPU_POWERPC_401C2 = 0x00230000, + CPU_POWERPC_401D2 = 0x00240000, + CPU_POWERPC_401E2 = 0x00250000, + CPU_POWERPC_401F2 = 0x00260000, + CPU_POWERPC_401G2 = 0x00270000, + /* PowerPC 401 microcontrolers */ +#if 0 + CPU_POWERPC_401GF = xxx, +#endif +#define CPU_POWERPC_IOP480 CPU_POWERPC_401B2 + /* IBM Processor for Network Resources */ + CPU_POWERPC_COBRA = 0x10100000, /* XXX: 405 ? */ +#if 0 + CPU_POWERPC_XIPCHIP = xxx, +#endif + /* PowerPC 403 family */ + /* PowerPC 403 microcontrollers */ + CPU_POWERPC_403GA = 0x00200011, + CPU_POWERPC_403GB = 0x00200100, + CPU_POWERPC_403GC = 0x00200200, + CPU_POWERPC_403GCX = 0x00201400, +#if 0 + CPU_POWERPC_403GP = xxx, +#endif + /* PowerPC 405 family */ + /* PowerPC 405 cores */ +#if 0 + CPU_POWERPC_405A3 = xxx, +#endif +#if 0 + CPU_POWERPC_405A4 = xxx, +#endif +#if 0 + CPU_POWERPC_405B3 = xxx, +#endif +#if 0 + CPU_POWERPC_405B4 = xxx, +#endif +#if 0 + CPU_POWERPC_405C3 = xxx, +#endif +#if 0 + CPU_POWERPC_405C4 = xxx, +#endif + CPU_POWERPC_405D2 = 0x20010000, +#if 0 + CPU_POWERPC_405D3 = xxx, +#endif + CPU_POWERPC_405D4 = 0x41810000, +#if 0 + CPU_POWERPC_405D5 = xxx, +#endif +#if 0 + CPU_POWERPC_405E4 = xxx, +#endif +#if 0 + CPU_POWERPC_405F4 = xxx, +#endif +#if 0 + CPU_POWERPC_405F5 = xxx, +#endif +#if 0 + CPU_POWERPC_405F6 = xxx, +#endif + /* PowerPC 405 microcontrolers */ + /* XXX: missing 0x200108a0 */ + CPU_POWERPC_405CRa = 0x40110041, + CPU_POWERPC_405CRb = 0x401100C5, + CPU_POWERPC_405CRc = 0x40110145, + CPU_POWERPC_405EP = 0x51210950, +#if 0 + CPU_POWERPC_405EXr = xxx, +#endif + CPU_POWERPC_405EZ = 0x41511460, /* 0x51210950 ? */ +#if 0 + CPU_POWERPC_405FX = xxx, +#endif + CPU_POWERPC_405GPa = 0x40110000, + CPU_POWERPC_405GPb = 0x40110040, + CPU_POWERPC_405GPc = 0x40110082, + CPU_POWERPC_405GPd = 0x401100C4, + CPU_POWERPC_405GPR = 0x50910951, +#if 0 + CPU_POWERPC_405H = xxx, +#endif +#if 0 + CPU_POWERPC_405L = xxx, +#endif + CPU_POWERPC_405LP = 0x41F10000, +#if 0 + CPU_POWERPC_405PM = xxx, +#endif +#if 0 + CPU_POWERPC_405PS = xxx, +#endif +#if 0 + CPU_POWERPC_405S = xxx, +#endif + /* IBM network processors */ + CPU_POWERPC_NPE405H = 0x414100C0, + CPU_POWERPC_NPE405H2 = 0x41410140, + CPU_POWERPC_NPE405L = 0x416100C0, + CPU_POWERPC_NPE4GS3 = 0x40B10000, +#if 0 + CPU_POWERPC_NPCxx1 = xxx, +#endif +#if 0 + CPU_POWERPC_NPR161 = xxx, +#endif +#if 0 + CPU_POWERPC_LC77700 = xxx, +#endif + /* IBM STBxxx (PowerPC 401/403/405 core based microcontrollers) */ +#if 0 + CPU_POWERPC_STB01000 = xxx, +#endif +#if 0 + CPU_POWERPC_STB01010 = xxx, +#endif +#if 0 + CPU_POWERPC_STB0210 = xxx, /* 401B3 */ +#endif + CPU_POWERPC_STB03 = 0x40310000, /* 0x40130000 ? */ +#if 0 + CPU_POWERPC_STB043 = xxx, +#endif +#if 0 + CPU_POWERPC_STB045 = xxx, +#endif + CPU_POWERPC_STB04 = 0x41810000, + CPU_POWERPC_STB25 = 0x51510950, +#if 0 + CPU_POWERPC_STB130 = xxx, +#endif + /* Xilinx cores */ + CPU_POWERPC_X2VP4 = 0x20010820, + CPU_POWERPC_X2VP20 = 0x20010860, +#if 0 + CPU_POWERPC_ZL10310 = xxx, +#endif +#if 0 + CPU_POWERPC_ZL10311 = xxx, +#endif +#if 0 + CPU_POWERPC_ZL10320 = xxx, +#endif +#if 0 + CPU_POWERPC_ZL10321 = xxx, +#endif + /* PowerPC 440 family */ + /* Generic PowerPC 440 */ +#define CPU_POWERPC_440 CPU_POWERPC_440GXf + /* PowerPC 440 cores */ +#if 0 + CPU_POWERPC_440A4 = xxx, +#endif + CPU_POWERPC_440_XILINX = 0x7ff21910, +#if 0 + CPU_POWERPC_440A5 = xxx, +#endif +#if 0 + CPU_POWERPC_440B4 = xxx, +#endif +#if 0 + CPU_POWERPC_440F5 = xxx, +#endif +#if 0 + CPU_POWERPC_440G5 = xxx, +#endif +#if 0 + CPU_POWERPC_440H4 = xxx, +#endif +#if 0 + CPU_POWERPC_440H6 = xxx, +#endif + /* PowerPC 440 microcontrolers */ + CPU_POWERPC_440EPa = 0x42221850, + CPU_POWERPC_440EPb = 0x422218D3, + CPU_POWERPC_440GPb = 0x40120440, + CPU_POWERPC_440GPc = 0x40120481, +#define CPU_POWERPC_440GRa CPU_POWERPC_440EPb + CPU_POWERPC_440GRX = 0x200008D0, +#define CPU_POWERPC_440EPX CPU_POWERPC_440GRX + CPU_POWERPC_440GXa = 0x51B21850, + CPU_POWERPC_440GXb = 0x51B21851, + CPU_POWERPC_440GXc = 0x51B21892, + CPU_POWERPC_440GXf = 0x51B21894, +#if 0 + CPU_POWERPC_440S = xxx, +#endif + CPU_POWERPC_440SP = 0x53221850, + CPU_POWERPC_440SP2 = 0x53221891, + CPU_POWERPC_440SPE = 0x53421890, + /* PowerPC 460 family */ +#if 0 + /* Generic PowerPC 464 */ +#define CPU_POWERPC_464 CPU_POWERPC_464H90 +#endif + /* PowerPC 464 microcontrolers */ +#if 0 + CPU_POWERPC_464H90 = xxx, +#endif +#if 0 + CPU_POWERPC_464H90FP = xxx, +#endif + /* Freescale embedded PowerPC cores */ + /* PowerPC MPC 5xx cores (aka RCPU) */ + CPU_POWERPC_MPC5xx = 0x00020020, + /* PowerPC MPC 8xx cores (aka PowerQUICC) */ + CPU_POWERPC_MPC8xx = 0x00500000, + /* G2 cores (aka PowerQUICC-II) */ + CPU_POWERPC_G2 = 0x00810011, + CPU_POWERPC_G2H4 = 0x80811010, + CPU_POWERPC_G2gp = 0x80821010, + CPU_POWERPC_G2ls = 0x90810010, + CPU_POWERPC_MPC603 = 0x00810100, + CPU_POWERPC_G2_HIP3 = 0x00810101, + CPU_POWERPC_G2_HIP4 = 0x80811014, + /* G2_LE core (aka PowerQUICC-II) */ + CPU_POWERPC_G2LE = 0x80820010, + CPU_POWERPC_G2LEgp = 0x80822010, + CPU_POWERPC_G2LEls = 0xA0822010, + CPU_POWERPC_G2LEgp1 = 0x80822011, + CPU_POWERPC_G2LEgp3 = 0x80822013, + /* MPC52xx microcontrollers */ + /* XXX: MPC 5121 ? */ +#define CPU_POWERPC_MPC5200_v10 CPU_POWERPC_G2LEgp1 +#define CPU_POWERPC_MPC5200_v11 CPU_POWERPC_G2LEgp1 +#define CPU_POWERPC_MPC5200_v12 CPU_POWERPC_G2LEgp1 +#define CPU_POWERPC_MPC5200B_v20 CPU_POWERPC_G2LEgp1 +#define CPU_POWERPC_MPC5200B_v21 CPU_POWERPC_G2LEgp1 + /* e200 family */ + /* e200 cores */ +#if 0 + CPU_POWERPC_e200z0 = xxx, +#endif +#if 0 + CPU_POWERPC_e200z1 = xxx, +#endif +#if 0 /* ? */ + CPU_POWERPC_e200z3 = 0x81120000, +#endif + CPU_POWERPC_e200z5 = 0x81000000, + CPU_POWERPC_e200z6 = 0x81120000, + /* MPC55xx microcontrollers */ +#define CPU_POWERPC_MPC55xx CPU_POWERPC_MPC5567 +#if 0 +#define CPU_POWERPC_MPC5514E CPU_POWERPC_MPC5514E_v1 +#define CPU_POWERPC_MPC5514E_v0 CPU_POWERPC_e200z0 +#define CPU_POWERPC_MPC5514E_v1 CPU_POWERPC_e200z1 +#define CPU_POWERPC_MPC5514G CPU_POWERPC_MPC5514G_v1 +#define CPU_POWERPC_MPC5514G_v0 CPU_POWERPC_e200z0 +#define CPU_POWERPC_MPC5514G_v1 CPU_POWERPC_e200z1 +#define CPU_POWERPC_MPC5515S CPU_POWERPC_e200z1 +#define CPU_POWERPC_MPC5516E CPU_POWERPC_MPC5516E_v1 +#define CPU_POWERPC_MPC5516E_v0 CPU_POWERPC_e200z0 +#define CPU_POWERPC_MPC5516E_v1 CPU_POWERPC_e200z1 +#define CPU_POWERPC_MPC5516G CPU_POWERPC_MPC5516G_v1 +#define CPU_POWERPC_MPC5516G_v0 CPU_POWERPC_e200z0 +#define CPU_POWERPC_MPC5516G_v1 CPU_POWERPC_e200z1 +#define CPU_POWERPC_MPC5516S CPU_POWERPC_e200z1 +#endif +#if 0 +#define CPU_POWERPC_MPC5533 CPU_POWERPC_e200z3 +#define CPU_POWERPC_MPC5534 CPU_POWERPC_e200z3 +#endif +#define CPU_POWERPC_MPC5553 CPU_POWERPC_e200z6 +#define CPU_POWERPC_MPC5554 CPU_POWERPC_e200z6 +#define CPU_POWERPC_MPC5561 CPU_POWERPC_e200z6 +#define CPU_POWERPC_MPC5565 CPU_POWERPC_e200z6 +#define CPU_POWERPC_MPC5566 CPU_POWERPC_e200z6 +#define CPU_POWERPC_MPC5567 CPU_POWERPC_e200z6 + /* e300 family */ + /* e300 cores */ + CPU_POWERPC_e300c1 = 0x00830010, + CPU_POWERPC_e300c2 = 0x00840010, + CPU_POWERPC_e300c3 = 0x00850010, + CPU_POWERPC_e300c4 = 0x00860010, + /* MPC83xx microcontrollers */ +#define CPU_POWERPC_MPC831x CPU_POWERPC_e300c3 +#define CPU_POWERPC_MPC832x CPU_POWERPC_e300c2 +#define CPU_POWERPC_MPC834x CPU_POWERPC_e300c1 +#define CPU_POWERPC_MPC835x CPU_POWERPC_e300c1 +#define CPU_POWERPC_MPC836x CPU_POWERPC_e300c1 +#define CPU_POWERPC_MPC837x CPU_POWERPC_e300c4 + /* e500 family */ + /* e500 cores */ +#define CPU_POWERPC_e500 CPU_POWERPC_e500v2_v22 + CPU_POWERPC_e500v1_v10 = 0x80200010, + CPU_POWERPC_e500v1_v20 = 0x80200020, + CPU_POWERPC_e500v2_v10 = 0x80210010, + CPU_POWERPC_e500v2_v11 = 0x80210011, + CPU_POWERPC_e500v2_v20 = 0x80210020, + CPU_POWERPC_e500v2_v21 = 0x80210021, + CPU_POWERPC_e500v2_v22 = 0x80210022, + CPU_POWERPC_e500v2_v30 = 0x80210030, + CPU_POWERPC_e500mc = 0x80230020, + CPU_POWERPC_e5500 = 0x80240020, + /* MPC85xx microcontrollers */ +#define CPU_POWERPC_MPC8533_v10 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8533_v11 CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8533E_v10 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8533E_v11 CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8540_v10 CPU_POWERPC_e500v1_v10 +#define CPU_POWERPC_MPC8540_v20 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8540_v21 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8541_v10 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8541_v11 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8541E_v10 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8541E_v11 CPU_POWERPC_e500v1_v20 +#define CPU_POWERPC_MPC8543_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8543_v11 CPU_POWERPC_e500v2_v11 +#define CPU_POWERPC_MPC8543_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8543_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8543E_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8543E_v11 CPU_POWERPC_e500v2_v11 +#define CPU_POWERPC_MPC8543E_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8543E_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8544_v10 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8544_v11 CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8544E_v11 CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8544E_v10 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8545_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8545_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8545_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8545E_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8545E_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8545E_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8547E_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8547E_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8547E_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8548_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8548_v11 CPU_POWERPC_e500v2_v11 +#define CPU_POWERPC_MPC8548_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8548_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8548E_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8548E_v11 CPU_POWERPC_e500v2_v11 +#define CPU_POWERPC_MPC8548E_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8548E_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8555_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8555_v11 CPU_POWERPC_e500v2_v11 +#define CPU_POWERPC_MPC8555E_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8555E_v11 CPU_POWERPC_e500v2_v11 +#define CPU_POWERPC_MPC8560_v10 CPU_POWERPC_e500v2_v10 +#define CPU_POWERPC_MPC8560_v20 CPU_POWERPC_e500v2_v20 +#define CPU_POWERPC_MPC8560_v21 CPU_POWERPC_e500v2_v21 +#define CPU_POWERPC_MPC8567 CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8567E CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8568 CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8568E CPU_POWERPC_e500v2_v22 +#define CPU_POWERPC_MPC8572 CPU_POWERPC_e500v2_v30 +#define CPU_POWERPC_MPC8572E CPU_POWERPC_e500v2_v30 + /* e600 family */ + /* e600 cores */ + CPU_POWERPC_e600 = 0x80040010, + /* MPC86xx microcontrollers */ +#define CPU_POWERPC_MPC8610 CPU_POWERPC_e600 +#define CPU_POWERPC_MPC8641 CPU_POWERPC_e600 +#define CPU_POWERPC_MPC8641D CPU_POWERPC_e600 + /* PowerPC 6xx cores */ + CPU_POWERPC_601_v0 = 0x00010001, + CPU_POWERPC_601_v1 = 0x00010001, + CPU_POWERPC_601_v2 = 0x00010002, + CPU_POWERPC_602 = 0x00050100, + CPU_POWERPC_603 = 0x00030100, + CPU_POWERPC_603E_v11 = 0x00060101, + CPU_POWERPC_603E_v12 = 0x00060102, + CPU_POWERPC_603E_v13 = 0x00060103, + CPU_POWERPC_603E_v14 = 0x00060104, + CPU_POWERPC_603E_v22 = 0x00060202, + CPU_POWERPC_603E_v3 = 0x00060300, + CPU_POWERPC_603E_v4 = 0x00060400, + CPU_POWERPC_603E_v41 = 0x00060401, + CPU_POWERPC_603E7t = 0x00071201, + CPU_POWERPC_603E7v = 0x00070100, + CPU_POWERPC_603E7v1 = 0x00070101, + CPU_POWERPC_603E7v2 = 0x00070201, + CPU_POWERPC_603E7 = 0x00070200, + CPU_POWERPC_603P = 0x00070000, + /* XXX: missing 0x00040303 (604) */ + CPU_POWERPC_604 = 0x00040103, + /* XXX: missing 0x00091203 */ + /* XXX: missing 0x00092110 */ + /* XXX: missing 0x00092120 */ + CPU_POWERPC_604E_v10 = 0x00090100, + CPU_POWERPC_604E_v22 = 0x00090202, + CPU_POWERPC_604E_v24 = 0x00090204, + /* XXX: missing 0x000a0100 */ + /* XXX: missing 0x00093102 */ + CPU_POWERPC_604R = 0x000a0101, +#if 0 + CPU_POWERPC_604EV = xxx, /* XXX: same as 604R ? */ +#endif + /* PowerPC 740/750 cores (aka G3) */ + /* XXX: missing 0x00084202 */ + CPU_POWERPC_7x0_v10 = 0x00080100, + CPU_POWERPC_7x0_v20 = 0x00080200, + CPU_POWERPC_7x0_v21 = 0x00080201, + CPU_POWERPC_7x0_v22 = 0x00080202, + CPU_POWERPC_7x0_v30 = 0x00080300, + CPU_POWERPC_7x0_v31 = 0x00080301, + CPU_POWERPC_740E = 0x00080100, + CPU_POWERPC_750E = 0x00080200, + CPU_POWERPC_7x0P = 0x10080000, + /* XXX: missing 0x00087010 (CL ?) */ + CPU_POWERPC_750CL_v10 = 0x00087200, + CPU_POWERPC_750CL_v20 = 0x00087210, /* aka rev E */ + CPU_POWERPC_750CX_v10 = 0x00082100, + CPU_POWERPC_750CX_v20 = 0x00082200, + CPU_POWERPC_750CX_v21 = 0x00082201, + CPU_POWERPC_750CX_v22 = 0x00082202, + CPU_POWERPC_750CXE_v21 = 0x00082211, + CPU_POWERPC_750CXE_v22 = 0x00082212, + CPU_POWERPC_750CXE_v23 = 0x00082213, + CPU_POWERPC_750CXE_v24 = 0x00082214, + CPU_POWERPC_750CXE_v24b = 0x00083214, + CPU_POWERPC_750CXE_v30 = 0x00082310, + CPU_POWERPC_750CXE_v31 = 0x00082311, + CPU_POWERPC_750CXE_v31b = 0x00083311, + CPU_POWERPC_750CXR = 0x00083410, + CPU_POWERPC_750FL = 0x70000203, + CPU_POWERPC_750FX_v10 = 0x70000100, + CPU_POWERPC_750FX_v20 = 0x70000200, + CPU_POWERPC_750FX_v21 = 0x70000201, + CPU_POWERPC_750FX_v22 = 0x70000202, + CPU_POWERPC_750FX_v23 = 0x70000203, + CPU_POWERPC_750GL = 0x70020102, + CPU_POWERPC_750GX_v10 = 0x70020100, + CPU_POWERPC_750GX_v11 = 0x70020101, + CPU_POWERPC_750GX_v12 = 0x70020102, + CPU_POWERPC_750L_v20 = 0x00088200, + CPU_POWERPC_750L_v21 = 0x00088201, + CPU_POWERPC_750L_v22 = 0x00088202, + CPU_POWERPC_750L_v30 = 0x00088300, + CPU_POWERPC_750L_v32 = 0x00088302, + /* PowerPC 745/755 cores */ + CPU_POWERPC_7x5_v10 = 0x00083100, + CPU_POWERPC_7x5_v11 = 0x00083101, + CPU_POWERPC_7x5_v20 = 0x00083200, + CPU_POWERPC_7x5_v21 = 0x00083201, + CPU_POWERPC_7x5_v22 = 0x00083202, /* aka D */ + CPU_POWERPC_7x5_v23 = 0x00083203, /* aka E */ + CPU_POWERPC_7x5_v24 = 0x00083204, + CPU_POWERPC_7x5_v25 = 0x00083205, + CPU_POWERPC_7x5_v26 = 0x00083206, + CPU_POWERPC_7x5_v27 = 0x00083207, + CPU_POWERPC_7x5_v28 = 0x00083208, +#if 0 + CPU_POWERPC_7x5P = xxx, +#endif + /* PowerPC 74xx cores (aka G4) */ + /* XXX: missing 0x000C1101 */ + CPU_POWERPC_7400_v10 = 0x000C0100, + CPU_POWERPC_7400_v11 = 0x000C0101, + CPU_POWERPC_7400_v20 = 0x000C0200, + CPU_POWERPC_7400_v21 = 0x000C0201, + CPU_POWERPC_7400_v22 = 0x000C0202, + CPU_POWERPC_7400_v26 = 0x000C0206, + CPU_POWERPC_7400_v27 = 0x000C0207, + CPU_POWERPC_7400_v28 = 0x000C0208, + CPU_POWERPC_7400_v29 = 0x000C0209, + CPU_POWERPC_7410_v10 = 0x800C1100, + CPU_POWERPC_7410_v11 = 0x800C1101, + CPU_POWERPC_7410_v12 = 0x800C1102, /* aka C */ + CPU_POWERPC_7410_v13 = 0x800C1103, /* aka D */ + CPU_POWERPC_7410_v14 = 0x800C1104, /* aka E */ + CPU_POWERPC_7448_v10 = 0x80040100, + CPU_POWERPC_7448_v11 = 0x80040101, + CPU_POWERPC_7448_v20 = 0x80040200, + CPU_POWERPC_7448_v21 = 0x80040201, + CPU_POWERPC_7450_v10 = 0x80000100, + CPU_POWERPC_7450_v11 = 0x80000101, + CPU_POWERPC_7450_v12 = 0x80000102, + CPU_POWERPC_7450_v20 = 0x80000200, /* aka A, B, C, D: 2.04 */ + CPU_POWERPC_7450_v21 = 0x80000201, /* aka E */ + CPU_POWERPC_74x1_v23 = 0x80000203, /* aka G: 2.3 */ + /* XXX: this entry might be a bug in some documentation */ + CPU_POWERPC_74x1_v210 = 0x80000210, /* aka G: 2.3 ? */ + CPU_POWERPC_74x5_v10 = 0x80010100, + /* XXX: missing 0x80010200 */ + CPU_POWERPC_74x5_v21 = 0x80010201, /* aka C: 2.1 */ + CPU_POWERPC_74x5_v32 = 0x80010302, + CPU_POWERPC_74x5_v33 = 0x80010303, /* aka F: 3.3 */ + CPU_POWERPC_74x5_v34 = 0x80010304, /* aka G: 3.4 */ + CPU_POWERPC_74x7_v10 = 0x80020100, /* aka A: 1.0 */ + CPU_POWERPC_74x7_v11 = 0x80020101, /* aka B: 1.1 */ + CPU_POWERPC_74x7_v12 = 0x80020102, /* aka C: 1.2 */ + CPU_POWERPC_74x7A_v10 = 0x80030100, /* aka A: 1.0 */ + CPU_POWERPC_74x7A_v11 = 0x80030101, /* aka B: 1.1 */ + CPU_POWERPC_74x7A_v12 = 0x80030102, /* aka C: 1.2 */ + /* 64 bits PowerPC */ +#if defined(TARGET_PPC64) + CPU_POWERPC_620 = 0x00140000, + CPU_POWERPC_630 = 0x00400000, + CPU_POWERPC_631 = 0x00410104, + CPU_POWERPC_POWER4 = 0x00350000, + CPU_POWERPC_POWER4P = 0x00380000, + /* XXX: missing 0x003A0201 */ + CPU_POWERPC_POWER5 = 0x003A0203, +#define CPU_POWERPC_POWER5GR CPU_POWERPC_POWER5 + CPU_POWERPC_POWER5P = 0x003B0000, +#define CPU_POWERPC_POWER5GS CPU_POWERPC_POWER5P + CPU_POWERPC_POWER6 = 0x003E0000, + CPU_POWERPC_POWER6_5 = 0x0F000001, /* POWER6 in POWER5 mode */ + CPU_POWERPC_POWER6A = 0x0F000002, + CPU_POWERPC_POWER7_v20 = 0x003F0200, + CPU_POWERPC_POWER7_v21 = 0x003F0201, + CPU_POWERPC_POWER7_v23 = 0x003F0203, + CPU_POWERPC_970 = 0x00390202, + CPU_POWERPC_970FX_v10 = 0x00391100, + CPU_POWERPC_970FX_v20 = 0x003C0200, + CPU_POWERPC_970FX_v21 = 0x003C0201, + CPU_POWERPC_970FX_v30 = 0x003C0300, + CPU_POWERPC_970FX_v31 = 0x003C0301, + CPU_POWERPC_970GX = 0x00450000, + CPU_POWERPC_970MP_v10 = 0x00440100, + CPU_POWERPC_970MP_v11 = 0x00440101, +#define CPU_POWERPC_CELL CPU_POWERPC_CELL_v32 + CPU_POWERPC_CELL_v10 = 0x00700100, + CPU_POWERPC_CELL_v20 = 0x00700400, + CPU_POWERPC_CELL_v30 = 0x00700500, + CPU_POWERPC_CELL_v31 = 0x00700501, +#define CPU_POWERPC_CELL_v32 CPU_POWERPC_CELL_v31 + CPU_POWERPC_RS64 = 0x00330000, + CPU_POWERPC_RS64II = 0x00340000, + CPU_POWERPC_RS64III = 0x00360000, + CPU_POWERPC_RS64IV = 0x00370000, +#endif /* defined(TARGET_PPC64) */ + /* Original POWER */ + /* XXX: should be POWER (RIOS), RSC3308, RSC4608, + * POWER2 (RIOS2) & RSC2 (P2SC) here + */ +#if 0 + CPU_POWER = xxx, /* 0x20000 ? 0x30000 for RSC ? */ +#endif +#if 0 + CPU_POWER2 = xxx, /* 0x40000 ? */ +#endif + /* PA Semi core */ + CPU_POWERPC_PA6T = 0x00900000, +}; + +/* System version register (used on MPC 8xxx) */ +enum { + POWERPC_SVR_NONE = 0x00000000, + POWERPC_SVR_5200_v10 = 0x80110010, + POWERPC_SVR_5200_v11 = 0x80110011, + POWERPC_SVR_5200_v12 = 0x80110012, + POWERPC_SVR_5200B_v20 = 0x80110020, + POWERPC_SVR_5200B_v21 = 0x80110021, +#define POWERPC_SVR_55xx POWERPC_SVR_5567 +#if 0 + POWERPC_SVR_5533 = xxx, +#endif +#if 0 + POWERPC_SVR_5534 = xxx, +#endif +#if 0 + POWERPC_SVR_5553 = xxx, +#endif +#if 0 + POWERPC_SVR_5554 = xxx, +#endif +#if 0 + POWERPC_SVR_5561 = xxx, +#endif +#if 0 + POWERPC_SVR_5565 = xxx, +#endif +#if 0 + POWERPC_SVR_5566 = xxx, +#endif +#if 0 + POWERPC_SVR_5567 = xxx, +#endif +#if 0 + POWERPC_SVR_8313 = xxx, +#endif +#if 0 + POWERPC_SVR_8313E = xxx, +#endif +#if 0 + POWERPC_SVR_8314 = xxx, +#endif +#if 0 + POWERPC_SVR_8314E = xxx, +#endif +#if 0 + POWERPC_SVR_8315 = xxx, +#endif +#if 0 + POWERPC_SVR_8315E = xxx, +#endif +#if 0 + POWERPC_SVR_8321 = xxx, +#endif +#if 0 + POWERPC_SVR_8321E = xxx, +#endif +#if 0 + POWERPC_SVR_8323 = xxx, +#endif +#if 0 + POWERPC_SVR_8323E = xxx, +#endif + POWERPC_SVR_8343 = 0x80570010, + POWERPC_SVR_8343A = 0x80570030, + POWERPC_SVR_8343E = 0x80560010, + POWERPC_SVR_8343EA = 0x80560030, + POWERPC_SVR_8347P = 0x80550010, /* PBGA package */ + POWERPC_SVR_8347T = 0x80530010, /* TBGA package */ + POWERPC_SVR_8347AP = 0x80550030, /* PBGA package */ + POWERPC_SVR_8347AT = 0x80530030, /* TBGA package */ + POWERPC_SVR_8347EP = 0x80540010, /* PBGA package */ + POWERPC_SVR_8347ET = 0x80520010, /* TBGA package */ + POWERPC_SVR_8347EAP = 0x80540030, /* PBGA package */ + POWERPC_SVR_8347EAT = 0x80520030, /* TBGA package */ + POWERPC_SVR_8349 = 0x80510010, + POWERPC_SVR_8349A = 0x80510030, + POWERPC_SVR_8349E = 0x80500010, + POWERPC_SVR_8349EA = 0x80500030, +#if 0 + POWERPC_SVR_8358E = xxx, +#endif +#if 0 + POWERPC_SVR_8360E = xxx, +#endif +#define POWERPC_SVR_E500 0x40000000 + POWERPC_SVR_8377 = 0x80C70010 | POWERPC_SVR_E500, + POWERPC_SVR_8377E = 0x80C60010 | POWERPC_SVR_E500, + POWERPC_SVR_8378 = 0x80C50010 | POWERPC_SVR_E500, + POWERPC_SVR_8378E = 0x80C40010 | POWERPC_SVR_E500, + POWERPC_SVR_8379 = 0x80C30010 | POWERPC_SVR_E500, + POWERPC_SVR_8379E = 0x80C00010 | POWERPC_SVR_E500, + POWERPC_SVR_8533_v10 = 0x80340010 | POWERPC_SVR_E500, + POWERPC_SVR_8533_v11 = 0x80340011 | POWERPC_SVR_E500, + POWERPC_SVR_8533E_v10 = 0x803C0010 | POWERPC_SVR_E500, + POWERPC_SVR_8533E_v11 = 0x803C0011 | POWERPC_SVR_E500, + POWERPC_SVR_8540_v10 = 0x80300010 | POWERPC_SVR_E500, + POWERPC_SVR_8540_v20 = 0x80300020 | POWERPC_SVR_E500, + POWERPC_SVR_8540_v21 = 0x80300021 | POWERPC_SVR_E500, + POWERPC_SVR_8541_v10 = 0x80720010 | POWERPC_SVR_E500, + POWERPC_SVR_8541_v11 = 0x80720011 | POWERPC_SVR_E500, + POWERPC_SVR_8541E_v10 = 0x807A0010 | POWERPC_SVR_E500, + POWERPC_SVR_8541E_v11 = 0x807A0011 | POWERPC_SVR_E500, + POWERPC_SVR_8543_v10 = 0x80320010 | POWERPC_SVR_E500, + POWERPC_SVR_8543_v11 = 0x80320011 | POWERPC_SVR_E500, + POWERPC_SVR_8543_v20 = 0x80320020 | POWERPC_SVR_E500, + POWERPC_SVR_8543_v21 = 0x80320021 | POWERPC_SVR_E500, + POWERPC_SVR_8543E_v10 = 0x803A0010 | POWERPC_SVR_E500, + POWERPC_SVR_8543E_v11 = 0x803A0011 | POWERPC_SVR_E500, + POWERPC_SVR_8543E_v20 = 0x803A0020 | POWERPC_SVR_E500, + POWERPC_SVR_8543E_v21 = 0x803A0021 | POWERPC_SVR_E500, + POWERPC_SVR_8544_v10 = 0x80340110 | POWERPC_SVR_E500, + POWERPC_SVR_8544_v11 = 0x80340111 | POWERPC_SVR_E500, + POWERPC_SVR_8544E_v10 = 0x803C0110 | POWERPC_SVR_E500, + POWERPC_SVR_8544E_v11 = 0x803C0111 | POWERPC_SVR_E500, + POWERPC_SVR_8545_v20 = 0x80310220 | POWERPC_SVR_E500, + POWERPC_SVR_8545_v21 = 0x80310221 | POWERPC_SVR_E500, + POWERPC_SVR_8545E_v20 = 0x80390220 | POWERPC_SVR_E500, + POWERPC_SVR_8545E_v21 = 0x80390221 | POWERPC_SVR_E500, + POWERPC_SVR_8547E_v20 = 0x80390120 | POWERPC_SVR_E500, + POWERPC_SVR_8547E_v21 = 0x80390121 | POWERPC_SVR_E500, + POWERPC_SVR_8548_v10 = 0x80310010 | POWERPC_SVR_E500, + POWERPC_SVR_8548_v11 = 0x80310011 | POWERPC_SVR_E500, + POWERPC_SVR_8548_v20 = 0x80310020 | POWERPC_SVR_E500, + POWERPC_SVR_8548_v21 = 0x80310021 | POWERPC_SVR_E500, + POWERPC_SVR_8548E_v10 = 0x80390010 | POWERPC_SVR_E500, + POWERPC_SVR_8548E_v11 = 0x80390011 | POWERPC_SVR_E500, + POWERPC_SVR_8548E_v20 = 0x80390020 | POWERPC_SVR_E500, + POWERPC_SVR_8548E_v21 = 0x80390021 | POWERPC_SVR_E500, + POWERPC_SVR_8555_v10 = 0x80710010 | POWERPC_SVR_E500, + POWERPC_SVR_8555_v11 = 0x80710011 | POWERPC_SVR_E500, + POWERPC_SVR_8555E_v10 = 0x80790010 | POWERPC_SVR_E500, + POWERPC_SVR_8555E_v11 = 0x80790011 | POWERPC_SVR_E500, + POWERPC_SVR_8560_v10 = 0x80700010 | POWERPC_SVR_E500, + POWERPC_SVR_8560_v20 = 0x80700020 | POWERPC_SVR_E500, + POWERPC_SVR_8560_v21 = 0x80700021 | POWERPC_SVR_E500, + POWERPC_SVR_8567 = 0x80750111 | POWERPC_SVR_E500, + POWERPC_SVR_8567E = 0x807D0111 | POWERPC_SVR_E500, + POWERPC_SVR_8568 = 0x80750011 | POWERPC_SVR_E500, + POWERPC_SVR_8568E = 0x807D0011 | POWERPC_SVR_E500, + POWERPC_SVR_8572 = 0x80E00010 | POWERPC_SVR_E500, + POWERPC_SVR_8572E = 0x80E80010 | POWERPC_SVR_E500, +#if 0 + POWERPC_SVR_8610 = xxx, +#endif + POWERPC_SVR_8641 = 0x80900021, + POWERPC_SVR_8641D = 0x80900121, +}; + +#endif diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h index fb6b5a4119..09bfae3d54 100644 --- a/target-ppc/cpu-qom.h +++ b/target-ppc/cpu-qom.h @@ -40,6 +40,7 @@ /** * PowerPCCPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. * * A PowerPC CPU model. @@ -49,7 +50,24 @@ typedef struct PowerPCCPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); + + uint32_t pvr; + uint32_t svr; + uint64_t insns_flags; + uint64_t insns_flags2; + uint64_t msr_mask; + powerpc_mmu_t mmu_model; + powerpc_excp_t excp_model; + powerpc_input_t bus_model; + uint32_t flags; + int bfd_mach; +#if defined(TARGET_PPC64) + const struct ppc_segment_page_sizes *sps; +#endif + void (*init_proc)(CPUPPCState *env); + int (*check_pow)(CPUPPCState *env); } PowerPCCPUClass; /** @@ -73,5 +91,10 @@ static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env) #define ENV_GET_CPU(e) CPU(ppc_env_get_cpu(e)) +#define ENV_OFFSET offsetof(PowerPCCPU, env) + +PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr); + +void ppc_cpu_do_interrupt(CPUState *cpu); #endif diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index e88ebe00d4..6886666d6e 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -307,7 +307,6 @@ enum powerpc_input_t { #define PPC_INPUT(env) (env->bus_model) /*****************************************************************************/ -typedef struct ppc_def_t ppc_def_t; typedef struct opc_handler_t opc_handler_t; /*****************************************************************************/ @@ -330,6 +329,12 @@ struct ppc_spr_t { void (*hea_write)(void *opaque, int spr_num, int gpr_num); #endif const char *name; +#ifdef CONFIG_KVM + /* We (ab)use the fact that all the SPRs will have ids for the + * ONE_REG interface will have KVM_REG_PPC to use 0 as meaning, + * don't sync this */ + uint64_t one_reg_id; +#endif }; /* Altivec registers (128 bits) */ @@ -902,25 +907,6 @@ struct ppc_segment_page_sizes { /* The whole PowerPC CPU context */ #define NB_MMU_MODES 3 -struct ppc_def_t { - const char *name; - uint32_t pvr; - uint32_t svr; - uint64_t insns_flags; - uint64_t insns_flags2; - uint64_t msr_mask; - powerpc_mmu_t mmu_model; - powerpc_excp_t excp_model; - powerpc_input_t bus_model; - uint32_t flags; - int bfd_mach; -#if defined(TARGET_PPC64) - const struct ppc_segment_page_sizes *sps; -#endif - void (*init_proc)(CPUPPCState *env); - int (*check_pow)(CPUPPCState *env); -}; - struct CPUPPCState { /* First are the most commonly used resources * during translated code execution @@ -941,8 +927,11 @@ struct CPUPPCState { /* CFAR */ target_ulong cfar; #endif - /* XER */ + /* XER (with SO, OV, CA split out) */ target_ulong xer; + target_ulong so; + target_ulong ov; + target_ulong ca; /* Reservation address */ target_ulong reserve_addr; /* Reservation value */ @@ -1067,7 +1056,9 @@ struct CPUPPCState { target_ulong ivor_mask; target_ulong ivpr_mask; target_ulong hreset_vector; - hwaddr mpic_cpu_base; + hwaddr mpic_iack; + /* true when the external proxy facility mode is enabled */ + bool mpic_proxy; #endif /* Those resources are used only during code translation */ @@ -1142,7 +1133,6 @@ int cpu_ppc_signal_handler (int host_signum, void *pinfo, int cpu_ppc_handle_mmu_fault (CPUPPCState *env, target_ulong address, int rw, int mmu_idx); #define cpu_handle_mmu_fault cpu_ppc_handle_mmu_fault -void do_interrupt (CPUPPCState *env); void ppc_hw_interrupt (CPUPPCState *env); #if !defined(CONFIG_USER_ONLY) @@ -1156,10 +1146,6 @@ void ppc_store_msr (CPUPPCState *env, target_ulong value); void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf); -const ppc_def_t *ppc_find_by_pvr(uint32_t pvr); -const ppc_def_t *cpu_ppc_find_by_name (const char *name); -int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def); - /* Time-base and decrementer management */ #ifndef NO_CPU_IO_DEFS uint64_t cpu_ppc_load_tbl (CPUPPCState *env); @@ -1270,9 +1256,9 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp) #define XER_CA 29 #define XER_CMP 8 #define XER_BC 0 -#define xer_so ((env->xer >> XER_SO) & 1) -#define xer_ov ((env->xer >> XER_OV) & 1) -#define xer_ca ((env->xer >> XER_CA) & 1) +#define xer_so (env->so) +#define xer_ov (env->ov) +#define xer_ca (env->ca) #define xer_cmp ((env->xer >> XER_CMP) & 0xFF) #define xer_bc ((env->xer >> XER_BC) & 0x7F) @@ -1859,10 +1845,8 @@ enum { PPC_CACHE = 0x0000000200000000ULL, /* icbi instruction */ PPC_CACHE_ICBI = 0x0000000400000000ULL, - /* dcbz instruction with fixed cache line size */ + /* dcbz instruction */ PPC_CACHE_DCBZ = 0x0000000800000000ULL, - /* dcbz instruction with tunable cache line size */ - PPC_CACHE_DCBZT = 0x0000001000000000ULL, /* dcba instruction */ PPC_CACHE_DCBA = 0x0000002000000000ULL, /* Freescale cache locking instructions */ @@ -1930,7 +1914,7 @@ enum { | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC \ | PPC_MEM_SYNC | PPC_MEM_EIEIO \ | PPC_CACHE | PPC_CACHE_ICBI \ - | PPC_CACHE_DCBZ | PPC_CACHE_DCBZT \ + | PPC_CACHE_DCBZ \ | PPC_CACHE_DCBA | PPC_CACHE_LOCK \ | PPC_EXTERN | PPC_SEGMENT | PPC_6xx_TLB \ | PPC_74xx_TLB | PPC_40x_TLB | PPC_SEGMENT_64B \ @@ -2091,6 +2075,19 @@ enum { /*****************************************************************************/ +static inline target_ulong cpu_read_xer(CPUPPCState *env) +{ + return env->xer | (env->so << XER_SO) | (env->ov << XER_OV) | (env->ca << XER_CA); +} + +static inline void cpu_write_xer(CPUPPCState *env, target_ulong xer) +{ + env->so = (xer >> XER_SO) & 1; + env->ov = (xer >> XER_OV) & 1; + env->ca = (xer >> XER_CA) & 1; + env->xer = xer & ~((1u << XER_SO) | (1u << XER_OV) | (1u << XER_CA)); +} + static inline void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { @@ -2219,9 +2216,10 @@ extern void (*cpu_ppc_hypercall)(PowerPCCPU *); static inline bool cpu_has_work(CPUState *cpu) { - CPUPPCState *env = &POWERPC_CPU(cpu)->env; + PowerPCCPU *ppc_cpu = POWERPC_CPU(cpu); + CPUPPCState *env = &ppc_cpu->env; - return msr_ee && (env->interrupt_request & CPU_INTERRUPT_HARD); + return msr_ee && (cpu->interrupt_request & CPU_INTERRUPT_HARD); } #include "exec/exec-all.h" diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index 5e34ad08a8..4a0fc6dd57 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -38,8 +38,11 @@ void (*cpu_ppc_hypercall)(PowerPCCPU *); /*****************************************************************************/ /* Exception processing */ #if defined(CONFIG_USER_ONLY) -void do_interrupt(CPUPPCState *env) +void ppc_cpu_do_interrupt(CPUState *cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + env->exception_index = POWERPC_EXCP_NONE; env->error_code = 0; } @@ -66,6 +69,7 @@ static inline void dump_syscall(CPUPPCState *env) static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) { CPUPPCState *env = &cpu->env; + CPUState *cs; target_ulong msr, new_msr, vector; int srr0, srr1, asrr0, asrr1; int lpes0, lpes1, lev; @@ -84,7 +88,11 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) " => %08x (%02x)\n", env->nip, excp, env->error_code); /* new srr1 value excluding must-be-zero bits */ - msr = env->msr & ~0x783f0000ULL; + if (excp_model == POWERPC_EXCP_BOOKE) { + msr = env->msr; + } else { + msr = env->msr & ~0x783f0000ULL; + } /* new interrupt handler msr */ new_msr = env->msr & ((target_ulong)1 << MSR_ME); @@ -127,8 +135,9 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) fprintf(stderr, "Machine check while not allowed. " "Entering checkstop state\n"); } - env->halted = 1; - env->interrupt_request |= CPU_INTERRUPT_EXITTB; + cs = CPU(cpu); + cs->halted = 1; + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } if (0) { /* XXX: find a suitable condition to enable the hypervisor mode */ @@ -145,6 +154,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) srr1 = SPR_40x_SRR3; break; case POWERPC_EXCP_BOOKE: + /* FIXME: choose one or the other based on CPU type */ srr0 = SPR_BOOKE_MCSRR0; srr1 = SPR_BOOKE_MCSRR1; asrr0 = SPR_BOOKE_CSRR0; @@ -173,6 +183,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) if (lpes0 == 1) { new_msr |= (target_ulong)MSR_HVB; } + if (env->mpic_proxy) { + /* IACK the IRQ on delivery */ + env->spr[SPR_BOOKE_EPR] = ldl_phys(env->mpic_iack); + } goto store_next; case POWERPC_EXCP_ALIGN: /* Alignment exception */ if (lpes1 == 0) { @@ -275,6 +289,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) case POWERPC_EXCP_DEBUG: /* Debug interrupt */ switch (excp_model) { case POWERPC_EXCP_BOOKE: + /* FIXME: choose one or the other based on CPU type */ srr0 = SPR_BOOKE_DSRR0; srr1 = SPR_BOOKE_DSRR1; asrr0 = SPR_BOOKE_CSRR0; @@ -642,9 +657,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } } -void do_interrupt(CPUPPCState *env) +void ppc_cpu_do_interrupt(CPUState *cs) { - PowerPCCPU *cpu = ppc_env_get_cpu(env); + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; powerpc_excp(cpu, env->excp_model, env->exception_index); } @@ -653,11 +669,12 @@ void ppc_hw_interrupt(CPUPPCState *env) { PowerPCCPU *cpu = ppc_env_get_cpu(env); int hdice; - #if 0 + CPUState *cs = CPU(cpu); + qemu_log_mask(CPU_LOG_INT, "%s: %p pending %08x req %08x me %d ee %d\n", - __func__, env, env->pending_interrupts, - env->interrupt_request, (int)msr_me, (int)msr_ee); + __func__, env, env->pending_interrupts, + cs->interrupt_request, (int)msr_me, (int)msr_ee); #endif /* External reset */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { @@ -797,9 +814,12 @@ void helper_raise_exception(CPUPPCState *env, uint32_t exception) #if !defined(CONFIG_USER_ONLY) void helper_store_msr(CPUPPCState *env, target_ulong val) { + CPUState *cs; + val = hreg_store_msr(env, val, 0); if (val != 0) { - env->interrupt_request |= CPU_INTERRUPT_EXITTB; + cs = CPU(ppc_env_get_cpu(env)); + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; helper_raise_exception(env, val); } } @@ -807,6 +827,8 @@ void helper_store_msr(CPUPPCState *env, target_ulong val) static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr, target_ulong msrm, int keep_msrh) { + CPUState *cs = CPU(ppc_env_get_cpu(env)); + #if defined(TARGET_PPC64) if (msr_is_64bit(env, msr)) { nip = (uint64_t)nip; @@ -831,13 +853,18 @@ static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr, /* No need to raise an exception here, * as rfi is always the last insn of a TB */ - env->interrupt_request |= CPU_INTERRUPT_EXITTB; + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } void helper_rfi(CPUPPCState *env) { - do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], - ~((target_ulong)0x783F0000), 1); + if (env->excp_model == POWERPC_EXCP_BOOKE) { + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], + ~((target_ulong)0), 0); + } else { + do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], + ~((target_ulong)0x783F0000), 1); + } } #if defined(TARGET_PPC64) @@ -864,20 +891,22 @@ void helper_40x_rfci(CPUPPCState *env) void helper_rfci(CPUPPCState *env) { - do_rfi(env, env->spr[SPR_BOOKE_CSRR0], SPR_BOOKE_CSRR1, - ~((target_ulong)0x3FFF0000), 0); + do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1], + ~((target_ulong)0), 0); } void helper_rfdi(CPUPPCState *env) { - do_rfi(env, env->spr[SPR_BOOKE_DSRR0], SPR_BOOKE_DSRR1, - ~((target_ulong)0x3FFF0000), 0); + /* FIXME: choose CSRR1 or DSRR1 based on cpu type */ + do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1], + ~((target_ulong)0), 0); } void helper_rfmci(CPUPPCState *env) { - do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], SPR_BOOKE_MCSRR1, - ~((target_ulong)0x3FFF0000), 0); + /* FIXME: choose CSRR1 or MCSRR1 based on cpu type */ + do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1], + ~((target_ulong)0), 0); } #endif @@ -966,7 +995,7 @@ void helper_msgsnd(target_ulong rb) for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) { if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { cenv->pending_interrupts |= 1 << irq; - cpu_interrupt(cenv, CPU_INTERRUPT_HARD); + cpu_interrupt(CPU(ppc_env_get_cpu(cenv)), CPU_INTERRUPT_HARD); } } } diff --git a/target-ppc/helper.c b/target-ppc/helper.c deleted file mode 100644 index 103855afe0..0000000000 --- a/target-ppc/helper.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * PowerPC emulation helpers for QEMU. - * - * Copyright (c) 2003-2007 Jocelyn Mayer - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "cpu.h" -#include "helper_regs.h" -#include "sysemu/kvm.h" -#include "kvm_ppc.h" -#include "sysemu/cpus.h" - -PowerPCCPU *cpu_ppc_init(const char *cpu_model) -{ - PowerPCCPU *cpu; - CPUPPCState *env; - const ppc_def_t *def; - - def = cpu_ppc_find_by_name(cpu_model); - if (!def) { - return NULL; - } - - cpu = POWERPC_CPU(object_new(TYPE_POWERPC_CPU)); - env = &cpu->env; - - if (tcg_enabled()) { - ppc_translate_init(); - } - - env->cpu_model_str = cpu_model; - cpu_ppc_register_internal(env, def); - - qemu_init_vcpu(env); - - return cpu; -} diff --git a/target-ppc/helper.h b/target-ppc/helper.h index d2e9a55f28..fcf372ab45 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -25,14 +25,11 @@ DEF_HELPER_3(stmw, void, env, tl, i32) DEF_HELPER_4(lsw, void, env, tl, i32, i32) DEF_HELPER_5(lswx, void, env, tl, i32, i32, i32) DEF_HELPER_4(stsw, void, env, tl, i32, i32) -DEF_HELPER_2(dcbz, void, env, tl) -DEF_HELPER_2(dcbz_970, void, env, tl) +DEF_HELPER_3(dcbz, void, env, tl, i32) DEF_HELPER_2(icbi, void, env, tl) DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) #if defined(TARGET_PPC64) -DEF_HELPER_FLAGS_2(mulhd, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(mulhdu, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_3(mulldo, i64, env, i64, i64) #endif @@ -405,7 +402,6 @@ DEF_HELPER_2(store_40x_dbcr0, void, env, tl) DEF_HELPER_2(store_40x_sler, void, env, tl) DEF_HELPER_2(store_booke_tcr, void, env, tl) DEF_HELPER_2(store_booke_tsr, void, env, tl) -DEF_HELPER_1(load_epr, tl, env) DEF_HELPER_3(store_ibatl, void, env, i32, tl) DEF_HELPER_3(store_ibatu, void, env, i32, tl) DEF_HELPER_3(store_dbatl, void, env, i32, tl) diff --git a/target-ppc/helper_regs.h b/target-ppc/helper_regs.h index 3c988502ed..a6d5e2fe2f 100644 --- a/target-ppc/helper_regs.h +++ b/target-ppc/helper_regs.h @@ -68,10 +68,13 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv) { int excp; +#if !defined(CONFIG_USER_ONLY) + CPUState *cs = CPU(ppc_env_get_cpu(env)); +#endif excp = 0; value &= env->msr_mask; -#if !defined (CONFIG_USER_ONLY) +#if !defined(CONFIG_USER_ONLY) if (!alter_hv) { /* mtmsr cannot alter the hypervisor state */ value &= ~MSR_HVB; @@ -82,7 +85,7 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value, /* Flush all tlb when changing translation mode */ tlb_flush(env, 1); excp = POWERPC_EXCP_NONE; - env->interrupt_request |= CPU_INTERRUPT_EXITTB; + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } if (unlikely((env->flags & POWERPC_FLAG_TGPR) && ((value ^ env->msr) & (1 << MSR_TGPR)))) { @@ -96,10 +99,10 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value, #endif env->msr = value; hreg_compute_hflags(env); -#if !defined (CONFIG_USER_ONLY) +#if !defined(CONFIG_USER_ONLY) if (unlikely(msr_pow == 1)) { if ((*env->check_pow)(env)) { - env->halted = 1; + cs->halted = 1; excp = EXCP_HALTED; } } diff --git a/target-ppc/int_helper.c b/target-ppc/int_helper.c index 783079d995..54eca9bbee 100644 --- a/target-ppc/int_helper.c +++ b/target-ppc/int_helper.c @@ -25,24 +25,6 @@ /* Fixed point operations helpers */ #if defined(TARGET_PPC64) -/* multiply high word */ -uint64_t helper_mulhd(uint64_t arg1, uint64_t arg2) -{ - uint64_t tl, th; - - muls64(&tl, &th, arg1, arg2); - return th; -} - -/* multiply high word unsigned */ -uint64_t helper_mulhdu(uint64_t arg1, uint64_t arg2) -{ - uint64_t tl, th; - - mulu64(&tl, &th, arg1, arg2); - return th; -} - uint64_t helper_mulldo(CPUPPCState *env, uint64_t arg1, uint64_t arg2) { int64_t th; @@ -51,9 +33,9 @@ uint64_t helper_mulldo(CPUPPCState *env, uint64_t arg1, uint64_t arg2) muls64(&tl, (uint64_t *)&th, arg1, arg2); /* If th != 0 && th != -1, then we had an overflow */ if (likely((uint64_t)(th + 1) <= 1)) { - env->xer &= ~(1 << XER_OV); + env->ov = 0; } else { - env->xer |= (1 << XER_OV) | (1 << XER_SO); + env->so = env->ov = 1; } return (int64_t)tl; } @@ -82,21 +64,17 @@ target_ulong helper_sraw(CPUPPCState *env, target_ulong value, shift &= 0x1f; ret = (int32_t)value >> shift; if (likely(ret >= 0 || (value & ((1 << shift) - 1)) == 0)) { - env->xer &= ~(1 << XER_CA); + env->ca = 0; } else { - env->xer |= (1 << XER_CA); + env->ca = 1; } } else { ret = (int32_t)value; - env->xer &= ~(1 << XER_CA); + env->ca = 0; } } else { ret = (int32_t)value >> 31; - if (ret) { - env->xer |= (1 << XER_CA); - } else { - env->xer &= ~(1 << XER_CA); - } + env->ca = (ret != 0); } return (target_long)ret; } @@ -112,21 +90,17 @@ target_ulong helper_srad(CPUPPCState *env, target_ulong value, shift &= 0x3f; ret = (int64_t)value >> shift; if (likely(ret >= 0 || (value & ((1 << shift) - 1)) == 0)) { - env->xer &= ~(1 << XER_CA); + env->ca = 0; } else { - env->xer |= (1 << XER_CA); + env->ca = 1; } } else { ret = (int64_t)value; - env->xer &= ~(1 << XER_CA); + env->ca = 0; } } else { ret = (int64_t)value >> 63; - if (ret) { - env->xer |= (1 << XER_CA); - } else { - env->xer &= ~(1 << XER_CA); - } + env->ca = (ret != 0); } return ret; } @@ -206,16 +180,16 @@ target_ulong helper_divo(CPUPPCState *env, target_ulong arg1, if (((int32_t)tmp == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || (int32_t)arg2 == 0) { - env->xer |= (1 << XER_OV) | (1 << XER_SO); + env->so = env->ov = 1; env->spr[SPR_MQ] = 0; return INT32_MIN; } else { env->spr[SPR_MQ] = tmp % arg2; tmp /= (int32_t)arg2; if ((int32_t)tmp != tmp) { - env->xer |= (1 << XER_OV) | (1 << XER_SO); + env->so = env->ov = 1; } else { - env->xer &= ~(1 << XER_OV); + env->ov = 0; } return tmp; } @@ -239,11 +213,11 @@ target_ulong helper_divso(CPUPPCState *env, target_ulong arg1, { if (((int32_t)arg1 == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || (int32_t)arg2 == 0) { - env->xer |= (1 << XER_OV) | (1 << XER_SO); + env->so = env->ov = 1; env->spr[SPR_MQ] = 0; return INT32_MIN; } else { - env->xer &= ~(1 << XER_OV); + env->ov = 0; env->spr[SPR_MQ] = (int32_t)arg1 % (int32_t)arg2; return (int32_t)arg1 / (int32_t)arg2; } diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 88650d4ae4..e663ff0acb 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -61,6 +61,7 @@ static int cap_ppc_smt; static int cap_ppc_rma; static int cap_spapr_tce; static int cap_hior; +static int cap_one_reg; /* XXX We have a race condition where we actually have a level triggered * interrupt, but the infrastructure can't expose that yet, so the guest @@ -80,6 +81,8 @@ static void kvm_kick_cpu(void *opaque) qemu_cpu_kick(CPU(cpu)); } +static int kvm_ppc_register_host_cpu_type(void); + int kvm_arch_init(KVMState *s) { cap_interrupt_unset = kvm_check_extension(s, KVM_CAP_PPC_UNSET_IRQ); @@ -89,6 +92,7 @@ int kvm_arch_init(KVMState *s) cap_ppc_smt = kvm_check_extension(s, KVM_CAP_PPC_SMT); cap_ppc_rma = kvm_check_extension(s, KVM_CAP_PPC_RMA); cap_spapr_tce = kvm_check_extension(s, KVM_CAP_SPAPR_TCE); + cap_one_reg = kvm_check_extension(s, KVM_CAP_ONE_REG); cap_hior = kvm_check_extension(s, KVM_CAP_PPC_HIOR); if (!cap_interrupt_level) { @@ -96,11 +100,15 @@ int kvm_arch_init(KVMState *s) "VM to stall at times!\n"); } + kvm_ppc_register_host_cpu_type(); + return 0; } -static int kvm_arch_sync_sregs(CPUPPCState *cenv) +static int kvm_arch_sync_sregs(PowerPCCPU *cpu) { + CPUPPCState *cenv = &cpu->env; + CPUState *cs = CPU(cpu); struct kvm_sregs sregs; int ret; @@ -117,18 +125,20 @@ static int kvm_arch_sync_sregs(CPUPPCState *cenv) } } - ret = kvm_vcpu_ioctl(cenv, KVM_GET_SREGS, &sregs); + ret = kvm_vcpu_ioctl(cs, KVM_GET_SREGS, &sregs); if (ret) { return ret; } sregs.pvr = cenv->spr[SPR_PVR]; - return kvm_vcpu_ioctl(cenv, KVM_SET_SREGS, &sregs); + return kvm_vcpu_ioctl(cs, KVM_SET_SREGS, &sregs); } /* Set up a shared TLB array with KVM */ -static int kvm_booke206_tlb_init(CPUPPCState *env) +static int kvm_booke206_tlb_init(PowerPCCPU *cpu) { + CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); struct kvm_book3e_206_tlb_params params = {}; struct kvm_config_tlb cfg = {}; struct kvm_enable_cap encap = {}; @@ -136,7 +146,7 @@ static int kvm_booke206_tlb_init(CPUPPCState *env) int ret, i; if (!kvm_enabled() || - !kvm_check_extension(env->kvm_state, KVM_CAP_SW_TLB)) { + !kvm_check_extension(cs->kvm_state, KVM_CAP_SW_TLB)) { return 0; } @@ -161,7 +171,7 @@ static int kvm_booke206_tlb_init(CPUPPCState *env) encap.cap = KVM_CAP_SW_TLB; encap.args[0] = (uintptr_t)&cfg; - ret = kvm_vcpu_ioctl(env, KVM_ENABLE_CAP, &encap); + ret = kvm_vcpu_ioctl(cs, KVM_ENABLE_CAP, &encap); if (ret < 0) { fprintf(stderr, "%s: couldn't enable KVM_CAP_SW_TLB: %s\n", __func__, strerror(-ret)); @@ -174,9 +184,12 @@ static int kvm_booke206_tlb_init(CPUPPCState *env) #if defined(TARGET_PPC64) -static void kvm_get_fallback_smmu_info(CPUPPCState *env, +static void kvm_get_fallback_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info) { + CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); + memset(info, 0, sizeof(*info)); /* We don't have the new KVM_PPC_GET_SMMU_INFO ioctl, so @@ -202,7 +215,7 @@ static void kvm_get_fallback_smmu_info(CPUPPCState *env, * implements KVM_CAP_PPC_GET_SMMU_INFO and thus doesn't hit * this fallback. */ - if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_GET_PVINFO)) { + if (kvm_check_extension(cs->kvm_state, KVM_CAP_PPC_GET_PVINFO)) { /* No flags */ info->flags = 0; info->slb_size = 64; @@ -258,18 +271,19 @@ static void kvm_get_fallback_smmu_info(CPUPPCState *env, } } -static void kvm_get_smmu_info(CPUPPCState *env, struct kvm_ppc_smmu_info *info) +static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info) { + CPUState *cs = CPU(cpu); int ret; - if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_GET_SMMU_INFO)) { - ret = kvm_vm_ioctl(env->kvm_state, KVM_PPC_GET_SMMU_INFO, info); + if (kvm_check_extension(cs->kvm_state, KVM_CAP_PPC_GET_SMMU_INFO)) { + ret = kvm_vm_ioctl(cs->kvm_state, KVM_PPC_GET_SMMU_INFO, info); if (ret == 0) { return; } } - kvm_get_fallback_smmu_info(env, info); + kvm_get_fallback_smmu_info(cpu, info); } static long getrampagesize(void) @@ -312,10 +326,11 @@ static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift) return (1ul << shift) <= rampgsize; } -static void kvm_fixup_page_sizes(CPUPPCState *env) +static void kvm_fixup_page_sizes(PowerPCCPU *cpu) { static struct kvm_ppc_smmu_info smmu_info; static bool has_smmu_info; + CPUPPCState *env = &cpu->env; long rampagesize; int iq, ik, jq, jk; @@ -326,7 +341,7 @@ static void kvm_fixup_page_sizes(CPUPPCState *env) /* Collect MMU info from kernel if not already */ if (!has_smmu_info) { - kvm_get_smmu_info(env, &smmu_info); + kvm_get_smmu_info(cpu, &smmu_info); has_smmu_info = true; } @@ -369,22 +384,28 @@ static void kvm_fixup_page_sizes(CPUPPCState *env) } #else /* defined (TARGET_PPC64) */ -static inline void kvm_fixup_page_sizes(CPUPPCState *env) +static inline void kvm_fixup_page_sizes(PowerPCCPU *cpu) { } #endif /* !defined (TARGET_PPC64) */ -int kvm_arch_init_vcpu(CPUPPCState *cenv) +unsigned long kvm_arch_vcpu_id(CPUState *cpu) { - PowerPCCPU *cpu = ppc_env_get_cpu(cenv); + return cpu->cpu_index; +} + +int kvm_arch_init_vcpu(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *cenv = &cpu->env; int ret; /* Gather server mmu info from KVM and update the CPU state */ - kvm_fixup_page_sizes(cenv); + kvm_fixup_page_sizes(cpu); /* Synchronize sregs with kvm */ - ret = kvm_arch_sync_sregs(cenv); + ret = kvm_arch_sync_sregs(cpu); if (ret) { return ret; } @@ -394,7 +415,7 @@ int kvm_arch_init_vcpu(CPUPPCState *cenv) /* Some targets support access to KVM's guest TLB. */ switch (cenv->mmu_model) { case POWERPC_MMU_BOOKE206: - ret = kvm_booke206_tlb_init(cenv); + ret = kvm_booke206_tlb_init(cpu); break; default: break; @@ -403,12 +424,14 @@ int kvm_arch_init_vcpu(CPUPPCState *cenv) return ret; } -void kvm_arch_reset_vcpu(CPUPPCState *env) +void kvm_arch_reset_vcpu(CPUState *cpu) { } -static void kvm_sw_tlb_put(CPUPPCState *env) +static void kvm_sw_tlb_put(PowerPCCPU *cpu) { + CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); struct kvm_dirty_tlb dirty_tlb; unsigned char *bitmap; int ret; @@ -423,7 +446,7 @@ static void kvm_sw_tlb_put(CPUPPCState *env) dirty_tlb.bitmap = (uintptr_t)bitmap; dirty_tlb.num_dirty = env->nb_tlb; - ret = kvm_vcpu_ioctl(env, KVM_DIRTY_TLB, &dirty_tlb); + ret = kvm_vcpu_ioctl(cs, KVM_DIRTY_TLB, &dirty_tlb); if (ret) { fprintf(stderr, "%s: KVM_DIRTY_TLB: %s\n", __func__, strerror(-ret)); @@ -432,19 +455,218 @@ static void kvm_sw_tlb_put(CPUPPCState *env) g_free(bitmap); } -int kvm_arch_put_registers(CPUPPCState *env, int level) +static void kvm_get_one_spr(CPUState *cs, uint64_t id, int spr) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + union { + uint32_t u32; + uint64_t u64; + } val; + struct kvm_one_reg reg = { + .id = id, + .addr = (uintptr_t) &val, + }; + int ret; + + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret != 0) { + fprintf(stderr, "Warning: Unable to retrieve SPR %d from KVM: %s\n", + spr, strerror(errno)); + } else { + switch (id & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + env->spr[spr] = val.u32; + break; + + case KVM_REG_SIZE_U64: + env->spr[spr] = val.u64; + break; + + default: + /* Don't handle this size yet */ + abort(); + } + } +} + +static void kvm_put_one_spr(CPUState *cs, uint64_t id, int spr) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + union { + uint32_t u32; + uint64_t u64; + } val; + struct kvm_one_reg reg = { + .id = id, + .addr = (uintptr_t) &val, + }; + int ret; + + switch (id & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U32: + val.u32 = env->spr[spr]; + break; + + case KVM_REG_SIZE_U64: + val.u64 = env->spr[spr]; + break; + + default: + /* Don't handle this size yet */ + abort(); + } + + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret != 0) { + fprintf(stderr, "Warning: Unable to set SPR %d to KVM: %s\n", + spr, strerror(errno)); + } +} + +static int kvm_put_fp(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + struct kvm_one_reg reg; + int i; + int ret; + + if (env->insns_flags & PPC_FLOAT) { + uint64_t fpscr = env->fpscr; + bool vsx = !!(env->insns_flags2 & PPC2_VSX); + + reg.id = KVM_REG_PPC_FPSCR; + reg.addr = (uintptr_t)&fpscr; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to set FPSCR to KVM: %s\n", strerror(errno)); + return ret; + } + + for (i = 0; i < 32; i++) { + uint64_t vsr[2]; + + vsr[0] = float64_val(env->fpr[i]); + vsr[1] = env->vsr[i]; + reg.addr = (uintptr_t) &vsr; + reg.id = vsx ? KVM_REG_PPC_VSR(i) : KVM_REG_PPC_FPR(i); + + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to set %s%d to KVM: %s\n", vsx ? "VSR" : "FPR", + i, strerror(errno)); + return ret; + } + } + } + + if (env->insns_flags & PPC_ALTIVEC) { + reg.id = KVM_REG_PPC_VSCR; + reg.addr = (uintptr_t)&env->vscr; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to set VSCR to KVM: %s\n", strerror(errno)); + return ret; + } + + for (i = 0; i < 32; i++) { + reg.id = KVM_REG_PPC_VR(i); + reg.addr = (uintptr_t)&env->avr[i]; + ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to set VR%d to KVM: %s\n", i, strerror(errno)); + return ret; + } + } + } + + return 0; +} + +static int kvm_get_fp(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + struct kvm_one_reg reg; + int i; + int ret; + + if (env->insns_flags & PPC_FLOAT) { + uint64_t fpscr; + bool vsx = !!(env->insns_flags2 & PPC2_VSX); + + reg.id = KVM_REG_PPC_FPSCR; + reg.addr = (uintptr_t)&fpscr; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to get FPSCR from KVM: %s\n", strerror(errno)); + return ret; + } else { + env->fpscr = fpscr; + } + + for (i = 0; i < 32; i++) { + uint64_t vsr[2]; + + reg.addr = (uintptr_t) &vsr; + reg.id = vsx ? KVM_REG_PPC_VSR(i) : KVM_REG_PPC_FPR(i); + + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to get %s%d from KVM: %s\n", + vsx ? "VSR" : "FPR", i, strerror(errno)); + return ret; + } else { + env->fpr[i] = vsr[0]; + if (vsx) { + env->vsr[i] = vsr[1]; + } + } + } + } + + if (env->insns_flags & PPC_ALTIVEC) { + reg.id = KVM_REG_PPC_VSCR; + reg.addr = (uintptr_t)&env->vscr; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to get VSCR from KVM: %s\n", strerror(errno)); + return ret; + } + + for (i = 0; i < 32; i++) { + reg.id = KVM_REG_PPC_VR(i); + reg.addr = (uintptr_t)&env->avr[i]; + ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); + if (ret < 0) { + dprintf("Unable to get VR%d from KVM: %s\n", + i, strerror(errno)); + return ret; + } + } + } + + return 0; +} + +int kvm_arch_put_registers(CPUState *cs, int level) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; struct kvm_regs regs; int ret; int i; - ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, ®s); - if (ret < 0) + ret = kvm_vcpu_ioctl(cs, KVM_GET_REGS, ®s); + if (ret < 0) { return ret; + } regs.ctr = env->ctr; regs.lr = env->lr; - regs.xer = env->xer; + regs.xer = cpu_read_xer(env); regs.msr = env->msr; regs.pc = env->nip; @@ -465,12 +687,14 @@ int kvm_arch_put_registers(CPUPPCState *env, int level) for (i = 0;i < 32; i++) regs.gpr[i] = env->gpr[i]; - ret = kvm_vcpu_ioctl(env, KVM_SET_REGS, ®s); + ret = kvm_vcpu_ioctl(cs, KVM_SET_REGS, ®s); if (ret < 0) return ret; + kvm_put_fp(cs); + if (env->tlb_dirty) { - kvm_sw_tlb_put(env); + kvm_sw_tlb_put(cpu); env->tlb_dirty = false; } @@ -503,36 +727,45 @@ int kvm_arch_put_registers(CPUPPCState *env, int level) | env->IBAT[1][i]; } - ret = kvm_vcpu_ioctl(env, KVM_SET_SREGS, &sregs); + ret = kvm_vcpu_ioctl(cs, KVM_SET_SREGS, &sregs); if (ret) { return ret; } } if (cap_hior && (level >= KVM_PUT_RESET_STATE)) { - uint64_t hior = env->spr[SPR_HIOR]; - struct kvm_one_reg reg = { - .id = KVM_REG_PPC_HIOR, - .addr = (uintptr_t) &hior, - }; + kvm_put_one_spr(cs, KVM_REG_PPC_HIOR, SPR_HIOR); + } - ret = kvm_vcpu_ioctl(env, KVM_SET_ONE_REG, ®); - if (ret) { - return ret; + if (cap_one_reg) { + int i; + + /* We deliberately ignore errors here, for kernels which have + * the ONE_REG calls, but don't support the specific + * registers, there's a reasonable chance things will still + * work, at least until we try to migrate. */ + for (i = 0; i < 1024; i++) { + uint64_t id = env->spr_cb[i].one_reg_id; + + if (id != 0) { + kvm_put_one_spr(cs, id, i); + } } } return ret; } -int kvm_arch_get_registers(CPUPPCState *env) +int kvm_arch_get_registers(CPUState *cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; struct kvm_regs regs; struct kvm_sregs sregs; uint32_t cr; int i, ret; - ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, ®s); + ret = kvm_vcpu_ioctl(cs, KVM_GET_REGS, ®s); if (ret < 0) return ret; @@ -544,7 +777,7 @@ int kvm_arch_get_registers(CPUPPCState *env) env->ctr = regs.ctr; env->lr = regs.lr; - env->xer = regs.xer; + cpu_write_xer(env, regs.xer); env->msr = regs.msr; env->nip = regs.pc; @@ -565,8 +798,10 @@ int kvm_arch_get_registers(CPUPPCState *env) for (i = 0;i < 32; i++) env->gpr[i] = regs.gpr[i]; + kvm_get_fp(cs); + if (cap_booke_sregs) { - ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); + ret = kvm_vcpu_ioctl(cs, KVM_GET_SREGS, &sregs); if (ret < 0) { return ret; } @@ -670,7 +905,7 @@ int kvm_arch_get_registers(CPUPPCState *env) } if (cap_segstate) { - ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); + ret = kvm_vcpu_ioctl(cs, KVM_GET_SREGS, &sregs); if (ret < 0) { return ret; } @@ -699,10 +934,30 @@ int kvm_arch_get_registers(CPUPPCState *env) } } + if (cap_hior) { + kvm_get_one_spr(cs, KVM_REG_PPC_HIOR, SPR_HIOR); + } + + if (cap_one_reg) { + int i; + + /* We deliberately ignore errors here, for kernels which have + * the ONE_REG calls, but don't support the specific + * registers, there's a reasonable chance things will still + * work, at least until we try to migrate. */ + for (i = 0; i < 1024; i++) { + uint64_t id = env->spr_cb[i].one_reg_id; + + if (id != 0) { + kvm_get_one_spr(cs, id, i); + } + } + } + return 0; } -int kvmppc_set_interrupt(CPUPPCState *env, int irq, int level) +int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level) { unsigned virq = level ? KVM_INTERRUPT_SET_LEVEL : KVM_INTERRUPT_UNSET; @@ -714,7 +969,7 @@ int kvmppc_set_interrupt(CPUPPCState *env, int irq, int level) return 0; } - kvm_vcpu_ioctl(env, KVM_INTERRUPT, &virq); + kvm_vcpu_ioctl(CPU(cpu), KVM_INTERRUPT, &virq); return 0; } @@ -727,8 +982,10 @@ int kvmppc_set_interrupt(CPUPPCState *env, int irq, int level) #define PPC_INPUT_INT PPC6xx_INPUT_INT #endif -void kvm_arch_pre_run(CPUPPCState *env, struct kvm_run *run) +void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; int r; unsigned irq; @@ -736,7 +993,7 @@ void kvm_arch_pre_run(CPUPPCState *env, struct kvm_run *run) * interrupt, reset, etc) in PPC-specific env->irq_input_state. */ if (!cap_interrupt_level && run->ready_for_interrupt_injection && - (env->interrupt_request & CPU_INTERRUPT_HARD) && + (cs->interrupt_request & CPU_INTERRUPT_HARD) && (env->irq_input_state & (1<cpu_index, irq); + r = kvm_vcpu_ioctl(cs, KVM_INTERRUPT, &irq); + if (r < 0) { + printf("cpu %d fail inject %x\n", cs->cpu_index, irq); + } /* Always wake up soon in case the interrupt was level based */ qemu_mod_timer(idle_timer, qemu_get_clock_ns(vm_clock) + @@ -760,19 +1018,22 @@ void kvm_arch_pre_run(CPUPPCState *env, struct kvm_run *run) * anyways, so we will get a chance to deliver the rest. */ } -void kvm_arch_post_run(CPUPPCState *env, struct kvm_run *run) +void kvm_arch_post_run(CPUState *cpu, struct kvm_run *run) { } -int kvm_arch_process_async_events(CPUPPCState *env) +int kvm_arch_process_async_events(CPUState *cs) { - return env->halted; + return cs->halted; } -static int kvmppc_handle_halt(CPUPPCState *env) +static int kvmppc_handle_halt(PowerPCCPU *cpu) { - if (!(env->interrupt_request & CPU_INTERRUPT_HARD) && (msr_ee)) { - env->halted = 1; + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + + if (!(cs->interrupt_request & CPU_INTERRUPT_HARD) && (msr_ee)) { + cs->halted = 1; env->exception_index = EXCP_HLT; } @@ -796,8 +1057,10 @@ static int kvmppc_handle_dcr_write(CPUPPCState *env, uint32_t dcrn, uint32_t dat return 0; } -int kvm_arch_handle_exit(CPUPPCState *env, struct kvm_run *run) +int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; int ret; switch (run->exit_reason) { @@ -812,17 +1075,22 @@ int kvm_arch_handle_exit(CPUPPCState *env, struct kvm_run *run) break; case KVM_EXIT_HLT: dprintf("handle halt\n"); - ret = kvmppc_handle_halt(env); + ret = kvmppc_handle_halt(cpu); break; #ifdef CONFIG_PSERIES case KVM_EXIT_PAPR_HCALL: dprintf("handle PAPR hypercall\n"); - run->papr_hcall.ret = spapr_hypercall(ppc_env_get_cpu(env), + run->papr_hcall.ret = spapr_hypercall(cpu, run->papr_hcall.nr, run->papr_hcall.args); ret = 0; break; #endif + case KVM_EXIT_EPR: + dprintf("handle epr\n"); + run->epr.epr = ldl_phys(env->mpic_iack); + ret = 0; + break; default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); ret = -1; @@ -967,16 +1235,38 @@ uint32_t kvmppc_get_dfp(void) return kvmppc_read_int_cpu_dt("ibm,dfp"); } +static int kvmppc_get_pvinfo(CPUPPCState *env, struct kvm_ppc_pvinfo *pvinfo) + { + PowerPCCPU *cpu = ppc_env_get_cpu(env); + CPUState *cs = CPU(cpu); + + if (kvm_check_extension(cs->kvm_state, KVM_CAP_PPC_GET_PVINFO) && + !kvm_vm_ioctl(cs->kvm_state, KVM_PPC_GET_PVINFO, pvinfo)) { + return 0; + } + + return 1; +} + +int kvmppc_get_hasidle(CPUPPCState *env) +{ + struct kvm_ppc_pvinfo pvinfo; + + if (!kvmppc_get_pvinfo(env, &pvinfo) && + (pvinfo.flags & KVM_PPC_PVINFO_FLAGS_EV_IDLE)) { + return 1; + } + + return 0; +} + int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len) { uint32_t *hc = (uint32_t*)buf; - struct kvm_ppc_pvinfo pvinfo; - if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_GET_PVINFO) && - !kvm_vm_ioctl(env->kvm_state, KVM_PPC_GET_PVINFO, &pvinfo)) { + if (!kvmppc_get_pvinfo(env, &pvinfo)) { memcpy(buf, pvinfo.hcall, buf_len); - return 0; } @@ -997,19 +1287,37 @@ int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len) return 0; } -void kvmppc_set_papr(CPUPPCState *env) +void kvmppc_set_papr(PowerPCCPU *cpu) { + CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); struct kvm_enable_cap cap = {}; int ret; cap.cap = KVM_CAP_PPC_PAPR; - ret = kvm_vcpu_ioctl(env, KVM_ENABLE_CAP, &cap); + ret = kvm_vcpu_ioctl(cs, KVM_ENABLE_CAP, &cap); if (ret) { cpu_abort(env, "This KVM version does not support PAPR\n"); } } +void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy) +{ + CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); + struct kvm_enable_cap cap = {}; + int ret; + + cap.cap = KVM_CAP_PPC_EPR; + cap.args[0] = mpic_proxy; + ret = kvm_vcpu_ioctl(cs, KVM_ENABLE_CAP, &cap); + + if (ret && mpic_proxy) { + cpu_abort(env, "This KVM version does not support EPR\n"); + } +} + int kvmppc_smt_threads(void) { return cap_ppc_smt ? cap_ppc_smt : 1; @@ -1184,53 +1492,69 @@ static void alter_insns(uint64_t *word, uint64_t flags, bool on) } } -const ppc_def_t *kvmppc_host_cpu_def(void) +static void kvmppc_host_cpu_initfn(Object *obj) { - uint32_t host_pvr = mfpvr(); - const ppc_def_t *base_spec; - ppc_def_t *spec; + assert(kvm_enabled()); +} + +static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); uint32_t vmx = kvmppc_get_vmx(); uint32_t dfp = kvmppc_get_dfp(); - base_spec = ppc_find_by_pvr(host_pvr); - - spec = g_malloc0(sizeof(*spec)); - memcpy(spec, base_spec, sizeof(*spec)); - - /* Now fix up the spec with information we can query from the host */ + /* Now fix up the class with information we can query from the host */ if (vmx != -1) { /* Only override when we know what the host supports */ - alter_insns(&spec->insns_flags, PPC_ALTIVEC, vmx > 0); - alter_insns(&spec->insns_flags2, PPC2_VSX, vmx > 1); + alter_insns(&pcc->insns_flags, PPC_ALTIVEC, vmx > 0); + alter_insns(&pcc->insns_flags2, PPC2_VSX, vmx > 1); } if (dfp != -1) { /* Only override when we know what the host supports */ - alter_insns(&spec->insns_flags2, PPC2_DFP, dfp); + alter_insns(&pcc->insns_flags2, PPC2_DFP, dfp); } - - return spec; } -int kvmppc_fixup_cpu(CPUPPCState *env) +int kvmppc_fixup_cpu(PowerPCCPU *cpu) { + CPUState *cs = CPU(cpu); int smt; /* Adjust cpu index for SMT */ smt = kvmppc_smt_threads(); - env->cpu_index = (env->cpu_index / smp_threads) * smt - + (env->cpu_index % smp_threads); + cs->cpu_index = (cs->cpu_index / smp_threads) * smt + + (cs->cpu_index % smp_threads); return 0; } +static int kvm_ppc_register_host_cpu_type(void) +{ + TypeInfo type_info = { + .name = TYPE_HOST_POWERPC_CPU, + .instance_init = kvmppc_host_cpu_initfn, + .class_init = kvmppc_host_cpu_class_init, + }; + uint32_t host_pvr = mfpvr(); + PowerPCCPUClass *pvr_pcc; -bool kvm_arch_stop_on_emulation_error(CPUPPCState *env) + pvr_pcc = ppc_cpu_class_by_pvr(host_pvr); + if (pvr_pcc == NULL) { + return -1; + } + type_info.parent = object_class_get_name(OBJECT_CLASS(pvr_pcc)); + type_register(&type_info); + return 0; +} + + +bool kvm_arch_stop_on_emulation_error(CPUState *cpu) { return true; } -int kvm_arch_on_sigbus_vcpu(CPUPPCState *env, int code, void *addr) +int kvm_arch_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) { return 1; } diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 83f98729a2..c30b006674 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -11,6 +11,8 @@ #include "exec/memory.h" +#define TYPE_HOST_POWERPC_CPU "host-" TYPE_POWERPC_CPU + void kvmppc_init(void); #ifdef CONFIG_KVM @@ -19,9 +21,11 @@ uint32_t kvmppc_get_tbfreq(void); uint64_t kvmppc_get_clockfreq(void); uint32_t kvmppc_get_vmx(void); uint32_t kvmppc_get_dfp(void); +int kvmppc_get_hasidle(CPUPPCState *env); int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len); -int kvmppc_set_interrupt(CPUPPCState *env, int irq, int level); -void kvmppc_set_papr(CPUPPCState *env); +int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level); +void kvmppc_set_papr(PowerPCCPU *cpu); +void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy); int kvmppc_smt_threads(void); #ifndef CONFIG_USER_ONLY off_t kvmppc_alloc_rma(const char *name, MemoryRegion *sysmem); @@ -30,8 +34,7 @@ int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size); int kvmppc_reset_htab(int shift_hint); uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift); #endif /* !CONFIG_USER_ONLY */ -const ppc_def_t *kvmppc_host_cpu_def(void); -int kvmppc_fixup_cpu(CPUPPCState *env); +int kvmppc_fixup_cpu(PowerPCCPU *cpu); #else @@ -55,6 +58,11 @@ static inline uint32_t kvmppc_get_dfp(void) return 0; } +static inline int kvmppc_get_hasidle(CPUPPCState *env) +{ + return 0; +} + static inline int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_len) { return -1; @@ -65,12 +73,16 @@ static inline int kvmppc_read_segment_page_sizes(uint32_t *prop, int maxcells) return -1; } -static inline int kvmppc_set_interrupt(CPUPPCState *env, int irq, int level) +static inline int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level) { return -1; } -static inline void kvmppc_set_papr(CPUPPCState *env) +static inline void kvmppc_set_papr(PowerPCCPU *cpu) +{ +} + +static inline void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy) { } @@ -115,12 +127,7 @@ static inline int kvmppc_update_sdr1(CPUPPCState *env) #endif /* !CONFIG_USER_ONLY */ -static inline const ppc_def_t *kvmppc_host_cpu_def(void) -{ - return NULL; -} - -static inline int kvmppc_fixup_cpu(CPUPPCState *env) +static inline int kvmppc_fixup_cpu(PowerPCCPU *cpu) { return -1; } diff --git a/target-ppc/machine.c b/target-ppc/machine.c index e014c0c1af..708a840da7 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -7,6 +7,7 @@ void cpu_save(QEMUFile *f, void *opaque) CPUPPCState *env = (CPUPPCState *)opaque; unsigned int i, j; uint32_t fpscr; + target_ulong xer; for (i = 0; i < 32; i++) qemu_put_betls(f, &env->gpr[i]); @@ -18,7 +19,8 @@ void cpu_save(QEMUFile *f, void *opaque) qemu_put_betls(f, &env->ctr); for (i = 0; i < 8; i++) qemu_put_be32s(f, &env->crf[i]); - qemu_put_betls(f, &env->xer); + xer = cpu_read_xer(env); + qemu_put_betls(f, &xer); qemu_put_betls(f, &env->reserve_addr); qemu_put_betls(f, &env->msr); for (i = 0; i < 4; i++) @@ -93,6 +95,7 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) unsigned int i, j; target_ulong sdr1; uint32_t fpscr; + target_ulong xer; for (i = 0; i < 32; i++) qemu_get_betls(f, &env->gpr[i]); @@ -104,7 +107,8 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id) qemu_get_betls(f, &env->ctr); for (i = 0; i < 8; i++) qemu_get_be32s(f, &env->crf[i]); - qemu_get_betls(f, &env->xer); + qemu_get_betls(f, &xer); + cpu_write_xer(env, xer); qemu_get_betls(f, &env->reserve_addr); qemu_get_betls(f, &env->msr); for (i = 0; i < 4; i++) diff --git a/target-ppc/mem_helper.c b/target-ppc/mem_helper.c index 902b1cd823..ba383c8f11 100644 --- a/target-ppc/mem_helper.c +++ b/target-ppc/mem_helper.c @@ -136,18 +136,21 @@ static void do_dcbz(CPUPPCState *env, target_ulong addr, int dcache_line_size) } } -void helper_dcbz(CPUPPCState *env, target_ulong addr) +void helper_dcbz(CPUPPCState *env, target_ulong addr, uint32_t is_dcbzl) { - do_dcbz(env, addr, env->dcache_line_size); -} + int dcbz_size = env->dcache_line_size; -void helper_dcbz_970(CPUPPCState *env, target_ulong addr) -{ - if (((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { - do_dcbz(env, addr, 32); - } else { - do_dcbz(env, addr, env->dcache_line_size); +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) + if (!is_dcbzl && + (env->excp_model == POWERPC_EXCP_970) && + ((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { + dcbz_size = 32; } +#endif + + /* XXX add e500mc support */ + + do_dcbz(env, addr, dcbz_size); } void helper_icbi(CPUPPCState *env, target_ulong addr) diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index 0aee7a9063..1cc1c1649a 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -587,7 +587,7 @@ static inline int find_pte2(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h, } r = pte64_check(ctx, pte0, pte1, h, rw, type); - LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " " + LOG_MMU("Load pte from %016" HWADDR_PRIx " => " TARGET_FMT_lx " " TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", pteg_off + (i * 16), pte0, pte1, (int)(pte0 & 1), h, (int)((pte0 >> 1) & 1), ctx->ptem); @@ -602,7 +602,7 @@ static inline int find_pte2(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h, pte1 = ldl_phys(env->htab_base + pteg_off + (i * 8) + 4); } r = pte32_check(ctx, pte0, pte1, h, rw, type); - LOG_MMU("Load pte from " TARGET_FMT_lx " => " TARGET_FMT_lx " " + LOG_MMU("Load pte from %08" HWADDR_PRIx " => " TARGET_FMT_lx " " TARGET_FMT_lx " %d %d %d " TARGET_FMT_lx "\n", pteg_off + (i * 8), pte0, pte1, (int)(pte0 >> 31), h, (int)((pte0 >> 6) & 1), ctx->ptem); @@ -633,7 +633,7 @@ static inline int find_pte2(CPUPPCState *env, mmu_ctx_t *ctx, int is_64b, int h, } if (good != -1) { done: - LOG_MMU("found PTE at addr " TARGET_FMT_lx " prot=%01x ret=%d\n", + LOG_MMU("found PTE at addr %08" HWADDR_PRIx " prot=%01x ret=%d\n", ctx->raddr, ctx->prot, ret); /* Update page flags */ pte1 = ctx->raddr; @@ -2260,8 +2260,9 @@ void helper_store_601_batu(CPUPPCState *env, uint32_t nr, target_ulong value) void helper_store_601_batl(CPUPPCState *env, uint32_t nr, target_ulong value) { +#if !defined(FLUSH_ALL_TLBS) target_ulong mask; -#if defined(FLUSH_ALL_TLBS) +#else int do_inval; #endif diff --git a/target-ppc/mpic_helper.c b/target-ppc/mpic_helper.c deleted file mode 100644 index 2c6a4d30a9..0000000000 --- a/target-ppc/mpic_helper.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * PowerPC emulation helpers for QEMU. - * - * Copyright (c) 2003-2007 Jocelyn Mayer - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ -#include "cpu.h" -#include "helper.h" - -/*****************************************************************************/ -/* SPR accesses */ - -#if !defined(CONFIG_USER_ONLY) -/* - * This is an ugly helper for EPR, which is basically the same as accessing - * the IACK (PIAC) register on the MPIC. Because we model the MPIC as a device - * that can only talk to the CPU through MMIO, let's access it that way! - */ -target_ulong helper_load_epr(CPUPPCState *env) -{ - return ldl_phys(env->mpic_cpu_base + 0xA0); -} -#endif diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 798b7acfc9..380a884131 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -66,7 +66,7 @@ static TCGv cpu_lr; #if defined(TARGET_PPC64) static TCGv cpu_cfar; #endif -static TCGv cpu_xer; +static TCGv cpu_xer, cpu_so, cpu_ov, cpu_ca; static TCGv cpu_reserve; static TCGv cpu_fpscr; static TCGv_i32 cpu_access_type; @@ -158,6 +158,12 @@ void ppc_translate_init(void) cpu_xer = tcg_global_mem_new(TCG_AREG0, offsetof(CPUPPCState, xer), "xer"); + cpu_so = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUPPCState, so), "SO"); + cpu_ov = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUPPCState, ov), "OV"); + cpu_ca = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUPPCState, ca), "CA"); cpu_reserve = tcg_global_mem_new(TCG_AREG0, offsetof(CPUPPCState, reserve_addr), @@ -590,35 +596,33 @@ static opc_handler_t invalid_handler = { static inline void gen_op_cmp(TCGv arg0, TCGv arg1, int s, int crf) { - int l1, l2, l3; + TCGv t0 = tcg_temp_new(); + TCGv_i32 t1 = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(cpu_crf[crf], cpu_xer); - tcg_gen_shri_i32(cpu_crf[crf], cpu_crf[crf], XER_SO); - tcg_gen_andi_i32(cpu_crf[crf], cpu_crf[crf], 1); + tcg_gen_trunc_tl_i32(cpu_crf[crf], cpu_so); - l1 = gen_new_label(); - l2 = gen_new_label(); - l3 = gen_new_label(); - if (s) { - tcg_gen_brcond_tl(TCG_COND_LT, arg0, arg1, l1); - tcg_gen_brcond_tl(TCG_COND_GT, arg0, arg1, l2); - } else { - tcg_gen_brcond_tl(TCG_COND_LTU, arg0, arg1, l1); - tcg_gen_brcond_tl(TCG_COND_GTU, arg0, arg1, l2); - } - tcg_gen_ori_i32(cpu_crf[crf], cpu_crf[crf], 1 << CRF_EQ); - tcg_gen_br(l3); - gen_set_label(l1); - tcg_gen_ori_i32(cpu_crf[crf], cpu_crf[crf], 1 << CRF_LT); - tcg_gen_br(l3); - gen_set_label(l2); - tcg_gen_ori_i32(cpu_crf[crf], cpu_crf[crf], 1 << CRF_GT); - gen_set_label(l3); + 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_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_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_or_i32(cpu_crf[crf], cpu_crf[crf], t1); + + tcg_temp_free(t0); + tcg_temp_free_i32(t1); } static inline void gen_op_cmpi(TCGv arg0, target_ulong arg1, int s, int crf) { - TCGv t0 = tcg_const_local_tl(arg1); + TCGv t0 = tcg_const_tl(arg1); gen_op_cmp(arg0, t0, s, crf); tcg_temp_free(t0); } @@ -627,8 +631,8 @@ static inline void gen_op_cmpi(TCGv arg0, target_ulong arg1, int s, int crf) static inline void gen_op_cmp32(TCGv arg0, TCGv arg1, int s, int crf) { TCGv t0, t1; - t0 = tcg_temp_local_new(); - t1 = tcg_temp_local_new(); + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); if (s) { tcg_gen_ext32s_tl(t0, arg0); tcg_gen_ext32s_tl(t1, arg1); @@ -643,7 +647,7 @@ static inline void gen_op_cmp32(TCGv arg0, TCGv arg1, int s, int crf) static inline void gen_op_cmpi32(TCGv arg0, target_ulong arg1, int s, int crf) { - TCGv t0 = tcg_const_local_tl(arg1); + TCGv t0 = tcg_const_tl(arg1); gen_op_cmp32(arg0, t0, s, crf); tcg_temp_free(t0); } @@ -742,120 +746,59 @@ static void gen_isel(DisasContext *ctx) static inline void gen_op_arith_compute_ov(DisasContext *ctx, TCGv arg0, TCGv arg1, TCGv arg2, int sub) { - int l1; - TCGv t0; + TCGv t0 = tcg_temp_new(); - l1 = gen_new_label(); - /* Start with XER OV disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV)); - t0 = tcg_temp_local_new(); - tcg_gen_xor_tl(t0, arg0, arg1); -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) - tcg_gen_ext32s_tl(t0, t0); -#endif - if (sub) - tcg_gen_brcondi_tl(TCG_COND_LT, t0, 0, l1); - else - tcg_gen_brcondi_tl(TCG_COND_GE, t0, 0, l1); + tcg_gen_xor_tl(cpu_ov, arg0, arg1); tcg_gen_xor_tl(t0, arg1, arg2); -#if defined(TARGET_PPC64) - if (!ctx->sf_mode) - tcg_gen_ext32s_tl(t0, t0); -#endif - if (sub) - tcg_gen_brcondi_tl(TCG_COND_GE, t0, 0, l1); - else - tcg_gen_brcondi_tl(TCG_COND_LT, t0, 0, l1); - tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO)); - gen_set_label(l1); - tcg_temp_free(t0); -} - -static inline void gen_op_arith_compute_ca(DisasContext *ctx, TCGv arg1, - TCGv arg2, int sub) -{ - int l1 = gen_new_label(); - -#if defined(TARGET_PPC64) - if (!(ctx->sf_mode)) { - TCGv t0, t1; - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - - tcg_gen_ext32u_tl(t0, arg1); - tcg_gen_ext32u_tl(t1, arg2); - if (sub) { - tcg_gen_brcond_tl(TCG_COND_GTU, t0, t1, l1); - } else { - tcg_gen_brcond_tl(TCG_COND_GEU, t0, t1, l1); - } - tcg_gen_ori_tl(cpu_xer, cpu_xer, 1 << XER_CA); - gen_set_label(l1); - tcg_temp_free(t0); - tcg_temp_free(t1); - } else -#endif - { - if (sub) { - tcg_gen_brcond_tl(TCG_COND_GTU, arg1, arg2, l1); - } else { - tcg_gen_brcond_tl(TCG_COND_GEU, arg1, arg2, l1); - } - tcg_gen_ori_tl(cpu_xer, cpu_xer, 1 << XER_CA); - gen_set_label(l1); + if (sub) { + tcg_gen_and_tl(cpu_ov, cpu_ov, t0); + } else { + tcg_gen_andc_tl(cpu_ov, cpu_ov, t0); } + tcg_temp_free(t0); +#if defined(TARGET_PPC64) + if (!ctx->sf_mode) { + tcg_gen_ext32s_tl(cpu_ov, cpu_ov); + } +#endif + tcg_gen_shri_tl(cpu_ov, cpu_ov, TARGET_LONG_BITS - 1); + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); } /* Common add function */ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, - TCGv arg2, int add_ca, int compute_ca, - int compute_ov) + TCGv arg2, bool add_ca, bool compute_ca, + bool compute_ov, bool compute_rc0) { - TCGv t0, t1; + TCGv t0 = ret; - if ((!compute_ca && !compute_ov) || - (!TCGV_EQUAL(ret,arg1) && !TCGV_EQUAL(ret, arg2))) { - t0 = ret; - } else { - t0 = tcg_temp_local_new(); + if (((compute_ca && add_ca) || compute_ov) + && (TCGV_EQUAL(ret, arg1) || TCGV_EQUAL(ret, arg2))) { + t0 = tcg_temp_new(); } - if (add_ca) { - t1 = tcg_temp_local_new(); - tcg_gen_andi_tl(t1, cpu_xer, (1 << XER_CA)); - tcg_gen_shri_tl(t1, t1, XER_CA); - } else { - TCGV_UNUSED(t1); - } - - if (compute_ca && compute_ov) { - /* Start with XER CA and OV disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~((1 << XER_CA) | (1 << XER_OV))); - } else if (compute_ca) { - /* Start with XER CA disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA)); - } else if (compute_ov) { - /* Start with XER OV disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV)); - } - - tcg_gen_add_tl(t0, arg1, arg2); - if (compute_ca) { - gen_op_arith_compute_ca(ctx, t0, arg1, 0); - } - if (add_ca) { - tcg_gen_add_tl(t0, t0, t1); - gen_op_arith_compute_ca(ctx, t0, t1, 0); - tcg_temp_free(t1); + TCGv zero = tcg_const_tl(0); + if (add_ca) { + tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, cpu_ca, zero); + tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, arg2, zero); + } else { + tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, arg2, zero); + } + tcg_temp_free(zero); + } else { + tcg_gen_add_tl(t0, arg1, arg2); + if (add_ca) { + tcg_gen_add_tl(t0, t0, cpu_ca); + } } + if (compute_ov) { gen_op_arith_compute_ov(ctx, t0, arg1, arg2, 0); } - - if (unlikely(Rc(ctx->opcode) != 0)) + if (unlikely(compute_rc0)) { gen_set_Rc0(ctx, t0); + } if (!TCGV_EQUAL(t0, ret)) { tcg_gen_mov_tl(ret, t0); @@ -864,21 +807,21 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, } /* Add functions with two operands */ #define GEN_INT_ARITH_ADD(name, opc3, add_ca, compute_ca, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ +static void glue(gen_, name)(DisasContext *ctx) \ { \ gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], \ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - add_ca, compute_ca, compute_ov); \ + add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ } /* Add functions with one operand and one immediate */ #define GEN_INT_ARITH_ADD_CONST(name, opc3, const_val, \ add_ca, compute_ca, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ +static void glue(gen_, name)(DisasContext *ctx) \ { \ - TCGv t0 = tcg_const_local_tl(const_val); \ + TCGv t0 = tcg_const_tl(const_val); \ gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], \ cpu_gpr[rA(ctx->opcode)], t0, \ - add_ca, compute_ca, compute_ov); \ + add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ tcg_temp_free(t0); \ } @@ -906,40 +849,27 @@ static void gen_addi(DisasContext *ctx) /* li case */ tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], simm); } else { - tcg_gen_addi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], simm); + tcg_gen_addi_tl(cpu_gpr[rD(ctx->opcode)], + cpu_gpr[rA(ctx->opcode)], simm); } } /* addic addic.*/ -static inline void gen_op_addic(DisasContext *ctx, TCGv ret, TCGv arg1, - int compute_Rc0) +static inline void gen_op_addic(DisasContext *ctx, bool compute_rc0) { - target_long simm = SIMM(ctx->opcode); - - /* Start with XER CA and OV disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA)); - - if (likely(simm != 0)) { - TCGv t0 = tcg_temp_local_new(); - tcg_gen_addi_tl(t0, arg1, simm); - gen_op_arith_compute_ca(ctx, t0, arg1, 0); - tcg_gen_mov_tl(ret, t0); - tcg_temp_free(t0); - } else { - tcg_gen_mov_tl(ret, arg1); - } - if (compute_Rc0) { - gen_set_Rc0(ctx, ret); - } + TCGv c = tcg_const_tl(SIMM(ctx->opcode)); + gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + c, 0, 1, 0, compute_rc0); + tcg_temp_free(c); } static void gen_addic(DisasContext *ctx) { - gen_op_addic(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0); + gen_op_addic(ctx, 0); } static void gen_addic_(DisasContext *ctx) { - gen_op_addic(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 1); + gen_op_addic(ctx, 1); } /* addis */ @@ -951,7 +881,8 @@ static void gen_addis(DisasContext *ctx) /* lis case */ tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], simm << 16); } else { - tcg_gen_addi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], simm << 16); + tcg_gen_addi_tl(cpu_gpr[rD(ctx->opcode)], + cpu_gpr[rA(ctx->opcode)], simm << 16); } } @@ -976,7 +907,7 @@ static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_divu_i32(t0, t0, t1); } if (compute_ov) { - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV)); + tcg_gen_movi_tl(cpu_ov, 0); } tcg_gen_br(l2); gen_set_label(l1); @@ -986,7 +917,8 @@ static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_movi_i32(t0, 0); } if (compute_ov) { - tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO)); + tcg_gen_movi_tl(cpu_ov, 1); + tcg_gen_movi_tl(cpu_so, 1); } gen_set_label(l2); tcg_gen_extu_i32_tl(ret, t0); @@ -1027,7 +959,7 @@ static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_divu_i64(ret, arg1, arg2); } if (compute_ov) { - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV)); + tcg_gen_movi_tl(cpu_ov, 0); } tcg_gen_br(l2); gen_set_label(l1); @@ -1037,7 +969,8 @@ static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_movi_i64(ret, 0); } if (compute_ov) { - tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO)); + tcg_gen_movi_tl(cpu_ov, 1); + tcg_gen_movi_tl(cpu_so, 1); } gen_set_label(l2); if (unlikely(Rc(ctx->opcode) != 0)) @@ -1061,24 +994,15 @@ GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1); /* mulhw mulhw. */ static void gen_mulhw(DisasContext *ctx) { - TCGv_i64 t0, t1; + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); -#if defined(TARGET_PPC64) - tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_ext32s_tl(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_mul_i64(t0, t0, t1); - tcg_gen_shri_i64(cpu_gpr[rD(ctx->opcode)], t0, 32); -#else - tcg_gen_ext_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_ext_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_mul_i64(t0, t0, t1); - tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_trunc_i64_tl(cpu_gpr[rD(ctx->opcode)], t0); -#endif - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); + tcg_gen_muls2_i32(t0, t1, t0, t1); + tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); if (unlikely(Rc(ctx->opcode) != 0)) gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -1086,24 +1010,15 @@ static void gen_mulhw(DisasContext *ctx) /* mulhwu mulhwu. */ static void gen_mulhwu(DisasContext *ctx) { - TCGv_i64 t0, t1; + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); -#if defined(TARGET_PPC64) - tcg_gen_ext32u_i64(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_ext32u_i64(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_mul_i64(t0, t0, t1); - tcg_gen_shri_i64(cpu_gpr[rD(ctx->opcode)], t0, 32); -#else - tcg_gen_extu_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_extu_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_mul_i64(t0, t0, t1); - tcg_gen_shri_i64(t0, t0, 32); - tcg_gen_trunc_i64_tl(cpu_gpr[rD(ctx->opcode)], t0); -#endif - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); + tcg_gen_mulu2_i32(t0, t1, t0, t1); + tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); if (unlikely(Rc(ctx->opcode) != 0)) gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -1121,34 +1036,21 @@ static void gen_mullw(DisasContext *ctx) /* mullwo mullwo. */ static void gen_mullwo(DisasContext *ctx) { - int l1; - TCGv_i64 t0, t1; + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - l1 = gen_new_label(); - /* Start with XER OV disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV)); -#if defined(TARGET_PPC64) - tcg_gen_ext32s_i64(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_ext32s_i64(t1, cpu_gpr[rB(ctx->opcode)]); -#else - tcg_gen_ext_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_ext_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]); -#endif - tcg_gen_mul_i64(t0, t0, t1); -#if defined(TARGET_PPC64) - tcg_gen_ext32s_i64(cpu_gpr[rD(ctx->opcode)], t0); - tcg_gen_brcond_i64(TCG_COND_EQ, t0, cpu_gpr[rD(ctx->opcode)], l1); -#else - tcg_gen_trunc_i64_tl(cpu_gpr[rD(ctx->opcode)], t0); - tcg_gen_ext32s_i64(t1, t0); - tcg_gen_brcond_i64(TCG_COND_EQ, t0, t1, l1); -#endif - tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO)); - gen_set_label(l1); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); + tcg_gen_muls2_i32(t0, t1, t0, t1); + tcg_gen_ext_i32_tl(cpu_gpr[rD(ctx->opcode)], t0); + + tcg_gen_sari_i32(t0, t0, 31); + tcg_gen_setcond_i32(TCG_COND_NE, t0, t0, t1); + tcg_gen_extu_i32_tl(cpu_ov, t0); + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); if (unlikely(Rc(ctx->opcode) != 0)) gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -1159,19 +1061,31 @@ static void gen_mulli(DisasContext *ctx) tcg_gen_muli_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode)); } + #if defined(TARGET_PPC64) -#define GEN_INT_ARITH_MUL_HELPER(name, opc3) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_helper_##name (cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \ - if (unlikely(Rc(ctx->opcode) != 0)) \ - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); \ -} /* mulhd mulhd. */ -GEN_INT_ARITH_MUL_HELPER(mulhdu, 0x00); +static void gen_mulhd(DisasContext *ctx) +{ + TCGv lo = tcg_temp_new(); + tcg_gen_muls2_tl(lo, cpu_gpr[rD(ctx->opcode)], + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + tcg_temp_free(lo); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); + } +} + /* mulhdu mulhdu. */ -GEN_INT_ARITH_MUL_HELPER(mulhd, 0x02); +static void gen_mulhdu(DisasContext *ctx) +{ + TCGv lo = tcg_temp_new(); + tcg_gen_mulu2_tl(lo, cpu_gpr[rD(ctx->opcode)], + cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); + tcg_temp_free(lo); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); + } +} /* mulld mulld. */ static void gen_mulld(DisasContext *ctx) @@ -1193,101 +1107,46 @@ static void gen_mulldo(DisasContext *ctx) } #endif -/* neg neg. nego nego. */ -static inline void gen_op_arith_neg(DisasContext *ctx, TCGv ret, TCGv arg1, - int ov_check) -{ - int l1 = gen_new_label(); - int l2 = gen_new_label(); - TCGv t0 = tcg_temp_local_new(); -#if defined(TARGET_PPC64) - if (ctx->sf_mode) { - tcg_gen_mov_tl(t0, arg1); - tcg_gen_brcondi_tl(TCG_COND_EQ, t0, INT64_MIN, l1); - } else -#endif - { - tcg_gen_ext32s_tl(t0, arg1); - tcg_gen_brcondi_tl(TCG_COND_EQ, t0, INT32_MIN, l1); - } - tcg_gen_neg_tl(ret, arg1); - if (ov_check) { - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV)); - } - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_mov_tl(ret, t0); - if (ov_check) { - tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO)); - } - gen_set_label(l2); - tcg_temp_free(t0); - if (unlikely(Rc(ctx->opcode) != 0)) - gen_set_Rc0(ctx, ret); -} - -static void gen_neg(DisasContext *ctx) -{ - gen_op_arith_neg(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0); -} - -static void gen_nego(DisasContext *ctx) -{ - gen_op_arith_neg(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 1); -} - /* Common subf function */ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, - TCGv arg2, int add_ca, int compute_ca, - int compute_ov) + TCGv arg2, bool add_ca, bool compute_ca, + bool compute_ov, bool compute_rc0) { - TCGv t0, t1; + TCGv t0 = ret; - if ((!compute_ca && !compute_ov) || - (!TCGV_EQUAL(ret, arg1) && !TCGV_EQUAL(ret, arg2))) { - t0 = ret; - } else { - t0 = tcg_temp_local_new(); + if (((add_ca && compute_ca) || compute_ov) + && (TCGV_EQUAL(ret, arg1) || TCGV_EQUAL(ret, arg2))) { + t0 = tcg_temp_new(); } if (add_ca) { - t1 = tcg_temp_local_new(); - tcg_gen_andi_tl(t1, cpu_xer, (1 << XER_CA)); - tcg_gen_shri_tl(t1, t1, XER_CA); - } else { - TCGV_UNUSED(t1); - } - - if (compute_ca && compute_ov) { - /* Start with XER CA and OV disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~((1 << XER_CA) | (1 << XER_OV))); - } else if (compute_ca) { - /* Start with XER CA disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA)); - } else if (compute_ov) { - /* Start with XER OV disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV)); - } - - if (add_ca) { - tcg_gen_not_tl(t0, arg1); - tcg_gen_add_tl(t0, t0, arg2); - gen_op_arith_compute_ca(ctx, t0, arg2, 0); - tcg_gen_add_tl(t0, t0, t1); - gen_op_arith_compute_ca(ctx, t0, t1, 0); - tcg_temp_free(t1); - } else { - tcg_gen_sub_tl(t0, arg2, arg1); + /* dest = ~arg1 + arg2 + ca. */ if (compute_ca) { - gen_op_arith_compute_ca(ctx, t0, arg2, 1); + TCGv zero, inv1 = tcg_temp_new(); + tcg_gen_not_tl(inv1, arg1); + zero = tcg_const_tl(0); + tcg_gen_add2_tl(t0, cpu_ca, arg2, zero, cpu_ca, zero); + tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, inv1, zero); + tcg_temp_free(zero); + tcg_temp_free(inv1); + } else { + tcg_gen_sub_tl(t0, arg2, arg1); + tcg_gen_add_tl(t0, t0, cpu_ca); + tcg_gen_subi_tl(t0, t0, 1); } + } else { + if (compute_ca) { + tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1); + } + tcg_gen_sub_tl(t0, arg2, arg1); } + if (compute_ov) { gen_op_arith_compute_ov(ctx, t0, arg1, arg2, 1); } - - if (unlikely(Rc(ctx->opcode) != 0)) + if (unlikely(compute_rc0)) { gen_set_Rc0(ctx, t0); + } if (!TCGV_EQUAL(t0, ret)) { tcg_gen_mov_tl(ret, t0); @@ -1296,21 +1155,21 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, } /* Sub functions with Two operands functions */ #define GEN_INT_ARITH_SUBF(name, opc3, add_ca, compute_ca, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ +static void glue(gen_, name)(DisasContext *ctx) \ { \ gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], \ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - add_ca, compute_ca, compute_ov); \ + add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ } /* Sub functions with one operand and one immediate */ #define GEN_INT_ARITH_SUBF_CONST(name, opc3, const_val, \ add_ca, compute_ca, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ +static void glue(gen_, name)(DisasContext *ctx) \ { \ - TCGv t0 = tcg_const_local_tl(const_val); \ + TCGv t0 = tcg_const_tl(const_val); \ gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], \ cpu_gpr[rA(ctx->opcode)], t0, \ - add_ca, compute_ca, compute_ov); \ + add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ tcg_temp_free(t0); \ } /* subf subf. subfo subfo. */ @@ -1332,15 +1191,29 @@ GEN_INT_ARITH_SUBF_CONST(subfzeo, 0x16, 0, 1, 1, 1) /* subfic */ static void gen_subfic(DisasContext *ctx) { - /* Start with XER CA and OV disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA)); - TCGv t0 = tcg_temp_local_new(); - TCGv t1 = tcg_const_local_tl(SIMM(ctx->opcode)); - tcg_gen_sub_tl(t0, t1, cpu_gpr[rA(ctx->opcode)]); - gen_op_arith_compute_ca(ctx, t0, t1, 1); - tcg_temp_free(t1); - tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0); - tcg_temp_free(t0); + TCGv c = tcg_const_tl(SIMM(ctx->opcode)); + gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + c, 0, 1, 0, 0); + tcg_temp_free(c); +} + +/* neg neg. nego nego. */ +static inline void gen_op_arith_neg(DisasContext *ctx, bool compute_ov) +{ + TCGv zero = tcg_const_tl(0); + gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], + zero, 0, 0, compute_ov, Rc(ctx->opcode)); + tcg_temp_free(zero); +} + +static void gen_neg(DisasContext *ctx) +{ + gen_op_arith_neg(ctx, 0); +} + +static void gen_nego(DisasContext *ctx) +{ + gen_op_arith_neg(ctx, 1); } /*** Integer logical ***/ @@ -1887,30 +1760,25 @@ static void gen_sraw(DisasContext *ctx) static void gen_srawi(DisasContext *ctx) { int sh = SH(ctx->opcode); - if (sh != 0) { - int l1, l2; - TCGv t0; - l1 = gen_new_label(); - l2 = gen_new_label(); - t0 = tcg_temp_local_new(); - tcg_gen_ext32s_tl(t0, cpu_gpr[rS(ctx->opcode)]); - tcg_gen_brcondi_tl(TCG_COND_GE, t0, 0, l1); - tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1ULL << sh) - 1); - tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); - tcg_gen_ori_tl(cpu_xer, cpu_xer, 1 << XER_CA); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA)); - gen_set_label(l2); - tcg_gen_ext32s_tl(t0, cpu_gpr[rS(ctx->opcode)]); - tcg_gen_sari_tl(cpu_gpr[rA(ctx->opcode)], t0, sh); - tcg_temp_free(t0); + TCGv dst = cpu_gpr[rA(ctx->opcode)]; + TCGv src = cpu_gpr[rS(ctx->opcode)]; + if (sh == 0) { + tcg_gen_mov_tl(dst, src); + tcg_gen_movi_tl(cpu_ca, 0); } else { - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA)); + TCGv t0; + tcg_gen_ext32s_tl(dst, src); + tcg_gen_andi_tl(cpu_ca, dst, (1ULL << sh) - 1); + t0 = tcg_temp_new(); + tcg_gen_sari_tl(t0, dst, TARGET_LONG_BITS - 1); + tcg_gen_and_tl(cpu_ca, cpu_ca, t0); + tcg_temp_free(t0); + tcg_gen_setcondi_tl(TCG_COND_NE, cpu_ca, cpu_ca, 0); + tcg_gen_sari_tl(dst, dst, sh); + } + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, dst); } - if (unlikely(Rc(ctx->opcode) != 0)) - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); } /* srw & srw. */ @@ -1970,28 +1838,24 @@ static void gen_srad(DisasContext *ctx) static inline void gen_sradi(DisasContext *ctx, int n) { int sh = SH(ctx->opcode) + (n << 5); - if (sh != 0) { - int l1, l2; - TCGv t0; - l1 = gen_new_label(); - l2 = gen_new_label(); - t0 = tcg_temp_local_new(); - tcg_gen_brcondi_tl(TCG_COND_GE, cpu_gpr[rS(ctx->opcode)], 0, l1); - tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1ULL << sh) - 1); - tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); - tcg_gen_ori_tl(cpu_xer, cpu_xer, 1 << XER_CA); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA)); - gen_set_label(l2); - tcg_temp_free(t0); - tcg_gen_sari_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], sh); + TCGv dst = cpu_gpr[rA(ctx->opcode)]; + TCGv src = cpu_gpr[rS(ctx->opcode)]; + if (sh == 0) { + tcg_gen_mov_tl(dst, src); + tcg_gen_movi_tl(cpu_ca, 0); } else { - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA)); + TCGv t0; + tcg_gen_andi_tl(cpu_ca, src, (1ULL << sh) - 1); + t0 = tcg_temp_new(); + tcg_gen_sari_tl(t0, src, TARGET_LONG_BITS - 1); + tcg_gen_and_tl(cpu_ca, cpu_ca, t0); + tcg_temp_free(t0); + tcg_gen_setcondi_tl(TCG_COND_NE, cpu_ca, cpu_ca, 0); + tcg_gen_sari_tl(dst, src, sh); + } + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_set_Rc0(ctx, dst); } - if (unlikely(Rc(ctx->opcode) != 0)) - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); } static void gen_sradi0(DisasContext *ctx) @@ -3176,9 +3040,7 @@ static void gen_stwcx_(DisasContext *ctx) { int l1; - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_xer); - tcg_gen_shri_i32(cpu_crf[0], cpu_crf[0], XER_SO); - tcg_gen_andi_i32(cpu_crf[0], cpu_crf[0], 1); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); l1 = gen_new_label(); tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1); tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); @@ -3219,9 +3081,7 @@ static void gen_stdcx_(DisasContext *ctx) #else { int l1; - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_xer); - tcg_gen_shri_i32(cpu_crf[0], cpu_crf[0], XER_SO); - tcg_gen_andi_i32(cpu_crf[0], cpu_crf[0], 1); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); l1 = gen_new_label(); tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1); tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ); @@ -3243,7 +3103,8 @@ static void gen_sync(DisasContext *ctx) static void gen_wait(DisasContext *ctx) { TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_st_i32(t0, cpu_env, offsetof(CPUPPCState, halted)); + tcg_gen_st_i32(t0, cpu_env, + -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted)); tcg_temp_free_i32(t0); /* Stop translation, as the CPU is supposed to sleep from now */ gen_exception_err(ctx, EXCP_HLT, 1); @@ -3797,12 +3658,55 @@ static void gen_tdi(DisasContext *ctx) /*** Processor control ***/ +static void gen_read_xer(TCGv dst) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + tcg_gen_mov_tl(dst, cpu_xer); + tcg_gen_shli_tl(t0, cpu_so, XER_SO); + tcg_gen_shli_tl(t1, cpu_ov, XER_OV); + tcg_gen_shli_tl(t2, cpu_ca, XER_CA); + tcg_gen_or_tl(t0, t0, t1); + tcg_gen_or_tl(dst, dst, t2); + tcg_gen_or_tl(dst, dst, t0); + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); +} + +static void gen_write_xer(TCGv src) +{ + tcg_gen_andi_tl(cpu_xer, src, + ~((1u << XER_SO) | (1u << XER_OV) | (1u << XER_CA))); + tcg_gen_shri_tl(cpu_so, src, XER_SO); + tcg_gen_shri_tl(cpu_ov, src, XER_OV); + tcg_gen_shri_tl(cpu_ca, src, XER_CA); + tcg_gen_andi_tl(cpu_so, cpu_so, 1); + tcg_gen_andi_tl(cpu_ov, cpu_ov, 1); + tcg_gen_andi_tl(cpu_ca, cpu_ca, 1); +} + /* mcrxr */ static void gen_mcrxr(DisasContext *ctx) { - tcg_gen_trunc_tl_i32(cpu_crf[crfD(ctx->opcode)], cpu_xer); - tcg_gen_shri_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)], XER_CA); - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_SO | 1 << XER_OV | 1 << XER_CA)); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 dst = cpu_crf[crfD(ctx->opcode)]; + + tcg_gen_trunc_tl_i32(t0, cpu_so); + tcg_gen_trunc_tl_i32(t1, cpu_ov); + tcg_gen_trunc_tl_i32(dst, cpu_ca); + tcg_gen_shri_i32(t0, t0, 2); + tcg_gen_shri_i32(t1, t1, 1); + tcg_gen_or_i32(dst, dst, t0); + tcg_gen_or_i32(dst, dst, t1); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + + tcg_gen_movi_tl(cpu_so, 0); + tcg_gen_movi_tl(cpu_ov, 0); + tcg_gen_movi_tl(cpu_ca, 0); } /* mfcr mfocrf */ @@ -4118,29 +4022,21 @@ static void gen_dcbtst(DisasContext *ctx) /* dcbz */ static void gen_dcbz(DisasContext *ctx) { - TCGv t0; - gen_set_access_type(ctx, ACCESS_CACHE); - /* NIP cannot be restored if the memory exception comes from an helper */ - gen_update_nip(ctx, ctx->nip - 4); - t0 = tcg_temp_new(); - gen_addr_reg_index(ctx, t0); - gen_helper_dcbz(cpu_env, t0); - tcg_temp_free(t0); -} + TCGv tcgv_addr; + TCGv_i32 tcgv_is_dcbzl; + int is_dcbzl = ctx->opcode & 0x00200000 ? 1 : 0; -static void gen_dcbz_970(DisasContext *ctx) -{ - TCGv t0; gen_set_access_type(ctx, ACCESS_CACHE); /* NIP cannot be restored if the memory exception comes from an helper */ gen_update_nip(ctx, ctx->nip - 4); - t0 = tcg_temp_new(); - gen_addr_reg_index(ctx, t0); - if (ctx->opcode & 0x00200000) - gen_helper_dcbz(cpu_env, t0); - else - gen_helper_dcbz_970(cpu_env, t0); - tcg_temp_free(t0); + tcgv_addr = tcg_temp_new(); + tcgv_is_dcbzl = tcg_const_i32(is_dcbzl); + + gen_addr_reg_index(ctx, tcgv_addr); + gen_helper_dcbz(cpu_env, tcgv_addr, tcgv_is_dcbzl); + + tcg_temp_free(tcgv_addr); + tcg_temp_free_i32(tcgv_is_dcbzl); } /* dst / dstt */ @@ -4540,10 +4436,11 @@ static void gen_abso(DisasContext *ctx) int l2 = gen_new_label(); int l3 = gen_new_label(); /* Start with XER OV disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV)); + tcg_gen_movi_tl(cpu_ov, 0); tcg_gen_brcondi_tl(TCG_COND_GE, cpu_gpr[rA(ctx->opcode)], 0, l2); tcg_gen_brcondi_tl(TCG_COND_NE, cpu_gpr[rA(ctx->opcode)], 0x80000000, l1); - tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO)); + tcg_gen_movi_tl(cpu_ov, 1); + tcg_gen_movi_tl(cpu_so, 1); tcg_gen_br(l2); gen_set_label(l1); tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); @@ -4624,7 +4521,7 @@ static void gen_dozo(DisasContext *ctx) TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); /* Start with XER OV disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV)); + tcg_gen_movi_tl(cpu_ov, 0); tcg_gen_brcond_tl(TCG_COND_GE, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], l1); tcg_gen_sub_tl(t0, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); tcg_gen_xor_tl(t1, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); @@ -4632,7 +4529,8 @@ static void gen_dozo(DisasContext *ctx) tcg_gen_andc_tl(t1, t1, t2); tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0); tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l2); - tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO)); + tcg_gen_movi_tl(cpu_ov, 1); + tcg_gen_movi_tl(cpu_so, 1); tcg_gen_br(l2); gen_set_label(l1); tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], 0); @@ -4750,7 +4648,7 @@ static void gen_mulo(DisasContext *ctx) TCGv_i64 t1 = tcg_temp_new_i64(); TCGv t2 = tcg_temp_new(); /* Start with XER OV disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV)); + tcg_gen_movi_tl(cpu_ov, 0); tcg_gen_extu_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]); tcg_gen_extu_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]); tcg_gen_mul_i64(t0, t0, t1); @@ -4760,7 +4658,8 @@ static void gen_mulo(DisasContext *ctx) tcg_gen_trunc_i64_tl(cpu_gpr[rD(ctx->opcode)], t1); tcg_gen_ext32s_i64(t1, t0); tcg_gen_brcond_i64(TCG_COND_EQ, t0, t1, l1); - tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO)); + tcg_gen_movi_tl(cpu_ov, 1); + tcg_gen_movi_tl(cpu_so, 1); gen_set_label(l1); tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); @@ -4796,7 +4695,7 @@ static void gen_nabso(DisasContext *ctx) tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); gen_set_label(l2); /* nabs never overflows */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV)); + tcg_gen_movi_tl(cpu_ov, 0); if (unlikely(Rc(ctx->opcode) != 0)) gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); } @@ -4973,10 +4872,10 @@ static void gen_sraiq(DisasContext *ctx) tcg_gen_shli_tl(t1, cpu_gpr[rS(ctx->opcode)], 32 - sh); tcg_gen_or_tl(t0, t0, t1); gen_store_spr(SPR_MQ, t0); - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA)); + tcg_gen_movi_tl(cpu_ca, 0); tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); tcg_gen_brcondi_tl(TCG_COND_GE, cpu_gpr[rS(ctx->opcode)], 0, l1); - tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_CA)); + tcg_gen_movi_tl(cpu_ca, 1); gen_set_label(l1); tcg_gen_sari_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], sh); tcg_temp_free(t0); @@ -5007,10 +4906,10 @@ static void gen_sraq(DisasContext *ctx) gen_set_label(l1); tcg_temp_free(t0); tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t1); - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA)); + tcg_gen_movi_tl(cpu_ca, 0); tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l2); tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, l2); - tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_CA)); + tcg_gen_movi_tl(cpu_ca, 1); gen_set_label(l2); tcg_temp_free(t1); tcg_temp_free(t2); @@ -5579,7 +5478,7 @@ static inline void gen_405_mulladd_insn(DisasContext *ctx, int opc2, int opc3, if (opc3 & 0x10) { /* Start with XER OV disabled, the most likely case */ - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV)); + tcg_gen_movi_tl(cpu_ov, 0); } if (opc3 & 0x01) { /* Signed */ @@ -5602,7 +5501,8 @@ static inline void gen_405_mulladd_insn(DisasContext *ctx, int opc2, int opc3, } if (opc3 & 0x10) { /* Check overflow */ - tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO)); + tcg_gen_movi_tl(cpu_ov, 1); + tcg_gen_movi_tl(cpu_so, 1); } gen_set_label(l1); tcg_gen_mov_tl(cpu_gpr[rt], t0); @@ -5990,9 +5890,7 @@ static void gen_tlbsx_40x(DisasContext *ctx) tcg_temp_free(t0); if (Rc(ctx->opcode)) { int l1 = gen_new_label(); - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_xer); - tcg_gen_shri_i32(cpu_crf[0], cpu_crf[0], XER_SO); - tcg_gen_andi_i32(cpu_crf[0], cpu_crf[0], 1); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rD(ctx->opcode)], -1, l1); tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02); gen_set_label(l1); @@ -6073,9 +5971,7 @@ static void gen_tlbsx_440(DisasContext *ctx) tcg_temp_free(t0); if (Rc(ctx->opcode)) { int l1 = gen_new_label(); - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_xer); - tcg_gen_shri_i32(cpu_crf[0], cpu_crf[0], XER_SO); - tcg_gen_andi_i32(cpu_crf[0], cpu_crf[0], 1); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rD(ctx->opcode)], -1, l1); tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02); gen_set_label(l1); @@ -8648,8 +8544,7 @@ GEN_HANDLER(dcbi, 0x1F, 0x16, 0x0E, 0x03E00001, PPC_CACHE), GEN_HANDLER(dcbst, 0x1F, 0x16, 0x01, 0x03E00001, PPC_CACHE), GEN_HANDLER(dcbt, 0x1F, 0x16, 0x08, 0x02000001, PPC_CACHE), GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x02000001, PPC_CACHE), -GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03E00001, PPC_CACHE_DCBZ), -GEN_HANDLER2(dcbz_970, "dcbz", 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZT), +GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZ), GEN_HANDLER(dst, 0x1F, 0x16, 0x0A, 0x01800001, PPC_ALTIVEC), GEN_HANDLER(dstst, 0x1F, 0x16, 0x0B, 0x02000001, PPC_ALTIVEC), GEN_HANDLER(dss, 0x1F, 0x16, 0x19, 0x019FF801, PPC_ALTIVEC), @@ -9425,7 +9320,7 @@ void cpu_dump_state (CPUPPCState *env, FILE *f, fprintf_function cpu_fprintf, cpu_fprintf(f, "NIP " TARGET_FMT_lx " LR " TARGET_FMT_lx " CTR " TARGET_FMT_lx " XER " TARGET_FMT_lx "\n", - env->nip, env->lr, env->ctr, env->xer); + env->nip, env->lr, env->ctr, cpu_read_xer(env)); cpu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx " HF " TARGET_FMT_lx " idx %d\n", env->msr, env->spr[SPR_HID0], env->hflags, env->mmu_idx); @@ -9663,7 +9558,7 @@ static inline void gen_intermediate_code_internal(CPUPPCState *env, if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_icount_start(); + gen_tb_start(); /* Set env in case of segfault during code fetch */ while (ctx.exception == POWERPC_EXCP_NONE && tcg_ctx.gen_opc_ptr < gen_opc_end) { @@ -9698,7 +9593,7 @@ static inline void gen_intermediate_code_internal(CPUPPCState *env, } LOG_DISAS("translate opcode %08x (%02x %02x %02x) (%s)\n", ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode), - opc3(ctx.opcode), little_endian ? "little" : "big"); + opc3(ctx.opcode), ctx.le_mode ? "little" : "big"); if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { tcg_gen_debug_insn_start(ctx.nip); } @@ -9775,7 +9670,7 @@ static inline void gen_intermediate_code_internal(CPUPPCState *env, /* Generate the return instruction */ tcg_gen_exit_tb(0); } - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; if (unlikely(search_pc)) { j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 42ed748b59..15eebe9177 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -18,23 +18,17 @@ * License along with this library; if not, see . */ -/* A lot of PowerPC definition have been included here. - * Most of them are not usable for now but have been kept - * inside "#if defined(TODO) ... #endif" statements to make tests easier. - */ - #include "disas/bfd.h" #include "exec/gdbstub.h" #include #include "kvm_ppc.h" #include "sysemu/arch_init.h" +#include "sysemu/cpus.h" +#include "cpu-models.h" //#define PPC_DUMP_CPU //#define PPC_DEBUG_SPR //#define PPC_DUMP_SPR_ACCESSES -#if defined(CONFIG_USER_ONLY) -#define TODO_USER_ONLY 1 -#endif /* For user-mode emulation, we don't emulate any IRQ controller */ #if defined(CONFIG_USER_ONLY) @@ -60,7 +54,7 @@ static void spr_load_dump_spr(int sprn) { #ifdef PPC_DUMP_SPR_ACCESSES TCGv_i32 t0 = tcg_const_i32(sprn); - gen_helper_load_dump_spr(t0); + gen_helper_load_dump_spr(cpu_env, t0); tcg_temp_free_i32(t0); #endif } @@ -75,7 +69,7 @@ static void spr_store_dump_spr(int sprn) { #ifdef PPC_DUMP_SPR_ACCESSES TCGv_i32 t0 = tcg_const_i32(sprn); - gen_helper_store_dump_spr(t0); + gen_helper_store_dump_spr(cpu_env, t0); tcg_temp_free_i32(t0); #endif } @@ -117,12 +111,12 @@ static void spr_write_clear (void *opaque, int sprn, int gprn) /* XER */ static void spr_read_xer (void *opaque, int gprn, int sprn) { - tcg_gen_mov_tl(cpu_gpr[gprn], cpu_xer); + gen_read_xer(cpu_gpr[gprn]); } static void spr_write_xer (void *opaque, int sprn, int gprn) { - tcg_gen_mov_tl(cpu_xer, cpu_gpr[gprn]); + gen_write_xer(cpu_gpr[gprn]); } /* LR */ @@ -578,26 +572,42 @@ static inline void vscr_init (CPUPPCState *env, uint32_t val) set_flush_to_zero(vscr_nj, &env->vec_status); } -#if defined(CONFIG_USER_ONLY) -#define spr_register(env, num, name, uea_read, uea_write, \ - oea_read, oea_write, initial_value) \ -do { \ - _spr_register(env, num, name, uea_read, uea_write, initial_value); \ -} while (0) -static inline void _spr_register (CPUPPCState *env, int num, - const char *name, - void (*uea_read)(void *opaque, int gprn, int sprn), - void (*uea_write)(void *opaque, int sprn, int gprn), - target_ulong initial_value) +#ifdef CONFIG_USER_ONLY +#define spr_register_kvm(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, initial_value) #else -static inline void spr_register (CPUPPCState *env, int num, +#if !defined(CONFIG_KVM) +#define spr_register_kvm(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, initial_value) +#else +#define spr_register_kvm(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, one_reg_id, initial_value) \ + _spr_register(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, one_reg_id, initial_value) +#endif +#endif + +#define spr_register(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, initial_value) \ + spr_register_kvm(env, num, name, uea_read, uea_write, \ + oea_read, oea_write, 0, initial_value) + +static inline void _spr_register(CPUPPCState *env, int num, const char *name, void (*uea_read)(void *opaque, int gprn, int sprn), void (*uea_write)(void *opaque, int sprn, int gprn), +#if !defined(CONFIG_USER_ONLY) + void (*oea_read)(void *opaque, int gprn, int sprn), void (*oea_write)(void *opaque, int sprn, int gprn), - target_ulong initial_value) #endif +#if defined(CONFIG_KVM) + uint64_t one_reg_id, +#endif + target_ulong initial_value) { ppc_spr_t *spr; @@ -673,14 +683,14 @@ static void gen_spr_generic (CPUPPCState *env) static void gen_spr_ne_601 (CPUPPCState *env) { /* Exception processing */ - spr_register(env, SPR_DSISR, "DSISR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - spr_register(env, SPR_DAR, "DAR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); + spr_register_kvm(env, SPR_DSISR, "DSISR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DSISR, 0x00000000); + spr_register_kvm(env, SPR_DAR, "DAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DAR, 0x00000000); /* Timer */ spr_register(env, SPR_DECR, "DECR", SPR_NOACCESS, SPR_NOACCESS, @@ -924,10 +934,10 @@ static void gen_spr_7xx (CPUPPCState *env) { /* Breakpoints */ /* XXX : not implemented */ - spr_register(env, SPR_DABR, "DABR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); + spr_register_kvm(env, SPR_DABR, "DABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DABR, 0x00000000); /* XXX : not implemented */ spr_register(env, SPR_IABR, "IABR", SPR_NOACCESS, SPR_NOACCESS, @@ -1053,10 +1063,10 @@ static void gen_spr_604 (CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* XXX : not implemented */ - spr_register(env, SPR_DABR, "DABR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); + spr_register_kvm(env, SPR_DABR, "DABR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DABR, 0x00000000); /* Performance counters */ /* XXX : not implemented */ spr_register(env, SPR_MMCR0, "MMCR0", @@ -2311,14 +2321,14 @@ static void gen_spr_620 (CPUPPCState *env) static void gen_spr_5xx_8xx (CPUPPCState *env) { /* Exception processing */ - spr_register(env, SPR_DSISR, "DSISR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - spr_register(env, SPR_DAR, "DAR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); + spr_register_kvm(env, SPR_DSISR, "DSISR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DSISR, 0x00000000); + spr_register_kvm(env, SPR_DAR, "DAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DAR, 0x00000000); /* Timer */ spr_register(env, SPR_DECR, "DECR", SPR_NOACCESS, SPR_NOACCESS, @@ -3251,22 +3261,27 @@ static int check_pow_hid0_74xx (CPUPPCState *env) /*****************************************************************************/ /* PowerPC implementations definitions */ -/* PowerPC 401 */ -#define POWERPC_INSNS_401 (PPC_INSNS_BASE | PPC_STRING | \ - PPC_WRTEE | PPC_DCR | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \ - PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_4xx_COMMON | PPC_40x_EXCP) -#define POWERPC_INSNS2_401 (PPC_NONE) -#define POWERPC_MSRM_401 (0x00000000000FD201ULL) -#define POWERPC_MMU_401 (POWERPC_MMU_REAL) -#define POWERPC_EXCP_401 (POWERPC_EXCP_40x) -#define POWERPC_INPUT_401 (PPC_FLAGS_INPUT_401) -#define POWERPC_BFDM_401 (bfd_mach_ppc_403) -#define POWERPC_FLAG_401 (POWERPC_FLAG_CE | POWERPC_FLAG_DE | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_401 check_pow_nocheck +#define POWERPC_FAMILY(_name) \ + static void \ + glue(glue(ppc_, _name), _cpu_family_class_init)(ObjectClass *, void *); \ + \ + static const TypeInfo \ + glue(glue(ppc_, _name), _cpu_family_type_info) = { \ + .name = stringify(_name) "-family-" TYPE_POWERPC_CPU, \ + .parent = TYPE_POWERPC_CPU, \ + .abstract = true, \ + .class_init = glue(glue(ppc_, _name), _cpu_family_class_init), \ + }; \ + \ + static void glue(glue(ppc_, _name), _cpu_family_register_types)(void) \ + { \ + type_register_static( \ + &glue(glue(ppc_, _name), _cpu_family_type_info)); \ + } \ + \ + type_init(glue(glue(ppc_, _name), _cpu_family_register_types)) \ + \ + static void glue(glue(ppc_, _name), _cpu_family_class_init) static void init_proc_401 (CPUPPCState *env) { @@ -3283,23 +3298,29 @@ static void init_proc_401 (CPUPPCState *env) SET_WDT_PERIOD(16, 20, 24, 28); } -/* PowerPC 401x2 */ -#define POWERPC_INSNS_401x2 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_DCR | PPC_WRTEE | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \ - PPC_4xx_COMMON | PPC_40x_EXCP) -#define POWERPC_INSNS2_401x2 (PPC_NONE) -#define POWERPC_MSRM_401x2 (0x00000000001FD231ULL) -#define POWERPC_MMU_401x2 (POWERPC_MMU_SOFT_4xx_Z) -#define POWERPC_EXCP_401x2 (POWERPC_EXCP_40x) -#define POWERPC_INPUT_401x2 (PPC_FLAGS_INPUT_401) -#define POWERPC_BFDM_401x2 (bfd_mach_ppc_403) -#define POWERPC_FLAG_401x2 (POWERPC_FLAG_CE | POWERPC_FLAG_DE | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_401x2 check_pow_nocheck +POWERPC_FAMILY(401)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 401"; + pcc->init_proc = init_proc_401; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_WRTEE | PPC_DCR | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_4xx_COMMON | PPC_40x_EXCP; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x00000000000FD201ULL; + pcc->mmu_model = POWERPC_MMU_REAL; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_401; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} static void init_proc_401x2 (CPUPPCState *env) { @@ -3324,25 +3345,31 @@ static void init_proc_401x2 (CPUPPCState *env) SET_WDT_PERIOD(16, 20, 24, 28); } -/* PowerPC 401x3 */ -#define POWERPC_INSNS_401x3 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_DCR | PPC_WRTEE | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \ - PPC_4xx_COMMON | PPC_40x_EXCP) -#define POWERPC_INSNS2_401x3 (PPC_NONE) -#define POWERPC_MSRM_401x3 (0x00000000001FD631ULL) -#define POWERPC_MMU_401x3 (POWERPC_MMU_SOFT_4xx_Z) -#define POWERPC_EXCP_401x3 (POWERPC_EXCP_40x) -#define POWERPC_INPUT_401x3 (PPC_FLAGS_INPUT_401) -#define POWERPC_BFDM_401x3 (bfd_mach_ppc_403) -#define POWERPC_FLAG_401x3 (POWERPC_FLAG_CE | POWERPC_FLAG_DE | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_401x3 check_pow_nocheck +POWERPC_FAMILY(401x2)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 401x2"; + pcc->init_proc = init_proc_401x2; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | + PPC_4xx_COMMON | PPC_40x_EXCP; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x00000000001FD231ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_4xx_Z; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_401; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_401x3 (CPUPPCState *env) { gen_spr_40x(env); @@ -3360,23 +3387,30 @@ static void init_proc_401x3 (CPUPPCState *env) SET_WDT_PERIOD(16, 20, 24, 28); } -/* IOP480 */ -#define POWERPC_INSNS_IOP480 (PPC_INSNS_BASE | PPC_STRING | \ - PPC_DCR | PPC_WRTEE | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \ - PPC_4xx_COMMON | PPC_40x_EXCP) -#define POWERPC_INSNS2_IOP480 (PPC_NONE) -#define POWERPC_MSRM_IOP480 (0x00000000001FD231ULL) -#define POWERPC_MMU_IOP480 (POWERPC_MMU_SOFT_4xx_Z) -#define POWERPC_EXCP_IOP480 (POWERPC_EXCP_40x) -#define POWERPC_INPUT_IOP480 (PPC_FLAGS_INPUT_401) -#define POWERPC_BFDM_IOP480 (bfd_mach_ppc_403) -#define POWERPC_FLAG_IOP480 (POWERPC_FLAG_CE | POWERPC_FLAG_DE | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_IOP480 check_pow_nocheck +POWERPC_FAMILY(401x3)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 401x3"; + pcc->init_proc = init_proc_401x3; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | + PPC_4xx_COMMON | PPC_40x_EXCP; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x00000000001FD631ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_4xx_Z; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_401; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} static void init_proc_IOP480 (CPUPPCState *env) { @@ -3401,22 +3435,30 @@ static void init_proc_IOP480 (CPUPPCState *env) SET_WDT_PERIOD(16, 20, 24, 28); } -/* PowerPC 403 */ -#define POWERPC_INSNS_403 (PPC_INSNS_BASE | PPC_STRING | \ - PPC_DCR | PPC_WRTEE | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \ - PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_4xx_COMMON | PPC_40x_EXCP) -#define POWERPC_INSNS2_403 (PPC_NONE) -#define POWERPC_MSRM_403 (0x000000000007D00DULL) -#define POWERPC_MMU_403 (POWERPC_MMU_REAL) -#define POWERPC_EXCP_403 (POWERPC_EXCP_40x) -#define POWERPC_INPUT_403 (PPC_FLAGS_INPUT_401) -#define POWERPC_BFDM_403 (bfd_mach_ppc_403) -#define POWERPC_FLAG_403 (POWERPC_FLAG_CE | POWERPC_FLAG_PX | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_403 check_pow_nocheck +POWERPC_FAMILY(IOP480)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "IOP480"; + pcc->init_proc = init_proc_IOP480; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | + PPC_4xx_COMMON | PPC_40x_EXCP; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x00000000001FD231ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_4xx_Z; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_401; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} static void init_proc_403 (CPUPPCState *env) { @@ -3434,23 +3476,29 @@ static void init_proc_403 (CPUPPCState *env) SET_WDT_PERIOD(16, 20, 24, 28); } -/* PowerPC 403 GCX */ -#define POWERPC_INSNS_403GCX (PPC_INSNS_BASE | PPC_STRING | \ - PPC_DCR | PPC_WRTEE | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \ - PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \ - PPC_4xx_COMMON | PPC_40x_EXCP) -#define POWERPC_INSNS2_403GCX (PPC_NONE) -#define POWERPC_MSRM_403GCX (0x000000000007D00DULL) -#define POWERPC_MMU_403GCX (POWERPC_MMU_SOFT_4xx_Z) -#define POWERPC_EXCP_403GCX (POWERPC_EXCP_40x) -#define POWERPC_INPUT_403GCX (PPC_FLAGS_INPUT_401) -#define POWERPC_BFDM_403GCX (bfd_mach_ppc_403) -#define POWERPC_FLAG_403GCX (POWERPC_FLAG_CE | POWERPC_FLAG_PX | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_403GCX check_pow_nocheck +POWERPC_FAMILY(403)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 403"; + pcc->init_proc = init_proc_403; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_4xx_COMMON | PPC_40x_EXCP; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000007D00DULL; + pcc->mmu_model = POWERPC_MMU_REAL; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_401; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_PX | + POWERPC_FLAG_BUS_CLK; +} static void init_proc_403GCX (CPUPPCState *env) { @@ -3487,23 +3535,30 @@ static void init_proc_403GCX (CPUPPCState *env) SET_WDT_PERIOD(16, 20, 24, 28); } -/* PowerPC 405 */ -#define POWERPC_INSNS_405 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_DCR | PPC_WRTEE | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \ - PPC_4xx_COMMON | PPC_405_MAC | PPC_40x_EXCP) -#define POWERPC_INSNS2_405 (PPC_NONE) -#define POWERPC_MSRM_405 (0x000000000006E630ULL) -#define POWERPC_MMU_405 (POWERPC_MMU_SOFT_4xx) -#define POWERPC_EXCP_405 (POWERPC_EXCP_40x) -#define POWERPC_INPUT_405 (PPC_FLAGS_INPUT_405) -#define POWERPC_BFDM_405 (bfd_mach_ppc_403) -#define POWERPC_FLAG_405 (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \ - POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK) -#define check_pow_405 check_pow_nocheck +POWERPC_FAMILY(403GCX)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 403 GCX"; + pcc->init_proc = init_proc_403GCX; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | + PPC_4xx_COMMON | PPC_40x_EXCP; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000007D00DULL; + pcc->mmu_model = POWERPC_MMU_SOFT_4xx_Z; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_401; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_PX | + POWERPC_FLAG_BUS_CLK; +} static void init_proc_405 (CPUPPCState *env) { @@ -3539,26 +3594,30 @@ static void init_proc_405 (CPUPPCState *env) SET_WDT_PERIOD(16, 20, 24, 28); } -/* PowerPC 440 EP */ -#define POWERPC_INSNS_440EP (PPC_INSNS_BASE | PPC_STRING | \ - PPC_FLOAT | PPC_FLOAT_FRES | PPC_FLOAT_FSEL | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_DCR | PPC_WRTEE | PPC_RFMCI | \ - PPC_CACHE | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_TLBSYNC | PPC_MFTB | \ - PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \ - PPC_440_SPEC) -#define POWERPC_INSNS2_440EP (PPC_NONE) -#define POWERPC_MSRM_440EP (0x000000000006FF30ULL) -#define POWERPC_MMU_440EP (POWERPC_MMU_BOOKE) -#define POWERPC_EXCP_440EP (POWERPC_EXCP_BOOKE) -#define POWERPC_INPUT_440EP (PPC_FLAGS_INPUT_BookE) -#define POWERPC_BFDM_440EP (bfd_mach_ppc_403) -#define POWERPC_FLAG_440EP (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \ - POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK) -#define check_pow_440EP check_pow_nocheck +POWERPC_FAMILY(405)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 405"; + pcc->init_proc = init_proc_405; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | + PPC_4xx_COMMON | PPC_405_MAC | PPC_40x_EXCP; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000006E630ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_4xx; + pcc->excp_model = POWERPC_EXCP_40x; + pcc->bus_model = PPC_FLAGS_INPUT_405; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} static void init_proc_440EP (CPUPPCState *env) { @@ -3626,25 +3685,34 @@ static void init_proc_440EP (CPUPPCState *env) SET_WDT_PERIOD(20, 24, 28, 32); } -/* PowerPC 440 GP */ -#define POWERPC_INSNS_440GP (PPC_INSNS_BASE | PPC_STRING | \ - PPC_DCR | PPC_DCRX | PPC_WRTEE | PPC_MFAPIDI | \ - PPC_CACHE | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_TLBSYNC | PPC_TLBIVA | PPC_MFTB | \ - PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \ - PPC_440_SPEC) -#define POWERPC_INSNS2_440GP (PPC_NONE) -#define POWERPC_MSRM_440GP (0x000000000006FF30ULL) -#define POWERPC_MMU_440GP (POWERPC_MMU_BOOKE) -#define POWERPC_EXCP_440GP (POWERPC_EXCP_BOOKE) -#define POWERPC_INPUT_440GP (PPC_FLAGS_INPUT_BookE) -#define POWERPC_BFDM_440GP (bfd_mach_ppc_403) -#define POWERPC_FLAG_440GP (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \ - POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK) -#define check_pow_440GP check_pow_nocheck +POWERPC_FAMILY(440EP)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 440 EP"; + pcc->init_proc = init_proc_440EP; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_FLOAT | PPC_FLOAT_FRES | PPC_FLOAT_FSEL | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_DCR | PPC_WRTEE | PPC_RFMCI | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_MFTB | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000006FF30ULL; + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_440GP (CPUPPCState *env) { /* Time base */ @@ -3693,25 +3761,31 @@ static void init_proc_440GP (CPUPPCState *env) SET_WDT_PERIOD(20, 24, 28, 32); } -/* PowerPC 440x4 */ -#define POWERPC_INSNS_440x4 (PPC_INSNS_BASE | PPC_STRING | \ - PPC_DCR | PPC_WRTEE | \ - PPC_CACHE | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_TLBSYNC | PPC_MFTB | \ - PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \ - PPC_440_SPEC) -#define POWERPC_INSNS2_440x4 (PPC_NONE) -#define POWERPC_MSRM_440x4 (0x000000000006FF30ULL) -#define POWERPC_MMU_440x4 (POWERPC_MMU_BOOKE) -#define POWERPC_EXCP_440x4 (POWERPC_EXCP_BOOKE) -#define POWERPC_INPUT_440x4 (PPC_FLAGS_INPUT_BookE) -#define POWERPC_BFDM_440x4 (bfd_mach_ppc_403) -#define POWERPC_FLAG_440x4 (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \ - POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK) -#define check_pow_440x4 check_pow_nocheck +POWERPC_FAMILY(440GP)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 440 GP"; + pcc->init_proc = init_proc_440GP; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_DCRX | PPC_WRTEE | PPC_MFAPIDI | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_TLBIVA | PPC_MFTB | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000006FF30ULL; + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_440x4 (CPUPPCState *env) { /* Time base */ @@ -3760,23 +3834,30 @@ static void init_proc_440x4 (CPUPPCState *env) SET_WDT_PERIOD(20, 24, 28, 32); } -/* PowerPC 440x5 */ -#define POWERPC_INSNS_440x5 (PPC_INSNS_BASE | PPC_STRING | \ - PPC_DCR | PPC_WRTEE | PPC_RFMCI | \ - PPC_CACHE | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_TLBSYNC | PPC_MFTB | \ - PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \ - PPC_440_SPEC) -#define POWERPC_INSNS2_440x5 (PPC_NONE) -#define POWERPC_MSRM_440x5 (0x000000000006FF30ULL) -#define POWERPC_MMU_440x5 (POWERPC_MMU_BOOKE) -#define POWERPC_EXCP_440x5 (POWERPC_EXCP_BOOKE) -#define POWERPC_INPUT_440x5 (PPC_FLAGS_INPUT_BookE) -#define POWERPC_BFDM_440x5 (bfd_mach_ppc_403) -#define POWERPC_FLAG_440x5 (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \ - POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK) -#define check_pow_440x5 check_pow_nocheck +POWERPC_FAMILY(440x4)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 440x4"; + pcc->init_proc = init_proc_440x4; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_WRTEE | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_MFTB | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000006FF30ULL; + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} static void init_proc_440x5 (CPUPPCState *env) { @@ -3844,26 +3925,31 @@ static void init_proc_440x5 (CPUPPCState *env) SET_WDT_PERIOD(20, 24, 28, 32); } -/* PowerPC 460 (guessed) */ -#define POWERPC_INSNS_460 (PPC_INSNS_BASE | PPC_STRING | \ - PPC_DCR | PPC_DCRX | PPC_DCRUX | \ - PPC_WRTEE | PPC_MFAPIDI | PPC_MFTB | \ - PPC_CACHE | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_TLBSYNC | PPC_TLBIVA | \ - PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \ - PPC_440_SPEC) -#define POWERPC_INSNS2_460 (PPC_NONE) -#define POWERPC_MSRM_460 (0x000000000006FF30ULL) -#define POWERPC_MMU_460 (POWERPC_MMU_BOOKE) -#define POWERPC_EXCP_460 (POWERPC_EXCP_BOOKE) -#define POWERPC_INPUT_460 (PPC_FLAGS_INPUT_BookE) -#define POWERPC_BFDM_460 (bfd_mach_ppc_403) -#define POWERPC_FLAG_460 (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \ - POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK) -#define check_pow_460 check_pow_nocheck +POWERPC_FAMILY(440x5)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 440x5"; + pcc->init_proc = init_proc_440x5; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_WRTEE | PPC_RFMCI | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_MFTB | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000006FF30ULL; + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_460 (CPUPPCState *env) { /* Time base */ @@ -3935,29 +4021,32 @@ static void init_proc_460 (CPUPPCState *env) SET_WDT_PERIOD(20, 24, 28, 32); } -/* PowerPC 460F (guessed) */ -#define POWERPC_INSNS_460F (PPC_INSNS_BASE | PPC_STRING | \ - PPC_FLOAT | PPC_FLOAT_FRES | PPC_FLOAT_FSEL | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | PPC_MFTB | \ - PPC_DCR | PPC_DCRX | PPC_DCRUX | \ - PPC_WRTEE | PPC_MFAPIDI | \ - PPC_CACHE | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_TLBSYNC | PPC_TLBIVA | \ - PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \ - PPC_440_SPEC) -#define POWERPC_INSNS2_460F (PPC_NONE) -#define POWERPC_MSRM_460 (0x000000000006FF30ULL) -#define POWERPC_MMU_460F (POWERPC_MMU_BOOKE) -#define POWERPC_EXCP_460F (POWERPC_EXCP_BOOKE) -#define POWERPC_INPUT_460F (PPC_FLAGS_INPUT_BookE) -#define POWERPC_BFDM_460F (bfd_mach_ppc_403) -#define POWERPC_FLAG_460F (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \ - POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK) -#define check_pow_460F check_pow_nocheck +POWERPC_FAMILY(460)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 460 (guessed)"; + pcc->init_proc = init_proc_460; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_DCR | PPC_DCRX | PPC_DCRUX | + PPC_WRTEE | PPC_MFAPIDI | PPC_MFTB | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_TLBIVA | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000006FF30ULL; + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_460F (CPUPPCState *env) { /* Time base */ @@ -4029,22 +4118,35 @@ static void init_proc_460F (CPUPPCState *env) SET_WDT_PERIOD(20, 24, 28, 32); } -/* Freescale 5xx cores (aka RCPU) */ -#define POWERPC_INSNS_MPC5xx (PPC_INSNS_BASE | PPC_STRING | \ - PPC_MEM_EIEIO | PPC_MEM_SYNC | \ - PPC_CACHE_ICBI | PPC_FLOAT | PPC_FLOAT_STFIWX | \ - PPC_MFTB) -#define POWERPC_INSNS2_MPC5xx (PPC_NONE) -#define POWERPC_MSRM_MPC5xx (0x000000000001FF43ULL) -#define POWERPC_MMU_MPC5xx (POWERPC_MMU_REAL) -#define POWERPC_EXCP_MPC5xx (POWERPC_EXCP_603) -#define POWERPC_INPUT_MPC5xx (PPC_FLAGS_INPUT_RCPU) -#define POWERPC_BFDM_MPC5xx (bfd_mach_ppc_505) -#define POWERPC_FLAG_MPC5xx (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_MPC5xx check_pow_none +POWERPC_FAMILY(460F)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 460F (guessed)"; + pcc->init_proc = init_proc_460F; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_FLOAT | PPC_FLOAT_FRES | PPC_FLOAT_FSEL | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | PPC_MFTB | + PPC_DCR | PPC_DCRX | PPC_DCRUX | + PPC_WRTEE | PPC_MFAPIDI | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_TLBIVA | + PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | + PPC_440_SPEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000006FF30ULL; + pcc->mmu_model = POWERPC_MMU_BOOKE; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_403; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | + POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_MPC5xx (CPUPPCState *env) { /* Time base */ @@ -4057,21 +4159,28 @@ static void init_proc_MPC5xx (CPUPPCState *env) /* XXX: TODO: allocate internal IRQ controller */ } -/* Freescale 8xx cores (aka PowerQUICC) */ -#define POWERPC_INSNS_MPC8xx (PPC_INSNS_BASE | PPC_STRING | \ - PPC_MEM_EIEIO | PPC_MEM_SYNC | \ - PPC_CACHE_ICBI | PPC_MFTB) -#define POWERPC_INSNS2_MPC8xx (PPC_NONE) -#define POWERPC_MSRM_MPC8xx (0x000000000001F673ULL) -#define POWERPC_MMU_MPC8xx (POWERPC_MMU_MPC8xx) -#define POWERPC_EXCP_MPC8xx (POWERPC_EXCP_603) -#define POWERPC_INPUT_MPC8xx (PPC_FLAGS_INPUT_RCPU) -#define POWERPC_BFDM_MPC8xx (bfd_mach_ppc_860) -#define POWERPC_FLAG_MPC8xx (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_MPC8xx check_pow_none +POWERPC_FAMILY(MPC5xx)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "Freescale 5xx cores (aka RCPU)"; + pcc->init_proc = init_proc_MPC5xx; + pcc->check_pow = check_pow_none; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_MEM_EIEIO | PPC_MEM_SYNC | + PPC_CACHE_ICBI | PPC_FLOAT | PPC_FLOAT_STFIWX | + PPC_MFTB; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000001FF43ULL; + pcc->mmu_model = POWERPC_MMU_REAL; + pcc->excp_model = POWERPC_EXCP_603; + pcc->bus_model = PPC_FLAGS_INPUT_RCPU; + pcc->bfd_mach = bfd_mach_ppc_505; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_MPC8xx (CPUPPCState *env) { /* Time base */ @@ -4084,24 +4193,28 @@ static void init_proc_MPC8xx (CPUPPCState *env) /* XXX: TODO: allocate internal IRQ controller */ } +POWERPC_FAMILY(MPC8xx)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "Freescale 8xx cores (aka PowerQUICC)"; + pcc->init_proc = init_proc_MPC8xx; + pcc->check_pow = check_pow_none; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | + PPC_MEM_EIEIO | PPC_MEM_SYNC | + PPC_CACHE_ICBI | PPC_MFTB; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000001F673ULL; + pcc->mmu_model = POWERPC_MMU_MPC8xx; + pcc->excp_model = POWERPC_EXCP_603; + pcc->bus_model = PPC_FLAGS_INPUT_RCPU; + pcc->bfd_mach = bfd_mach_ppc_860; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_BUS_CLK; +} + /* Freescale 82xx cores (aka PowerQUICC-II) */ -/* PowerPC G2 */ -#define POWERPC_INSNS_G2 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_G2 (PPC_NONE) -#define POWERPC_MSRM_G2 (0x000000000006FFF2ULL) -#define POWERPC_MMU_G2 (POWERPC_MMU_SOFT_6xx) -//#define POWERPC_EXCP_G2 (POWERPC_EXCP_G2) -#define POWERPC_INPUT_G2 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_G2 (bfd_mach_ppc_ec603e) -#define POWERPC_FLAG_G2 (POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK) -#define check_pow_G2 check_pow_hid0 static void init_proc_G2 (CPUPPCState *env) { @@ -4143,23 +4256,30 @@ static void init_proc_G2 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC G2LE */ -#define POWERPC_INSNS_G2LE (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_G2LE (PPC_NONE) -#define POWERPC_MSRM_G2LE (0x000000000007FFF3ULL) -#define POWERPC_MMU_G2LE (POWERPC_MMU_SOFT_6xx) -#define POWERPC_EXCP_G2LE (POWERPC_EXCP_G2) -#define POWERPC_INPUT_G2LE (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_G2LE (bfd_mach_ppc_ec603e) -#define POWERPC_FLAG_G2LE (POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK) -#define check_pow_G2LE check_pow_hid0 +POWERPC_FAMILY(G2)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC G2"; + pcc->init_proc = init_proc_G2; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000006FFF2ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_G2; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_ec603e; + pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; +} static void init_proc_G2LE (CPUPPCState *env) { @@ -4201,35 +4321,31 @@ static void init_proc_G2LE (CPUPPCState *env) ppc6xx_irq_init(env); } -/* e200 core */ -/* XXX: unimplemented instructions: - * dcblc - * dcbtlst - * dcbtstls - * icblc - * icbtls - * tlbivax - * all SPE multiply-accumulate instructions - */ -#define POWERPC_INSNS_e200 (PPC_INSNS_BASE | PPC_ISEL | \ - PPC_SPE | PPC_SPE_SINGLE | \ - PPC_WRTEE | PPC_RFDI | \ - PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_TLBSYNC | PPC_TLBIVAX | \ - PPC_BOOKE) -#define POWERPC_INSNS2_e200 (PPC_NONE) -#define POWERPC_MSRM_e200 (0x000000000606FF30ULL) -#define POWERPC_MMU_e200 (POWERPC_MMU_BOOKE206) -#define POWERPC_EXCP_e200 (POWERPC_EXCP_BOOKE) -#define POWERPC_INPUT_e200 (PPC_FLAGS_INPUT_BookE) -#define POWERPC_BFDM_e200 (bfd_mach_ppc_860) -#define POWERPC_FLAG_e200 (POWERPC_FLAG_SPE | POWERPC_FLAG_CE | \ - POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_e200 check_pow_hid0 +POWERPC_FAMILY(G2LE)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC G2LE"; + pcc->init_proc = init_proc_G2LE; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000007FFF3ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_G2; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_ec603e; + pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_e200 (CPUPPCState *env) { /* Time base */ @@ -4337,25 +4453,41 @@ static void init_proc_e200 (CPUPPCState *env) /* XXX: TODO: allocate internal IRQ controller */ } -/* e300 core */ -#define POWERPC_INSNS_e300 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_e300 (PPC_NONE) -#define POWERPC_MSRM_e300 (0x000000000007FFF3ULL) -#define POWERPC_MMU_e300 (POWERPC_MMU_SOFT_6xx) -#define POWERPC_EXCP_e300 (POWERPC_EXCP_603) -#define POWERPC_INPUT_e300 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_e300 (bfd_mach_ppc_603) -#define POWERPC_FLAG_e300 (POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK) -#define check_pow_e300 check_pow_hid0 +POWERPC_FAMILY(e200)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "e200 core"; + pcc->init_proc = init_proc_e200; + pcc->check_pow = check_pow_hid0; + /* XXX: unimplemented instructions: + * dcblc + * dcbtlst + * dcbtstls + * icblc + * icbtls + * tlbivax + * all SPE multiply-accumulate instructions + */ + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | + PPC_SPE | PPC_SPE_SINGLE | + PPC_WRTEE | PPC_RFDI | + PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_TLBIVAX | + PPC_BOOKE; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000606FF30ULL; + pcc->mmu_model = POWERPC_MMU_BOOKE206; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_860; + pcc->flags = POWERPC_FLAG_SPE | POWERPC_FLAG_CE | + POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_e300 (CPUPPCState *env) { gen_spr_ne_601(env); @@ -4389,86 +4521,30 @@ static void init_proc_e300 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* e500v1 core */ -#define POWERPC_INSNS_e500v1 (PPC_INSNS_BASE | PPC_ISEL | \ - PPC_SPE | PPC_SPE_SINGLE | \ - PPC_WRTEE | PPC_RFDI | \ - PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC) -#define POWERPC_INSNS2_e500v1 (PPC2_BOOKE206) -#define POWERPC_MSRM_e500v1 (0x000000000606FF30ULL) -#define POWERPC_MMU_e500v1 (POWERPC_MMU_BOOKE206) -#define POWERPC_EXCP_e500v1 (POWERPC_EXCP_BOOKE) -#define POWERPC_INPUT_e500v1 (PPC_FLAGS_INPUT_BookE) -#define POWERPC_BFDM_e500v1 (bfd_mach_ppc_860) -#define POWERPC_FLAG_e500v1 (POWERPC_FLAG_SPE | POWERPC_FLAG_CE | \ - POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_e500v1 check_pow_hid0 -#define init_proc_e500v1 init_proc_e500v1 +POWERPC_FAMILY(e300)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); -/* e500v2 core */ -#define POWERPC_INSNS_e500v2 (PPC_INSNS_BASE | PPC_ISEL | \ - PPC_SPE | PPC_SPE_SINGLE | PPC_SPE_DOUBLE | \ - PPC_WRTEE | PPC_RFDI | \ - PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC) -#define POWERPC_INSNS2_e500v2 (PPC2_BOOKE206) -#define POWERPC_MSRM_e500v2 (0x000000000606FF30ULL) -#define POWERPC_MMU_e500v2 (POWERPC_MMU_BOOKE206) -#define POWERPC_EXCP_e500v2 (POWERPC_EXCP_BOOKE) -#define POWERPC_INPUT_e500v2 (PPC_FLAGS_INPUT_BookE) -#define POWERPC_BFDM_e500v2 (bfd_mach_ppc_860) -#define POWERPC_FLAG_e500v2 (POWERPC_FLAG_SPE | POWERPC_FLAG_CE | \ - POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_e500v2 check_pow_hid0 -#define init_proc_e500v2 init_proc_e500v2 - -/* e500mc core */ -#define POWERPC_INSNS_e500mc (PPC_INSNS_BASE | PPC_ISEL | \ - PPC_WRTEE | PPC_RFDI | PPC_RFMCI | \ - PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_FLOAT | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_FSEL | \ - PPC_FLOAT_STFIWX | PPC_WAIT | \ - PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC) -#define POWERPC_INSNS2_e500mc (PPC2_BOOKE206 | PPC2_PRCNTL) -#define POWERPC_MSRM_e500mc (0x000000001402FB36ULL) -#define POWERPC_MMU_e500mc (POWERPC_MMU_BOOKE206) -#define POWERPC_EXCP_e500mc (POWERPC_EXCP_BOOKE) -#define POWERPC_INPUT_e500mc (PPC_FLAGS_INPUT_BookE) -/* Fixme: figure out the correct flag for e500mc */ -#define POWERPC_BFDM_e500mc (bfd_mach_ppc_e500) -#define POWERPC_FLAG_e500mc (POWERPC_FLAG_CE | POWERPC_FLAG_DE | \ - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) -#define check_pow_e500mc check_pow_none -#define init_proc_e500mc init_proc_e500mc - -/* e5500 core */ -#define POWERPC_INSNS_e5500 (PPC_INSNS_BASE | PPC_ISEL | \ - PPC_WRTEE | PPC_RFDI | PPC_RFMCI | \ - PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \ - PPC_FLOAT | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_FSEL | \ - PPC_FLOAT_STFIWX | PPC_WAIT | \ - PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC | \ - PPC_64B | PPC_POPCNTB | PPC_POPCNTWD) -#define POWERPC_INSNS2_e5500 (PPC2_BOOKE206 | PPC2_PRCNTL) -#define POWERPC_MSRM_e5500 (0x000000009402FB36ULL) -#define POWERPC_MMU_e5500 (POWERPC_MMU_BOOKE206) -#define POWERPC_EXCP_e5500 (POWERPC_EXCP_BOOKE) -#define POWERPC_INPUT_e5500 (PPC_FLAGS_INPUT_BookE) -/* Fixme: figure out the correct flag for e5500 */ -#define POWERPC_BFDM_e5500 (bfd_mach_ppc_e500) -#define POWERPC_FLAG_e5500 (POWERPC_FLAG_CE | POWERPC_FLAG_DE | \ - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) -#define check_pow_e5500 check_pow_none -#define init_proc_e5500 init_proc_e5500 + dc->desc = "e300 core"; + pcc->init_proc = init_proc_e300; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000007FFF3ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_603; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_603; + pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; +} #if !defined(CONFIG_USER_ONLY) static void spr_write_mas73(void *opaque, int sprn, int gprn) @@ -4493,11 +4569,6 @@ static void spr_read_mas73(void *opaque, int gprn, int sprn) tcg_temp_free(mas7); } -static void spr_load_epr(void *opaque, int gprn, int sprn) -{ - gen_helper_load_epr(cpu_gpr[gprn], cpu_env); -} - #endif enum fsl_e500_version { @@ -4656,7 +4727,7 @@ static void init_proc_e500 (CPUPPCState *env, int version) 0x00000000); spr_register(env, SPR_BOOKE_EPR, "EPR", SPR_NOACCESS, SPR_NOACCESS, - &spr_load_epr, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, 0x00000000); /* XXX better abstract into Emb.xxx features */ if (version == fsl_e5500) { @@ -4689,47 +4760,143 @@ static void init_proc_e500v1(CPUPPCState *env) init_proc_e500(env, fsl_e500v1); } +POWERPC_FAMILY(e500v1)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "e500v1 core"; + pcc->init_proc = init_proc_e500v1; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | + PPC_SPE | PPC_SPE_SINGLE | + PPC_WRTEE | PPC_RFDI | + PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC; + pcc->insns_flags2 = PPC2_BOOKE206; + pcc->msr_mask = 0x000000000606FF30ULL; + pcc->mmu_model = POWERPC_MMU_BOOKE206; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_860; + pcc->flags = POWERPC_FLAG_SPE | POWERPC_FLAG_CE | + POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} + static void init_proc_e500v2(CPUPPCState *env) { init_proc_e500(env, fsl_e500v2); } +POWERPC_FAMILY(e500v2)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "e500v2 core"; + pcc->init_proc = init_proc_e500v2; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | + PPC_SPE | PPC_SPE_SINGLE | PPC_SPE_DOUBLE | + PPC_WRTEE | PPC_RFDI | + PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC; + pcc->insns_flags2 = PPC2_BOOKE206; + pcc->msr_mask = 0x000000000606FF30ULL; + pcc->mmu_model = POWERPC_MMU_BOOKE206; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + pcc->bfd_mach = bfd_mach_ppc_860; + pcc->flags = POWERPC_FLAG_SPE | POWERPC_FLAG_CE | + POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | + POWERPC_FLAG_BUS_CLK; +} + static void init_proc_e500mc(CPUPPCState *env) { init_proc_e500(env, fsl_e500mc); } +POWERPC_FAMILY(e500mc)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "e500mc core"; + pcc->init_proc = init_proc_e500mc; + pcc->check_pow = check_pow_none; + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | + PPC_WRTEE | PPC_RFDI | PPC_RFMCI | + PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_FLOAT | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_FSEL | + PPC_FLOAT_STFIWX | PPC_WAIT | + PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC; + pcc->insns_flags2 = PPC2_BOOKE206 | PPC2_PRCNTL; + pcc->msr_mask = 0x000000001402FB36ULL; + pcc->mmu_model = POWERPC_MMU_BOOKE206; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + /* FIXME: figure out the correct flag for e500mc */ + pcc->bfd_mach = bfd_mach_ppc_e500; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} + #ifdef TARGET_PPC64 static void init_proc_e5500(CPUPPCState *env) { init_proc_e500(env, fsl_e5500); } + +POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "e5500 core"; + pcc->init_proc = init_proc_e5500; + pcc->check_pow = check_pow_none; + pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | + PPC_WRTEE | PPC_RFDI | PPC_RFMCI | + PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | + PPC_CACHE_DCBZ | PPC_CACHE_DCBA | + PPC_FLOAT | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_FSEL | + PPC_FLOAT_STFIWX | PPC_WAIT | + PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC | + PPC_64B | PPC_POPCNTB | PPC_POPCNTWD; + pcc->insns_flags2 = PPC2_BOOKE206 | PPC2_PRCNTL; + pcc->msr_mask = 0x000000009402FB36ULL; + pcc->mmu_model = POWERPC_MMU_BOOKE206; + pcc->excp_model = POWERPC_EXCP_BOOKE; + pcc->bus_model = PPC_FLAGS_INPUT_BookE; + /* FIXME: figure out the correct flag for e5500 */ + pcc->bfd_mach = bfd_mach_ppc_e500; + pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} #endif /* Non-embedded PowerPC */ /* POWER : same as 601, without mfmsr, mfsr */ -#if defined(TODO) -#define POWERPC_INSNS_POWER (XXX_TODO) -/* POWER RSC (from RAD6000) */ -#define POWERPC_MSRM_POWER (0x00000000FEF0ULL) -#endif /* TODO */ +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 = 0x00000000FEF0ULL; +} -/* PowerPC 601 */ -#define POWERPC_INSNS_601 (PPC_INSNS_BASE | PPC_STRING | PPC_POWER_BR | \ - PPC_FLOAT | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_601 (PPC_NONE) -#define POWERPC_MSRM_601 (0x000000000000FD70ULL) #define POWERPC_MSRR_601 (0x0000000000001040ULL) -//#define POWERPC_MMU_601 (POWERPC_MMU_601) -//#define POWERPC_EXCP_601 (POWERPC_EXCP_601) -#define POWERPC_INPUT_601 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_601 (bfd_mach_ppc_601) -#define POWERPC_FLAG_601 (POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK) -#define check_pow_601 check_pow_none static void init_proc_601 (CPUPPCState *env) { @@ -4768,21 +4935,29 @@ static void init_proc_601 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 601v */ -#define POWERPC_INSNS_601v (PPC_INSNS_BASE | PPC_STRING | PPC_POWER_BR | \ - PPC_FLOAT | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_601v (PPC_NONE) -#define POWERPC_MSRM_601v (0x000000000000FD70ULL) +POWERPC_FAMILY(601)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 601"; + pcc->init_proc = init_proc_601; + pcc->check_pow = check_pow_none; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_POWER_BR | + PPC_FLOAT | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000000FD70ULL; + pcc->mmu_model = POWERPC_MMU_601; + pcc->excp_model = POWERPC_EXCP_601; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_601; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK; +} + #define POWERPC_MSRR_601v (0x0000000000001040ULL) -#define POWERPC_MMU_601v (POWERPC_MMU_601) -#define POWERPC_EXCP_601v (POWERPC_EXCP_601) -#define POWERPC_INPUT_601v (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_601v (bfd_mach_ppc_601) -#define POWERPC_FLAG_601v (POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK) -#define check_pow_601v check_pow_none static void init_proc_601v (CPUPPCState *env) { @@ -4794,24 +4969,27 @@ static void init_proc_601v (CPUPPCState *env) 0x00000000); } -/* PowerPC 602 */ -#define POWERPC_INSNS_602 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_6xx_TLB | PPC_MEM_TLBSYNC | \ - PPC_SEGMENT | PPC_602_SPEC) -#define POWERPC_INSNS2_602 (PPC_NONE) -#define POWERPC_MSRM_602 (0x0000000000C7FF73ULL) -/* XXX: 602 MMU is quite specific. Should add a special case */ -#define POWERPC_MMU_602 (POWERPC_MMU_SOFT_6xx) -//#define POWERPC_EXCP_602 (POWERPC_EXCP_602) -#define POWERPC_INPUT_602 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_602 (bfd_mach_ppc_602) -#define POWERPC_FLAG_602 (POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK) -#define check_pow_602 check_pow_hid0 +POWERPC_FAMILY(601v)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 601v"; + pcc->init_proc = init_proc_601v; + pcc->check_pow = check_pow_none; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_POWER_BR | + PPC_FLOAT | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000000FD70ULL; + pcc->mmu_model = POWERPC_MMU_601; + pcc->excp_model = POWERPC_EXCP_601; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_601; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK; +} static void init_proc_602 (CPUPPCState *env) { @@ -4840,23 +5018,31 @@ static void init_proc_602 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 603 */ -#define POWERPC_INSNS_603 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_603 (PPC_NONE) -#define POWERPC_MSRM_603 (0x000000000007FF73ULL) -#define POWERPC_MMU_603 (POWERPC_MMU_SOFT_6xx) -//#define POWERPC_EXCP_603 (POWERPC_EXCP_603) -#define POWERPC_INPUT_603 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_603 (bfd_mach_ppc_603) -#define POWERPC_FLAG_603 (POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK) -#define check_pow_603 check_pow_hid0 +POWERPC_FAMILY(602)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 602"; + pcc->init_proc = init_proc_602; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_6xx_TLB | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_602_SPEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x0000000000C7FF73ULL; + /* XXX: 602 MMU is quite specific. Should add a special case */ + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_602; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_602; + pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; +} static void init_proc_603 (CPUPPCState *env) { @@ -4885,23 +5071,30 @@ static void init_proc_603 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 603e */ -#define POWERPC_INSNS_603E (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_603E (PPC_NONE) -#define POWERPC_MSRM_603E (0x000000000007FF73ULL) -#define POWERPC_MMU_603E (POWERPC_MMU_SOFT_6xx) -//#define POWERPC_EXCP_603E (POWERPC_EXCP_603E) -#define POWERPC_INPUT_603E (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_603E (bfd_mach_ppc_ec603e) -#define POWERPC_FLAG_603E (POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK) -#define check_pow_603E check_pow_hid0 +POWERPC_FAMILY(603)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 603"; + pcc->init_proc = init_proc_603; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000007FF73ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_603; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_603; + pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; +} static void init_proc_603E (CPUPPCState *env) { @@ -4935,23 +5128,30 @@ static void init_proc_603E (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 604 */ -#define POWERPC_INSNS_604 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_604 (PPC_NONE) -#define POWERPC_MSRM_604 (0x000000000005FF77ULL) -#define POWERPC_MMU_604 (POWERPC_MMU_32B) -//#define POWERPC_EXCP_604 (POWERPC_EXCP_604) -#define POWERPC_INPUT_604 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_604 (bfd_mach_ppc_604) -#define POWERPC_FLAG_604 (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) -#define check_pow_604 check_pow_nocheck +POWERPC_FAMILY(603E)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 603e"; + pcc->init_proc = init_proc_603E; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000007FF73ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_603E; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_ec603e; + pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; +} static void init_proc_604 (CPUPPCState *env) { @@ -4974,23 +5174,30 @@ static void init_proc_604 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 604E */ -#define POWERPC_INSNS_604E (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_604E (PPC_NONE) -#define POWERPC_MSRM_604E (0x000000000005FF77ULL) -#define POWERPC_MMU_604E (POWERPC_MMU_32B) -#define POWERPC_EXCP_604E (POWERPC_EXCP_604) -#define POWERPC_INPUT_604E (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_604E (bfd_mach_ppc_604) -#define POWERPC_FLAG_604E (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) -#define check_pow_604E check_pow_nocheck +POWERPC_FAMILY(604)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 604"; + pcc->init_proc = init_proc_604; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000005FF77ULL; + pcc->mmu_model = POWERPC_MMU_32B; + pcc->excp_model = POWERPC_EXCP_604; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_604; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} static void init_proc_604E (CPUPPCState *env) { @@ -5033,23 +5240,30 @@ static void init_proc_604E (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 740 */ -#define POWERPC_INSNS_740 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_740 (PPC_NONE) -#define POWERPC_MSRM_740 (0x000000000005FF77ULL) -#define POWERPC_MMU_740 (POWERPC_MMU_32B) -#define POWERPC_EXCP_740 (POWERPC_EXCP_7x0) -#define POWERPC_INPUT_740 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_740 (bfd_mach_ppc_750) -#define POWERPC_FLAG_740 (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) -#define check_pow_740 check_pow_hid0 +POWERPC_FAMILY(604E)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 604E"; + pcc->init_proc = init_proc_604E; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000005FF77ULL; + pcc->mmu_model = POWERPC_MMU_32B; + pcc->excp_model = POWERPC_EXCP_604; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_604; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} static void init_proc_740 (CPUPPCState *env) { @@ -5079,23 +5293,30 @@ static void init_proc_740 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 750 */ -#define POWERPC_INSNS_750 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_750 (PPC_NONE) -#define POWERPC_MSRM_750 (0x000000000005FF77ULL) -#define POWERPC_MMU_750 (POWERPC_MMU_32B) -#define POWERPC_EXCP_750 (POWERPC_EXCP_7x0) -#define POWERPC_INPUT_750 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_750 (bfd_mach_ppc_750) -#define POWERPC_FLAG_750 (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) -#define check_pow_750 check_pow_hid0 +POWERPC_FAMILY(740)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 740"; + pcc->init_proc = init_proc_740; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000005FF77ULL; + pcc->mmu_model = POWERPC_MMU_32B; + pcc->excp_model = POWERPC_EXCP_7x0; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} static void init_proc_750 (CPUPPCState *env) { @@ -5133,61 +5354,30 @@ static void init_proc_750 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 750 CL */ -/* XXX: not implemented: - * cache lock instructions: - * dcbz_l - * floating point paired instructions - * psq_lux - * psq_lx - * psq_stux - * psq_stx - * ps_abs - * ps_add - * ps_cmpo0 - * ps_cmpo1 - * ps_cmpu0 - * ps_cmpu1 - * ps_div - * ps_madd - * ps_madds0 - * ps_madds1 - * ps_merge00 - * ps_merge01 - * ps_merge10 - * ps_merge11 - * ps_mr - * ps_msub - * ps_mul - * ps_muls0 - * ps_muls1 - * ps_nabs - * ps_neg - * ps_nmadd - * ps_nmsub - * ps_res - * ps_rsqrte - * ps_sel - * ps_sub - * ps_sum0 - * ps_sum1 - */ -#define POWERPC_INSNS_750cl (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_750cl (PPC_NONE) -#define POWERPC_MSRM_750cl (0x000000000005FF77ULL) -#define POWERPC_MMU_750cl (POWERPC_MMU_32B) -#define POWERPC_EXCP_750cl (POWERPC_EXCP_7x0) -#define POWERPC_INPUT_750cl (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_750cl (bfd_mach_ppc_750) -#define POWERPC_FLAG_750cl (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) -#define check_pow_750cl check_pow_hid0 +POWERPC_FAMILY(750)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 750"; + pcc->init_proc = init_proc_750; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000005FF77ULL; + pcc->mmu_model = POWERPC_MMU_32B; + pcc->excp_model = POWERPC_EXCP_7x0; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} static void init_proc_750cl (CPUPPCState *env) { @@ -5310,23 +5500,68 @@ static void init_proc_750cl (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 750CX */ -#define POWERPC_INSNS_750cx (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_750cx (PPC_NONE) -#define POWERPC_MSRM_750cx (0x000000000005FF77ULL) -#define POWERPC_MMU_750cx (POWERPC_MMU_32B) -#define POWERPC_EXCP_750cx (POWERPC_EXCP_7x0) -#define POWERPC_INPUT_750cx (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_750cx (bfd_mach_ppc_750) -#define POWERPC_FLAG_750cx (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) -#define check_pow_750cx check_pow_hid0 +POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 750 CL"; + pcc->init_proc = init_proc_750cl; + pcc->check_pow = check_pow_hid0; + /* XXX: not implemented: + * cache lock instructions: + * dcbz_l + * floating point paired instructions + * psq_lux + * psq_lx + * psq_stux + * psq_stx + * ps_abs + * ps_add + * ps_cmpo0 + * ps_cmpo1 + * ps_cmpu0 + * ps_cmpu1 + * ps_div + * ps_madd + * ps_madds0 + * ps_madds1 + * ps_merge00 + * ps_merge01 + * ps_merge10 + * ps_merge11 + * ps_mr + * ps_msub + * ps_mul + * ps_muls0 + * ps_muls1 + * ps_nabs + * ps_neg + * ps_nmadd + * ps_nmsub + * ps_res + * ps_rsqrte + * ps_sel + * ps_sub + * ps_sum0 + * ps_sum1 + */ + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000005FF77ULL; + pcc->mmu_model = POWERPC_MMU_32B; + pcc->excp_model = POWERPC_EXCP_7x0; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} static void init_proc_750cx (CPUPPCState *env) { @@ -5368,23 +5603,30 @@ static void init_proc_750cx (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 750FX */ -#define POWERPC_INSNS_750fx (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_750fx (PPC_NONE) -#define POWERPC_MSRM_750fx (0x000000000005FF77ULL) -#define POWERPC_MMU_750fx (POWERPC_MMU_32B) -#define POWERPC_EXCP_750fx (POWERPC_EXCP_7x0) -#define POWERPC_INPUT_750fx (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_750fx (bfd_mach_ppc_750) -#define POWERPC_FLAG_750fx (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) -#define check_pow_750fx check_pow_hid0 +POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 750CX"; + pcc->init_proc = init_proc_750cx; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000005FF77ULL; + pcc->mmu_model = POWERPC_MMU_32B; + pcc->excp_model = POWERPC_EXCP_7x0; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} static void init_proc_750fx (CPUPPCState *env) { @@ -5431,23 +5673,30 @@ static void init_proc_750fx (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 750GX */ -#define POWERPC_INSNS_750gx (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_750gx (PPC_NONE) -#define POWERPC_MSRM_750gx (0x000000000005FF77ULL) -#define POWERPC_MMU_750gx (POWERPC_MMU_32B) -#define POWERPC_EXCP_750gx (POWERPC_EXCP_7x0) -#define POWERPC_INPUT_750gx (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_750gx (bfd_mach_ppc_750) -#define POWERPC_FLAG_750gx (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) -#define check_pow_750gx check_pow_hid0 +POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 750FX"; + pcc->init_proc = init_proc_750fx; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000005FF77ULL; + pcc->mmu_model = POWERPC_MMU_32B; + pcc->excp_model = POWERPC_EXCP_7x0; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} static void init_proc_750gx (CPUPPCState *env) { @@ -5494,23 +5743,30 @@ static void init_proc_750gx (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 745 */ -#define POWERPC_INSNS_745 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_745 (PPC_NONE) -#define POWERPC_MSRM_745 (0x000000000005FF77ULL) -#define POWERPC_MMU_745 (POWERPC_MMU_SOFT_6xx) -#define POWERPC_EXCP_745 (POWERPC_EXCP_7x5) -#define POWERPC_INPUT_745 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_745 (bfd_mach_ppc_750) -#define POWERPC_FLAG_745 (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) -#define check_pow_745 check_pow_hid0 +POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 750GX"; + pcc->init_proc = init_proc_750gx; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000005FF77ULL; + pcc->mmu_model = POWERPC_MMU_32B; + pcc->excp_model = POWERPC_EXCP_7x0; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} static void init_proc_745 (CPUPPCState *env) { @@ -5548,23 +5804,30 @@ static void init_proc_745 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 755 */ -#define POWERPC_INSNS_755 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \ - PPC_SEGMENT | PPC_EXTERN) -#define POWERPC_INSNS2_755 (PPC_NONE) -#define POWERPC_MSRM_755 (0x000000000005FF77ULL) -#define POWERPC_MMU_755 (POWERPC_MMU_SOFT_6xx) -#define POWERPC_EXCP_755 (POWERPC_EXCP_7x5) -#define POWERPC_INPUT_755 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_755 (bfd_mach_ppc_750) -#define POWERPC_FLAG_755 (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) -#define check_pow_755 check_pow_hid0 +POWERPC_FAMILY(745)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 745"; + pcc->init_proc = init_proc_745; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000005FF77ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_7x5; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} static void init_proc_755 (CPUPPCState *env) { @@ -5613,28 +5876,30 @@ static void init_proc_755 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 7400 (aka G4) */ -#define POWERPC_INSNS_7400 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_MEM_TLBIA | \ - PPC_SEGMENT | PPC_EXTERN | \ - PPC_ALTIVEC) -#define POWERPC_INSNS2_7400 (PPC_NONE) -#define POWERPC_MSRM_7400 (0x000000000205FF77ULL) -#define POWERPC_MMU_7400 (POWERPC_MMU_32B) -#define POWERPC_EXCP_7400 (POWERPC_EXCP_74xx) -#define POWERPC_INPUT_7400 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_7400 (bfd_mach_ppc_7400) -#define POWERPC_FLAG_7400 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_7400 check_pow_hid0 +POWERPC_FAMILY(755)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 755"; + pcc->init_proc = init_proc_755; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | + PPC_SEGMENT | PPC_EXTERN; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000005FF77ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_6xx; + pcc->excp_model = POWERPC_EXCP_7x5; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_750; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} static void init_proc_7400 (CPUPPCState *env) { @@ -5666,28 +5931,35 @@ static void init_proc_7400 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 7410 (aka G4) */ -#define POWERPC_INSNS_7410 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_MEM_TLBIA | \ - PPC_SEGMENT | PPC_EXTERN | \ - PPC_ALTIVEC) -#define POWERPC_INSNS2_7410 (PPC_NONE) -#define POWERPC_MSRM_7410 (0x000000000205FF77ULL) -#define POWERPC_MMU_7410 (POWERPC_MMU_32B) -#define POWERPC_EXCP_7410 (POWERPC_EXCP_74xx) -#define POWERPC_INPUT_7410 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_7410 (bfd_mach_ppc_7400) -#define POWERPC_FLAG_7410 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_7410 check_pow_hid0 +POWERPC_FAMILY(7400)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 7400 (aka G4)"; + pcc->init_proc = init_proc_7400; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000205FF77ULL; + pcc->mmu_model = POWERPC_MMU_32B; + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} static void init_proc_7410 (CPUPPCState *env) { @@ -5725,30 +5997,36 @@ static void init_proc_7410 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 7440 (aka G4) */ -#define POWERPC_INSNS_7440 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_MEM_TLBIA | PPC_74xx_TLB | \ - PPC_SEGMENT | PPC_EXTERN | \ - PPC_ALTIVEC) -#define POWERPC_INSNS2_7440 (PPC_NONE) -#define POWERPC_MSRM_7440 (0x000000000205FF77ULL) -#define POWERPC_MMU_7440 (POWERPC_MMU_SOFT_74xx) -#define POWERPC_EXCP_7440 (POWERPC_EXCP_74xx) -#define POWERPC_INPUT_7440 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_7440 (bfd_mach_ppc_7400) -#define POWERPC_FLAG_7440 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_7440 check_pow_hid0_74xx +POWERPC_FAMILY(7410)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 7410 (aka G4)"; + pcc->init_proc = init_proc_7410; + pcc->check_pow = check_pow_hid0; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000205FF77ULL; + pcc->mmu_model = POWERPC_MMU_32B; + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_7440 (CPUPPCState *env) { gen_spr_ne_601(env); @@ -5811,30 +6089,36 @@ static void init_proc_7440 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 7450 (aka G4) */ -#define POWERPC_INSNS_7450 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_MEM_TLBIA | PPC_74xx_TLB | \ - PPC_SEGMENT | PPC_EXTERN | \ - PPC_ALTIVEC) -#define POWERPC_INSNS2_7450 (PPC_NONE) -#define POWERPC_MSRM_7450 (0x000000000205FF77ULL) -#define POWERPC_MMU_7450 (POWERPC_MMU_SOFT_74xx) -#define POWERPC_EXCP_7450 (POWERPC_EXCP_74xx) -#define POWERPC_INPUT_7450 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_7450 (bfd_mach_ppc_7400) -#define POWERPC_FLAG_7450 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_7450 check_pow_hid0_74xx +POWERPC_FAMILY(7440)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 7440 (aka G4)"; + pcc->init_proc = init_proc_7440; + pcc->check_pow = check_pow_hid0_74xx; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | PPC_74xx_TLB | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000205FF77ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_74xx; + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_7450 (CPUPPCState *env) { gen_spr_ne_601(env); @@ -5923,30 +6207,36 @@ static void init_proc_7450 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 7445 (aka G4) */ -#define POWERPC_INSNS_7445 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_MEM_TLBIA | PPC_74xx_TLB | \ - PPC_SEGMENT | PPC_EXTERN | \ - PPC_ALTIVEC) -#define POWERPC_INSNS2_7445 (PPC_NONE) -#define POWERPC_MSRM_7445 (0x000000000205FF77ULL) -#define POWERPC_MMU_7445 (POWERPC_MMU_SOFT_74xx) -#define POWERPC_EXCP_7445 (POWERPC_EXCP_74xx) -#define POWERPC_INPUT_7445 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_7445 (bfd_mach_ppc_7400) -#define POWERPC_FLAG_7445 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_7445 check_pow_hid0_74xx +POWERPC_FAMILY(7450)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 7450 (aka G4)"; + pcc->init_proc = init_proc_7450; + pcc->check_pow = check_pow_hid0_74xx; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | PPC_74xx_TLB | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000205FF77ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_74xx; + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_7445 (CPUPPCState *env) { gen_spr_ne_601(env); @@ -6038,30 +6328,36 @@ static void init_proc_7445 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 7455 (aka G4) */ -#define POWERPC_INSNS_7455 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_MEM_TLBIA | PPC_74xx_TLB | \ - PPC_SEGMENT | PPC_EXTERN | \ - PPC_ALTIVEC) -#define POWERPC_INSNS2_7455 (PPC_NONE) -#define POWERPC_MSRM_7455 (0x000000000205FF77ULL) -#define POWERPC_MMU_7455 (POWERPC_MMU_SOFT_74xx) -#define POWERPC_EXCP_7455 (POWERPC_EXCP_74xx) -#define POWERPC_INPUT_7455 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_7455 (bfd_mach_ppc_7400) -#define POWERPC_FLAG_7455 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_7455 check_pow_hid0_74xx +POWERPC_FAMILY(7445)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 7445 (aka G4)"; + pcc->init_proc = init_proc_7445; + pcc->check_pow = check_pow_hid0_74xx; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | PPC_74xx_TLB | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000205FF77ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_74xx; + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_7455 (CPUPPCState *env) { gen_spr_ne_601(env); @@ -6155,30 +6451,36 @@ static void init_proc_7455 (CPUPPCState *env) ppc6xx_irq_init(env); } -/* PowerPC 7457 (aka G4) */ -#define POWERPC_INSNS_7457 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | \ - PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_MEM_TLBIA | PPC_74xx_TLB | \ - PPC_SEGMENT | PPC_EXTERN | \ - PPC_ALTIVEC) -#define POWERPC_INSNS2_7457 (PPC_NONE) -#define POWERPC_MSRM_7457 (0x000000000205FF77ULL) -#define POWERPC_MMU_7457 (POWERPC_MMU_SOFT_74xx) -#define POWERPC_EXCP_7457 (POWERPC_EXCP_74xx) -#define POWERPC_INPUT_7457 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_7457 (bfd_mach_ppc_7400) -#define POWERPC_FLAG_7457 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK) -#define check_pow_7457 check_pow_hid0_74xx +POWERPC_FAMILY(7455)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 7455 (aka G4)"; + pcc->init_proc = init_proc_7455; + pcc->check_pow = check_pow_hid0_74xx; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | PPC_74xx_TLB | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000205FF77ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_74xx; + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} -__attribute__ (( unused )) static void init_proc_7457 (CPUPPCState *env) { gen_spr_ne_601(env); @@ -6296,27 +6598,37 @@ static void init_proc_7457 (CPUPPCState *env) ppc6xx_irq_init(env); } -#if defined (TARGET_PPC64) -/* PowerPC 970 */ -#define POWERPC_INSNS_970 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_64B | PPC_ALTIVEC | \ - PPC_SEGMENT_64B | PPC_SLBI) -#define POWERPC_INSNS2_970 (PPC_NONE) -#define POWERPC_MSRM_970 (0x900000000204FF36ULL) -#define POWERPC_MMU_970 (POWERPC_MMU_64B) -//#define POWERPC_EXCP_970 (POWERPC_EXCP_970) -#define POWERPC_INPUT_970 (PPC_FLAGS_INPUT_970) -#define POWERPC_BFDM_970 (bfd_mach_ppc64) -#define POWERPC_FLAG_970 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK) +POWERPC_FAMILY(7457)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + dc->desc = "PowerPC 7457 (aka G4)"; + pcc->init_proc = init_proc_7457; + pcc->check_pow = check_pow_hid0_74xx; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | + PPC_CACHE_DCBA | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_MEM_TLBIA | PPC_74xx_TLB | + PPC_SEGMENT | PPC_EXTERN | + PPC_ALTIVEC; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x000000000205FF77ULL; + pcc->mmu_model = POWERPC_MMU_SOFT_74xx; + pcc->excp_model = POWERPC_EXCP_74xx; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc_7400; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} + +#if defined (TARGET_PPC64) #if defined(CONFIG_USER_ONLY) #define POWERPC970_HID5_INIT 0x00000080 #else @@ -6393,25 +6705,33 @@ static void init_proc_970 (CPUPPCState *env) vscr_init(env, 0x00010000); } -/* PowerPC 970FX (aka G5) */ -#define POWERPC_INSNS_970FX (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_64B | PPC_ALTIVEC | \ - PPC_SEGMENT_64B | PPC_SLBI) -#define POWERPC_INSNS2_970FX (PPC_NONE) -#define POWERPC_MSRM_970FX (0x800000000204FF36ULL) -#define POWERPC_MMU_970FX (POWERPC_MMU_64B) -#define POWERPC_EXCP_970FX (POWERPC_EXCP_970) -#define POWERPC_INPUT_970FX (PPC_FLAGS_INPUT_970) -#define POWERPC_BFDM_970FX (bfd_mach_ppc64) -#define POWERPC_FLAG_970FX (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK) +POWERPC_FAMILY(970)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 970"; + pcc->init_proc = init_proc_970; + pcc->check_pow = check_pow_970; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_64B | PPC_ALTIVEC | + PPC_SEGMENT_64B | PPC_SLBI; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x900000000204FF36ULL; + pcc->mmu_model = POWERPC_MMU_64B; + pcc->excp_model = POWERPC_EXCP_970; + pcc->bus_model = PPC_FLAGS_INPUT_970; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} static int check_pow_970FX (CPUPPCState *env) { @@ -6495,25 +6815,33 @@ static void init_proc_970FX (CPUPPCState *env) vscr_init(env, 0x00010000); } -/* PowerPC 970 GX */ -#define POWERPC_INSNS_970GX (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_64B | PPC_ALTIVEC | \ - PPC_SEGMENT_64B | PPC_SLBI) -#define POWERPC_INSNS2_970GX (PPC_NONE) -#define POWERPC_MSRM_970GX (0x800000000204FF36ULL) -#define POWERPC_MMU_970GX (POWERPC_MMU_64B) -#define POWERPC_EXCP_970GX (POWERPC_EXCP_970) -#define POWERPC_INPUT_970GX (PPC_FLAGS_INPUT_970) -#define POWERPC_BFDM_970GX (bfd_mach_ppc64) -#define POWERPC_FLAG_970GX (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK) +POWERPC_FAMILY(970FX)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 970FX (aka G5)"; + pcc->init_proc = init_proc_970FX; + pcc->check_pow = check_pow_970FX; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_64B | PPC_ALTIVEC | + PPC_SEGMENT_64B | PPC_SLBI; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x800000000204FF36ULL; + pcc->mmu_model = POWERPC_MMU_64B; + pcc->excp_model = POWERPC_EXCP_970; + pcc->bus_model = PPC_FLAGS_INPUT_970; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} static int check_pow_970GX (CPUPPCState *env) { @@ -6585,25 +6913,33 @@ static void init_proc_970GX (CPUPPCState *env) vscr_init(env, 0x00010000); } -/* PowerPC 970 MP */ -#define POWERPC_INSNS_970MP (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_64B | PPC_ALTIVEC | \ - PPC_SEGMENT_64B | PPC_SLBI) -#define POWERPC_INSNS2_970MP (PPC_NONE) -#define POWERPC_MSRM_970MP (0x900000000204FF36ULL) -#define POWERPC_MMU_970MP (POWERPC_MMU_64B) -#define POWERPC_EXCP_970MP (POWERPC_EXCP_970) -#define POWERPC_INPUT_970MP (PPC_FLAGS_INPUT_970) -#define POWERPC_BFDM_970MP (bfd_mach_ppc64) -#define POWERPC_FLAG_970MP (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK) +POWERPC_FAMILY(970GX)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 970 GX"; + pcc->init_proc = init_proc_970GX; + pcc->check_pow = check_pow_970GX; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_64B | PPC_ALTIVEC | + PPC_SEGMENT_64B | PPC_SLBI; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x800000000204FF36ULL; + pcc->mmu_model = POWERPC_MMU_64B; + pcc->excp_model = POWERPC_EXCP_970; + pcc->bus_model = PPC_FLAGS_INPUT_970; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} static int check_pow_970MP (CPUPPCState *env) { @@ -6675,28 +7011,33 @@ static void init_proc_970MP (CPUPPCState *env) vscr_init(env, 0x00010000); } -#if defined(TARGET_PPC64) -/* POWER7 */ -#define POWERPC_INSNS_POWER7 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_64B | PPC_ALTIVEC | \ - PPC_SEGMENT_64B | PPC_SLBI | \ - PPC_POPCNTB | PPC_POPCNTWD) -#define POWERPC_INSNS2_POWER7 (PPC2_VSX | PPC2_DFP | PPC2_DBRX) -#define POWERPC_MSRM_POWER7 (0x800000000204FF36ULL) -#define POWERPC_MMU_POWER7 (POWERPC_MMU_2_06) -#define POWERPC_EXCP_POWER7 (POWERPC_EXCP_POWER7) -#define POWERPC_INPUT_POWER7 (PPC_FLAGS_INPUT_POWER7) -#define POWERPC_BFDM_POWER7 (bfd_mach_ppc64) -#define POWERPC_FLAG_POWER7 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR) -#define check_pow_POWER7 check_pow_nocheck +POWERPC_FAMILY(970MP)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 970 MP"; + pcc->init_proc = init_proc_970MP; + pcc->check_pow = check_pow_970MP; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_64B | PPC_ALTIVEC | + PPC_SEGMENT_64B | PPC_SLBI; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x900000000204FF36ULL; + pcc->mmu_model = POWERPC_MMU_64B; + pcc->excp_model = POWERPC_EXCP_970; + pcc->bus_model = PPC_FLAGS_INPUT_970; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK; +} static void init_proc_POWER7 (CPUPPCState *env) { @@ -6711,22 +7052,22 @@ static void init_proc_POWER7 (CPUPPCState *env) 0x00000000); #if !defined(CONFIG_USER_ONLY) /* PURR & SPURR: Hack - treat these as aliases for the TB for now */ - spr_register(env, SPR_PURR, "PURR", - &spr_read_purr, SPR_NOACCESS, - &spr_read_purr, SPR_NOACCESS, - 0x00000000); - spr_register(env, SPR_SPURR, "SPURR", - &spr_read_purr, SPR_NOACCESS, - &spr_read_purr, SPR_NOACCESS, - 0x00000000); + spr_register_kvm(env, SPR_PURR, "PURR", + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, SPR_NOACCESS, + KVM_REG_PPC_PURR, 0x00000000); + spr_register_kvm(env, SPR_SPURR, "SPURR", + &spr_read_purr, SPR_NOACCESS, + &spr_read_purr, SPR_NOACCESS, + KVM_REG_PPC_SPURR, 0x00000000); spr_register(env, SPR_CFAR, "SPR_CFAR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_cfar, &spr_write_cfar, 0x00000000); - spr_register(env, SPR_DSCR, "SPR_DSCR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); + spr_register_kvm(env, SPR_DSCR, "SPR_DSCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_DSCR, 0x00000000); #endif /* !CONFIG_USER_ONLY */ /* Memory management */ /* XXX : not implemented */ @@ -6759,29 +7100,36 @@ static void init_proc_POWER7 (CPUPPCState *env) * value is the one used by 74xx processors. */ vscr_init(env, 0x00010000); } -#endif /* TARGET_PPC64 */ -/* PowerPC 620 */ -#define POWERPC_INSNS_620 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \ - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \ - PPC_FLOAT_STFIWX | \ - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \ - PPC_MEM_SYNC | PPC_MEM_EIEIO | \ - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \ - PPC_SEGMENT | PPC_EXTERN | \ - PPC_64B | PPC_SLBI) -#define POWERPC_INSNS2_620 (PPC_NONE) -#define POWERPC_MSRM_620 (0x800000000005FF77ULL) -//#define POWERPC_MMU_620 (POWERPC_MMU_620) -#define POWERPC_EXCP_620 (POWERPC_EXCP_970) -#define POWERPC_INPUT_620 (PPC_FLAGS_INPUT_6xx) -#define POWERPC_BFDM_620 (bfd_mach_ppc64) -#define POWERPC_FLAG_620 (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ - POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK) -#define check_pow_620 check_pow_nocheck /* Check this */ +POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "POWER7"; + pcc->init_proc = init_proc_POWER7; + pcc->check_pow = check_pow_nocheck; + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_64B | PPC_ALTIVEC | + PPC_SEGMENT_64B | PPC_SLBI | + PPC_POPCNTB | PPC_POPCNTWD; + pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX; + pcc->msr_mask = 0x800000000204FF36ULL; + pcc->mmu_model = POWERPC_MMU_2_06; + pcc->excp_model = POWERPC_EXCP_POWER7; + pcc->bus_model = PPC_FLAGS_INPUT_POWER7; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | + POWERPC_FLAG_BE | POWERPC_FLAG_PMM | + POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR; +} -__attribute__ (( unused )) static void init_proc_620 (CPUPPCState *env) { gen_spr_ne_601(env); @@ -6802,2611 +7150,43 @@ static void init_proc_620 (CPUPPCState *env) /* Allocate hardware IRQ controller */ ppc6xx_irq_init(env); } + +POWERPC_FAMILY(620)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->desc = "PowerPC 620"; + pcc->init_proc = init_proc_620; + pcc->check_pow = check_pow_nocheck; /* Check this */ + pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | + PPC_FLOAT_STFIWX | + PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | + PPC_MEM_SYNC | PPC_MEM_EIEIO | + PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | + PPC_SEGMENT | PPC_EXTERN | + PPC_64B | PPC_SLBI; + pcc->insns_flags2 = PPC_NONE; + pcc->msr_mask = 0x800000000005FF77ULL; + pcc->mmu_model = POWERPC_MMU_620; + pcc->excp_model = POWERPC_EXCP_970; + pcc->bus_model = PPC_FLAGS_INPUT_6xx; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK; +} + #endif /* defined (TARGET_PPC64) */ -/* Default 32 bits PowerPC target will be 604 */ -#define CPU_POWERPC_PPC32 CPU_POWERPC_604 -#define POWERPC_INSNS_PPC32 POWERPC_INSNS_604 -#define POWERPC_INSNS2_PPC32 POWERPC_INSNS2_604 -#define POWERPC_MSRM_PPC32 POWERPC_MSRM_604 -#define POWERPC_MMU_PPC32 POWERPC_MMU_604 -#define POWERPC_EXCP_PPC32 POWERPC_EXCP_604 -#define POWERPC_INPUT_PPC32 POWERPC_INPUT_604 -#define POWERPC_BFDM_PPC32 POWERPC_BFDM_604 -#define POWERPC_FLAG_PPC32 POWERPC_FLAG_604 -#define check_pow_PPC32 check_pow_604 -#define init_proc_PPC32 init_proc_604 - -/* Default 64 bits PowerPC target will be 970 FX */ -#define CPU_POWERPC_PPC64 CPU_POWERPC_970FX -#define POWERPC_INSNS_PPC64 POWERPC_INSNS_970FX -#define POWERPC_INSNS2_PPC64 POWERPC_INSNS2_970FX -#define POWERPC_MSRM_PPC64 POWERPC_MSRM_970FX -#define POWERPC_MMU_PPC64 POWERPC_MMU_970FX -#define POWERPC_EXCP_PPC64 POWERPC_EXCP_970FX -#define POWERPC_INPUT_PPC64 POWERPC_INPUT_970FX -#define POWERPC_BFDM_PPC64 POWERPC_BFDM_970FX -#define POWERPC_FLAG_PPC64 POWERPC_FLAG_970FX -#define check_pow_PPC64 check_pow_970FX -#define init_proc_PPC64 init_proc_970FX - -/* Default PowerPC target will be PowerPC 32 */ -#if defined (TARGET_PPC64) && 0 // XXX: TODO -#define CPU_POWERPC_DEFAULT CPU_POWERPC_PPC64 -#define POWERPC_INSNS_DEFAULT POWERPC_INSNS_PPC64 -#define POWERPC_INSNS2_DEFAULT POWERPC_INSNS2_PPC64 -#define POWERPC_MSRM_DEFAULT POWERPC_MSRM_PPC64 -#define POWERPC_MMU_DEFAULT POWERPC_MMU_PPC64 -#define POWERPC_EXCP_DEFAULT POWERPC_EXCP_PPC64 -#define POWERPC_INPUT_DEFAULT POWERPC_INPUT_PPC64 -#define POWERPC_BFDM_DEFAULT POWERPC_BFDM_PPC64 -#define POWERPC_FLAG_DEFAULT POWERPC_FLAG_PPC64 -#define check_pow_DEFAULT check_pow_PPC64 -#define init_proc_DEFAULT init_proc_PPC64 -#else -#define CPU_POWERPC_DEFAULT CPU_POWERPC_PPC32 -#define POWERPC_INSNS_DEFAULT POWERPC_INSNS_PPC32 -#define POWERPC_INSNS2_DEFAULT POWERPC_INSNS2_PPC32 -#define POWERPC_MSRM_DEFAULT POWERPC_MSRM_PPC32 -#define POWERPC_MMU_DEFAULT POWERPC_MMU_PPC32 -#define POWERPC_EXCP_DEFAULT POWERPC_EXCP_PPC32 -#define POWERPC_INPUT_DEFAULT POWERPC_INPUT_PPC32 -#define POWERPC_BFDM_DEFAULT POWERPC_BFDM_PPC32 -#define POWERPC_FLAG_DEFAULT POWERPC_FLAG_PPC32 -#define check_pow_DEFAULT check_pow_PPC32 -#define init_proc_DEFAULT init_proc_PPC32 -#endif - -/*****************************************************************************/ -/* PVR definitions for most known PowerPC */ -enum { - /* PowerPC 401 family */ - /* Generic PowerPC 401 */ -#define CPU_POWERPC_401 CPU_POWERPC_401G2 - /* PowerPC 401 cores */ - CPU_POWERPC_401A1 = 0x00210000, - CPU_POWERPC_401B2 = 0x00220000, -#if 0 - CPU_POWERPC_401B3 = xxx, -#endif - CPU_POWERPC_401C2 = 0x00230000, - CPU_POWERPC_401D2 = 0x00240000, - CPU_POWERPC_401E2 = 0x00250000, - CPU_POWERPC_401F2 = 0x00260000, - CPU_POWERPC_401G2 = 0x00270000, - /* PowerPC 401 microcontrolers */ -#if 0 - CPU_POWERPC_401GF = xxx, -#endif -#define CPU_POWERPC_IOP480 CPU_POWERPC_401B2 - /* IBM Processor for Network Resources */ - CPU_POWERPC_COBRA = 0x10100000, /* XXX: 405 ? */ -#if 0 - CPU_POWERPC_XIPCHIP = xxx, -#endif - /* PowerPC 403 family */ - /* Generic PowerPC 403 */ -#define CPU_POWERPC_403 CPU_POWERPC_403GC - /* PowerPC 403 microcontrollers */ - CPU_POWERPC_403GA = 0x00200011, - CPU_POWERPC_403GB = 0x00200100, - CPU_POWERPC_403GC = 0x00200200, - CPU_POWERPC_403GCX = 0x00201400, -#if 0 - CPU_POWERPC_403GP = xxx, -#endif - /* PowerPC 405 family */ - /* Generic PowerPC 405 */ -#define CPU_POWERPC_405 CPU_POWERPC_405D4 - /* PowerPC 405 cores */ -#if 0 - CPU_POWERPC_405A3 = xxx, -#endif -#if 0 - CPU_POWERPC_405A4 = xxx, -#endif -#if 0 - CPU_POWERPC_405B3 = xxx, -#endif -#if 0 - CPU_POWERPC_405B4 = xxx, -#endif -#if 0 - CPU_POWERPC_405C3 = xxx, -#endif -#if 0 - CPU_POWERPC_405C4 = xxx, -#endif - CPU_POWERPC_405D2 = 0x20010000, -#if 0 - CPU_POWERPC_405D3 = xxx, -#endif - CPU_POWERPC_405D4 = 0x41810000, -#if 0 - CPU_POWERPC_405D5 = xxx, -#endif -#if 0 - CPU_POWERPC_405E4 = xxx, -#endif -#if 0 - CPU_POWERPC_405F4 = xxx, -#endif -#if 0 - CPU_POWERPC_405F5 = xxx, -#endif -#if 0 - CPU_POWERPC_405F6 = xxx, -#endif - /* PowerPC 405 microcontrolers */ - /* XXX: missing 0x200108a0 */ -#define CPU_POWERPC_405CR CPU_POWERPC_405CRc - CPU_POWERPC_405CRa = 0x40110041, - CPU_POWERPC_405CRb = 0x401100C5, - CPU_POWERPC_405CRc = 0x40110145, - CPU_POWERPC_405EP = 0x51210950, -#if 0 - CPU_POWERPC_405EXr = xxx, -#endif - CPU_POWERPC_405EZ = 0x41511460, /* 0x51210950 ? */ -#if 0 - CPU_POWERPC_405FX = xxx, -#endif -#define CPU_POWERPC_405GP CPU_POWERPC_405GPd - CPU_POWERPC_405GPa = 0x40110000, - CPU_POWERPC_405GPb = 0x40110040, - CPU_POWERPC_405GPc = 0x40110082, - CPU_POWERPC_405GPd = 0x401100C4, -#define CPU_POWERPC_405GPe CPU_POWERPC_405CRc - CPU_POWERPC_405GPR = 0x50910951, -#if 0 - CPU_POWERPC_405H = xxx, -#endif -#if 0 - CPU_POWERPC_405L = xxx, -#endif - CPU_POWERPC_405LP = 0x41F10000, -#if 0 - CPU_POWERPC_405PM = xxx, -#endif -#if 0 - CPU_POWERPC_405PS = xxx, -#endif -#if 0 - CPU_POWERPC_405S = xxx, -#endif - /* IBM network processors */ - CPU_POWERPC_NPE405H = 0x414100C0, - CPU_POWERPC_NPE405H2 = 0x41410140, - CPU_POWERPC_NPE405L = 0x416100C0, - CPU_POWERPC_NPE4GS3 = 0x40B10000, -#if 0 - CPU_POWERPC_NPCxx1 = xxx, -#endif -#if 0 - CPU_POWERPC_NPR161 = xxx, -#endif -#if 0 - CPU_POWERPC_LC77700 = xxx, -#endif - /* IBM STBxxx (PowerPC 401/403/405 core based microcontrollers) */ -#if 0 - CPU_POWERPC_STB01000 = xxx, -#endif -#if 0 - CPU_POWERPC_STB01010 = xxx, -#endif -#if 0 - CPU_POWERPC_STB0210 = xxx, /* 401B3 */ -#endif - CPU_POWERPC_STB03 = 0x40310000, /* 0x40130000 ? */ -#if 0 - CPU_POWERPC_STB043 = xxx, -#endif -#if 0 - CPU_POWERPC_STB045 = xxx, -#endif - CPU_POWERPC_STB04 = 0x41810000, - CPU_POWERPC_STB25 = 0x51510950, -#if 0 - CPU_POWERPC_STB130 = xxx, -#endif - /* Xilinx cores */ - CPU_POWERPC_X2VP4 = 0x20010820, -#define CPU_POWERPC_X2VP7 CPU_POWERPC_X2VP4 - CPU_POWERPC_X2VP20 = 0x20010860, -#define CPU_POWERPC_X2VP50 CPU_POWERPC_X2VP20 -#if 0 - CPU_POWERPC_ZL10310 = xxx, -#endif -#if 0 - CPU_POWERPC_ZL10311 = xxx, -#endif -#if 0 - CPU_POWERPC_ZL10320 = xxx, -#endif -#if 0 - CPU_POWERPC_ZL10321 = xxx, -#endif - /* PowerPC 440 family */ - /* Generic PowerPC 440 */ -#define CPU_POWERPC_440 CPU_POWERPC_440GXf - /* PowerPC 440 cores */ -#if 0 - CPU_POWERPC_440A4 = xxx, -#endif - CPU_POWERPC_440_XILINX = 0x7ff21910, -#if 0 - CPU_POWERPC_440A5 = xxx, -#endif -#if 0 - CPU_POWERPC_440B4 = xxx, -#endif -#if 0 - CPU_POWERPC_440F5 = xxx, -#endif -#if 0 - CPU_POWERPC_440G5 = xxx, -#endif -#if 0 - CPU_POWERPC_440H4 = xxx, -#endif -#if 0 - CPU_POWERPC_440H6 = xxx, -#endif - /* PowerPC 440 microcontrolers */ -#define CPU_POWERPC_440EP CPU_POWERPC_440EPb - CPU_POWERPC_440EPa = 0x42221850, - CPU_POWERPC_440EPb = 0x422218D3, -#define CPU_POWERPC_440GP CPU_POWERPC_440GPc - CPU_POWERPC_440GPb = 0x40120440, - CPU_POWERPC_440GPc = 0x40120481, -#define CPU_POWERPC_440GR CPU_POWERPC_440GRa -#define CPU_POWERPC_440GRa CPU_POWERPC_440EPb - CPU_POWERPC_440GRX = 0x200008D0, -#define CPU_POWERPC_440EPX CPU_POWERPC_440GRX -#define CPU_POWERPC_440GX CPU_POWERPC_440GXf - CPU_POWERPC_440GXa = 0x51B21850, - CPU_POWERPC_440GXb = 0x51B21851, - CPU_POWERPC_440GXc = 0x51B21892, - CPU_POWERPC_440GXf = 0x51B21894, -#if 0 - CPU_POWERPC_440S = xxx, -#endif - CPU_POWERPC_440SP = 0x53221850, - CPU_POWERPC_440SP2 = 0x53221891, - CPU_POWERPC_440SPE = 0x53421890, - /* PowerPC 460 family */ -#if 0 - /* Generic PowerPC 464 */ -#define CPU_POWERPC_464 CPU_POWERPC_464H90 -#endif - /* PowerPC 464 microcontrolers */ -#if 0 - CPU_POWERPC_464H90 = xxx, -#endif -#if 0 - CPU_POWERPC_464H90FP = xxx, -#endif - /* Freescale embedded PowerPC cores */ - /* PowerPC MPC 5xx cores (aka RCPU) */ - CPU_POWERPC_MPC5xx = 0x00020020, -#define CPU_POWERPC_MGT560 CPU_POWERPC_MPC5xx -#define CPU_POWERPC_MPC509 CPU_POWERPC_MPC5xx -#define CPU_POWERPC_MPC533 CPU_POWERPC_MPC5xx -#define CPU_POWERPC_MPC534 CPU_POWERPC_MPC5xx -#define CPU_POWERPC_MPC555 CPU_POWERPC_MPC5xx -#define CPU_POWERPC_MPC556 CPU_POWERPC_MPC5xx -#define CPU_POWERPC_MPC560 CPU_POWERPC_MPC5xx -#define CPU_POWERPC_MPC561 CPU_POWERPC_MPC5xx -#define CPU_POWERPC_MPC562 CPU_POWERPC_MPC5xx -#define CPU_POWERPC_MPC563 CPU_POWERPC_MPC5xx -#define CPU_POWERPC_MPC564 CPU_POWERPC_MPC5xx -#define CPU_POWERPC_MPC565 CPU_POWERPC_MPC5xx -#define CPU_POWERPC_MPC566 CPU_POWERPC_MPC5xx - /* PowerPC MPC 8xx cores (aka PowerQUICC) */ - CPU_POWERPC_MPC8xx = 0x00500000, -#define CPU_POWERPC_MGT823 CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC821 CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC823 CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC850 CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC852T CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC855T CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC857 CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC859 CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC860 CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC862 CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC866 CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC870 CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC875 CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC880 CPU_POWERPC_MPC8xx -#define CPU_POWERPC_MPC885 CPU_POWERPC_MPC8xx - /* G2 cores (aka PowerQUICC-II) */ - CPU_POWERPC_G2 = 0x00810011, - CPU_POWERPC_G2H4 = 0x80811010, - CPU_POWERPC_G2gp = 0x80821010, - CPU_POWERPC_G2ls = 0x90810010, - CPU_POWERPC_MPC603 = 0x00810100, - CPU_POWERPC_G2_HIP3 = 0x00810101, - CPU_POWERPC_G2_HIP4 = 0x80811014, - /* G2_LE core (aka PowerQUICC-II) */ - CPU_POWERPC_G2LE = 0x80820010, - CPU_POWERPC_G2LEgp = 0x80822010, - CPU_POWERPC_G2LEls = 0xA0822010, - CPU_POWERPC_G2LEgp1 = 0x80822011, - CPU_POWERPC_G2LEgp3 = 0x80822013, - /* MPC52xx microcontrollers */ - /* XXX: MPC 5121 ? */ -#define CPU_POWERPC_MPC52xx CPU_POWERPC_MPC5200 -#define CPU_POWERPC_MPC5200 CPU_POWERPC_MPC5200_v12 -#define CPU_POWERPC_MPC5200_v10 CPU_POWERPC_G2LEgp1 -#define CPU_POWERPC_MPC5200_v11 CPU_POWERPC_G2LEgp1 -#define CPU_POWERPC_MPC5200_v12 CPU_POWERPC_G2LEgp1 -#define CPU_POWERPC_MPC5200B CPU_POWERPC_MPC5200B_v21 -#define CPU_POWERPC_MPC5200B_v20 CPU_POWERPC_G2LEgp1 -#define CPU_POWERPC_MPC5200B_v21 CPU_POWERPC_G2LEgp1 - /* MPC82xx microcontrollers */ -#define CPU_POWERPC_MPC82xx CPU_POWERPC_MPC8280 -#define CPU_POWERPC_MPC8240 CPU_POWERPC_MPC603 -#define CPU_POWERPC_MPC8241 CPU_POWERPC_G2_HIP4 -#define CPU_POWERPC_MPC8245 CPU_POWERPC_G2_HIP4 -#define CPU_POWERPC_MPC8247 CPU_POWERPC_G2LEgp3 -#define CPU_POWERPC_MPC8248 CPU_POWERPC_G2LEgp3 -#define CPU_POWERPC_MPC8250 CPU_POWERPC_MPC8250_HiP4 -#define CPU_POWERPC_MPC8250_HiP3 CPU_POWERPC_G2_HIP3 -#define CPU_POWERPC_MPC8250_HiP4 CPU_POWERPC_G2_HIP4 -#define CPU_POWERPC_MPC8255 CPU_POWERPC_MPC8255_HiP4 -#define CPU_POWERPC_MPC8255_HiP3 CPU_POWERPC_G2_HIP3 -#define CPU_POWERPC_MPC8255_HiP4 CPU_POWERPC_G2_HIP4 -#define CPU_POWERPC_MPC8260 CPU_POWERPC_MPC8260_HiP4 -#define CPU_POWERPC_MPC8260_HiP3 CPU_POWERPC_G2_HIP3 -#define CPU_POWERPC_MPC8260_HiP4 CPU_POWERPC_G2_HIP4 -#define CPU_POWERPC_MPC8264 CPU_POWERPC_MPC8264_HiP4 -#define CPU_POWERPC_MPC8264_HiP3 CPU_POWERPC_G2_HIP3 -#define CPU_POWERPC_MPC8264_HiP4 CPU_POWERPC_G2_HIP4 -#define CPU_POWERPC_MPC8265 CPU_POWERPC_MPC8265_HiP4 -#define CPU_POWERPC_MPC8265_HiP3 CPU_POWERPC_G2_HIP3 -#define CPU_POWERPC_MPC8265_HiP4 CPU_POWERPC_G2_HIP4 -#define CPU_POWERPC_MPC8266 CPU_POWERPC_MPC8266_HiP4 -#define CPU_POWERPC_MPC8266_HiP3 CPU_POWERPC_G2_HIP3 -#define CPU_POWERPC_MPC8266_HiP4 CPU_POWERPC_G2_HIP4 -#define CPU_POWERPC_MPC8270 CPU_POWERPC_G2LEgp3 -#define CPU_POWERPC_MPC8271 CPU_POWERPC_G2LEgp3 -#define CPU_POWERPC_MPC8272 CPU_POWERPC_G2LEgp3 -#define CPU_POWERPC_MPC8275 CPU_POWERPC_G2LEgp3 -#define CPU_POWERPC_MPC8280 CPU_POWERPC_G2LEgp3 - /* e200 family */ - /* e200 cores */ -#define CPU_POWERPC_e200 CPU_POWERPC_e200z6 -#if 0 - CPU_POWERPC_e200z0 = xxx, -#endif -#if 0 - CPU_POWERPC_e200z1 = xxx, -#endif -#if 0 /* ? */ - CPU_POWERPC_e200z3 = 0x81120000, -#endif - CPU_POWERPC_e200z5 = 0x81000000, - CPU_POWERPC_e200z6 = 0x81120000, - /* MPC55xx microcontrollers */ -#define CPU_POWERPC_MPC55xx CPU_POWERPC_MPC5567 -#if 0 -#define CPU_POWERPC_MPC5514E CPU_POWERPC_MPC5514E_v1 -#define CPU_POWERPC_MPC5514E_v0 CPU_POWERPC_e200z0 -#define CPU_POWERPC_MPC5514E_v1 CPU_POWERPC_e200z1 -#define CPU_POWERPC_MPC5514G CPU_POWERPC_MPC5514G_v1 -#define CPU_POWERPC_MPC5514G_v0 CPU_POWERPC_e200z0 -#define CPU_POWERPC_MPC5514G_v1 CPU_POWERPC_e200z1 -#define CPU_POWERPC_MPC5515S CPU_POWERPC_e200z1 -#define CPU_POWERPC_MPC5516E CPU_POWERPC_MPC5516E_v1 -#define CPU_POWERPC_MPC5516E_v0 CPU_POWERPC_e200z0 -#define CPU_POWERPC_MPC5516E_v1 CPU_POWERPC_e200z1 -#define CPU_POWERPC_MPC5516G CPU_POWERPC_MPC5516G_v1 -#define CPU_POWERPC_MPC5516G_v0 CPU_POWERPC_e200z0 -#define CPU_POWERPC_MPC5516G_v1 CPU_POWERPC_e200z1 -#define CPU_POWERPC_MPC5516S CPU_POWERPC_e200z1 -#endif -#if 0 -#define CPU_POWERPC_MPC5533 CPU_POWERPC_e200z3 -#define CPU_POWERPC_MPC5534 CPU_POWERPC_e200z3 -#endif -#define CPU_POWERPC_MPC5553 CPU_POWERPC_e200z6 -#define CPU_POWERPC_MPC5554 CPU_POWERPC_e200z6 -#define CPU_POWERPC_MPC5561 CPU_POWERPC_e200z6 -#define CPU_POWERPC_MPC5565 CPU_POWERPC_e200z6 -#define CPU_POWERPC_MPC5566 CPU_POWERPC_e200z6 -#define CPU_POWERPC_MPC5567 CPU_POWERPC_e200z6 - /* e300 family */ - /* e300 cores */ -#define CPU_POWERPC_e300 CPU_POWERPC_e300c3 - CPU_POWERPC_e300c1 = 0x00830010, - CPU_POWERPC_e300c2 = 0x00840010, - CPU_POWERPC_e300c3 = 0x00850010, - CPU_POWERPC_e300c4 = 0x00860010, - /* MPC83xx microcontrollers */ -#define CPU_POWERPC_MPC831x CPU_POWERPC_e300c3 -#define CPU_POWERPC_MPC832x CPU_POWERPC_e300c2 -#define CPU_POWERPC_MPC834x CPU_POWERPC_e300c1 -#define CPU_POWERPC_MPC835x CPU_POWERPC_e300c1 -#define CPU_POWERPC_MPC836x CPU_POWERPC_e300c1 -#define CPU_POWERPC_MPC837x CPU_POWERPC_e300c4 - /* e500 family */ - /* e500 cores */ -#define CPU_POWERPC_e500 CPU_POWERPC_e500v2_v22 -#define CPU_POWERPC_e500v1 CPU_POWERPC_e500v1_v20 -#define CPU_POWERPC_e500v2 CPU_POWERPC_e500v2_v22 - CPU_POWERPC_e500v1_v10 = 0x80200010, - CPU_POWERPC_e500v1_v20 = 0x80200020, - CPU_POWERPC_e500v2_v10 = 0x80210010, - CPU_POWERPC_e500v2_v11 = 0x80210011, - CPU_POWERPC_e500v2_v20 = 0x80210020, - CPU_POWERPC_e500v2_v21 = 0x80210021, - CPU_POWERPC_e500v2_v22 = 0x80210022, - CPU_POWERPC_e500v2_v30 = 0x80210030, - CPU_POWERPC_e500mc = 0x80230020, - CPU_POWERPC_e5500 = 0x80240020, - /* MPC85xx microcontrollers */ -#define CPU_POWERPC_MPC8533 CPU_POWERPC_MPC8533_v11 -#define CPU_POWERPC_MPC8533_v10 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8533_v11 CPU_POWERPC_e500v2_v22 -#define CPU_POWERPC_MPC8533E CPU_POWERPC_MPC8533E_v11 -#define CPU_POWERPC_MPC8533E_v10 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8533E_v11 CPU_POWERPC_e500v2_v22 -#define CPU_POWERPC_MPC8540 CPU_POWERPC_MPC8540_v21 -#define CPU_POWERPC_MPC8540_v10 CPU_POWERPC_e500v1_v10 -#define CPU_POWERPC_MPC8540_v20 CPU_POWERPC_e500v1_v20 -#define CPU_POWERPC_MPC8540_v21 CPU_POWERPC_e500v1_v20 -#define CPU_POWERPC_MPC8541 CPU_POWERPC_MPC8541_v11 -#define CPU_POWERPC_MPC8541_v10 CPU_POWERPC_e500v1_v20 -#define CPU_POWERPC_MPC8541_v11 CPU_POWERPC_e500v1_v20 -#define CPU_POWERPC_MPC8541E CPU_POWERPC_MPC8541E_v11 -#define CPU_POWERPC_MPC8541E_v10 CPU_POWERPC_e500v1_v20 -#define CPU_POWERPC_MPC8541E_v11 CPU_POWERPC_e500v1_v20 -#define CPU_POWERPC_MPC8543 CPU_POWERPC_MPC8543_v21 -#define CPU_POWERPC_MPC8543_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8543_v11 CPU_POWERPC_e500v2_v11 -#define CPU_POWERPC_MPC8543_v20 CPU_POWERPC_e500v2_v20 -#define CPU_POWERPC_MPC8543_v21 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8543E CPU_POWERPC_MPC8543E_v21 -#define CPU_POWERPC_MPC8543E_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8543E_v11 CPU_POWERPC_e500v2_v11 -#define CPU_POWERPC_MPC8543E_v20 CPU_POWERPC_e500v2_v20 -#define CPU_POWERPC_MPC8543E_v21 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8544 CPU_POWERPC_MPC8544_v11 -#define CPU_POWERPC_MPC8544_v10 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8544_v11 CPU_POWERPC_e500v2_v22 -#define CPU_POWERPC_MPC8544E_v11 CPU_POWERPC_e500v2_v22 -#define CPU_POWERPC_MPC8544E CPU_POWERPC_MPC8544E_v11 -#define CPU_POWERPC_MPC8544E_v10 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8545 CPU_POWERPC_MPC8545_v21 -#define CPU_POWERPC_MPC8545_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8545_v20 CPU_POWERPC_e500v2_v20 -#define CPU_POWERPC_MPC8545_v21 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8545E CPU_POWERPC_MPC8545E_v21 -#define CPU_POWERPC_MPC8545E_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8545E_v20 CPU_POWERPC_e500v2_v20 -#define CPU_POWERPC_MPC8545E_v21 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8547E CPU_POWERPC_MPC8545E_v21 -#define CPU_POWERPC_MPC8547E_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8547E_v20 CPU_POWERPC_e500v2_v20 -#define CPU_POWERPC_MPC8547E_v21 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8548 CPU_POWERPC_MPC8548_v21 -#define CPU_POWERPC_MPC8548_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8548_v11 CPU_POWERPC_e500v2_v11 -#define CPU_POWERPC_MPC8548_v20 CPU_POWERPC_e500v2_v20 -#define CPU_POWERPC_MPC8548_v21 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8548E CPU_POWERPC_MPC8548E_v21 -#define CPU_POWERPC_MPC8548E_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8548E_v11 CPU_POWERPC_e500v2_v11 -#define CPU_POWERPC_MPC8548E_v20 CPU_POWERPC_e500v2_v20 -#define CPU_POWERPC_MPC8548E_v21 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8555 CPU_POWERPC_MPC8555_v11 -#define CPU_POWERPC_MPC8555_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8555_v11 CPU_POWERPC_e500v2_v11 -#define CPU_POWERPC_MPC8555E CPU_POWERPC_MPC8555E_v11 -#define CPU_POWERPC_MPC8555E_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8555E_v11 CPU_POWERPC_e500v2_v11 -#define CPU_POWERPC_MPC8560 CPU_POWERPC_MPC8560_v21 -#define CPU_POWERPC_MPC8560_v10 CPU_POWERPC_e500v2_v10 -#define CPU_POWERPC_MPC8560_v20 CPU_POWERPC_e500v2_v20 -#define CPU_POWERPC_MPC8560_v21 CPU_POWERPC_e500v2_v21 -#define CPU_POWERPC_MPC8567 CPU_POWERPC_e500v2_v22 -#define CPU_POWERPC_MPC8567E CPU_POWERPC_e500v2_v22 -#define CPU_POWERPC_MPC8568 CPU_POWERPC_e500v2_v22 -#define CPU_POWERPC_MPC8568E CPU_POWERPC_e500v2_v22 -#define CPU_POWERPC_MPC8572 CPU_POWERPC_e500v2_v30 -#define CPU_POWERPC_MPC8572E CPU_POWERPC_e500v2_v30 - /* e600 family */ - /* e600 cores */ - CPU_POWERPC_e600 = 0x80040010, - /* MPC86xx microcontrollers */ -#define CPU_POWERPC_MPC8610 CPU_POWERPC_e600 -#define CPU_POWERPC_MPC8641 CPU_POWERPC_e600 -#define CPU_POWERPC_MPC8641D CPU_POWERPC_e600 - /* PowerPC 6xx cores */ -#define CPU_POWERPC_601 CPU_POWERPC_601_v2 - CPU_POWERPC_601_v0 = 0x00010001, - CPU_POWERPC_601_v1 = 0x00010001, -#define CPU_POWERPC_601v CPU_POWERPC_601_v2 - CPU_POWERPC_601_v2 = 0x00010002, - CPU_POWERPC_602 = 0x00050100, - CPU_POWERPC_603 = 0x00030100, -#define CPU_POWERPC_603E CPU_POWERPC_603E_v41 - CPU_POWERPC_603E_v11 = 0x00060101, - CPU_POWERPC_603E_v12 = 0x00060102, - CPU_POWERPC_603E_v13 = 0x00060103, - CPU_POWERPC_603E_v14 = 0x00060104, - CPU_POWERPC_603E_v22 = 0x00060202, - CPU_POWERPC_603E_v3 = 0x00060300, - CPU_POWERPC_603E_v4 = 0x00060400, - CPU_POWERPC_603E_v41 = 0x00060401, - CPU_POWERPC_603E7t = 0x00071201, - CPU_POWERPC_603E7v = 0x00070100, - CPU_POWERPC_603E7v1 = 0x00070101, - CPU_POWERPC_603E7v2 = 0x00070201, - CPU_POWERPC_603E7 = 0x00070200, - CPU_POWERPC_603P = 0x00070000, -#define CPU_POWERPC_603R CPU_POWERPC_603E7t - /* XXX: missing 0x00040303 (604) */ - CPU_POWERPC_604 = 0x00040103, -#define CPU_POWERPC_604E CPU_POWERPC_604E_v24 - /* XXX: missing 0x00091203 */ - /* XXX: missing 0x00092110 */ - /* XXX: missing 0x00092120 */ - CPU_POWERPC_604E_v10 = 0x00090100, - CPU_POWERPC_604E_v22 = 0x00090202, - CPU_POWERPC_604E_v24 = 0x00090204, - /* XXX: missing 0x000a0100 */ - /* XXX: missing 0x00093102 */ - CPU_POWERPC_604R = 0x000a0101, -#if 0 - CPU_POWERPC_604EV = xxx, /* XXX: same as 604R ? */ -#endif - /* PowerPC 740/750 cores (aka G3) */ - /* XXX: missing 0x00084202 */ -#define CPU_POWERPC_7x0 CPU_POWERPC_7x0_v31 - CPU_POWERPC_7x0_v10 = 0x00080100, - CPU_POWERPC_7x0_v20 = 0x00080200, - CPU_POWERPC_7x0_v21 = 0x00080201, - CPU_POWERPC_7x0_v22 = 0x00080202, - CPU_POWERPC_7x0_v30 = 0x00080300, - CPU_POWERPC_7x0_v31 = 0x00080301, - CPU_POWERPC_740E = 0x00080100, - CPU_POWERPC_750E = 0x00080200, - CPU_POWERPC_7x0P = 0x10080000, - /* XXX: missing 0x00087010 (CL ?) */ -#define CPU_POWERPC_750CL CPU_POWERPC_750CL_v20 - CPU_POWERPC_750CL_v10 = 0x00087200, - CPU_POWERPC_750CL_v20 = 0x00087210, /* aka rev E */ -#define CPU_POWERPC_750CX CPU_POWERPC_750CX_v22 - CPU_POWERPC_750CX_v10 = 0x00082100, - CPU_POWERPC_750CX_v20 = 0x00082200, - CPU_POWERPC_750CX_v21 = 0x00082201, - CPU_POWERPC_750CX_v22 = 0x00082202, -#define CPU_POWERPC_750CXE CPU_POWERPC_750CXE_v31b - CPU_POWERPC_750CXE_v21 = 0x00082211, - CPU_POWERPC_750CXE_v22 = 0x00082212, - CPU_POWERPC_750CXE_v23 = 0x00082213, - CPU_POWERPC_750CXE_v24 = 0x00082214, - CPU_POWERPC_750CXE_v24b = 0x00083214, - CPU_POWERPC_750CXE_v30 = 0x00082310, - CPU_POWERPC_750CXE_v31 = 0x00082311, - CPU_POWERPC_750CXE_v31b = 0x00083311, - CPU_POWERPC_750CXR = 0x00083410, - CPU_POWERPC_750FL = 0x70000203, -#define CPU_POWERPC_750FX CPU_POWERPC_750FX_v23 - CPU_POWERPC_750FX_v10 = 0x70000100, - CPU_POWERPC_750FX_v20 = 0x70000200, - CPU_POWERPC_750FX_v21 = 0x70000201, - CPU_POWERPC_750FX_v22 = 0x70000202, - CPU_POWERPC_750FX_v23 = 0x70000203, - CPU_POWERPC_750GL = 0x70020102, -#define CPU_POWERPC_750GX CPU_POWERPC_750GX_v12 - CPU_POWERPC_750GX_v10 = 0x70020100, - CPU_POWERPC_750GX_v11 = 0x70020101, - CPU_POWERPC_750GX_v12 = 0x70020102, -#define CPU_POWERPC_750L CPU_POWERPC_750L_v32 /* Aka LoneStar */ - CPU_POWERPC_750L_v20 = 0x00088200, - CPU_POWERPC_750L_v21 = 0x00088201, - CPU_POWERPC_750L_v22 = 0x00088202, - CPU_POWERPC_750L_v30 = 0x00088300, - CPU_POWERPC_750L_v32 = 0x00088302, - /* PowerPC 745/755 cores */ -#define CPU_POWERPC_7x5 CPU_POWERPC_7x5_v28 - CPU_POWERPC_7x5_v10 = 0x00083100, - CPU_POWERPC_7x5_v11 = 0x00083101, - CPU_POWERPC_7x5_v20 = 0x00083200, - CPU_POWERPC_7x5_v21 = 0x00083201, - CPU_POWERPC_7x5_v22 = 0x00083202, /* aka D */ - CPU_POWERPC_7x5_v23 = 0x00083203, /* aka E */ - CPU_POWERPC_7x5_v24 = 0x00083204, - CPU_POWERPC_7x5_v25 = 0x00083205, - CPU_POWERPC_7x5_v26 = 0x00083206, - CPU_POWERPC_7x5_v27 = 0x00083207, - CPU_POWERPC_7x5_v28 = 0x00083208, -#if 0 - CPU_POWERPC_7x5P = xxx, -#endif - /* PowerPC 74xx cores (aka G4) */ - /* XXX: missing 0x000C1101 */ -#define CPU_POWERPC_7400 CPU_POWERPC_7400_v29 - CPU_POWERPC_7400_v10 = 0x000C0100, - CPU_POWERPC_7400_v11 = 0x000C0101, - CPU_POWERPC_7400_v20 = 0x000C0200, - CPU_POWERPC_7400_v21 = 0x000C0201, - CPU_POWERPC_7400_v22 = 0x000C0202, - CPU_POWERPC_7400_v26 = 0x000C0206, - CPU_POWERPC_7400_v27 = 0x000C0207, - CPU_POWERPC_7400_v28 = 0x000C0208, - CPU_POWERPC_7400_v29 = 0x000C0209, -#define CPU_POWERPC_7410 CPU_POWERPC_7410_v14 - CPU_POWERPC_7410_v10 = 0x800C1100, - CPU_POWERPC_7410_v11 = 0x800C1101, - CPU_POWERPC_7410_v12 = 0x800C1102, /* aka C */ - CPU_POWERPC_7410_v13 = 0x800C1103, /* aka D */ - CPU_POWERPC_7410_v14 = 0x800C1104, /* aka E */ -#define CPU_POWERPC_7448 CPU_POWERPC_7448_v21 - CPU_POWERPC_7448_v10 = 0x80040100, - CPU_POWERPC_7448_v11 = 0x80040101, - CPU_POWERPC_7448_v20 = 0x80040200, - CPU_POWERPC_7448_v21 = 0x80040201, -#define CPU_POWERPC_7450 CPU_POWERPC_7450_v21 - CPU_POWERPC_7450_v10 = 0x80000100, - CPU_POWERPC_7450_v11 = 0x80000101, - CPU_POWERPC_7450_v12 = 0x80000102, - CPU_POWERPC_7450_v20 = 0x80000200, /* aka A, B, C, D: 2.04 */ - CPU_POWERPC_7450_v21 = 0x80000201, /* aka E */ -#define CPU_POWERPC_74x1 CPU_POWERPC_74x1_v23 - CPU_POWERPC_74x1_v23 = 0x80000203, /* aka G: 2.3 */ - /* XXX: this entry might be a bug in some documentation */ - CPU_POWERPC_74x1_v210 = 0x80000210, /* aka G: 2.3 ? */ -#define CPU_POWERPC_74x5 CPU_POWERPC_74x5_v32 - CPU_POWERPC_74x5_v10 = 0x80010100, - /* XXX: missing 0x80010200 */ - CPU_POWERPC_74x5_v21 = 0x80010201, /* aka C: 2.1 */ - CPU_POWERPC_74x5_v32 = 0x80010302, - CPU_POWERPC_74x5_v33 = 0x80010303, /* aka F: 3.3 */ - CPU_POWERPC_74x5_v34 = 0x80010304, /* aka G: 3.4 */ -#define CPU_POWERPC_74x7 CPU_POWERPC_74x7_v12 - CPU_POWERPC_74x7_v10 = 0x80020100, /* aka A: 1.0 */ - CPU_POWERPC_74x7_v11 = 0x80020101, /* aka B: 1.1 */ - CPU_POWERPC_74x7_v12 = 0x80020102, /* aka C: 1.2 */ -#define CPU_POWERPC_74x7A CPU_POWERPC_74x7A_v12 - CPU_POWERPC_74x7A_v10 = 0x80030100, /* aka A: 1.0 */ - CPU_POWERPC_74x7A_v11 = 0x80030101, /* aka B: 1.1 */ - CPU_POWERPC_74x7A_v12 = 0x80030102, /* aka C: 1.2 */ - /* 64 bits PowerPC */ -#if defined(TARGET_PPC64) - CPU_POWERPC_620 = 0x00140000, - CPU_POWERPC_630 = 0x00400000, - CPU_POWERPC_631 = 0x00410104, - CPU_POWERPC_POWER4 = 0x00350000, - CPU_POWERPC_POWER4P = 0x00380000, - /* XXX: missing 0x003A0201 */ - CPU_POWERPC_POWER5 = 0x003A0203, -#define CPU_POWERPC_POWER5GR CPU_POWERPC_POWER5 - CPU_POWERPC_POWER5P = 0x003B0000, -#define CPU_POWERPC_POWER5GS CPU_POWERPC_POWER5P - CPU_POWERPC_POWER6 = 0x003E0000, - CPU_POWERPC_POWER6_5 = 0x0F000001, /* POWER6 in POWER5 mode */ - CPU_POWERPC_POWER6A = 0x0F000002, -#define CPU_POWERPC_POWER7 CPU_POWERPC_POWER7_v20 - CPU_POWERPC_POWER7_v20 = 0x003F0200, - CPU_POWERPC_POWER7_v21 = 0x003F0201, - CPU_POWERPC_POWER7_v23 = 0x003F0203, - CPU_POWERPC_970 = 0x00390202, -#define CPU_POWERPC_970FX CPU_POWERPC_970FX_v31 - CPU_POWERPC_970FX_v10 = 0x00391100, - CPU_POWERPC_970FX_v20 = 0x003C0200, - CPU_POWERPC_970FX_v21 = 0x003C0201, - CPU_POWERPC_970FX_v30 = 0x003C0300, - CPU_POWERPC_970FX_v31 = 0x003C0301, - CPU_POWERPC_970GX = 0x00450000, -#define CPU_POWERPC_970MP CPU_POWERPC_970MP_v11 - CPU_POWERPC_970MP_v10 = 0x00440100, - CPU_POWERPC_970MP_v11 = 0x00440101, -#define CPU_POWERPC_CELL CPU_POWERPC_CELL_v32 - CPU_POWERPC_CELL_v10 = 0x00700100, - CPU_POWERPC_CELL_v20 = 0x00700400, - CPU_POWERPC_CELL_v30 = 0x00700500, - CPU_POWERPC_CELL_v31 = 0x00700501, -#define CPU_POWERPC_CELL_v32 CPU_POWERPC_CELL_v31 - CPU_POWERPC_RS64 = 0x00330000, - CPU_POWERPC_RS64II = 0x00340000, - CPU_POWERPC_RS64III = 0x00360000, - CPU_POWERPC_RS64IV = 0x00370000, -#endif /* defined(TARGET_PPC64) */ - /* Original POWER */ - /* XXX: should be POWER (RIOS), RSC3308, RSC4608, - * POWER2 (RIOS2) & RSC2 (P2SC) here - */ -#if 0 - CPU_POWER = xxx, /* 0x20000 ? 0x30000 for RSC ? */ -#endif -#if 0 - CPU_POWER2 = xxx, /* 0x40000 ? */ -#endif - /* PA Semi core */ - CPU_POWERPC_PA6T = 0x00900000, -}; - -/* System version register (used on MPC 8xxx) */ -enum { - POWERPC_SVR_NONE = 0x00000000, -#define POWERPC_SVR_52xx POWERPC_SVR_5200 -#define POWERPC_SVR_5200 POWERPC_SVR_5200_v12 - POWERPC_SVR_5200_v10 = 0x80110010, - POWERPC_SVR_5200_v11 = 0x80110011, - POWERPC_SVR_5200_v12 = 0x80110012, -#define POWERPC_SVR_5200B POWERPC_SVR_5200B_v21 - POWERPC_SVR_5200B_v20 = 0x80110020, - POWERPC_SVR_5200B_v21 = 0x80110021, -#define POWERPC_SVR_55xx POWERPC_SVR_5567 -#if 0 - POWERPC_SVR_5533 = xxx, -#endif -#if 0 - POWERPC_SVR_5534 = xxx, -#endif -#if 0 - POWERPC_SVR_5553 = xxx, -#endif -#if 0 - POWERPC_SVR_5554 = xxx, -#endif -#if 0 - POWERPC_SVR_5561 = xxx, -#endif -#if 0 - POWERPC_SVR_5565 = xxx, -#endif -#if 0 - POWERPC_SVR_5566 = xxx, -#endif -#if 0 - POWERPC_SVR_5567 = xxx, -#endif -#if 0 - POWERPC_SVR_8313 = xxx, -#endif -#if 0 - POWERPC_SVR_8313E = xxx, -#endif -#if 0 - POWERPC_SVR_8314 = xxx, -#endif -#if 0 - POWERPC_SVR_8314E = xxx, -#endif -#if 0 - POWERPC_SVR_8315 = xxx, -#endif -#if 0 - POWERPC_SVR_8315E = xxx, -#endif -#if 0 - POWERPC_SVR_8321 = xxx, -#endif -#if 0 - POWERPC_SVR_8321E = xxx, -#endif -#if 0 - POWERPC_SVR_8323 = xxx, -#endif -#if 0 - POWERPC_SVR_8323E = xxx, -#endif - POWERPC_SVR_8343 = 0x80570010, - POWERPC_SVR_8343A = 0x80570030, - POWERPC_SVR_8343E = 0x80560010, - POWERPC_SVR_8343EA = 0x80560030, -#define POWERPC_SVR_8347 POWERPC_SVR_8347T - POWERPC_SVR_8347P = 0x80550010, /* PBGA package */ - POWERPC_SVR_8347T = 0x80530010, /* TBGA package */ -#define POWERPC_SVR_8347A POWERPC_SVR_8347AT - POWERPC_SVR_8347AP = 0x80550030, /* PBGA package */ - POWERPC_SVR_8347AT = 0x80530030, /* TBGA package */ -#define POWERPC_SVR_8347E POWERPC_SVR_8347ET - POWERPC_SVR_8347EP = 0x80540010, /* PBGA package */ - POWERPC_SVR_8347ET = 0x80520010, /* TBGA package */ -#define POWERPC_SVR_8347EA POWERPC_SVR_8347EAT - POWERPC_SVR_8347EAP = 0x80540030, /* PBGA package */ - POWERPC_SVR_8347EAT = 0x80520030, /* TBGA package */ - POWERPC_SVR_8349 = 0x80510010, - POWERPC_SVR_8349A = 0x80510030, - POWERPC_SVR_8349E = 0x80500010, - POWERPC_SVR_8349EA = 0x80500030, -#if 0 - POWERPC_SVR_8358E = xxx, -#endif -#if 0 - POWERPC_SVR_8360E = xxx, -#endif -#define POWERPC_SVR_E500 0x40000000 - POWERPC_SVR_8377 = 0x80C70010 | POWERPC_SVR_E500, - POWERPC_SVR_8377E = 0x80C60010 | POWERPC_SVR_E500, - POWERPC_SVR_8378 = 0x80C50010 | POWERPC_SVR_E500, - POWERPC_SVR_8378E = 0x80C40010 | POWERPC_SVR_E500, - POWERPC_SVR_8379 = 0x80C30010 | POWERPC_SVR_E500, - POWERPC_SVR_8379E = 0x80C00010 | POWERPC_SVR_E500, -#define POWERPC_SVR_8533 POWERPC_SVR_8533_v11 - POWERPC_SVR_8533_v10 = 0x80340010 | POWERPC_SVR_E500, - POWERPC_SVR_8533_v11 = 0x80340011 | POWERPC_SVR_E500, -#define POWERPC_SVR_8533E POWERPC_SVR_8533E_v11 - POWERPC_SVR_8533E_v10 = 0x803C0010 | POWERPC_SVR_E500, - POWERPC_SVR_8533E_v11 = 0x803C0011 | POWERPC_SVR_E500, -#define POWERPC_SVR_8540 POWERPC_SVR_8540_v21 - POWERPC_SVR_8540_v10 = 0x80300010 | POWERPC_SVR_E500, - POWERPC_SVR_8540_v20 = 0x80300020 | POWERPC_SVR_E500, - POWERPC_SVR_8540_v21 = 0x80300021 | POWERPC_SVR_E500, -#define POWERPC_SVR_8541 POWERPC_SVR_8541_v11 - POWERPC_SVR_8541_v10 = 0x80720010 | POWERPC_SVR_E500, - POWERPC_SVR_8541_v11 = 0x80720011 | POWERPC_SVR_E500, -#define POWERPC_SVR_8541E POWERPC_SVR_8541E_v11 - POWERPC_SVR_8541E_v10 = 0x807A0010 | POWERPC_SVR_E500, - POWERPC_SVR_8541E_v11 = 0x807A0011 | POWERPC_SVR_E500, -#define POWERPC_SVR_8543 POWERPC_SVR_8543_v21 - POWERPC_SVR_8543_v10 = 0x80320010 | POWERPC_SVR_E500, - POWERPC_SVR_8543_v11 = 0x80320011 | POWERPC_SVR_E500, - POWERPC_SVR_8543_v20 = 0x80320020 | POWERPC_SVR_E500, - POWERPC_SVR_8543_v21 = 0x80320021 | POWERPC_SVR_E500, -#define POWERPC_SVR_8543E POWERPC_SVR_8543E_v21 - POWERPC_SVR_8543E_v10 = 0x803A0010 | POWERPC_SVR_E500, - POWERPC_SVR_8543E_v11 = 0x803A0011 | POWERPC_SVR_E500, - POWERPC_SVR_8543E_v20 = 0x803A0020 | POWERPC_SVR_E500, - POWERPC_SVR_8543E_v21 = 0x803A0021 | POWERPC_SVR_E500, -#define POWERPC_SVR_8544 POWERPC_SVR_8544_v11 - POWERPC_SVR_8544_v10 = 0x80340110 | POWERPC_SVR_E500, - POWERPC_SVR_8544_v11 = 0x80340111 | POWERPC_SVR_E500, -#define POWERPC_SVR_8544E POWERPC_SVR_8544E_v11 - POWERPC_SVR_8544E_v10 = 0x803C0110 | POWERPC_SVR_E500, - POWERPC_SVR_8544E_v11 = 0x803C0111 | POWERPC_SVR_E500, -#define POWERPC_SVR_8545 POWERPC_SVR_8545_v21 - POWERPC_SVR_8545_v20 = 0x80310220 | POWERPC_SVR_E500, - POWERPC_SVR_8545_v21 = 0x80310221 | POWERPC_SVR_E500, -#define POWERPC_SVR_8545E POWERPC_SVR_8545E_v21 - POWERPC_SVR_8545E_v20 = 0x80390220 | POWERPC_SVR_E500, - POWERPC_SVR_8545E_v21 = 0x80390221 | POWERPC_SVR_E500, -#define POWERPC_SVR_8547E POWERPC_SVR_8547E_v21 - POWERPC_SVR_8547E_v20 = 0x80390120 | POWERPC_SVR_E500, - POWERPC_SVR_8547E_v21 = 0x80390121 | POWERPC_SVR_E500, -#define POWERPC_SVR_8548 POWERPC_SVR_8548_v21 - POWERPC_SVR_8548_v10 = 0x80310010 | POWERPC_SVR_E500, - POWERPC_SVR_8548_v11 = 0x80310011 | POWERPC_SVR_E500, - POWERPC_SVR_8548_v20 = 0x80310020 | POWERPC_SVR_E500, - POWERPC_SVR_8548_v21 = 0x80310021 | POWERPC_SVR_E500, -#define POWERPC_SVR_8548E POWERPC_SVR_8548E_v21 - POWERPC_SVR_8548E_v10 = 0x80390010 | POWERPC_SVR_E500, - POWERPC_SVR_8548E_v11 = 0x80390011 | POWERPC_SVR_E500, - POWERPC_SVR_8548E_v20 = 0x80390020 | POWERPC_SVR_E500, - POWERPC_SVR_8548E_v21 = 0x80390021 | POWERPC_SVR_E500, -#define POWERPC_SVR_8555 POWERPC_SVR_8555_v11 - POWERPC_SVR_8555_v10 = 0x80710010 | POWERPC_SVR_E500, - POWERPC_SVR_8555_v11 = 0x80710011 | POWERPC_SVR_E500, -#define POWERPC_SVR_8555E POWERPC_SVR_8555_v11 - POWERPC_SVR_8555E_v10 = 0x80790010 | POWERPC_SVR_E500, - POWERPC_SVR_8555E_v11 = 0x80790011 | POWERPC_SVR_E500, -#define POWERPC_SVR_8560 POWERPC_SVR_8560_v21 - POWERPC_SVR_8560_v10 = 0x80700010 | POWERPC_SVR_E500, - POWERPC_SVR_8560_v20 = 0x80700020 | POWERPC_SVR_E500, - POWERPC_SVR_8560_v21 = 0x80700021 | POWERPC_SVR_E500, - POWERPC_SVR_8567 = 0x80750111 | POWERPC_SVR_E500, - POWERPC_SVR_8567E = 0x807D0111 | POWERPC_SVR_E500, - POWERPC_SVR_8568 = 0x80750011 | POWERPC_SVR_E500, - POWERPC_SVR_8568E = 0x807D0011 | POWERPC_SVR_E500, - POWERPC_SVR_8572 = 0x80E00010 | POWERPC_SVR_E500, - POWERPC_SVR_8572E = 0x80E80010 | POWERPC_SVR_E500, -#if 0 - POWERPC_SVR_8610 = xxx, -#endif - POWERPC_SVR_8641 = 0x80900021, - POWERPC_SVR_8641D = 0x80900121, -}; - -/*****************************************************************************/ -/* PowerPC CPU definitions */ -#define POWERPC_DEF_SVR(_name, _pvr, _svr, _type) \ - { \ - .name = _name, \ - .pvr = _pvr, \ - .svr = _svr, \ - .insns_flags = glue(POWERPC_INSNS_,_type), \ - .insns_flags2 = glue(POWERPC_INSNS2_,_type), \ - .msr_mask = glue(POWERPC_MSRM_,_type), \ - .mmu_model = glue(POWERPC_MMU_,_type), \ - .excp_model = glue(POWERPC_EXCP_,_type), \ - .bus_model = glue(POWERPC_INPUT_,_type), \ - .bfd_mach = glue(POWERPC_BFDM_,_type), \ - .flags = glue(POWERPC_FLAG_,_type), \ - .init_proc = &glue(init_proc_,_type), \ - .check_pow = &glue(check_pow_,_type), \ - } -#define POWERPC_DEF(_name, _pvr, _type) \ -POWERPC_DEF_SVR(_name, _pvr, POWERPC_SVR_NONE, _type) - -static const ppc_def_t ppc_defs[] = { - /* Embedded PowerPC */ - /* PowerPC 401 family */ - /* Generic PowerPC 401 */ - POWERPC_DEF("401", CPU_POWERPC_401, 401), - /* PowerPC 401 cores */ - /* PowerPC 401A1 */ - POWERPC_DEF("401A1", CPU_POWERPC_401A1, 401), - /* PowerPC 401B2 */ - POWERPC_DEF("401B2", CPU_POWERPC_401B2, 401x2), -#if defined (TODO) - /* PowerPC 401B3 */ - POWERPC_DEF("401B3", CPU_POWERPC_401B3, 401x3), -#endif - /* PowerPC 401C2 */ - POWERPC_DEF("401C2", CPU_POWERPC_401C2, 401x2), - /* PowerPC 401D2 */ - POWERPC_DEF("401D2", CPU_POWERPC_401D2, 401x2), - /* PowerPC 401E2 */ - POWERPC_DEF("401E2", CPU_POWERPC_401E2, 401x2), - /* PowerPC 401F2 */ - POWERPC_DEF("401F2", CPU_POWERPC_401F2, 401x2), - /* PowerPC 401G2 */ - /* XXX: to be checked */ - POWERPC_DEF("401G2", CPU_POWERPC_401G2, 401x2), - /* PowerPC 401 microcontrolers */ -#if defined (TODO) - /* PowerPC 401GF */ - POWERPC_DEF("401GF", CPU_POWERPC_401GF, 401), -#endif - /* IOP480 (401 microcontroler) */ - POWERPC_DEF("IOP480", CPU_POWERPC_IOP480, IOP480), - /* IBM Processor for Network Resources */ - POWERPC_DEF("Cobra", CPU_POWERPC_COBRA, 401), -#if defined (TODO) - POWERPC_DEF("Xipchip", CPU_POWERPC_XIPCHIP, 401), -#endif - /* PowerPC 403 family */ - /* Generic PowerPC 403 */ - POWERPC_DEF("403", CPU_POWERPC_403, 403), - /* PowerPC 403 microcontrolers */ - /* PowerPC 403 GA */ - POWERPC_DEF("403GA", CPU_POWERPC_403GA, 403), - /* PowerPC 403 GB */ - POWERPC_DEF("403GB", CPU_POWERPC_403GB, 403), - /* PowerPC 403 GC */ - POWERPC_DEF("403GC", CPU_POWERPC_403GC, 403), - /* PowerPC 403 GCX */ - POWERPC_DEF("403GCX", CPU_POWERPC_403GCX, 403GCX), -#if defined (TODO) - /* PowerPC 403 GP */ - POWERPC_DEF("403GP", CPU_POWERPC_403GP, 403), -#endif - /* PowerPC 405 family */ - /* Generic PowerPC 405 */ - POWERPC_DEF("405", CPU_POWERPC_405, 405), - /* PowerPC 405 cores */ -#if defined (TODO) - /* PowerPC 405 A3 */ - POWERPC_DEF("405A3", CPU_POWERPC_405A3, 405), -#endif -#if defined (TODO) - /* PowerPC 405 A4 */ - POWERPC_DEF("405A4", CPU_POWERPC_405A4, 405), -#endif -#if defined (TODO) - /* PowerPC 405 B3 */ - POWERPC_DEF("405B3", CPU_POWERPC_405B3, 405), -#endif -#if defined (TODO) - /* PowerPC 405 B4 */ - POWERPC_DEF("405B4", CPU_POWERPC_405B4, 405), -#endif -#if defined (TODO) - /* PowerPC 405 C3 */ - POWERPC_DEF("405C3", CPU_POWERPC_405C3, 405), -#endif -#if defined (TODO) - /* PowerPC 405 C4 */ - POWERPC_DEF("405C4", CPU_POWERPC_405C4, 405), -#endif - /* PowerPC 405 D2 */ - POWERPC_DEF("405D2", CPU_POWERPC_405D2, 405), -#if defined (TODO) - /* PowerPC 405 D3 */ - POWERPC_DEF("405D3", CPU_POWERPC_405D3, 405), -#endif - /* PowerPC 405 D4 */ - POWERPC_DEF("405D4", CPU_POWERPC_405D4, 405), -#if defined (TODO) - /* PowerPC 405 D5 */ - POWERPC_DEF("405D5", CPU_POWERPC_405D5, 405), -#endif -#if defined (TODO) - /* PowerPC 405 E4 */ - POWERPC_DEF("405E4", CPU_POWERPC_405E4, 405), -#endif -#if defined (TODO) - /* PowerPC 405 F4 */ - POWERPC_DEF("405F4", CPU_POWERPC_405F4, 405), -#endif -#if defined (TODO) - /* PowerPC 405 F5 */ - POWERPC_DEF("405F5", CPU_POWERPC_405F5, 405), -#endif -#if defined (TODO) - /* PowerPC 405 F6 */ - POWERPC_DEF("405F6", CPU_POWERPC_405F6, 405), -#endif - /* PowerPC 405 microcontrolers */ - /* PowerPC 405 CR */ - POWERPC_DEF("405CR", CPU_POWERPC_405CR, 405), - /* PowerPC 405 CRa */ - POWERPC_DEF("405CRa", CPU_POWERPC_405CRa, 405), - /* PowerPC 405 CRb */ - POWERPC_DEF("405CRb", CPU_POWERPC_405CRb, 405), - /* PowerPC 405 CRc */ - POWERPC_DEF("405CRc", CPU_POWERPC_405CRc, 405), - /* PowerPC 405 EP */ - POWERPC_DEF("405EP", CPU_POWERPC_405EP, 405), -#if defined(TODO) - /* PowerPC 405 EXr */ - POWERPC_DEF("405EXr", CPU_POWERPC_405EXr, 405), -#endif - /* PowerPC 405 EZ */ - POWERPC_DEF("405EZ", CPU_POWERPC_405EZ, 405), -#if defined(TODO) - /* PowerPC 405 FX */ - POWERPC_DEF("405FX", CPU_POWERPC_405FX, 405), -#endif - /* PowerPC 405 GP */ - POWERPC_DEF("405GP", CPU_POWERPC_405GP, 405), - /* PowerPC 405 GPa */ - POWERPC_DEF("405GPa", CPU_POWERPC_405GPa, 405), - /* PowerPC 405 GPb */ - POWERPC_DEF("405GPb", CPU_POWERPC_405GPb, 405), - /* PowerPC 405 GPc */ - POWERPC_DEF("405GPc", CPU_POWERPC_405GPc, 405), - /* PowerPC 405 GPd */ - POWERPC_DEF("405GPd", CPU_POWERPC_405GPd, 405), - /* PowerPC 405 GPe */ - POWERPC_DEF("405GPe", CPU_POWERPC_405GPe, 405), - /* PowerPC 405 GPR */ - POWERPC_DEF("405GPR", CPU_POWERPC_405GPR, 405), -#if defined(TODO) - /* PowerPC 405 H */ - POWERPC_DEF("405H", CPU_POWERPC_405H, 405), -#endif -#if defined(TODO) - /* PowerPC 405 L */ - POWERPC_DEF("405L", CPU_POWERPC_405L, 405), -#endif - /* PowerPC 405 LP */ - POWERPC_DEF("405LP", CPU_POWERPC_405LP, 405), -#if defined(TODO) - /* PowerPC 405 PM */ - POWERPC_DEF("405PM", CPU_POWERPC_405PM, 405), -#endif -#if defined(TODO) - /* PowerPC 405 PS */ - POWERPC_DEF("405PS", CPU_POWERPC_405PS, 405), -#endif -#if defined(TODO) - /* PowerPC 405 S */ - POWERPC_DEF("405S", CPU_POWERPC_405S, 405), -#endif - /* Npe405 H */ - POWERPC_DEF("Npe405H", CPU_POWERPC_NPE405H, 405), - /* Npe405 H2 */ - POWERPC_DEF("Npe405H2", CPU_POWERPC_NPE405H2, 405), - /* Npe405 L */ - POWERPC_DEF("Npe405L", CPU_POWERPC_NPE405L, 405), - /* Npe4GS3 */ - POWERPC_DEF("Npe4GS3", CPU_POWERPC_NPE4GS3, 405), -#if defined (TODO) - POWERPC_DEF("Npcxx1", CPU_POWERPC_NPCxx1, 405), -#endif -#if defined (TODO) - POWERPC_DEF("Npr161", CPU_POWERPC_NPR161, 405), -#endif -#if defined (TODO) - /* PowerPC LC77700 (Sanyo) */ - POWERPC_DEF("LC77700", CPU_POWERPC_LC77700, 405), -#endif - /* PowerPC 401/403/405 based set-top-box microcontrolers */ -#if defined (TODO) - /* STB010000 */ - POWERPC_DEF("STB01000", CPU_POWERPC_STB01000, 401x2), -#endif -#if defined (TODO) - /* STB01010 */ - POWERPC_DEF("STB01010", CPU_POWERPC_STB01010, 401x2), -#endif -#if defined (TODO) - /* STB0210 */ - POWERPC_DEF("STB0210", CPU_POWERPC_STB0210, 401x3), -#endif - /* STB03xx */ - POWERPC_DEF("STB03", CPU_POWERPC_STB03, 405), -#if defined (TODO) - /* STB043x */ - POWERPC_DEF("STB043", CPU_POWERPC_STB043, 405), -#endif -#if defined (TODO) - /* STB045x */ - POWERPC_DEF("STB045", CPU_POWERPC_STB045, 405), -#endif - /* STB04xx */ - POWERPC_DEF("STB04", CPU_POWERPC_STB04, 405), - /* STB25xx */ - POWERPC_DEF("STB25", CPU_POWERPC_STB25, 405), -#if defined (TODO) - /* STB130 */ - POWERPC_DEF("STB130", CPU_POWERPC_STB130, 405), -#endif - /* Xilinx PowerPC 405 cores */ - POWERPC_DEF("x2vp4", CPU_POWERPC_X2VP4, 405), - POWERPC_DEF("x2vp7", CPU_POWERPC_X2VP7, 405), - POWERPC_DEF("x2vp20", CPU_POWERPC_X2VP20, 405), - POWERPC_DEF("x2vp50", CPU_POWERPC_X2VP50, 405), -#if defined (TODO) - /* Zarlink ZL10310 */ - POWERPC_DEF("zl10310", CPU_POWERPC_ZL10310, 405), -#endif -#if defined (TODO) - /* Zarlink ZL10311 */ - POWERPC_DEF("zl10311", CPU_POWERPC_ZL10311, 405), -#endif -#if defined (TODO) - /* Zarlink ZL10320 */ - POWERPC_DEF("zl10320", CPU_POWERPC_ZL10320, 405), -#endif -#if defined (TODO) - /* Zarlink ZL10321 */ - POWERPC_DEF("zl10321", CPU_POWERPC_ZL10321, 405), -#endif - /* PowerPC 440 family */ -#if defined(TODO_USER_ONLY) - /* Generic PowerPC 440 */ - POWERPC_DEF("440", CPU_POWERPC_440, 440GP), -#endif - /* PowerPC 440 cores */ -#if defined (TODO) - /* PowerPC 440 A4 */ - POWERPC_DEF("440A4", CPU_POWERPC_440A4, 440x4), -#endif - /* PowerPC 440 Xilinx 5 */ - POWERPC_DEF("440-Xilinx", CPU_POWERPC_440_XILINX, 440x5), -#if defined (TODO) - /* PowerPC 440 A5 */ - POWERPC_DEF("440A5", CPU_POWERPC_440A5, 440x5), -#endif -#if defined (TODO) - /* PowerPC 440 B4 */ - POWERPC_DEF("440B4", CPU_POWERPC_440B4, 440x4), -#endif -#if defined (TODO) - /* PowerPC 440 G4 */ - POWERPC_DEF("440G4", CPU_POWERPC_440G4, 440x4), -#endif -#if defined (TODO) - /* PowerPC 440 F5 */ - POWERPC_DEF("440F5", CPU_POWERPC_440F5, 440x5), -#endif -#if defined (TODO) - /* PowerPC 440 G5 */ - POWERPC_DEF("440G5", CPU_POWERPC_440G5, 440x5), -#endif -#if defined (TODO) - /* PowerPC 440H4 */ - POWERPC_DEF("440H4", CPU_POWERPC_440H4, 440x4), -#endif -#if defined (TODO) - /* PowerPC 440H6 */ - POWERPC_DEF("440H6", CPU_POWERPC_440H6, 440Gx5), -#endif - /* PowerPC 440 microcontrolers */ - /* PowerPC 440 EP */ - POWERPC_DEF("440EP", CPU_POWERPC_440EP, 440EP), - /* PowerPC 440 EPa */ - POWERPC_DEF("440EPa", CPU_POWERPC_440EPa, 440EP), - /* PowerPC 440 EPb */ - POWERPC_DEF("440EPb", CPU_POWERPC_440EPb, 440EP), - /* PowerPC 440 EPX */ - POWERPC_DEF("440EPX", CPU_POWERPC_440EPX, 440EP), -#if defined(TODO_USER_ONLY) - /* PowerPC 440 GP */ - POWERPC_DEF("440GP", CPU_POWERPC_440GP, 440GP), -#endif -#if defined(TODO_USER_ONLY) - /* PowerPC 440 GPb */ - POWERPC_DEF("440GPb", CPU_POWERPC_440GPb, 440GP), -#endif -#if defined(TODO_USER_ONLY) - /* PowerPC 440 GPc */ - POWERPC_DEF("440GPc", CPU_POWERPC_440GPc, 440GP), -#endif -#if defined(TODO_USER_ONLY) - /* PowerPC 440 GR */ - POWERPC_DEF("440GR", CPU_POWERPC_440GR, 440x5), -#endif -#if defined(TODO_USER_ONLY) - /* PowerPC 440 GRa */ - POWERPC_DEF("440GRa", CPU_POWERPC_440GRa, 440x5), -#endif -#if defined(TODO_USER_ONLY) - /* PowerPC 440 GRX */ - POWERPC_DEF("440GRX", CPU_POWERPC_440GRX, 440x5), -#endif -#if defined(TODO_USER_ONLY) - /* PowerPC 440 GX */ - POWERPC_DEF("440GX", CPU_POWERPC_440GX, 440EP), -#endif -#if defined(TODO_USER_ONLY) - /* PowerPC 440 GXa */ - POWERPC_DEF("440GXa", CPU_POWERPC_440GXa, 440EP), -#endif -#if defined(TODO_USER_ONLY) - /* PowerPC 440 GXb */ - POWERPC_DEF("440GXb", CPU_POWERPC_440GXb, 440EP), -#endif -#if defined(TODO_USER_ONLY) - /* PowerPC 440 GXc */ - POWERPC_DEF("440GXc", CPU_POWERPC_440GXc, 440EP), -#endif -#if defined(TODO_USER_ONLY) - /* PowerPC 440 GXf */ - POWERPC_DEF("440GXf", CPU_POWERPC_440GXf, 440EP), -#endif -#if defined(TODO) - /* PowerPC 440 S */ - POWERPC_DEF("440S", CPU_POWERPC_440S, 440), -#endif -#if defined(TODO_USER_ONLY) - /* PowerPC 440 SP */ - POWERPC_DEF("440SP", CPU_POWERPC_440SP, 440EP), -#endif -#if defined(TODO_USER_ONLY) - /* PowerPC 440 SP2 */ - POWERPC_DEF("440SP2", CPU_POWERPC_440SP2, 440EP), -#endif -#if defined(TODO_USER_ONLY) - /* PowerPC 440 SPE */ - POWERPC_DEF("440SPE", CPU_POWERPC_440SPE, 440EP), -#endif - /* PowerPC 460 family */ -#if defined (TODO) - /* Generic PowerPC 464 */ - POWERPC_DEF("464", CPU_POWERPC_464, 460), -#endif - /* PowerPC 464 microcontrolers */ -#if defined (TODO) - /* PowerPC 464H90 */ - POWERPC_DEF("464H90", CPU_POWERPC_464H90, 460), -#endif -#if defined (TODO) - /* PowerPC 464H90F */ - POWERPC_DEF("464H90F", CPU_POWERPC_464H90F, 460F), -#endif - /* Freescale embedded PowerPC cores */ - /* MPC5xx family (aka RCPU) */ -#if defined(TODO_USER_ONLY) - /* Generic MPC5xx core */ - POWERPC_DEF("MPC5xx", CPU_POWERPC_MPC5xx, MPC5xx), -#endif -#if defined(TODO_USER_ONLY) - /* Codename for MPC5xx core */ - POWERPC_DEF("RCPU", CPU_POWERPC_MPC5xx, MPC5xx), -#endif - /* MPC5xx microcontrollers */ -#if defined(TODO_USER_ONLY) - /* MGT560 */ - POWERPC_DEF("MGT560", CPU_POWERPC_MGT560, MPC5xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC509 */ - POWERPC_DEF("MPC509", CPU_POWERPC_MPC509, MPC5xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC533 */ - POWERPC_DEF("MPC533", CPU_POWERPC_MPC533, MPC5xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC534 */ - POWERPC_DEF("MPC534", CPU_POWERPC_MPC534, MPC5xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC555 */ - POWERPC_DEF("MPC555", CPU_POWERPC_MPC555, MPC5xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC556 */ - POWERPC_DEF("MPC556", CPU_POWERPC_MPC556, MPC5xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC560 */ - POWERPC_DEF("MPC560", CPU_POWERPC_MPC560, MPC5xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC561 */ - POWERPC_DEF("MPC561", CPU_POWERPC_MPC561, MPC5xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC562 */ - POWERPC_DEF("MPC562", CPU_POWERPC_MPC562, MPC5xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC563 */ - POWERPC_DEF("MPC563", CPU_POWERPC_MPC563, MPC5xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC564 */ - POWERPC_DEF("MPC564", CPU_POWERPC_MPC564, MPC5xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC565 */ - POWERPC_DEF("MPC565", CPU_POWERPC_MPC565, MPC5xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC566 */ - POWERPC_DEF("MPC566", CPU_POWERPC_MPC566, MPC5xx), -#endif - /* MPC8xx family (aka PowerQUICC) */ -#if defined(TODO_USER_ONLY) - /* Generic MPC8xx core */ - POWERPC_DEF("MPC8xx", CPU_POWERPC_MPC8xx, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* Codename for MPC8xx core */ - POWERPC_DEF("PowerQUICC", CPU_POWERPC_MPC8xx, MPC8xx), -#endif - /* MPC8xx microcontrollers */ -#if defined(TODO_USER_ONLY) - /* MGT823 */ - POWERPC_DEF("MGT823", CPU_POWERPC_MGT823, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC821 */ - POWERPC_DEF("MPC821", CPU_POWERPC_MPC821, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC823 */ - POWERPC_DEF("MPC823", CPU_POWERPC_MPC823, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC850 */ - POWERPC_DEF("MPC850", CPU_POWERPC_MPC850, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC852T */ - POWERPC_DEF("MPC852T", CPU_POWERPC_MPC852T, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC855T */ - POWERPC_DEF("MPC855T", CPU_POWERPC_MPC855T, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC857 */ - POWERPC_DEF("MPC857", CPU_POWERPC_MPC857, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC859 */ - POWERPC_DEF("MPC859", CPU_POWERPC_MPC859, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC860 */ - POWERPC_DEF("MPC860", CPU_POWERPC_MPC860, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC862 */ - POWERPC_DEF("MPC862", CPU_POWERPC_MPC862, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC866 */ - POWERPC_DEF("MPC866", CPU_POWERPC_MPC866, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC870 */ - POWERPC_DEF("MPC870", CPU_POWERPC_MPC870, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC875 */ - POWERPC_DEF("MPC875", CPU_POWERPC_MPC875, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC880 */ - POWERPC_DEF("MPC880", CPU_POWERPC_MPC880, MPC8xx), -#endif -#if defined(TODO_USER_ONLY) - /* MPC885 */ - POWERPC_DEF("MPC885", CPU_POWERPC_MPC885, MPC8xx), -#endif - /* MPC82xx family (aka PowerQUICC-II) */ - /* Generic MPC52xx core */ - POWERPC_DEF_SVR("MPC52xx", - CPU_POWERPC_MPC52xx, POWERPC_SVR_52xx, G2LE), - /* Generic MPC82xx core */ - POWERPC_DEF("MPC82xx", CPU_POWERPC_MPC82xx, G2), - /* Codename for MPC82xx */ - POWERPC_DEF("PowerQUICC-II", CPU_POWERPC_MPC82xx, G2), - /* PowerPC G2 core */ - POWERPC_DEF("G2", CPU_POWERPC_G2, G2), - /* PowerPC G2 H4 core */ - POWERPC_DEF("G2H4", CPU_POWERPC_G2H4, G2), - /* PowerPC G2 GP core */ - POWERPC_DEF("G2GP", CPU_POWERPC_G2gp, G2), - /* PowerPC G2 LS core */ - POWERPC_DEF("G2LS", CPU_POWERPC_G2ls, G2), - /* PowerPC G2 HiP3 core */ - POWERPC_DEF("G2HiP3", CPU_POWERPC_G2_HIP3, G2), - /* PowerPC G2 HiP4 core */ - POWERPC_DEF("G2HiP4", CPU_POWERPC_G2_HIP4, G2), - /* PowerPC MPC603 core */ - POWERPC_DEF("MPC603", CPU_POWERPC_MPC603, 603E), - /* PowerPC G2le core (same as G2 plus little-endian mode support) */ - POWERPC_DEF("G2le", CPU_POWERPC_G2LE, G2LE), - /* PowerPC G2LE GP core */ - POWERPC_DEF("G2leGP", CPU_POWERPC_G2LEgp, G2LE), - /* PowerPC G2LE LS core */ - POWERPC_DEF("G2leLS", CPU_POWERPC_G2LEls, G2LE), - /* PowerPC G2LE GP1 core */ - POWERPC_DEF("G2leGP1", CPU_POWERPC_G2LEgp1, G2LE), - /* PowerPC G2LE GP3 core */ - POWERPC_DEF("G2leGP3", CPU_POWERPC_G2LEgp1, G2LE), - /* PowerPC MPC603 microcontrollers */ - /* MPC8240 */ - POWERPC_DEF("MPC8240", CPU_POWERPC_MPC8240, 603E), - /* PowerPC G2 microcontrollers */ -#if defined(TODO) - /* MPC5121 */ - POWERPC_DEF_SVR("MPC5121", - CPU_POWERPC_MPC5121, POWERPC_SVR_5121, G2LE), -#endif - /* MPC5200 */ - POWERPC_DEF_SVR("MPC5200", - CPU_POWERPC_MPC5200, POWERPC_SVR_5200, G2LE), - /* MPC5200 v1.0 */ - POWERPC_DEF_SVR("MPC5200_v10", - CPU_POWERPC_MPC5200_v10, POWERPC_SVR_5200_v10, G2LE), - /* MPC5200 v1.1 */ - POWERPC_DEF_SVR("MPC5200_v11", - CPU_POWERPC_MPC5200_v11, POWERPC_SVR_5200_v11, G2LE), - /* MPC5200 v1.2 */ - POWERPC_DEF_SVR("MPC5200_v12", - CPU_POWERPC_MPC5200_v12, POWERPC_SVR_5200_v12, G2LE), - /* MPC5200B */ - POWERPC_DEF_SVR("MPC5200B", - CPU_POWERPC_MPC5200B, POWERPC_SVR_5200B, G2LE), - /* MPC5200B v2.0 */ - POWERPC_DEF_SVR("MPC5200B_v20", - CPU_POWERPC_MPC5200B_v20, POWERPC_SVR_5200B_v20, G2LE), - /* MPC5200B v2.1 */ - POWERPC_DEF_SVR("MPC5200B_v21", - CPU_POWERPC_MPC5200B_v21, POWERPC_SVR_5200B_v21, G2LE), - /* MPC8241 */ - POWERPC_DEF("MPC8241", CPU_POWERPC_MPC8241, G2), - /* MPC8245 */ - POWERPC_DEF("MPC8245", CPU_POWERPC_MPC8245, G2), - /* MPC8247 */ - POWERPC_DEF("MPC8247", CPU_POWERPC_MPC8247, G2LE), - /* MPC8248 */ - POWERPC_DEF("MPC8248", CPU_POWERPC_MPC8248, G2LE), - /* MPC8250 */ - POWERPC_DEF("MPC8250", CPU_POWERPC_MPC8250, G2), - /* MPC8250 HiP3 */ - POWERPC_DEF("MPC8250_HiP3", CPU_POWERPC_MPC8250_HiP3, G2), - /* MPC8250 HiP4 */ - POWERPC_DEF("MPC8250_HiP4", CPU_POWERPC_MPC8250_HiP4, G2), - /* MPC8255 */ - POWERPC_DEF("MPC8255", CPU_POWERPC_MPC8255, G2), - /* MPC8255 HiP3 */ - POWERPC_DEF("MPC8255_HiP3", CPU_POWERPC_MPC8255_HiP3, G2), - /* MPC8255 HiP4 */ - POWERPC_DEF("MPC8255_HiP4", CPU_POWERPC_MPC8255_HiP4, G2), - /* MPC8260 */ - POWERPC_DEF("MPC8260", CPU_POWERPC_MPC8260, G2), - /* MPC8260 HiP3 */ - POWERPC_DEF("MPC8260_HiP3", CPU_POWERPC_MPC8260_HiP3, G2), - /* MPC8260 HiP4 */ - POWERPC_DEF("MPC8260_HiP4", CPU_POWERPC_MPC8260_HiP4, G2), - /* MPC8264 */ - POWERPC_DEF("MPC8264", CPU_POWERPC_MPC8264, G2), - /* MPC8264 HiP3 */ - POWERPC_DEF("MPC8264_HiP3", CPU_POWERPC_MPC8264_HiP3, G2), - /* MPC8264 HiP4 */ - POWERPC_DEF("MPC8264_HiP4", CPU_POWERPC_MPC8264_HiP4, G2), - /* MPC8265 */ - POWERPC_DEF("MPC8265", CPU_POWERPC_MPC8265, G2), - /* MPC8265 HiP3 */ - POWERPC_DEF("MPC8265_HiP3", CPU_POWERPC_MPC8265_HiP3, G2), - /* MPC8265 HiP4 */ - POWERPC_DEF("MPC8265_HiP4", CPU_POWERPC_MPC8265_HiP4, G2), - /* MPC8266 */ - POWERPC_DEF("MPC8266", CPU_POWERPC_MPC8266, G2), - /* MPC8266 HiP3 */ - POWERPC_DEF("MPC8266_HiP3", CPU_POWERPC_MPC8266_HiP3, G2), - /* MPC8266 HiP4 */ - POWERPC_DEF("MPC8266_HiP4", CPU_POWERPC_MPC8266_HiP4, G2), - /* MPC8270 */ - POWERPC_DEF("MPC8270", CPU_POWERPC_MPC8270, G2LE), - /* MPC8271 */ - POWERPC_DEF("MPC8271", CPU_POWERPC_MPC8271, G2LE), - /* MPC8272 */ - POWERPC_DEF("MPC8272", CPU_POWERPC_MPC8272, G2LE), - /* MPC8275 */ - POWERPC_DEF("MPC8275", CPU_POWERPC_MPC8275, G2LE), - /* MPC8280 */ - POWERPC_DEF("MPC8280", CPU_POWERPC_MPC8280, G2LE), - /* e200 family */ - /* Generic PowerPC e200 core */ - POWERPC_DEF("e200", CPU_POWERPC_e200, e200), - /* Generic MPC55xx core */ -#if defined (TODO) - POWERPC_DEF_SVR("MPC55xx", - CPU_POWERPC_MPC55xx, POWERPC_SVR_55xx, e200), -#endif -#if defined (TODO) - /* PowerPC e200z0 core */ - POWERPC_DEF("e200z0", CPU_POWERPC_e200z0, e200), -#endif -#if defined (TODO) - /* PowerPC e200z1 core */ - POWERPC_DEF("e200z1", CPU_POWERPC_e200z1, e200), -#endif -#if defined (TODO) - /* PowerPC e200z3 core */ - POWERPC_DEF("e200z3", CPU_POWERPC_e200z3, e200), -#endif - /* PowerPC e200z5 core */ - POWERPC_DEF("e200z5", CPU_POWERPC_e200z5, e200), - /* PowerPC e200z6 core */ - POWERPC_DEF("e200z6", CPU_POWERPC_e200z6, e200), - /* PowerPC e200 microcontrollers */ -#if defined (TODO) - /* MPC5514E */ - POWERPC_DEF_SVR("MPC5514E", - CPU_POWERPC_MPC5514E, POWERPC_SVR_5514E, e200), -#endif -#if defined (TODO) - /* MPC5514E v0 */ - POWERPC_DEF_SVR("MPC5514E_v0", - CPU_POWERPC_MPC5514E_v0, POWERPC_SVR_5514E_v0, e200), -#endif -#if defined (TODO) - /* MPC5514E v1 */ - POWERPC_DEF_SVR("MPC5514E_v1", - CPU_POWERPC_MPC5514E_v1, POWERPC_SVR_5514E_v1, e200), -#endif -#if defined (TODO) - /* MPC5514G */ - POWERPC_DEF_SVR("MPC5514G", - CPU_POWERPC_MPC5514G, POWERPC_SVR_5514G, e200), -#endif -#if defined (TODO) - /* MPC5514G v0 */ - POWERPC_DEF_SVR("MPC5514G_v0", - CPU_POWERPC_MPC5514G_v0, POWERPC_SVR_5514G_v0, e200), -#endif -#if defined (TODO) - /* MPC5514G v1 */ - POWERPC_DEF_SVR("MPC5514G_v1", - CPU_POWERPC_MPC5514G_v1, POWERPC_SVR_5514G_v1, e200), -#endif -#if defined (TODO) - /* MPC5515S */ - POWERPC_DEF_SVR("MPC5515S", - CPU_POWERPC_MPC5515S, POWERPC_SVR_5515S, e200), -#endif -#if defined (TODO) - /* MPC5516E */ - POWERPC_DEF_SVR("MPC5516E", - CPU_POWERPC_MPC5516E, POWERPC_SVR_5516E, e200), -#endif -#if defined (TODO) - /* MPC5516E v0 */ - POWERPC_DEF_SVR("MPC5516E_v0", - CPU_POWERPC_MPC5516E_v0, POWERPC_SVR_5516E_v0, e200), -#endif -#if defined (TODO) - /* MPC5516E v1 */ - POWERPC_DEF_SVR("MPC5516E_v1", - CPU_POWERPC_MPC5516E_v1, POWERPC_SVR_5516E_v1, e200), -#endif -#if defined (TODO) - /* MPC5516G */ - POWERPC_DEF_SVR("MPC5516G", - CPU_POWERPC_MPC5516G, POWERPC_SVR_5516G, e200), -#endif -#if defined (TODO) - /* MPC5516G v0 */ - POWERPC_DEF_SVR("MPC5516G_v0", - CPU_POWERPC_MPC5516G_v0, POWERPC_SVR_5516G_v0, e200), -#endif -#if defined (TODO) - /* MPC5516G v1 */ - POWERPC_DEF_SVR("MPC5516G_v1", - CPU_POWERPC_MPC5516G_v1, POWERPC_SVR_5516G_v1, e200), -#endif -#if defined (TODO) - /* MPC5516S */ - POWERPC_DEF_SVR("MPC5516S", - CPU_POWERPC_MPC5516S, POWERPC_SVR_5516S, e200), -#endif -#if defined (TODO) - /* MPC5533 */ - POWERPC_DEF_SVR("MPC5533", - CPU_POWERPC_MPC5533, POWERPC_SVR_5533, e200), -#endif -#if defined (TODO) - /* MPC5534 */ - POWERPC_DEF_SVR("MPC5534", - CPU_POWERPC_MPC5534, POWERPC_SVR_5534, e200), -#endif -#if defined (TODO) - /* MPC5553 */ - POWERPC_DEF_SVR("MPC5553", - CPU_POWERPC_MPC5553, POWERPC_SVR_5553, e200), -#endif -#if defined (TODO) - /* MPC5554 */ - POWERPC_DEF_SVR("MPC5554", - CPU_POWERPC_MPC5554, POWERPC_SVR_5554, e200), -#endif -#if defined (TODO) - /* MPC5561 */ - POWERPC_DEF_SVR("MPC5561", - CPU_POWERPC_MPC5561, POWERPC_SVR_5561, e200), -#endif -#if defined (TODO) - /* MPC5565 */ - POWERPC_DEF_SVR("MPC5565", - CPU_POWERPC_MPC5565, POWERPC_SVR_5565, e200), -#endif -#if defined (TODO) - /* MPC5566 */ - POWERPC_DEF_SVR("MPC5566", - CPU_POWERPC_MPC5566, POWERPC_SVR_5566, e200), -#endif -#if defined (TODO) - /* MPC5567 */ - POWERPC_DEF_SVR("MPC5567", - CPU_POWERPC_MPC5567, POWERPC_SVR_5567, e200), -#endif - /* e300 family */ - /* Generic PowerPC e300 core */ - POWERPC_DEF("e300", CPU_POWERPC_e300, e300), - /* PowerPC e300c1 core */ - POWERPC_DEF("e300c1", CPU_POWERPC_e300c1, e300), - /* PowerPC e300c2 core */ - POWERPC_DEF("e300c2", CPU_POWERPC_e300c2, e300), - /* PowerPC e300c3 core */ - POWERPC_DEF("e300c3", CPU_POWERPC_e300c3, e300), - /* PowerPC e300c4 core */ - POWERPC_DEF("e300c4", CPU_POWERPC_e300c4, e300), - /* PowerPC e300 microcontrollers */ -#if defined (TODO) - /* MPC8313 */ - POWERPC_DEF_SVR("MPC8313", - CPU_POWERPC_MPC831x, POWERPC_SVR_8313, e300), -#endif -#if defined (TODO) - /* MPC8313E */ - POWERPC_DEF_SVR("MPC8313E", - CPU_POWERPC_MPC831x, POWERPC_SVR_8313E, e300), -#endif -#if defined (TODO) - /* MPC8314 */ - POWERPC_DEF_SVR("MPC8314", - CPU_POWERPC_MPC831x, POWERPC_SVR_8314, e300), -#endif -#if defined (TODO) - /* MPC8314E */ - POWERPC_DEF_SVR("MPC8314E", - CPU_POWERPC_MPC831x, POWERPC_SVR_8314E, e300), -#endif -#if defined (TODO) - /* MPC8315 */ - POWERPC_DEF_SVR("MPC8315", - CPU_POWERPC_MPC831x, POWERPC_SVR_8315, e300), -#endif -#if defined (TODO) - /* MPC8315E */ - POWERPC_DEF_SVR("MPC8315E", - CPU_POWERPC_MPC831x, POWERPC_SVR_8315E, e300), -#endif -#if defined (TODO) - /* MPC8321 */ - POWERPC_DEF_SVR("MPC8321", - CPU_POWERPC_MPC832x, POWERPC_SVR_8321, e300), -#endif -#if defined (TODO) - /* MPC8321E */ - POWERPC_DEF_SVR("MPC8321E", - CPU_POWERPC_MPC832x, POWERPC_SVR_8321E, e300), -#endif -#if defined (TODO) - /* MPC8323 */ - POWERPC_DEF_SVR("MPC8323", - CPU_POWERPC_MPC832x, POWERPC_SVR_8323, e300), -#endif -#if defined (TODO) - /* MPC8323E */ - POWERPC_DEF_SVR("MPC8323E", - CPU_POWERPC_MPC832x, POWERPC_SVR_8323E, e300), -#endif - /* MPC8343 */ - POWERPC_DEF_SVR("MPC8343", - CPU_POWERPC_MPC834x, POWERPC_SVR_8343, e300), - /* MPC8343A */ - POWERPC_DEF_SVR("MPC8343A", - CPU_POWERPC_MPC834x, POWERPC_SVR_8343A, e300), - /* MPC8343E */ - POWERPC_DEF_SVR("MPC8343E", - CPU_POWERPC_MPC834x, POWERPC_SVR_8343E, e300), - /* MPC8343EA */ - POWERPC_DEF_SVR("MPC8343EA", - CPU_POWERPC_MPC834x, POWERPC_SVR_8343EA, e300), - /* MPC8347 */ - POWERPC_DEF_SVR("MPC8347", - CPU_POWERPC_MPC834x, POWERPC_SVR_8347, e300), - /* MPC8347T */ - POWERPC_DEF_SVR("MPC8347T", - CPU_POWERPC_MPC834x, POWERPC_SVR_8347T, e300), - /* MPC8347P */ - POWERPC_DEF_SVR("MPC8347P", - CPU_POWERPC_MPC834x, POWERPC_SVR_8347P, e300), - /* MPC8347A */ - POWERPC_DEF_SVR("MPC8347A", - CPU_POWERPC_MPC834x, POWERPC_SVR_8347A, e300), - /* MPC8347AT */ - POWERPC_DEF_SVR("MPC8347AT", - CPU_POWERPC_MPC834x, POWERPC_SVR_8347AT, e300), - /* MPC8347AP */ - POWERPC_DEF_SVR("MPC8347AP", - CPU_POWERPC_MPC834x, POWERPC_SVR_8347AP, e300), - /* MPC8347E */ - POWERPC_DEF_SVR("MPC8347E", - CPU_POWERPC_MPC834x, POWERPC_SVR_8347E, e300), - /* MPC8347ET */ - POWERPC_DEF_SVR("MPC8347ET", - CPU_POWERPC_MPC834x, POWERPC_SVR_8347ET, e300), - /* MPC8343EP */ - POWERPC_DEF_SVR("MPC8347EP", - CPU_POWERPC_MPC834x, POWERPC_SVR_8347EP, e300), - /* MPC8347EA */ - POWERPC_DEF_SVR("MPC8347EA", - CPU_POWERPC_MPC834x, POWERPC_SVR_8347EA, e300), - /* MPC8347EAT */ - POWERPC_DEF_SVR("MPC8347EAT", - CPU_POWERPC_MPC834x, POWERPC_SVR_8347EAT, e300), - /* MPC8343EAP */ - POWERPC_DEF_SVR("MPC8347EAP", - CPU_POWERPC_MPC834x, POWERPC_SVR_8347EAP, e300), - /* MPC8349 */ - POWERPC_DEF_SVR("MPC8349", - CPU_POWERPC_MPC834x, POWERPC_SVR_8349, e300), - /* MPC8349A */ - POWERPC_DEF_SVR("MPC8349A", - CPU_POWERPC_MPC834x, POWERPC_SVR_8349A, e300), - /* MPC8349E */ - POWERPC_DEF_SVR("MPC8349E", - CPU_POWERPC_MPC834x, POWERPC_SVR_8349E, e300), - /* MPC8349EA */ - POWERPC_DEF_SVR("MPC8349EA", - CPU_POWERPC_MPC834x, POWERPC_SVR_8349EA, e300), -#if defined (TODO) - /* MPC8358E */ - POWERPC_DEF_SVR("MPC8358E", - CPU_POWERPC_MPC835x, POWERPC_SVR_8358E, e300), -#endif -#if defined (TODO) - /* MPC8360E */ - POWERPC_DEF_SVR("MPC8360E", - CPU_POWERPC_MPC836x, POWERPC_SVR_8360E, e300), -#endif - /* MPC8377 */ - POWERPC_DEF_SVR("MPC8377", - CPU_POWERPC_MPC837x, POWERPC_SVR_8377, e300), - /* MPC8377E */ - POWERPC_DEF_SVR("MPC8377E", - CPU_POWERPC_MPC837x, POWERPC_SVR_8377E, e300), - /* MPC8378 */ - POWERPC_DEF_SVR("MPC8378", - CPU_POWERPC_MPC837x, POWERPC_SVR_8378, e300), - /* MPC8378E */ - POWERPC_DEF_SVR("MPC8378E", - CPU_POWERPC_MPC837x, POWERPC_SVR_8378E, e300), - /* MPC8379 */ - POWERPC_DEF_SVR("MPC8379", - CPU_POWERPC_MPC837x, POWERPC_SVR_8379, e300), - /* MPC8379E */ - POWERPC_DEF_SVR("MPC8379E", - CPU_POWERPC_MPC837x, POWERPC_SVR_8379E, e300), - /* e500 family */ - /* PowerPC e500 core */ - POWERPC_DEF("e500", CPU_POWERPC_e500v2_v22, e500v2), - /* PowerPC e500v1 core */ - POWERPC_DEF("e500v1", CPU_POWERPC_e500v1, e500v1), - /* PowerPC e500 v1.0 core */ - POWERPC_DEF("e500_v10", CPU_POWERPC_e500v1_v10, e500v1), - /* PowerPC e500 v2.0 core */ - POWERPC_DEF("e500_v20", CPU_POWERPC_e500v1_v20, e500v1), - /* PowerPC e500v2 core */ - POWERPC_DEF("e500v2", CPU_POWERPC_e500v2, e500v2), - /* PowerPC e500v2 v1.0 core */ - POWERPC_DEF("e500v2_v10", CPU_POWERPC_e500v2_v10, e500v2), - /* PowerPC e500v2 v2.0 core */ - POWERPC_DEF("e500v2_v20", CPU_POWERPC_e500v2_v20, e500v2), - /* PowerPC e500v2 v2.1 core */ - POWERPC_DEF("e500v2_v21", CPU_POWERPC_e500v2_v21, e500v2), - /* PowerPC e500v2 v2.2 core */ - POWERPC_DEF("e500v2_v22", CPU_POWERPC_e500v2_v22, e500v2), - /* PowerPC e500v2 v3.0 core */ - POWERPC_DEF("e500v2_v30", CPU_POWERPC_e500v2_v30, e500v2), - POWERPC_DEF("e500mc", CPU_POWERPC_e500mc, e500mc), -#ifdef TARGET_PPC64 - POWERPC_DEF("e5500", CPU_POWERPC_e5500, e5500), -#endif - /* PowerPC e500 microcontrollers */ - /* MPC8533 */ - POWERPC_DEF_SVR("MPC8533", - CPU_POWERPC_MPC8533, POWERPC_SVR_8533, e500v2), - /* MPC8533 v1.0 */ - POWERPC_DEF_SVR("MPC8533_v10", - CPU_POWERPC_MPC8533_v10, POWERPC_SVR_8533_v10, e500v2), - /* MPC8533 v1.1 */ - POWERPC_DEF_SVR("MPC8533_v11", - CPU_POWERPC_MPC8533_v11, POWERPC_SVR_8533_v11, e500v2), - /* MPC8533E */ - POWERPC_DEF_SVR("MPC8533E", - CPU_POWERPC_MPC8533E, POWERPC_SVR_8533E, e500v2), - /* MPC8533E v1.0 */ - POWERPC_DEF_SVR("MPC8533E_v10", - CPU_POWERPC_MPC8533E_v10, POWERPC_SVR_8533E_v10, e500v2), - POWERPC_DEF_SVR("MPC8533E_v11", - CPU_POWERPC_MPC8533E_v11, POWERPC_SVR_8533E_v11, e500v2), - /* MPC8540 */ - POWERPC_DEF_SVR("MPC8540", - CPU_POWERPC_MPC8540, POWERPC_SVR_8540, e500v1), - /* MPC8540 v1.0 */ - POWERPC_DEF_SVR("MPC8540_v10", - CPU_POWERPC_MPC8540_v10, POWERPC_SVR_8540_v10, e500v1), - /* MPC8540 v2.0 */ - POWERPC_DEF_SVR("MPC8540_v20", - CPU_POWERPC_MPC8540_v20, POWERPC_SVR_8540_v20, e500v1), - /* MPC8540 v2.1 */ - POWERPC_DEF_SVR("MPC8540_v21", - CPU_POWERPC_MPC8540_v21, POWERPC_SVR_8540_v21, e500v1), - /* MPC8541 */ - POWERPC_DEF_SVR("MPC8541", - CPU_POWERPC_MPC8541, POWERPC_SVR_8541, e500v1), - /* MPC8541 v1.0 */ - POWERPC_DEF_SVR("MPC8541_v10", - CPU_POWERPC_MPC8541_v10, POWERPC_SVR_8541_v10, e500v1), - /* MPC8541 v1.1 */ - POWERPC_DEF_SVR("MPC8541_v11", - CPU_POWERPC_MPC8541_v11, POWERPC_SVR_8541_v11, e500v1), - /* MPC8541E */ - POWERPC_DEF_SVR("MPC8541E", - CPU_POWERPC_MPC8541E, POWERPC_SVR_8541E, e500v1), - /* MPC8541E v1.0 */ - POWERPC_DEF_SVR("MPC8541E_v10", - CPU_POWERPC_MPC8541E_v10, POWERPC_SVR_8541E_v10, e500v1), - /* MPC8541E v1.1 */ - POWERPC_DEF_SVR("MPC8541E_v11", - CPU_POWERPC_MPC8541E_v11, POWERPC_SVR_8541E_v11, e500v1), - /* MPC8543 */ - POWERPC_DEF_SVR("MPC8543", - CPU_POWERPC_MPC8543, POWERPC_SVR_8543, e500v2), - /* MPC8543 v1.0 */ - POWERPC_DEF_SVR("MPC8543_v10", - CPU_POWERPC_MPC8543_v10, POWERPC_SVR_8543_v10, e500v2), - /* MPC8543 v1.1 */ - POWERPC_DEF_SVR("MPC8543_v11", - CPU_POWERPC_MPC8543_v11, POWERPC_SVR_8543_v11, e500v2), - /* MPC8543 v2.0 */ - POWERPC_DEF_SVR("MPC8543_v20", - CPU_POWERPC_MPC8543_v20, POWERPC_SVR_8543_v20, e500v2), - /* MPC8543 v2.1 */ - POWERPC_DEF_SVR("MPC8543_v21", - CPU_POWERPC_MPC8543_v21, POWERPC_SVR_8543_v21, e500v2), - /* MPC8543E */ - POWERPC_DEF_SVR("MPC8543E", - CPU_POWERPC_MPC8543E, POWERPC_SVR_8543E, e500v2), - /* MPC8543E v1.0 */ - POWERPC_DEF_SVR("MPC8543E_v10", - CPU_POWERPC_MPC8543E_v10, POWERPC_SVR_8543E_v10, e500v2), - /* MPC8543E v1.1 */ - POWERPC_DEF_SVR("MPC8543E_v11", - CPU_POWERPC_MPC8543E_v11, POWERPC_SVR_8543E_v11, e500v2), - /* MPC8543E v2.0 */ - POWERPC_DEF_SVR("MPC8543E_v20", - CPU_POWERPC_MPC8543E_v20, POWERPC_SVR_8543E_v20, e500v2), - /* MPC8543E v2.1 */ - POWERPC_DEF_SVR("MPC8543E_v21", - CPU_POWERPC_MPC8543E_v21, POWERPC_SVR_8543E_v21, e500v2), - /* MPC8544 */ - POWERPC_DEF_SVR("MPC8544", - CPU_POWERPC_MPC8544, POWERPC_SVR_8544, e500v2), - /* MPC8544 v1.0 */ - POWERPC_DEF_SVR("MPC8544_v10", - CPU_POWERPC_MPC8544_v10, POWERPC_SVR_8544_v10, e500v2), - /* MPC8544 v1.1 */ - POWERPC_DEF_SVR("MPC8544_v11", - CPU_POWERPC_MPC8544_v11, POWERPC_SVR_8544_v11, e500v2), - /* MPC8544E */ - POWERPC_DEF_SVR("MPC8544E", - CPU_POWERPC_MPC8544E, POWERPC_SVR_8544E, e500v2), - /* MPC8544E v1.0 */ - POWERPC_DEF_SVR("MPC8544E_v10", - CPU_POWERPC_MPC8544E_v10, POWERPC_SVR_8544E_v10, e500v2), - /* MPC8544E v1.1 */ - POWERPC_DEF_SVR("MPC8544E_v11", - CPU_POWERPC_MPC8544E_v11, POWERPC_SVR_8544E_v11, e500v2), - /* MPC8545 */ - POWERPC_DEF_SVR("MPC8545", - CPU_POWERPC_MPC8545, POWERPC_SVR_8545, e500v2), - /* MPC8545 v2.0 */ - POWERPC_DEF_SVR("MPC8545_v20", - CPU_POWERPC_MPC8545_v20, POWERPC_SVR_8545_v20, e500v2), - /* MPC8545 v2.1 */ - POWERPC_DEF_SVR("MPC8545_v21", - CPU_POWERPC_MPC8545_v21, POWERPC_SVR_8545_v21, e500v2), - /* MPC8545E */ - POWERPC_DEF_SVR("MPC8545E", - CPU_POWERPC_MPC8545E, POWERPC_SVR_8545E, e500v2), - /* MPC8545E v2.0 */ - POWERPC_DEF_SVR("MPC8545E_v20", - CPU_POWERPC_MPC8545E_v20, POWERPC_SVR_8545E_v20, e500v2), - /* MPC8545E v2.1 */ - POWERPC_DEF_SVR("MPC8545E_v21", - CPU_POWERPC_MPC8545E_v21, POWERPC_SVR_8545E_v21, e500v2), - /* MPC8547E */ - POWERPC_DEF_SVR("MPC8547E", - CPU_POWERPC_MPC8547E, POWERPC_SVR_8547E, e500v2), - /* MPC8547E v2.0 */ - POWERPC_DEF_SVR("MPC8547E_v20", - CPU_POWERPC_MPC8547E_v20, POWERPC_SVR_8547E_v20, e500v2), - /* MPC8547E v2.1 */ - POWERPC_DEF_SVR("MPC8547E_v21", - CPU_POWERPC_MPC8547E_v21, POWERPC_SVR_8547E_v21, e500v2), - /* MPC8548 */ - POWERPC_DEF_SVR("MPC8548", - CPU_POWERPC_MPC8548, POWERPC_SVR_8548, e500v2), - /* MPC8548 v1.0 */ - POWERPC_DEF_SVR("MPC8548_v10", - CPU_POWERPC_MPC8548_v10, POWERPC_SVR_8548_v10, e500v2), - /* MPC8548 v1.1 */ - POWERPC_DEF_SVR("MPC8548_v11", - CPU_POWERPC_MPC8548_v11, POWERPC_SVR_8548_v11, e500v2), - /* MPC8548 v2.0 */ - POWERPC_DEF_SVR("MPC8548_v20", - CPU_POWERPC_MPC8548_v20, POWERPC_SVR_8548_v20, e500v2), - /* MPC8548 v2.1 */ - POWERPC_DEF_SVR("MPC8548_v21", - CPU_POWERPC_MPC8548_v21, POWERPC_SVR_8548_v21, e500v2), - /* MPC8548E */ - POWERPC_DEF_SVR("MPC8548E", - CPU_POWERPC_MPC8548E, POWERPC_SVR_8548E, e500v2), - /* MPC8548E v1.0 */ - POWERPC_DEF_SVR("MPC8548E_v10", - CPU_POWERPC_MPC8548E_v10, POWERPC_SVR_8548E_v10, e500v2), - /* MPC8548E v1.1 */ - POWERPC_DEF_SVR("MPC8548E_v11", - CPU_POWERPC_MPC8548E_v11, POWERPC_SVR_8548E_v11, e500v2), - /* MPC8548E v2.0 */ - POWERPC_DEF_SVR("MPC8548E_v20", - CPU_POWERPC_MPC8548E_v20, POWERPC_SVR_8548E_v20, e500v2), - /* MPC8548E v2.1 */ - POWERPC_DEF_SVR("MPC8548E_v21", - CPU_POWERPC_MPC8548E_v21, POWERPC_SVR_8548E_v21, e500v2), - /* MPC8555 */ - POWERPC_DEF_SVR("MPC8555", - CPU_POWERPC_MPC8555, POWERPC_SVR_8555, e500v2), - /* MPC8555 v1.0 */ - POWERPC_DEF_SVR("MPC8555_v10", - CPU_POWERPC_MPC8555_v10, POWERPC_SVR_8555_v10, e500v2), - /* MPC8555 v1.1 */ - POWERPC_DEF_SVR("MPC8555_v11", - CPU_POWERPC_MPC8555_v11, POWERPC_SVR_8555_v11, e500v2), - /* MPC8555E */ - POWERPC_DEF_SVR("MPC8555E", - CPU_POWERPC_MPC8555E, POWERPC_SVR_8555E, e500v2), - /* MPC8555E v1.0 */ - POWERPC_DEF_SVR("MPC8555E_v10", - CPU_POWERPC_MPC8555E_v10, POWERPC_SVR_8555E_v10, e500v2), - /* MPC8555E v1.1 */ - POWERPC_DEF_SVR("MPC8555E_v11", - CPU_POWERPC_MPC8555E_v11, POWERPC_SVR_8555E_v11, e500v2), - /* MPC8560 */ - POWERPC_DEF_SVR("MPC8560", - CPU_POWERPC_MPC8560, POWERPC_SVR_8560, e500v2), - /* MPC8560 v1.0 */ - POWERPC_DEF_SVR("MPC8560_v10", - CPU_POWERPC_MPC8560_v10, POWERPC_SVR_8560_v10, e500v2), - /* MPC8560 v2.0 */ - POWERPC_DEF_SVR("MPC8560_v20", - CPU_POWERPC_MPC8560_v20, POWERPC_SVR_8560_v20, e500v2), - /* MPC8560 v2.1 */ - POWERPC_DEF_SVR("MPC8560_v21", - CPU_POWERPC_MPC8560_v21, POWERPC_SVR_8560_v21, e500v2), - /* MPC8567 */ - POWERPC_DEF_SVR("MPC8567", - CPU_POWERPC_MPC8567, POWERPC_SVR_8567, e500v2), - /* MPC8567E */ - POWERPC_DEF_SVR("MPC8567E", - CPU_POWERPC_MPC8567E, POWERPC_SVR_8567E, e500v2), - /* MPC8568 */ - POWERPC_DEF_SVR("MPC8568", - CPU_POWERPC_MPC8568, POWERPC_SVR_8568, e500v2), - /* MPC8568E */ - POWERPC_DEF_SVR("MPC8568E", - CPU_POWERPC_MPC8568E, POWERPC_SVR_8568E, e500v2), - /* MPC8572 */ - POWERPC_DEF_SVR("MPC8572", - CPU_POWERPC_MPC8572, POWERPC_SVR_8572, e500v2), - /* MPC8572E */ - POWERPC_DEF_SVR("MPC8572E", - CPU_POWERPC_MPC8572E, POWERPC_SVR_8572E, e500v2), - /* e600 family */ - /* PowerPC e600 core */ - POWERPC_DEF("e600", CPU_POWERPC_e600, 7400), - /* PowerPC e600 microcontrollers */ -#if defined (TODO) - /* MPC8610 */ - POWERPC_DEF_SVR("MPC8610", - CPU_POWERPC_MPC8610, POWERPC_SVR_8610, 7400), -#endif - /* MPC8641 */ - POWERPC_DEF_SVR("MPC8641", - CPU_POWERPC_MPC8641, POWERPC_SVR_8641, 7400), - /* MPC8641D */ - POWERPC_DEF_SVR("MPC8641D", - CPU_POWERPC_MPC8641D, POWERPC_SVR_8641D, 7400), - /* 32 bits "classic" PowerPC */ - /* PowerPC 6xx family */ - /* PowerPC 601 */ - POWERPC_DEF("601", CPU_POWERPC_601, 601v), - /* PowerPC 601v0 */ - POWERPC_DEF("601_v0", CPU_POWERPC_601_v0, 601), - /* PowerPC 601v1 */ - POWERPC_DEF("601_v1", CPU_POWERPC_601_v1, 601), - /* PowerPC 601v */ - POWERPC_DEF("601v", CPU_POWERPC_601v, 601v), - /* PowerPC 601v2 */ - POWERPC_DEF("601_v2", CPU_POWERPC_601_v2, 601v), - /* PowerPC 602 */ - POWERPC_DEF("602", CPU_POWERPC_602, 602), - /* PowerPC 603 */ - POWERPC_DEF("603", CPU_POWERPC_603, 603), - /* Code name for PowerPC 603 */ - POWERPC_DEF("Vanilla", CPU_POWERPC_603, 603), - /* PowerPC 603e (aka PID6) */ - POWERPC_DEF("603e", CPU_POWERPC_603E, 603E), - /* Code name for PowerPC 603e */ - POWERPC_DEF("Stretch", CPU_POWERPC_603E, 603E), - /* PowerPC 603e v1.1 */ - POWERPC_DEF("603e_v1.1", CPU_POWERPC_603E_v11, 603E), - /* PowerPC 603e v1.2 */ - POWERPC_DEF("603e_v1.2", CPU_POWERPC_603E_v12, 603E), - /* PowerPC 603e v1.3 */ - POWERPC_DEF("603e_v1.3", CPU_POWERPC_603E_v13, 603E), - /* PowerPC 603e v1.4 */ - POWERPC_DEF("603e_v1.4", CPU_POWERPC_603E_v14, 603E), - /* PowerPC 603e v2.2 */ - POWERPC_DEF("603e_v2.2", CPU_POWERPC_603E_v22, 603E), - /* PowerPC 603e v3 */ - POWERPC_DEF("603e_v3", CPU_POWERPC_603E_v3, 603E), - /* PowerPC 603e v4 */ - POWERPC_DEF("603e_v4", CPU_POWERPC_603E_v4, 603E), - /* PowerPC 603e v4.1 */ - POWERPC_DEF("603e_v4.1", CPU_POWERPC_603E_v41, 603E), - /* PowerPC 603e (aka PID7) */ - POWERPC_DEF("603e7", CPU_POWERPC_603E7, 603E), - /* PowerPC 603e7t */ - POWERPC_DEF("603e7t", CPU_POWERPC_603E7t, 603E), - /* PowerPC 603e7v */ - POWERPC_DEF("603e7v", CPU_POWERPC_603E7v, 603E), - /* Code name for PowerPC 603ev */ - POWERPC_DEF("Vaillant", CPU_POWERPC_603E7v, 603E), - /* PowerPC 603e7v1 */ - POWERPC_DEF("603e7v1", CPU_POWERPC_603E7v1, 603E), - /* PowerPC 603e7v2 */ - POWERPC_DEF("603e7v2", CPU_POWERPC_603E7v2, 603E), - /* PowerPC 603p (aka PID7v) */ - POWERPC_DEF("603p", CPU_POWERPC_603P, 603E), - /* PowerPC 603r (aka PID7t) */ - POWERPC_DEF("603r", CPU_POWERPC_603R, 603E), - /* Code name for PowerPC 603r */ - POWERPC_DEF("Goldeneye", CPU_POWERPC_603R, 603E), - /* PowerPC 604 */ - POWERPC_DEF("604", CPU_POWERPC_604, 604), - /* PowerPC 604e (aka PID9) */ - POWERPC_DEF("604e", CPU_POWERPC_604E, 604E), - /* Code name for PowerPC 604e */ - POWERPC_DEF("Sirocco", CPU_POWERPC_604E, 604E), - /* PowerPC 604e v1.0 */ - POWERPC_DEF("604e_v1.0", CPU_POWERPC_604E_v10, 604E), - /* PowerPC 604e v2.2 */ - POWERPC_DEF("604e_v2.2", CPU_POWERPC_604E_v22, 604E), - /* PowerPC 604e v2.4 */ - POWERPC_DEF("604e_v2.4", CPU_POWERPC_604E_v24, 604E), - /* PowerPC 604r (aka PIDA) */ - POWERPC_DEF("604r", CPU_POWERPC_604R, 604E), - /* Code name for PowerPC 604r */ - POWERPC_DEF("Mach5", CPU_POWERPC_604R, 604E), -#if defined(TODO) - /* PowerPC 604ev */ - POWERPC_DEF("604ev", CPU_POWERPC_604EV, 604E), -#endif - /* PowerPC 7xx family */ - /* Generic PowerPC 740 (G3) */ - POWERPC_DEF("740", CPU_POWERPC_7x0, 740), - /* Code name for PowerPC 740 */ - POWERPC_DEF("Arthur", CPU_POWERPC_7x0, 740), - /* Generic PowerPC 750 (G3) */ - POWERPC_DEF("750", CPU_POWERPC_7x0, 750), - /* Code name for PowerPC 750 */ - POWERPC_DEF("Typhoon", CPU_POWERPC_7x0, 750), - /* PowerPC 740/750 is also known as G3 */ - POWERPC_DEF("G3", CPU_POWERPC_7x0, 750), - /* PowerPC 740 v1.0 (G3) */ - POWERPC_DEF("740_v1.0", CPU_POWERPC_7x0_v10, 740), - /* PowerPC 750 v1.0 (G3) */ - POWERPC_DEF("750_v1.0", CPU_POWERPC_7x0_v10, 750), - /* PowerPC 740 v2.0 (G3) */ - POWERPC_DEF("740_v2.0", CPU_POWERPC_7x0_v20, 740), - /* PowerPC 750 v2.0 (G3) */ - POWERPC_DEF("750_v2.0", CPU_POWERPC_7x0_v20, 750), - /* PowerPC 740 v2.1 (G3) */ - POWERPC_DEF("740_v2.1", CPU_POWERPC_7x0_v21, 740), - /* PowerPC 750 v2.1 (G3) */ - POWERPC_DEF("750_v2.1", CPU_POWERPC_7x0_v21, 750), - /* PowerPC 740 v2.2 (G3) */ - POWERPC_DEF("740_v2.2", CPU_POWERPC_7x0_v22, 740), - /* PowerPC 750 v2.2 (G3) */ - POWERPC_DEF("750_v2.2", CPU_POWERPC_7x0_v22, 750), - /* PowerPC 740 v3.0 (G3) */ - POWERPC_DEF("740_v3.0", CPU_POWERPC_7x0_v30, 740), - /* PowerPC 750 v3.0 (G3) */ - POWERPC_DEF("750_v3.0", CPU_POWERPC_7x0_v30, 750), - /* PowerPC 740 v3.1 (G3) */ - POWERPC_DEF("740_v3.1", CPU_POWERPC_7x0_v31, 740), - /* PowerPC 750 v3.1 (G3) */ - POWERPC_DEF("750_v3.1", CPU_POWERPC_7x0_v31, 750), - /* PowerPC 740E (G3) */ - POWERPC_DEF("740e", CPU_POWERPC_740E, 740), - /* PowerPC 750E (G3) */ - POWERPC_DEF("750e", CPU_POWERPC_750E, 750), - /* PowerPC 740P (G3) */ - POWERPC_DEF("740p", CPU_POWERPC_7x0P, 740), - /* PowerPC 750P (G3) */ - POWERPC_DEF("750p", CPU_POWERPC_7x0P, 750), - /* Code name for PowerPC 740P/750P (G3) */ - POWERPC_DEF("Conan/Doyle", CPU_POWERPC_7x0P, 750), - /* PowerPC 750CL (G3 embedded) */ - POWERPC_DEF("750cl", CPU_POWERPC_750CL, 750cl), - /* PowerPC 750CL v1.0 */ - POWERPC_DEF("750cl_v1.0", CPU_POWERPC_750CL_v10, 750cl), - /* PowerPC 750CL v2.0 */ - POWERPC_DEF("750cl_v2.0", CPU_POWERPC_750CL_v20, 750cl), - /* PowerPC 750CX (G3 embedded) */ - POWERPC_DEF("750cx", CPU_POWERPC_750CX, 750cx), - /* PowerPC 750CX v1.0 (G3 embedded) */ - POWERPC_DEF("750cx_v1.0", CPU_POWERPC_750CX_v10, 750cx), - /* PowerPC 750CX v2.1 (G3 embedded) */ - POWERPC_DEF("750cx_v2.0", CPU_POWERPC_750CX_v20, 750cx), - /* PowerPC 750CX v2.1 (G3 embedded) */ - POWERPC_DEF("750cx_v2.1", CPU_POWERPC_750CX_v21, 750cx), - /* PowerPC 750CX v2.2 (G3 embedded) */ - POWERPC_DEF("750cx_v2.2", CPU_POWERPC_750CX_v22, 750cx), - /* PowerPC 750CXe (G3 embedded) */ - POWERPC_DEF("750cxe", CPU_POWERPC_750CXE, 750cx), - /* PowerPC 750CXe v2.1 (G3 embedded) */ - POWERPC_DEF("750cxe_v2.1", CPU_POWERPC_750CXE_v21, 750cx), - /* PowerPC 750CXe v2.2 (G3 embedded) */ - POWERPC_DEF("750cxe_v2.2", CPU_POWERPC_750CXE_v22, 750cx), - /* PowerPC 750CXe v2.3 (G3 embedded) */ - POWERPC_DEF("750cxe_v2.3", CPU_POWERPC_750CXE_v23, 750cx), - /* PowerPC 750CXe v2.4 (G3 embedded) */ - POWERPC_DEF("750cxe_v2.4", CPU_POWERPC_750CXE_v24, 750cx), - /* PowerPC 750CXe v2.4b (G3 embedded) */ - POWERPC_DEF("750cxe_v2.4b", CPU_POWERPC_750CXE_v24b, 750cx), - /* PowerPC 750CXe v3.0 (G3 embedded) */ - POWERPC_DEF("750cxe_v3.0", CPU_POWERPC_750CXE_v30, 750cx), - /* PowerPC 750CXe v3.1 (G3 embedded) */ - POWERPC_DEF("750cxe_v3.1", CPU_POWERPC_750CXE_v31, 750cx), - /* PowerPC 750CXe v3.1b (G3 embedded) */ - POWERPC_DEF("750cxe_v3.1b", CPU_POWERPC_750CXE_v31b, 750cx), - /* PowerPC 750CXr (G3 embedded) */ - POWERPC_DEF("750cxr", CPU_POWERPC_750CXR, 750cx), - /* PowerPC 750FL (G3 embedded) */ - POWERPC_DEF("750fl", CPU_POWERPC_750FL, 750fx), - /* PowerPC 750FX (G3 embedded) */ - POWERPC_DEF("750fx", CPU_POWERPC_750FX, 750fx), - /* PowerPC 750FX v1.0 (G3 embedded) */ - POWERPC_DEF("750fx_v1.0", CPU_POWERPC_750FX_v10, 750fx), - /* PowerPC 750FX v2.0 (G3 embedded) */ - POWERPC_DEF("750fx_v2.0", CPU_POWERPC_750FX_v20, 750fx), - /* PowerPC 750FX v2.1 (G3 embedded) */ - POWERPC_DEF("750fx_v2.1", CPU_POWERPC_750FX_v21, 750fx), - /* PowerPC 750FX v2.2 (G3 embedded) */ - POWERPC_DEF("750fx_v2.2", CPU_POWERPC_750FX_v22, 750fx), - /* PowerPC 750FX v2.3 (G3 embedded) */ - POWERPC_DEF("750fx_v2.3", CPU_POWERPC_750FX_v23, 750fx), - /* PowerPC 750GL (G3 embedded) */ - POWERPC_DEF("750gl", CPU_POWERPC_750GL, 750gx), - /* PowerPC 750GX (G3 embedded) */ - POWERPC_DEF("750gx", CPU_POWERPC_750GX, 750gx), - /* PowerPC 750GX v1.0 (G3 embedded) */ - POWERPC_DEF("750gx_v1.0", CPU_POWERPC_750GX_v10, 750gx), - /* PowerPC 750GX v1.1 (G3 embedded) */ - POWERPC_DEF("750gx_v1.1", CPU_POWERPC_750GX_v11, 750gx), - /* PowerPC 750GX v1.2 (G3 embedded) */ - POWERPC_DEF("750gx_v1.2", CPU_POWERPC_750GX_v12, 750gx), - /* PowerPC 750L (G3 embedded) */ - POWERPC_DEF("750l", CPU_POWERPC_750L, 750), - /* Code name for PowerPC 750L (G3 embedded) */ - POWERPC_DEF("LoneStar", CPU_POWERPC_750L, 750), - /* PowerPC 750L v2.0 (G3 embedded) */ - POWERPC_DEF("750l_v2.0", CPU_POWERPC_750L_v20, 750), - /* PowerPC 750L v2.1 (G3 embedded) */ - POWERPC_DEF("750l_v2.1", CPU_POWERPC_750L_v21, 750), - /* PowerPC 750L v2.2 (G3 embedded) */ - POWERPC_DEF("750l_v2.2", CPU_POWERPC_750L_v22, 750), - /* PowerPC 750L v3.0 (G3 embedded) */ - POWERPC_DEF("750l_v3.0", CPU_POWERPC_750L_v30, 750), - /* PowerPC 750L v3.2 (G3 embedded) */ - POWERPC_DEF("750l_v3.2", CPU_POWERPC_750L_v32, 750), - /* Generic PowerPC 745 */ - POWERPC_DEF("745", CPU_POWERPC_7x5, 745), - /* Generic PowerPC 755 */ - POWERPC_DEF("755", CPU_POWERPC_7x5, 755), - /* Code name for PowerPC 745/755 */ - POWERPC_DEF("Goldfinger", CPU_POWERPC_7x5, 755), - /* PowerPC 745 v1.0 */ - POWERPC_DEF("745_v1.0", CPU_POWERPC_7x5_v10, 745), - /* PowerPC 755 v1.0 */ - POWERPC_DEF("755_v1.0", CPU_POWERPC_7x5_v10, 755), - /* PowerPC 745 v1.1 */ - POWERPC_DEF("745_v1.1", CPU_POWERPC_7x5_v11, 745), - /* PowerPC 755 v1.1 */ - POWERPC_DEF("755_v1.1", CPU_POWERPC_7x5_v11, 755), - /* PowerPC 745 v2.0 */ - POWERPC_DEF("745_v2.0", CPU_POWERPC_7x5_v20, 745), - /* PowerPC 755 v2.0 */ - POWERPC_DEF("755_v2.0", CPU_POWERPC_7x5_v20, 755), - /* PowerPC 745 v2.1 */ - POWERPC_DEF("745_v2.1", CPU_POWERPC_7x5_v21, 745), - /* PowerPC 755 v2.1 */ - POWERPC_DEF("755_v2.1", CPU_POWERPC_7x5_v21, 755), - /* PowerPC 745 v2.2 */ - POWERPC_DEF("745_v2.2", CPU_POWERPC_7x5_v22, 745), - /* PowerPC 755 v2.2 */ - POWERPC_DEF("755_v2.2", CPU_POWERPC_7x5_v22, 755), - /* PowerPC 745 v2.3 */ - POWERPC_DEF("745_v2.3", CPU_POWERPC_7x5_v23, 745), - /* PowerPC 755 v2.3 */ - POWERPC_DEF("755_v2.3", CPU_POWERPC_7x5_v23, 755), - /* PowerPC 745 v2.4 */ - POWERPC_DEF("745_v2.4", CPU_POWERPC_7x5_v24, 745), - /* PowerPC 755 v2.4 */ - POWERPC_DEF("755_v2.4", CPU_POWERPC_7x5_v24, 755), - /* PowerPC 745 v2.5 */ - POWERPC_DEF("745_v2.5", CPU_POWERPC_7x5_v25, 745), - /* PowerPC 755 v2.5 */ - POWERPC_DEF("755_v2.5", CPU_POWERPC_7x5_v25, 755), - /* PowerPC 745 v2.6 */ - POWERPC_DEF("745_v2.6", CPU_POWERPC_7x5_v26, 745), - /* PowerPC 755 v2.6 */ - POWERPC_DEF("755_v2.6", CPU_POWERPC_7x5_v26, 755), - /* PowerPC 745 v2.7 */ - POWERPC_DEF("745_v2.7", CPU_POWERPC_7x5_v27, 745), - /* PowerPC 755 v2.7 */ - POWERPC_DEF("755_v2.7", CPU_POWERPC_7x5_v27, 755), - /* PowerPC 745 v2.8 */ - POWERPC_DEF("745_v2.8", CPU_POWERPC_7x5_v28, 745), - /* PowerPC 755 v2.8 */ - POWERPC_DEF("755_v2.8", CPU_POWERPC_7x5_v28, 755), -#if defined (TODO) - /* PowerPC 745P (G3) */ - POWERPC_DEF("745p", CPU_POWERPC_7x5P, 745), - /* PowerPC 755P (G3) */ - POWERPC_DEF("755p", CPU_POWERPC_7x5P, 755), -#endif - /* PowerPC 74xx family */ - /* PowerPC 7400 (G4) */ - POWERPC_DEF("7400", CPU_POWERPC_7400, 7400), - /* Code name for PowerPC 7400 */ - POWERPC_DEF("Max", CPU_POWERPC_7400, 7400), - /* PowerPC 74xx is also well known as G4 */ - POWERPC_DEF("G4", CPU_POWERPC_7400, 7400), - /* PowerPC 7400 v1.0 (G4) */ - POWERPC_DEF("7400_v1.0", CPU_POWERPC_7400_v10, 7400), - /* PowerPC 7400 v1.1 (G4) */ - POWERPC_DEF("7400_v1.1", CPU_POWERPC_7400_v11, 7400), - /* PowerPC 7400 v2.0 (G4) */ - POWERPC_DEF("7400_v2.0", CPU_POWERPC_7400_v20, 7400), - /* PowerPC 7400 v2.1 (G4) */ - POWERPC_DEF("7400_v2.1", CPU_POWERPC_7400_v21, 7400), - /* PowerPC 7400 v2.2 (G4) */ - POWERPC_DEF("7400_v2.2", CPU_POWERPC_7400_v22, 7400), - /* PowerPC 7400 v2.6 (G4) */ - POWERPC_DEF("7400_v2.6", CPU_POWERPC_7400_v26, 7400), - /* PowerPC 7400 v2.7 (G4) */ - POWERPC_DEF("7400_v2.7", CPU_POWERPC_7400_v27, 7400), - /* PowerPC 7400 v2.8 (G4) */ - POWERPC_DEF("7400_v2.8", CPU_POWERPC_7400_v28, 7400), - /* PowerPC 7400 v2.9 (G4) */ - POWERPC_DEF("7400_v2.9", CPU_POWERPC_7400_v29, 7400), - /* PowerPC 7410 (G4) */ - POWERPC_DEF("7410", CPU_POWERPC_7410, 7410), - /* Code name for PowerPC 7410 */ - POWERPC_DEF("Nitro", CPU_POWERPC_7410, 7410), - /* PowerPC 7410 v1.0 (G4) */ - POWERPC_DEF("7410_v1.0", CPU_POWERPC_7410_v10, 7410), - /* PowerPC 7410 v1.1 (G4) */ - POWERPC_DEF("7410_v1.1", CPU_POWERPC_7410_v11, 7410), - /* PowerPC 7410 v1.2 (G4) */ - POWERPC_DEF("7410_v1.2", CPU_POWERPC_7410_v12, 7410), - /* PowerPC 7410 v1.3 (G4) */ - POWERPC_DEF("7410_v1.3", CPU_POWERPC_7410_v13, 7410), - /* PowerPC 7410 v1.4 (G4) */ - POWERPC_DEF("7410_v1.4", CPU_POWERPC_7410_v14, 7410), - /* PowerPC 7448 (G4) */ - POWERPC_DEF("7448", CPU_POWERPC_7448, 7400), - /* PowerPC 7448 v1.0 (G4) */ - POWERPC_DEF("7448_v1.0", CPU_POWERPC_7448_v10, 7400), - /* PowerPC 7448 v1.1 (G4) */ - POWERPC_DEF("7448_v1.1", CPU_POWERPC_7448_v11, 7400), - /* PowerPC 7448 v2.0 (G4) */ - POWERPC_DEF("7448_v2.0", CPU_POWERPC_7448_v20, 7400), - /* PowerPC 7448 v2.1 (G4) */ - POWERPC_DEF("7448_v2.1", CPU_POWERPC_7448_v21, 7400), - /* PowerPC 7450 (G4) */ - POWERPC_DEF("7450", CPU_POWERPC_7450, 7450), - /* Code name for PowerPC 7450 */ - POWERPC_DEF("Vger", CPU_POWERPC_7450, 7450), - /* PowerPC 7450 v1.0 (G4) */ - POWERPC_DEF("7450_v1.0", CPU_POWERPC_7450_v10, 7450), - /* PowerPC 7450 v1.1 (G4) */ - POWERPC_DEF("7450_v1.1", CPU_POWERPC_7450_v11, 7450), - /* PowerPC 7450 v1.2 (G4) */ - POWERPC_DEF("7450_v1.2", CPU_POWERPC_7450_v12, 7450), - /* PowerPC 7450 v2.0 (G4) */ - POWERPC_DEF("7450_v2.0", CPU_POWERPC_7450_v20, 7450), - /* PowerPC 7450 v2.1 (G4) */ - POWERPC_DEF("7450_v2.1", CPU_POWERPC_7450_v21, 7450), - /* PowerPC 7441 (G4) */ - POWERPC_DEF("7441", CPU_POWERPC_74x1, 7440), - /* PowerPC 7451 (G4) */ - POWERPC_DEF("7451", CPU_POWERPC_74x1, 7450), - /* PowerPC 7441 v2.1 (G4) */ - POWERPC_DEF("7441_v2.1", CPU_POWERPC_7450_v21, 7440), - /* PowerPC 7441 v2.3 (G4) */ - POWERPC_DEF("7441_v2.3", CPU_POWERPC_74x1_v23, 7440), - /* PowerPC 7451 v2.3 (G4) */ - POWERPC_DEF("7451_v2.3", CPU_POWERPC_74x1_v23, 7450), - /* PowerPC 7441 v2.10 (G4) */ - POWERPC_DEF("7441_v2.10", CPU_POWERPC_74x1_v210, 7440), - /* PowerPC 7451 v2.10 (G4) */ - POWERPC_DEF("7451_v2.10", CPU_POWERPC_74x1_v210, 7450), - /* PowerPC 7445 (G4) */ - POWERPC_DEF("7445", CPU_POWERPC_74x5, 7445), - /* PowerPC 7455 (G4) */ - POWERPC_DEF("7455", CPU_POWERPC_74x5, 7455), - /* Code name for PowerPC 7445/7455 */ - POWERPC_DEF("Apollo6", CPU_POWERPC_74x5, 7455), - /* PowerPC 7445 v1.0 (G4) */ - POWERPC_DEF("7445_v1.0", CPU_POWERPC_74x5_v10, 7445), - /* PowerPC 7455 v1.0 (G4) */ - POWERPC_DEF("7455_v1.0", CPU_POWERPC_74x5_v10, 7455), - /* PowerPC 7445 v2.1 (G4) */ - POWERPC_DEF("7445_v2.1", CPU_POWERPC_74x5_v21, 7445), - /* PowerPC 7455 v2.1 (G4) */ - POWERPC_DEF("7455_v2.1", CPU_POWERPC_74x5_v21, 7455), - /* PowerPC 7445 v3.2 (G4) */ - POWERPC_DEF("7445_v3.2", CPU_POWERPC_74x5_v32, 7445), - /* PowerPC 7455 v3.2 (G4) */ - POWERPC_DEF("7455_v3.2", CPU_POWERPC_74x5_v32, 7455), - /* PowerPC 7445 v3.3 (G4) */ - POWERPC_DEF("7445_v3.3", CPU_POWERPC_74x5_v33, 7445), - /* PowerPC 7455 v3.3 (G4) */ - POWERPC_DEF("7455_v3.3", CPU_POWERPC_74x5_v33, 7455), - /* PowerPC 7445 v3.4 (G4) */ - POWERPC_DEF("7445_v3.4", CPU_POWERPC_74x5_v34, 7445), - /* PowerPC 7455 v3.4 (G4) */ - POWERPC_DEF("7455_v3.4", CPU_POWERPC_74x5_v34, 7455), - /* PowerPC 7447 (G4) */ - POWERPC_DEF("7447", CPU_POWERPC_74x7, 7445), - /* PowerPC 7457 (G4) */ - POWERPC_DEF("7457", CPU_POWERPC_74x7, 7455), - /* Code name for PowerPC 7447/7457 */ - POWERPC_DEF("Apollo7", CPU_POWERPC_74x7, 7455), - /* PowerPC 7447 v1.0 (G4) */ - POWERPC_DEF("7447_v1.0", CPU_POWERPC_74x7_v10, 7445), - /* PowerPC 7457 v1.0 (G4) */ - POWERPC_DEF("7457_v1.0", CPU_POWERPC_74x7_v10, 7455), - /* PowerPC 7447 v1.1 (G4) */ - POWERPC_DEF("7447_v1.1", CPU_POWERPC_74x7_v11, 7445), - /* PowerPC 7457 v1.1 (G4) */ - POWERPC_DEF("7457_v1.1", CPU_POWERPC_74x7_v11, 7455), - /* PowerPC 7457 v1.2 (G4) */ - POWERPC_DEF("7457_v1.2", CPU_POWERPC_74x7_v12, 7455), - /* PowerPC 7447A (G4) */ - POWERPC_DEF("7447A", CPU_POWERPC_74x7A, 7445), - /* PowerPC 7457A (G4) */ - POWERPC_DEF("7457A", CPU_POWERPC_74x7A, 7455), - /* PowerPC 7447A v1.0 (G4) */ - POWERPC_DEF("7447A_v1.0", CPU_POWERPC_74x7A_v10, 7445), - /* PowerPC 7457A v1.0 (G4) */ - POWERPC_DEF("7457A_v1.0", CPU_POWERPC_74x7A_v10, 7455), - /* Code name for PowerPC 7447A/7457A */ - POWERPC_DEF("Apollo7PM", CPU_POWERPC_74x7A_v10, 7455), - /* PowerPC 7447A v1.1 (G4) */ - POWERPC_DEF("7447A_v1.1", CPU_POWERPC_74x7A_v11, 7445), - /* PowerPC 7457A v1.1 (G4) */ - POWERPC_DEF("7457A_v1.1", CPU_POWERPC_74x7A_v11, 7455), - /* PowerPC 7447A v1.2 (G4) */ - POWERPC_DEF("7447A_v1.2", CPU_POWERPC_74x7A_v12, 7445), - /* PowerPC 7457A v1.2 (G4) */ - POWERPC_DEF("7457A_v1.2", CPU_POWERPC_74x7A_v12, 7455), - /* 64 bits PowerPC */ -#if defined (TARGET_PPC64) - /* PowerPC 620 */ - POWERPC_DEF("620", CPU_POWERPC_620, 620), - /* Code name for PowerPC 620 */ - POWERPC_DEF("Trident", CPU_POWERPC_620, 620), -#if defined (TODO) - /* PowerPC 630 (POWER3) */ - POWERPC_DEF("630", CPU_POWERPC_630, 630), - POWERPC_DEF("POWER3", CPU_POWERPC_630, 630), - /* Code names for POWER3 */ - POWERPC_DEF("Boxer", CPU_POWERPC_630, 630), - POWERPC_DEF("Dino", CPU_POWERPC_630, 630), -#endif -#if defined (TODO) - /* PowerPC 631 (Power 3+) */ - POWERPC_DEF("631", CPU_POWERPC_631, 631), - POWERPC_DEF("POWER3+", CPU_POWERPC_631, 631), -#endif -#if defined (TODO) - /* POWER4 */ - POWERPC_DEF("POWER4", CPU_POWERPC_POWER4, POWER4), -#endif -#if defined (TODO) - /* POWER4p */ - POWERPC_DEF("POWER4+", CPU_POWERPC_POWER4P, POWER4P), -#endif -#if defined (TODO) - /* POWER5 */ - POWERPC_DEF("POWER5", CPU_POWERPC_POWER5, POWER5), - /* POWER5GR */ - POWERPC_DEF("POWER5gr", CPU_POWERPC_POWER5GR, POWER5), -#endif -#if defined (TODO) - /* POWER5+ */ - POWERPC_DEF("POWER5+", CPU_POWERPC_POWER5P, POWER5P), - /* POWER5GS */ - POWERPC_DEF("POWER5gs", CPU_POWERPC_POWER5GS, POWER5P), -#endif -#if defined (TODO) - /* POWER6 */ - POWERPC_DEF("POWER6", CPU_POWERPC_POWER6, POWER6), - /* POWER6 running in POWER5 mode */ - POWERPC_DEF("POWER6_5", CPU_POWERPC_POWER6_5, POWER5), - /* POWER6A */ - POWERPC_DEF("POWER6A", CPU_POWERPC_POWER6A, POWER6), -#endif - /* POWER7 */ - POWERPC_DEF("POWER7", CPU_POWERPC_POWER7, POWER7), - POWERPC_DEF("POWER7_v2.0", CPU_POWERPC_POWER7_v20, POWER7), - POWERPC_DEF("POWER7_v2.1", CPU_POWERPC_POWER7_v21, POWER7), - POWERPC_DEF("POWER7_v2.3", CPU_POWERPC_POWER7_v23, POWER7), - /* PowerPC 970 */ - POWERPC_DEF("970", CPU_POWERPC_970, 970), - /* PowerPC 970FX (G5) */ - POWERPC_DEF("970fx", CPU_POWERPC_970FX, 970FX), - /* PowerPC 970FX v1.0 (G5) */ - POWERPC_DEF("970fx_v1.0", CPU_POWERPC_970FX_v10, 970FX), - /* PowerPC 970FX v2.0 (G5) */ - POWERPC_DEF("970fx_v2.0", CPU_POWERPC_970FX_v20, 970FX), - /* PowerPC 970FX v2.1 (G5) */ - POWERPC_DEF("970fx_v2.1", CPU_POWERPC_970FX_v21, 970FX), - /* PowerPC 970FX v3.0 (G5) */ - POWERPC_DEF("970fx_v3.0", CPU_POWERPC_970FX_v30, 970FX), - /* PowerPC 970FX v3.1 (G5) */ - POWERPC_DEF("970fx_v3.1", CPU_POWERPC_970FX_v31, 970FX), - /* PowerPC 970GX (G5) */ - POWERPC_DEF("970gx", CPU_POWERPC_970GX, 970GX), - /* PowerPC 970MP */ - POWERPC_DEF("970mp", CPU_POWERPC_970MP, 970MP), - /* PowerPC 970MP v1.0 */ - POWERPC_DEF("970mp_v1.0", CPU_POWERPC_970MP_v10, 970MP), - /* PowerPC 970MP v1.1 */ - POWERPC_DEF("970mp_v1.1", CPU_POWERPC_970MP_v11, 970MP), -#if defined (TODO) - /* PowerPC Cell */ - POWERPC_DEF("Cell", CPU_POWERPC_CELL, 970), -#endif -#if defined (TODO) - /* PowerPC Cell v1.0 */ - POWERPC_DEF("Cell_v1.0", CPU_POWERPC_CELL_v10, 970), -#endif -#if defined (TODO) - /* PowerPC Cell v2.0 */ - POWERPC_DEF("Cell_v2.0", CPU_POWERPC_CELL_v20, 970), -#endif -#if defined (TODO) - /* PowerPC Cell v3.0 */ - POWERPC_DEF("Cell_v3.0", CPU_POWERPC_CELL_v30, 970), -#endif -#if defined (TODO) - /* PowerPC Cell v3.1 */ - POWERPC_DEF("Cell_v3.1", CPU_POWERPC_CELL_v31, 970), -#endif -#if defined (TODO) - /* PowerPC Cell v3.2 */ - POWERPC_DEF("Cell_v3.2", CPU_POWERPC_CELL_v32, 970), -#endif -#if defined (TODO) - /* RS64 (Apache/A35) */ - /* This one seems to support the whole POWER2 instruction set - * and the PowerPC 64 one. - */ - /* What about A10 & A30 ? */ - POWERPC_DEF("RS64", CPU_POWERPC_RS64, RS64), - POWERPC_DEF("Apache", CPU_POWERPC_RS64, RS64), - POWERPC_DEF("A35", CPU_POWERPC_RS64, RS64), -#endif -#if defined (TODO) - /* RS64-II (NorthStar/A50) */ - POWERPC_DEF("RS64-II", CPU_POWERPC_RS64II, RS64), - POWERPC_DEF("NorthStar", CPU_POWERPC_RS64II, RS64), - POWERPC_DEF("A50", CPU_POWERPC_RS64II, RS64), -#endif -#if defined (TODO) - /* RS64-III (Pulsar) */ - POWERPC_DEF("RS64-III", CPU_POWERPC_RS64III, RS64), - POWERPC_DEF("Pulsar", CPU_POWERPC_RS64III, RS64), -#endif -#if defined (TODO) - /* RS64-IV (IceStar/IStar/SStar) */ - POWERPC_DEF("RS64-IV", CPU_POWERPC_RS64IV, RS64), - POWERPC_DEF("IceStar", CPU_POWERPC_RS64IV, RS64), - POWERPC_DEF("IStar", CPU_POWERPC_RS64IV, RS64), - POWERPC_DEF("SStar", CPU_POWERPC_RS64IV, RS64), -#endif -#endif /* defined (TARGET_PPC64) */ - /* POWER */ -#if defined (TODO) - /* Original POWER */ - POWERPC_DEF("POWER", CPU_POWERPC_POWER, POWER), - POWERPC_DEF("RIOS", CPU_POWERPC_POWER, POWER), - POWERPC_DEF("RSC", CPU_POWERPC_POWER, POWER), - POWERPC_DEF("RSC3308", CPU_POWERPC_POWER, POWER), - POWERPC_DEF("RSC4608", CPU_POWERPC_POWER, POWER), -#endif -#if defined (TODO) - /* POWER2 */ - POWERPC_DEF("POWER2", CPU_POWERPC_POWER2, POWER), - POWERPC_DEF("RSC2", CPU_POWERPC_POWER2, POWER), - POWERPC_DEF("P2SC", CPU_POWERPC_POWER2, POWER), -#endif - /* PA semi cores */ -#if defined (TODO) - /* PA PA6T */ - POWERPC_DEF("PA6T", CPU_POWERPC_PA6T, PA6T), -#endif - /* Generic PowerPCs */ -#if defined (TARGET_PPC64) - POWERPC_DEF("ppc64", CPU_POWERPC_PPC64, PPC64), -#endif - POWERPC_DEF("ppc32", CPU_POWERPC_PPC32, PPC32), - POWERPC_DEF("ppc", CPU_POWERPC_DEFAULT, DEFAULT), - /* Fallback */ - POWERPC_DEF("default", CPU_POWERPC_DEFAULT, DEFAULT), -}; /*****************************************************************************/ /* Generic CPU instantiation routine */ -static void init_ppc_proc (CPUPPCState *env, const ppc_def_t *def) +static void init_ppc_proc(PowerPCCPU *cpu) { + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + CPUPPCState *env = &cpu->env; #if !defined(CONFIG_USER_ONLY) int i; @@ -9434,23 +7214,23 @@ static void init_ppc_proc (CPUPPCState *env, const ppc_def_t *def) #endif SPR_NOACCESS, &spr_read_generic, SPR_NOACCESS, - def->pvr); + pcc->pvr); /* Register SVR if it's defined to anything else than POWERPC_SVR_NONE */ - if (def->svr != POWERPC_SVR_NONE) { - if (def->svr & POWERPC_SVR_E500) { + if (pcc->svr != POWERPC_SVR_NONE) { + if (pcc->svr & POWERPC_SVR_E500) { spr_register(env, SPR_E500_SVR, "SVR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, SPR_NOACCESS, - def->svr & ~POWERPC_SVR_E500); + pcc->svr & ~POWERPC_SVR_E500); } else { spr_register(env, SPR_SVR, "SVR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, SPR_NOACCESS, - def->svr); + pcc->svr); } } /* PowerPC implementation specific initialisations (SPRs, timers, ...) */ - (*def->init_proc)(env); + (*pcc->init_proc)(env); #if !defined(CONFIG_USER_ONLY) env->excp_prefix = env->hreset_excp_prefix; #endif @@ -9797,27 +7577,27 @@ static void fix_opcode_tables (opc_handler_t **ppc_opcodes) } /*****************************************************************************/ -static int create_ppc_opcodes (CPUPPCState *env, const ppc_def_t *def) +static void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp) { + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + CPUPPCState *env = &cpu->env; opcode_t *opc; fill_new_table(env->opcodes, 0x40); for (opc = opcodes; opc < &opcodes[ARRAY_SIZE(opcodes)]; opc++) { - if (((opc->handler.type & def->insns_flags) != 0) || - ((opc->handler.type2 & def->insns_flags2) != 0)) { + if (((opc->handler.type & pcc->insns_flags) != 0) || + ((opc->handler.type2 & pcc->insns_flags2) != 0)) { if (register_insn(env->opcodes, opc) < 0) { - printf("*** ERROR initializing PowerPC instruction " - "0x%02x 0x%02x 0x%02x\n", opc->opc1, opc->opc2, - opc->opc3); - return -1; + error_setg(errp, "ERROR initializing PowerPC instruction " + "0x%02x 0x%02x 0x%02x", opc->opc1, opc->opc2, + opc->opc3); + return; } } } fix_opcode_tables(env->opcodes); fflush(stdout); fflush(stderr); - - return 0; } #if defined(PPC_DUMP_CPU) @@ -10009,8 +7789,10 @@ static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 0; } -static int ppc_fixup_cpu(CPUPPCState *env) +static int ppc_fixup_cpu(PowerPCCPU *cpu) { + CPUPPCState *env = &cpu->env; + /* TCG doesn't (yet) emulate some groups of instructions that * are implemented on some otherwise supported CPUs (e.g. VSX * and decimal floating point instructions on POWER7). We @@ -10031,68 +7813,69 @@ static int ppc_fixup_cpu(CPUPPCState *env) return 0; } -int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def) +static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) { - env->msr_mask = def->msr_mask; - env->mmu_model = def->mmu_model; - env->excp_model = def->excp_model; - env->bus_model = def->bus_model; - env->insns_flags = def->insns_flags; - env->insns_flags2 = def->insns_flags2; - env->flags = def->flags; - env->bfd_mach = def->bfd_mach; - env->check_pow = def->check_pow; + PowerPCCPU *cpu = POWERPC_CPU(dev); + CPUPPCState *env = &cpu->env; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + Error *local_err = NULL; +#if !defined(CONFIG_USER_ONLY) + int max_smt = kvm_enabled() ? kvmppc_smt_threads() : 1; +#endif -#if defined(TARGET_PPC64) - if (def->sps) - env->sps = *def->sps; - else if (env->mmu_model & POWERPC_MMU_64) { - /* Use default sets of page sizes */ - static const struct ppc_segment_page_sizes defsps = { - .sps = { - { .page_shift = 12, /* 4K */ - .slb_enc = 0, - .enc = { { .page_shift = 12, .pte_enc = 0 } } - }, - { .page_shift = 24, /* 16M */ - .slb_enc = 0x100, - .enc = { { .page_shift = 24, .pte_enc = 0 } } - }, - }, - }; - env->sps = defsps; +#if !defined(CONFIG_USER_ONLY) + if (smp_threads > max_smt) { + error_setg(errp, "Cannot support more than %d threads on PPC with %s", + max_smt, kvm_enabled() ? "KVM" : "TCG"); + return; } -#endif /* defined(TARGET_PPC64) */ +#endif if (kvm_enabled()) { - if (kvmppc_fixup_cpu(env) != 0) { - fprintf(stderr, "Unable to virtualize selected CPU with KVM\n"); - exit(1); + if (kvmppc_fixup_cpu(cpu) != 0) { + error_setg(errp, "Unable to virtualize selected CPU with KVM"); + return; } } else { - if (ppc_fixup_cpu(env) != 0) { - fprintf(stderr, "Unable to emulate selected CPU with TCG\n"); - exit(1); + if (ppc_fixup_cpu(cpu) != 0) { + error_setg(errp, "Unable to emulate selected CPU with TCG"); + return; } } - if (create_ppc_opcodes(env, def) < 0) - return -1; - init_ppc_proc(env, def); +#if defined(TARGET_PPCEMB) + if (pcc->mmu_model != POWERPC_MMU_BOOKE) { + error_setg(errp, "CPU does not possess a BookE MMU. " + "Please use qemu-system-ppc or qemu-system-ppc64 instead " + "or choose another CPU model."); + return; + } +#endif - if (def->insns_flags & PPC_FLOAT) { + create_ppc_opcodes(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + init_ppc_proc(cpu); + + if (pcc->insns_flags & PPC_FLOAT) { gdb_register_coprocessor(env, gdb_get_float_reg, gdb_set_float_reg, 33, "power-fpu.xml", 0); } - if (def->insns_flags & PPC_ALTIVEC) { + if (pcc->insns_flags & PPC_ALTIVEC) { gdb_register_coprocessor(env, gdb_get_avr_reg, gdb_set_avr_reg, 34, "power-altivec.xml", 0); } - if (def->insns_flags & PPC_SPE) { + if (pcc->insns_flags & PPC_SPE) { gdb_register_coprocessor(env, gdb_get_spe_reg, gdb_set_spe_reg, 34, "power-spe.xml", 0); } + qemu_init_vcpu(env); + + pcc->parent_realize(dev, errp); + #if defined(PPC_DUMP_CPU) { const char *mmu_model, *excp_model, *bus_model; @@ -10210,7 +7993,7 @@ int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def) } printf("PowerPC %-12s : PVR %08x MSR %016" PRIx64 "\n" " MMU model : %s\n", - def->name, def->pvr, def->msr_mask, mmu_model); + pcc->name, pcc->pvr, pcc->msr_mask, mmu_model); #if !defined(CONFIG_USER_ONLY) if (env->tlb != NULL) { printf(" %d %s TLB in %d ways\n", @@ -10254,51 +8037,71 @@ int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def) dump_ppc_sprs(env); fflush(stdout); #endif - - return 0; } -static bool ppc_cpu_usable(const ppc_def_t *def) +static gint ppc_cpu_compare_class_pvr(gconstpointer a, gconstpointer b) { + ObjectClass *oc = (ObjectClass *)a; + uint32_t pvr = *(uint32_t *)b; + PowerPCCPUClass *pcc = (PowerPCCPUClass *)a; + + /* -cpu host does a PVR lookup during construction */ + if (unlikely(strcmp(object_class_get_name(oc), + TYPE_HOST_POWERPC_CPU) == 0)) { + return -1; + } + #if defined(TARGET_PPCEMB) - /* When using the ppcemb target, we only support 440 style cores */ - if (def->mmu_model != POWERPC_MMU_BOOKE) { - return false; + if (pcc->mmu_model != POWERPC_MMU_BOOKE) { + return -1; } #endif - return true; + return pcc->pvr == pvr ? 0 : -1; } -const ppc_def_t *ppc_find_by_pvr(uint32_t pvr) +PowerPCCPUClass *ppc_cpu_class_by_pvr(uint32_t pvr) { - int i; + GSList *list, *item; + PowerPCCPUClass *pcc = NULL; - for (i = 0; i < ARRAY_SIZE(ppc_defs); i++) { - if (!ppc_cpu_usable(&ppc_defs[i])) { - continue; - } - - /* If we have an exact match, we're done */ - if (pvr == ppc_defs[i].pvr) { - return &ppc_defs[i]; - } + list = object_class_get_list(TYPE_POWERPC_CPU, false); + item = g_slist_find_custom(list, &pvr, ppc_cpu_compare_class_pvr); + if (item != NULL) { + pcc = POWERPC_CPU_CLASS(item->data); } + g_slist_free(list); - return NULL; + return pcc; +} + +static gint ppc_cpu_compare_class_name(gconstpointer a, gconstpointer b) +{ + ObjectClass *oc = (ObjectClass *)a; + const char *name = b; +#if defined(TARGET_PPCEMB) + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); +#endif + + if (strncasecmp(name, object_class_get_name(oc), strlen(name)) == 0 && +#if defined(TARGET_PPCEMB) + pcc->mmu_model == POWERPC_MMU_BOOKE && +#endif + strcmp(object_class_get_name(oc) + strlen(name), + "-" TYPE_POWERPC_CPU) == 0) { + return 0; + } + return -1; } #include -const ppc_def_t *cpu_ppc_find_by_name (const char *name) +static ObjectClass *ppc_cpu_class_by_name(const char *name) { - const ppc_def_t *ret; + GSList *list, *item; + ObjectClass *ret = NULL; const char *p; - int i, max, len; - - if (kvm_enabled() && (strcasecmp(name, "host") == 0)) { - return kvmppc_host_cpu_def(); - } + int i, len; /* Check if the given name is a PVR */ len = strlen(name); @@ -10312,55 +8115,184 @@ const ppc_def_t *cpu_ppc_find_by_name (const char *name) if (!qemu_isxdigit(*p++)) break; } - if (i == 8) - return ppc_find_by_pvr(strtoul(name, NULL, 16)); - } - ret = NULL; - max = ARRAY_SIZE(ppc_defs); - for (i = 0; i < max; i++) { - if (!ppc_cpu_usable(&ppc_defs[i])) { - continue; + if (i == 8) { + ret = OBJECT_CLASS(ppc_cpu_class_by_pvr(strtoul(name, NULL, 16))); + return ret; } + } - if (strcasecmp(name, ppc_defs[i].name) == 0) { - ret = &ppc_defs[i]; - break; + for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) { + if (strcmp(ppc_cpu_aliases[i].alias, name) == 0) { + return ppc_cpu_class_by_name(ppc_cpu_aliases[i].model); } } + list = object_class_get_list(TYPE_POWERPC_CPU, false); + item = g_slist_find_custom(list, name, ppc_cpu_compare_class_name); + if (item != NULL) { + ret = OBJECT_CLASS(item->data); + } + g_slist_free(list); + return ret; } -void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf) +PowerPCCPU *cpu_ppc_init(const char *cpu_model) { - int i, max; + PowerPCCPU *cpu; + CPUPPCState *env; + ObjectClass *oc; + Error *err = NULL; - max = ARRAY_SIZE(ppc_defs); - for (i = 0; i < max; i++) { - if (!ppc_cpu_usable(&ppc_defs[i])) { + oc = ppc_cpu_class_by_name(cpu_model); + if (oc == NULL) { + return NULL; + } + + cpu = POWERPC_CPU(object_new(object_class_get_name(oc))); + env = &cpu->env; + env->cpu_model_str = cpu_model; + + object_property_set_bool(OBJECT(cpu), true, "realized", &err); + if (err != NULL) { + fprintf(stderr, "%s\n", error_get_pretty(err)); + error_free(err); + object_unref(OBJECT(cpu)); + return NULL; + } + + return cpu; +} + +/* Sort by PVR, ordering special case "host" last. */ +static gint ppc_cpu_list_compare(gconstpointer a, gconstpointer b) +{ + ObjectClass *oc_a = (ObjectClass *)a; + ObjectClass *oc_b = (ObjectClass *)b; + PowerPCCPUClass *pcc_a = POWERPC_CPU_CLASS(oc_a); + PowerPCCPUClass *pcc_b = POWERPC_CPU_CLASS(oc_b); + const char *name_a = object_class_get_name(oc_a); + const char *name_b = object_class_get_name(oc_b); + + if (strcmp(name_a, TYPE_HOST_POWERPC_CPU) == 0) { + return 1; + } else if (strcmp(name_b, TYPE_HOST_POWERPC_CPU) == 0) { + return -1; + } else { + /* Avoid an integer overflow during subtraction */ + if (pcc_a->pvr < pcc_b->pvr) { + return -1; + } else if (pcc_a->pvr > pcc_b->pvr) { + return 1; + } else { + return 0; + } + } +} + +static void ppc_cpu_list_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CPUListState *s = user_data; + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + const char *typename = object_class_get_name(oc); + char *name; + int i; + +#if defined(TARGET_PPCEMB) + if (pcc->mmu_model != POWERPC_MMU_BOOKE) { + return; + } +#endif + if (unlikely(strcmp(typename, TYPE_HOST_POWERPC_CPU) == 0)) { + return; + } + + name = g_strndup(typename, + strlen(typename) - strlen("-" TYPE_POWERPC_CPU)); + (*s->cpu_fprintf)(s->file, "PowerPC %-16s PVR %08x\n", + name, pcc->pvr); + for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) { + const PowerPCCPUAlias *alias = &ppc_cpu_aliases[i]; + ObjectClass *alias_oc = ppc_cpu_class_by_name(alias->model); + + if (alias_oc != oc) { continue; } - - (*cpu_fprintf)(f, "PowerPC %-16s PVR %08x\n", - ppc_defs[i].name, ppc_defs[i].pvr); + (*s->cpu_fprintf)(s->file, "PowerPC %-16s (alias for %s)\n", + alias->alias, name); } + g_free(name); +} + +void ppc_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ + CPUListState s = { + .file = f, + .cpu_fprintf = cpu_fprintf, + }; + GSList *list; + + list = object_class_get_list(TYPE_POWERPC_CPU, false); + list = g_slist_sort(list, ppc_cpu_list_compare); + g_slist_foreach(list, ppc_cpu_list_entry, &s); + g_slist_free(list); + +#ifdef CONFIG_KVM + cpu_fprintf(f, "\n"); + cpu_fprintf(f, "PowerPC %-16s\n", "host"); +#endif +} + +static void ppc_cpu_defs_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CpuDefinitionInfoList **first = user_data; + const char *typename; + CpuDefinitionInfoList *entry; + CpuDefinitionInfo *info; +#if defined(TARGET_PPCEMB) + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + if (pcc->mmu_model != POWERPC_MMU_BOOKE) { + return; + } +#endif + + typename = object_class_get_name(oc); + info = g_malloc0(sizeof(*info)); + info->name = g_strndup(typename, + strlen(typename) - strlen("-" TYPE_POWERPC_CPU)); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = *first; + *first = entry; } CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) { CpuDefinitionInfoList *cpu_list = NULL; + GSList *list; int i; - for (i = 0; i < ARRAY_SIZE(ppc_defs); i++) { + list = object_class_get_list(TYPE_POWERPC_CPU, false); + g_slist_foreach(list, ppc_cpu_defs_entry, &cpu_list); + g_slist_free(list); + + for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) { + const PowerPCCPUAlias *alias = &ppc_cpu_aliases[i]; + ObjectClass *oc; CpuDefinitionInfoList *entry; CpuDefinitionInfo *info; - if (!ppc_cpu_usable(&ppc_defs[i])) { + oc = ppc_cpu_class_by_name(alias->model); + if (oc == NULL) { continue; } info = g_malloc0(sizeof(*info)); - info->name = g_strdup(ppc_defs[i].name); + info->name = g_strdup(alias->alias); entry = g_malloc0(sizeof(*entry)); entry->value = info; @@ -10380,7 +8312,7 @@ static void ppc_cpu_reset(CPUState *s) target_ulong msr; if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); + qemu_log("CPU Reset (CPU %d)\n", s->cpu_index); log_cpu_state(env, 0); } @@ -10438,19 +8370,64 @@ static void ppc_cpu_reset(CPUState *s) static void ppc_cpu_initfn(Object *obj) { + CPUState *cs = CPU(obj); PowerPCCPU *cpu = POWERPC_CPU(obj); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; + cs->env_ptr = env; cpu_exec_init(env); + + env->msr_mask = pcc->msr_mask; + env->mmu_model = pcc->mmu_model; + env->excp_model = pcc->excp_model; + env->bus_model = pcc->bus_model; + env->insns_flags = pcc->insns_flags; + env->insns_flags2 = pcc->insns_flags2; + env->flags = pcc->flags; + env->bfd_mach = pcc->bfd_mach; + env->check_pow = pcc->check_pow; + +#if defined(TARGET_PPC64) + if (pcc->sps) { + env->sps = *pcc->sps; + } else if (env->mmu_model & POWERPC_MMU_64) { + /* Use default sets of page sizes */ + static const struct ppc_segment_page_sizes defsps = { + .sps = { + { .page_shift = 12, /* 4K */ + .slb_enc = 0, + .enc = { { .page_shift = 12, .pte_enc = 0 } } + }, + { .page_shift = 24, /* 16M */ + .slb_enc = 0x100, + .enc = { { .page_shift = 24, .pte_enc = 0 } } + }, + }, + }; + env->sps = defsps; + } +#endif /* defined(TARGET_PPC64) */ + + if (tcg_enabled()) { + ppc_translate_init(); + } } static void ppc_cpu_class_init(ObjectClass *oc, void *data) { PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + pcc->parent_realize = dc->realize; + dc->realize = ppc_cpu_realizefn; pcc->parent_reset = cc->reset; cc->reset = ppc_cpu_reset; + + cc->class_by_name = ppc_cpu_class_by_name; + cc->do_interrupt = ppc_cpu_do_interrupt; } static const TypeInfo ppc_cpu_type_info = { @@ -10458,7 +8435,7 @@ static const TypeInfo ppc_cpu_type_info = { .parent = TYPE_CPU, .instance_size = sizeof(PowerPCCPU), .instance_init = ppc_cpu_initfn, - .abstract = false, + .abstract = true, .class_size = sizeof(PowerPCCPUClass), .class_init = ppc_cpu_class_init, }; diff --git a/target-s390x/Makefile.objs b/target-s390x/Makefile.objs index e728abf759..4e634173a4 100644 --- a/target-s390x/Makefile.objs +++ b/target-s390x/Makefile.objs @@ -1,4 +1,4 @@ obj-y += translate.o helper.o cpu.o interrupt.o obj-y += int_helper.o fpu_helper.o cc_helper.o mem_helper.o misc_helper.o -obj-$(CONFIG_SOFTMMU) += machine.o +obj-$(CONFIG_SOFTMMU) += ioinst.o obj-$(CONFIG_KVM) += kvm.o diff --git a/target-s390x/cc_helper.c b/target-s390x/cc_helper.c index 19ef145da9..a6d60bf885 100644 --- a/target-s390x/cc_helper.c +++ b/target-s390x/cc_helper.c @@ -20,6 +20,7 @@ #include "cpu.h" #include "helper.h" +#include "qemu/host-utils.h" /* #define DEBUG_HELPER */ #ifdef DEBUG_HELPER @@ -28,8 +29,7 @@ #define HELPER_LOG(x...) #endif -static inline uint32_t cc_calc_ltgt_32(CPUS390XState *env, int32_t src, - int32_t dst) +static uint32_t cc_calc_ltgt_32(int32_t src, int32_t dst) { if (src == dst) { return 0; @@ -40,13 +40,12 @@ static inline uint32_t cc_calc_ltgt_32(CPUS390XState *env, int32_t src, } } -static inline uint32_t cc_calc_ltgt0_32(CPUS390XState *env, int32_t dst) +static uint32_t cc_calc_ltgt0_32(int32_t dst) { - return cc_calc_ltgt_32(env, dst, 0); + return cc_calc_ltgt_32(dst, 0); } -static inline uint32_t cc_calc_ltgt_64(CPUS390XState *env, int64_t src, - int64_t dst) +static uint32_t cc_calc_ltgt_64(int64_t src, int64_t dst) { if (src == dst) { return 0; @@ -57,13 +56,12 @@ static inline uint32_t cc_calc_ltgt_64(CPUS390XState *env, int64_t src, } } -static inline uint32_t cc_calc_ltgt0_64(CPUS390XState *env, int64_t dst) +static uint32_t cc_calc_ltgt0_64(int64_t dst) { - return cc_calc_ltgt_64(env, dst, 0); + return cc_calc_ltgt_64(dst, 0); } -static inline uint32_t cc_calc_ltugtu_32(CPUS390XState *env, uint32_t src, - uint32_t dst) +static uint32_t cc_calc_ltugtu_32(uint32_t src, uint32_t dst) { if (src == dst) { return 0; @@ -74,8 +72,7 @@ static inline uint32_t cc_calc_ltugtu_32(CPUS390XState *env, uint32_t src, } } -static inline uint32_t cc_calc_ltugtu_64(CPUS390XState *env, uint64_t src, - uint64_t dst) +static uint32_t cc_calc_ltugtu_64(uint64_t src, uint64_t dst) { if (src == dst) { return 0; @@ -86,13 +83,11 @@ static inline uint32_t cc_calc_ltugtu_64(CPUS390XState *env, uint64_t src, } } -static inline uint32_t cc_calc_tm_32(CPUS390XState *env, uint32_t val, - uint32_t mask) +static uint32_t cc_calc_tm_32(uint32_t val, uint32_t mask) { - uint16_t r = val & mask; + uint32_t r = val & mask; - HELPER_LOG("%s: val 0x%x mask 0x%x\n", __func__, val, mask); - if (r == 0 || mask == 0) { + if (r == 0) { return 0; } else if (r == mask) { return 3; @@ -101,23 +96,17 @@ static inline uint32_t cc_calc_tm_32(CPUS390XState *env, uint32_t val, } } -/* set condition code for test under mask */ -static inline uint32_t cc_calc_tm_64(CPUS390XState *env, uint64_t val, - uint32_t mask) +static uint32_t cc_calc_tm_64(uint64_t val, uint64_t mask) { - uint16_t r = val & mask; + uint64_t r = val & mask; - HELPER_LOG("%s: val 0x%lx mask 0x%x r 0x%x\n", __func__, val, mask, r); - if (r == 0 || mask == 0) { + if (r == 0) { return 0; } else if (r == mask) { return 3; } else { - while (!(mask & 0x8000)) { - mask <<= 1; - val <<= 1; - } - if (val & 0x8000) { + int top = clz64(mask); + if ((int64_t)(val << top) < 0) { return 2; } else { return 1; @@ -125,13 +114,12 @@ static inline uint32_t cc_calc_tm_64(CPUS390XState *env, uint64_t val, } } -static inline uint32_t cc_calc_nz(CPUS390XState *env, uint64_t dst) +static uint32_t cc_calc_nz(uint64_t dst) { return !!dst; } -static inline uint32_t cc_calc_add_64(CPUS390XState *env, int64_t a1, - int64_t a2, int64_t ar) +static uint32_t cc_calc_add_64(int64_t a1, int64_t a2, int64_t ar) { if ((a1 > 0 && a2 > 0 && ar < 0) || (a1 < 0 && a2 < 0 && ar > 0)) { return 3; /* overflow */ @@ -146,26 +134,22 @@ static inline uint32_t cc_calc_add_64(CPUS390XState *env, int64_t a1, } } -static inline uint32_t cc_calc_addu_64(CPUS390XState *env, uint64_t a1, - uint64_t a2, uint64_t ar) +static uint32_t cc_calc_addu_64(uint64_t a1, uint64_t a2, uint64_t ar) { - if (ar == 0) { - if (a1) { - return 2; - } else { - return 0; - } - } else { - if (ar < a1 || ar < a2) { - return 3; - } else { - return 1; - } - } + return (ar != 0) + 2 * (ar < a1); } -static inline uint32_t cc_calc_sub_64(CPUS390XState *env, int64_t a1, - int64_t a2, int64_t ar) +static uint32_t cc_calc_addc_64(uint64_t a1, uint64_t a2, uint64_t ar) +{ + /* Recover a2 + carry_in. */ + uint64_t a2c = ar - a1; + /* Check for a2+carry_in overflow, then a1+a2c overflow. */ + int carry_out = (a2c < a2) || (ar < a1); + + return (ar != 0) + 2 * carry_out; +} + +static uint32_t cc_calc_sub_64(int64_t a1, int64_t a2, int64_t ar) { if ((a1 > 0 && a2 < 0 && ar < 0) || (a1 < 0 && a2 > 0 && ar > 0)) { return 3; /* overflow */ @@ -180,8 +164,7 @@ static inline uint32_t cc_calc_sub_64(CPUS390XState *env, int64_t a1, } } -static inline uint32_t cc_calc_subu_64(CPUS390XState *env, uint64_t a1, - uint64_t a2, uint64_t ar) +static uint32_t cc_calc_subu_64(uint64_t a1, uint64_t a2, uint64_t ar) { if (ar == 0) { return 2; @@ -194,7 +177,25 @@ static inline uint32_t cc_calc_subu_64(CPUS390XState *env, uint64_t a1, } } -static inline uint32_t cc_calc_abs_64(CPUS390XState *env, int64_t dst) +static uint32_t cc_calc_subb_64(uint64_t a1, uint64_t a2, uint64_t ar) +{ + /* We had borrow-in if normal subtraction isn't equal. */ + int borrow_in = ar - (a1 - a2); + int borrow_out; + + /* If a2 was ULONG_MAX, and borrow_in, then a2 is logically 65 bits, + and we must have had borrow out. */ + if (borrow_in && a2 == (uint64_t)-1) { + borrow_out = 1; + } else { + a2 += borrow_in; + borrow_out = (a2 > a1); + } + + return (ar != 0) + 2 * !borrow_out; +} + +static uint32_t cc_calc_abs_64(int64_t dst) { if ((uint64_t)dst == 0x8000000000000000ULL) { return 3; @@ -205,12 +206,12 @@ static inline uint32_t cc_calc_abs_64(CPUS390XState *env, int64_t dst) } } -static inline uint32_t cc_calc_nabs_64(CPUS390XState *env, int64_t dst) +static uint32_t cc_calc_nabs_64(int64_t dst) { return !!dst; } -static inline uint32_t cc_calc_comp_64(CPUS390XState *env, int64_t dst) +static uint32_t cc_calc_comp_64(int64_t dst) { if ((uint64_t)dst == 0x8000000000000000ULL) { return 3; @@ -224,8 +225,7 @@ static inline uint32_t cc_calc_comp_64(CPUS390XState *env, int64_t dst) } -static inline uint32_t cc_calc_add_32(CPUS390XState *env, int32_t a1, - int32_t a2, int32_t ar) +static uint32_t cc_calc_add_32(int32_t a1, int32_t a2, int32_t ar) { if ((a1 > 0 && a2 > 0 && ar < 0) || (a1 < 0 && a2 < 0 && ar > 0)) { return 3; /* overflow */ @@ -240,26 +240,22 @@ static inline uint32_t cc_calc_add_32(CPUS390XState *env, int32_t a1, } } -static inline uint32_t cc_calc_addu_32(CPUS390XState *env, uint32_t a1, - uint32_t a2, uint32_t ar) +static uint32_t cc_calc_addu_32(uint32_t a1, uint32_t a2, uint32_t ar) { - if (ar == 0) { - if (a1) { - return 2; - } else { - return 0; - } - } else { - if (ar < a1 || ar < a2) { - return 3; - } else { - return 1; - } - } + return (ar != 0) + 2 * (ar < a1); } -static inline uint32_t cc_calc_sub_32(CPUS390XState *env, int32_t a1, - int32_t a2, int32_t ar) +static uint32_t cc_calc_addc_32(uint32_t a1, uint32_t a2, uint32_t ar) +{ + /* Recover a2 + carry_in. */ + uint32_t a2c = ar - a1; + /* Check for a2+carry_in overflow, then a1+a2c overflow. */ + int carry_out = (a2c < a2) || (ar < a1); + + return (ar != 0) + 2 * carry_out; +} + +static uint32_t cc_calc_sub_32(int32_t a1, int32_t a2, int32_t ar) { if ((a1 > 0 && a2 < 0 && ar < 0) || (a1 < 0 && a2 > 0 && ar > 0)) { return 3; /* overflow */ @@ -274,8 +270,7 @@ static inline uint32_t cc_calc_sub_32(CPUS390XState *env, int32_t a1, } } -static inline uint32_t cc_calc_subu_32(CPUS390XState *env, uint32_t a1, - uint32_t a2, uint32_t ar) +static uint32_t cc_calc_subu_32(uint32_t a1, uint32_t a2, uint32_t ar) { if (ar == 0) { return 2; @@ -288,7 +283,25 @@ static inline uint32_t cc_calc_subu_32(CPUS390XState *env, uint32_t a1, } } -static inline uint32_t cc_calc_abs_32(CPUS390XState *env, int32_t dst) +static uint32_t cc_calc_subb_32(uint32_t a1, uint32_t a2, uint32_t ar) +{ + /* We had borrow-in if normal subtraction isn't equal. */ + int borrow_in = ar - (a1 - a2); + int borrow_out; + + /* If a2 was UINT_MAX, and borrow_in, then a2 is logically 65 bits, + and we must have had borrow out. */ + if (borrow_in && a2 == (uint32_t)-1) { + borrow_out = 1; + } else { + a2 += borrow_in; + borrow_out = (a2 > a1); + } + + return (ar != 0) + 2 * !borrow_out; +} + +static uint32_t cc_calc_abs_32(int32_t dst) { if ((uint32_t)dst == 0x80000000UL) { return 3; @@ -299,12 +312,12 @@ static inline uint32_t cc_calc_abs_32(CPUS390XState *env, int32_t dst) } } -static inline uint32_t cc_calc_nabs_32(CPUS390XState *env, int32_t dst) +static uint32_t cc_calc_nabs_32(int32_t dst) { return !!dst; } -static inline uint32_t cc_calc_comp_32(CPUS390XState *env, int32_t dst) +static uint32_t cc_calc_comp_32(int32_t dst) { if ((uint32_t)dst == 0x80000000UL) { return 3; @@ -318,69 +331,80 @@ static inline uint32_t cc_calc_comp_32(CPUS390XState *env, int32_t dst) } /* calculate condition code for insert character under mask insn */ -static inline uint32_t cc_calc_icm_32(CPUS390XState *env, uint32_t mask, - uint32_t val) +static uint32_t cc_calc_icm(uint64_t mask, uint64_t val) { - uint32_t cc; - - HELPER_LOG("%s: mask 0x%x val %d\n", __func__, mask, val); - if (mask == 0xf) { - if (!val) { - return 0; - } else if (val & 0x80000000) { + if ((val & mask) == 0) { + return 0; + } else { + int top = clz64(mask); + if ((int64_t)(val << top) < 0) { return 1; } else { return 2; } } - - if (!val || !mask) { - cc = 0; - } else { - while (mask != 1) { - mask >>= 1; - val >>= 8; - } - if (val & 0x80) { - cc = 1; - } else { - cc = 2; - } - } - return cc; } -static inline uint32_t cc_calc_slag(CPUS390XState *env, uint64_t src, - uint64_t shift) +static uint32_t cc_calc_sla_32(uint32_t src, int shift) { - uint64_t mask = ((1ULL << shift) - 1ULL) << (64 - shift); - uint64_t match, r; + uint32_t mask = ((1U << shift) - 1U) << (32 - shift); + uint32_t sign = 1U << 31; + uint32_t match; + int32_t r; - /* check if the sign bit stays the same */ - if (src & (1ULL << 63)) { + /* Check if the sign bit stays the same. */ + if (src & sign) { match = mask; } else { match = 0; } - if ((src & mask) != match) { - /* overflow */ + /* Overflow. */ return 3; } - r = ((src << shift) & ((1ULL << 63) - 1)) | (src & (1ULL << 63)); - - if ((int64_t)r == 0) { + r = ((src << shift) & ~sign) | (src & sign); + if (r == 0) { return 0; - } else if ((int64_t)r < 0) { + } else if (r < 0) { return 1; } - return 2; } +static uint32_t cc_calc_sla_64(uint64_t src, int shift) +{ + uint64_t mask = ((1ULL << shift) - 1ULL) << (64 - shift); + uint64_t sign = 1ULL << 63; + uint64_t match; + int64_t r; -static inline uint32_t do_calc_cc(CPUS390XState *env, uint32_t cc_op, + /* Check if the sign bit stays the same. */ + if (src & sign) { + match = mask; + } else { + match = 0; + } + if ((src & mask) != match) { + /* Overflow. */ + return 3; + } + + r = ((src << shift) & ~sign) | (src & sign); + if (r == 0) { + return 0; + } else if (r < 0) { + return 1; + } + return 2; +} + +static uint32_t cc_calc_flogr(uint64_t dst) +{ + return dst ? 2 : 0; +} + +static uint32_t do_calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst, uint64_t vr) { uint32_t r = 0; @@ -394,95 +418,110 @@ static inline uint32_t do_calc_cc(CPUS390XState *env, uint32_t cc_op, r = cc_op; break; case CC_OP_LTGT0_32: - r = cc_calc_ltgt0_32(env, dst); + r = cc_calc_ltgt0_32(dst); break; case CC_OP_LTGT0_64: - r = cc_calc_ltgt0_64(env, dst); + r = cc_calc_ltgt0_64(dst); break; case CC_OP_LTGT_32: - r = cc_calc_ltgt_32(env, src, dst); + r = cc_calc_ltgt_32(src, dst); break; case CC_OP_LTGT_64: - r = cc_calc_ltgt_64(env, src, dst); + r = cc_calc_ltgt_64(src, dst); break; case CC_OP_LTUGTU_32: - r = cc_calc_ltugtu_32(env, src, dst); + r = cc_calc_ltugtu_32(src, dst); break; case CC_OP_LTUGTU_64: - r = cc_calc_ltugtu_64(env, src, dst); + r = cc_calc_ltugtu_64(src, dst); break; case CC_OP_TM_32: - r = cc_calc_tm_32(env, src, dst); + r = cc_calc_tm_32(src, dst); break; case CC_OP_TM_64: - r = cc_calc_tm_64(env, src, dst); + r = cc_calc_tm_64(src, dst); break; case CC_OP_NZ: - r = cc_calc_nz(env, dst); + r = cc_calc_nz(dst); break; case CC_OP_ADD_64: - r = cc_calc_add_64(env, src, dst, vr); + r = cc_calc_add_64(src, dst, vr); break; case CC_OP_ADDU_64: - r = cc_calc_addu_64(env, src, dst, vr); + r = cc_calc_addu_64(src, dst, vr); + break; + case CC_OP_ADDC_64: + r = cc_calc_addc_64(src, dst, vr); break; case CC_OP_SUB_64: - r = cc_calc_sub_64(env, src, dst, vr); + r = cc_calc_sub_64(src, dst, vr); break; case CC_OP_SUBU_64: - r = cc_calc_subu_64(env, src, dst, vr); + r = cc_calc_subu_64(src, dst, vr); + break; + case CC_OP_SUBB_64: + r = cc_calc_subb_64(src, dst, vr); break; case CC_OP_ABS_64: - r = cc_calc_abs_64(env, dst); + r = cc_calc_abs_64(dst); break; case CC_OP_NABS_64: - r = cc_calc_nabs_64(env, dst); + r = cc_calc_nabs_64(dst); break; case CC_OP_COMP_64: - r = cc_calc_comp_64(env, dst); + r = cc_calc_comp_64(dst); break; case CC_OP_ADD_32: - r = cc_calc_add_32(env, src, dst, vr); + r = cc_calc_add_32(src, dst, vr); break; case CC_OP_ADDU_32: - r = cc_calc_addu_32(env, src, dst, vr); + r = cc_calc_addu_32(src, dst, vr); + break; + case CC_OP_ADDC_32: + r = cc_calc_addc_32(src, dst, vr); break; case CC_OP_SUB_32: - r = cc_calc_sub_32(env, src, dst, vr); + r = cc_calc_sub_32(src, dst, vr); break; case CC_OP_SUBU_32: - r = cc_calc_subu_32(env, src, dst, vr); + r = cc_calc_subu_32(src, dst, vr); + break; + case CC_OP_SUBB_32: + r = cc_calc_subb_32(src, dst, vr); break; case CC_OP_ABS_32: - r = cc_calc_abs_64(env, dst); + r = cc_calc_abs_32(dst); break; case CC_OP_NABS_32: - r = cc_calc_nabs_64(env, dst); + r = cc_calc_nabs_32(dst); break; case CC_OP_COMP_32: - r = cc_calc_comp_32(env, dst); + r = cc_calc_comp_32(dst); break; case CC_OP_ICM: - r = cc_calc_icm_32(env, src, dst); + r = cc_calc_icm(src, dst); break; - case CC_OP_SLAG: - r = cc_calc_slag(env, src, dst); + case CC_OP_SLA_32: + r = cc_calc_sla_32(src, dst); + break; + case CC_OP_SLA_64: + r = cc_calc_sla_64(src, dst); + break; + case CC_OP_FLOGR: + r = cc_calc_flogr(dst); break; - case CC_OP_LTGT_F32: - r = set_cc_f32(env, src, dst); - break; - case CC_OP_LTGT_F64: - r = set_cc_f64(env, src, dst); - break; case CC_OP_NZ_F32: r = set_cc_nz_f32(dst); break; case CC_OP_NZ_F64: r = set_cc_nz_f64(dst); break; + case CC_OP_NZ_F128: + r = set_cc_nz_f128(make_float128(src, dst)); + break; default: cpu_abort(env, "Unknown CC operation: %s\n", cc_name(cc_op)); @@ -505,18 +544,6 @@ uint32_t HELPER(calc_cc)(CPUS390XState *env, uint32_t cc_op, uint64_t src, return do_calc_cc(env, cc_op, src, dst, vr); } -/* insert psw mask and condition code into r1 */ -void HELPER(ipm)(CPUS390XState *env, uint32_t cc, uint32_t r1) -{ - uint64_t r = env->regs[r1]; - - r &= 0xffffffff00ffffffULL; - r |= (cc << 28) | ((env->psw.mask >> 40) & 0xf); - env->regs[r1] = r; - HELPER_LOG("%s: cc %d psw.mask 0x%lx r1 0x%lx\n", __func__, - cc, env->psw.mask, r); -} - #ifndef CONFIG_USER_ONLY void HELPER(load_psw)(CPUS390XState *env, uint64_t mask, uint64_t addr) { diff --git a/target-s390x/cpu-qom.h b/target-s390x/cpu-qom.h index d54e4a2ee2..34d45c262b 100644 --- a/target-s390x/cpu-qom.h +++ b/target-s390x/cpu-qom.h @@ -34,6 +34,7 @@ /** * S390CPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. * * An S/390 CPU model. @@ -43,6 +44,7 @@ typedef struct S390CPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); } S390CPUClass; @@ -67,5 +69,8 @@ static inline S390CPU *s390_env_get_cpu(CPUS390XState *env) #define ENV_GET_CPU(e) CPU(s390_env_get_cpu(e)) +#define ENV_OFFSET offsetof(S390CPU, env) + +void s390_cpu_do_interrupt(CPUState *cpu); #endif diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c index 249f063d94..23fe51f0f4 100644 --- a/target-s390x/cpu.c +++ b/target-s390x/cpu.c @@ -4,6 +4,7 @@ * Copyright (c) 2009 Ulrich Hecht * Copyright (c) 2011 Alexander Graf * Copyright (c) 2012 SUSE LINUX Products GmbH + * Copyright (c) 2012 IBM Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,12 +19,44 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * + * Contributions after 2012-12-11 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. */ #include "cpu.h" #include "qemu-common.h" #include "qemu/timer.h" +#include "hw/hw.h" +#ifndef CONFIG_USER_ONLY +#include "sysemu/arch_init.h" +#endif +#define CR0_RESET 0xE0UL +#define CR14_RESET 0xC2000000UL; + +/* generate CPU information for cpu -? */ +void s390_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ +#ifdef CONFIG_KVM + (*cpu_fprintf)(f, "s390 %16s\n", "host"); +#endif +} + +#ifndef CONFIG_USER_ONLY +CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) +{ + CpuDefinitionInfoList *entry; + CpuDefinitionInfo *info; + + info = g_malloc0(sizeof(*info)); + info->name = g_strdup("host"); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + + return entry; +} +#endif /* CPUClass::reset() */ static void s390_cpu_reset(CPUState *s) @@ -33,49 +66,111 @@ static void s390_cpu_reset(CPUState *s) CPUS390XState *env = &cpu->env; if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); + qemu_log("CPU Reset (CPU %d)\n", s->cpu_index); log_cpu_state(env, 0); } + s390_del_running_cpu(cpu); + scc->parent_reset(s); memset(env, 0, offsetof(CPUS390XState, breakpoints)); - /* FIXME: reset vector? */ + + /* architectured initial values for CR 0 and 14 */ + env->cregs[0] = CR0_RESET; + env->cregs[14] = CR14_RESET; + /* set halted to 1 to make sure we can add the cpu in + * s390_ipl_cpu code, where CPUState::halted is set back to 0 + * after incrementing the cpu counter */ +#if !defined(CONFIG_USER_ONLY) + s->halted = 1; +#endif tlb_flush(env, 1); - s390_add_running_cpu(env); +} + +#if !defined(CONFIG_USER_ONLY) +static void s390_cpu_machine_reset_cb(void *opaque) +{ + S390CPU *cpu = opaque; + + cpu_reset(CPU(cpu)); +} +#endif + +static void s390_cpu_realizefn(DeviceState *dev, Error **errp) +{ + S390CPU *cpu = S390_CPU(dev); + S390CPUClass *scc = S390_CPU_GET_CLASS(dev); + + qemu_init_vcpu(&cpu->env); + cpu_reset(CPU(cpu)); + + scc->parent_realize(dev, errp); } static void s390_cpu_initfn(Object *obj) { + CPUState *cs = CPU(obj); S390CPU *cpu = S390_CPU(obj); CPUS390XState *env = &cpu->env; + static bool inited; static int cpu_num = 0; #if !defined(CONFIG_USER_ONLY) struct tm tm; #endif + cs->env_ptr = env; cpu_exec_init(env); #if !defined(CONFIG_USER_ONLY) + qemu_register_reset(s390_cpu_machine_reset_cb, cpu); qemu_get_timedate(&tm, 0); env->tod_offset = TOD_UNIX_EPOCH + (time2tod(mktimegm(&tm)) * 1000000000ULL); env->tod_basetime = 0; env->tod_timer = qemu_new_timer_ns(vm_clock, s390x_tod_timer, cpu); env->cpu_timer = qemu_new_timer_ns(vm_clock, s390x_cpu_timer, cpu); + /* set CPUState::halted state to 1 to avoid decrementing the running + * cpu counter in s390_cpu_reset to a negative number at + * initial ipl */ + cs->halted = 1; #endif env->cpu_num = cpu_num++; env->ext_index = -1; - cpu_reset(CPU(cpu)); + if (tcg_enabled() && !inited) { + inited = true; + s390x_translate_init(); + } } +static void s390_cpu_finalize(Object *obj) +{ +#if !defined(CONFIG_USER_ONLY) + S390CPU *cpu = S390_CPU(obj); + + qemu_unregister_reset(s390_cpu_machine_reset_cb, cpu); +#endif +} + +static const VMStateDescription vmstate_s390_cpu = { + .name = "cpu", + .unmigratable = 1, +}; + static void s390_cpu_class_init(ObjectClass *oc, void *data) { S390CPUClass *scc = S390_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(scc); + DeviceClass *dc = DEVICE_CLASS(oc); + + scc->parent_realize = dc->realize; + dc->realize = s390_cpu_realizefn; scc->parent_reset = cc->reset; cc->reset = s390_cpu_reset; + + cc->do_interrupt = s390_cpu_do_interrupt; + dc->vmsd = &vmstate_s390_cpu; } static const TypeInfo s390_cpu_type_info = { @@ -83,6 +178,7 @@ static const TypeInfo s390_cpu_type_info = { .parent = TYPE_CPU, .instance_size = sizeof(S390CPU), .instance_init = s390_cpu_initfn, + .instance_finalize = s390_cpu_finalize, .abstract = false, .class_size = sizeof(S390CPUClass), .class_init = s390_cpu_class_init, diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index dda0b9af66..e351005901 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -13,7 +13,10 @@ * 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 + * Contributions after 2012-10-29 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + * + * You should have received a copy of the GNU (Lesser) General Public * License along with this library; if not, see . */ #ifndef CPU_S390X_H @@ -47,6 +50,11 @@ #define MMU_USER_IDX 1 #define MAX_EXT_QUEUE 16 +#define MAX_IO_QUEUE 16 +#define MAX_MCHK_QUEUE 16 + +#define PSW_MCHK_MASK 0x0004000000000000 +#define PSW_IO_MASK 0x0200000000000000 typedef struct PSW { uint64_t mask; @@ -59,18 +67,32 @@ typedef struct ExtQueue { uint32_t param64; } ExtQueue; +typedef struct IOIntQueue { + uint16_t id; + uint16_t nr; + uint32_t parm; + uint32_t word; +} IOIntQueue; + +typedef struct MchkQueue { + uint16_t type; +} MchkQueue; + typedef struct CPUS390XState { - uint64_t regs[16]; /* GP registers */ - - uint32_t aregs[16]; /* access registers */ - - uint32_t fpc; /* floating-point control register */ + uint64_t regs[16]; /* GP registers */ CPU_DoubleU fregs[16]; /* FP registers */ + uint32_t aregs[16]; /* access registers */ + + uint32_t fpc; /* floating-point control register */ + uint32_t cc_op; + float_status fpu_status; /* passed to softfloat lib */ + /* The low part of a 128-bit return, or remainder of a divide. */ + uint64_t retxl; + PSW psw; - uint32_t cc_op; uint64_t cc_src; uint64_t cc_dst; uint64_t cc_vr; @@ -79,17 +101,25 @@ typedef struct CPUS390XState { uint64_t psa; uint32_t int_pgm_code; - uint32_t int_pgm_ilc; + uint32_t int_pgm_ilen; uint32_t int_svc_code; - uint32_t int_svc_ilc; + uint32_t int_svc_ilen; uint64_t cregs[16]; /* control registers */ - int pending_int; ExtQueue ext_queue[MAX_EXT_QUEUE]; + IOIntQueue io_queue[MAX_IO_QUEUE][8]; + MchkQueue mchk_queue[MAX_MCHK_QUEUE]; + int pending_int; int ext_index; + int io_index[8]; + int mchk_index; + + uint64_t ckc; + uint64_t cputm; + uint32_t todpr; CPU_COMMON @@ -113,10 +143,13 @@ static inline void cpu_clone_regs(CPUS390XState *env, target_ulong newsp) if (newsp) { env->regs[15] = newsp; } - env->regs[0] = 0; + env->regs[2] = 0; } #endif +/* distinguish between 24 bit and 31 bit addressing */ +#define HIGH_ORDER_BIT 0x80000000 + /* Interrupt Codes */ /* Program Interrupts */ #define PGM_OPERATION 0x0001 @@ -253,31 +286,35 @@ static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc, ((env->psw.mask & PSW_MASK_32) ? FLAG_MASK_32 : 0); } -static inline int get_ilc(uint8_t opc) +/* While the PoO talks about ILC (a number between 1-3) what is actually + stored in LowCore is shifted left one bit (an even between 2-6). As + this is the actual length of the insn and therefore more useful, that + is what we want to pass around and manipulate. To make sure that we + have applied this distinction universally, rename the "ILC" to "ILEN". */ +static inline int get_ilen(uint8_t opc) { switch (opc >> 6) { case 0: - return 1; + return 2; case 1: case 2: - return 2; - case 3: - return 3; + return 4; + default: + return 6; } - - return 0; } -#define ILC_LATER 0x20 -#define ILC_LATER_INC 0x21 -#define ILC_LATER_INC_2 0x22 - +#ifndef CONFIG_USER_ONLY +/* In several cases of runtime exceptions, we havn't recorded the true + instruction length. Use these codes when raising exceptions in order + to re-compute the length by examining the insn in memory. */ +#define ILEN_LATER 0x20 +#define ILEN_LATER_INC 0x21 +#endif S390CPU *cpu_s390x_init(const char *cpu_model); void s390x_translate_init(void); int cpu_s390x_exec(CPUS390XState *s); -void cpu_s390x_close(CPUS390XState *s); -void do_interrupt (CPUS390XState *env); /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero @@ -288,37 +325,56 @@ int cpu_s390x_handle_mmu_fault (CPUS390XState *env, target_ulong address, int rw int mmu_idx); #define cpu_handle_mmu_fault cpu_s390x_handle_mmu_fault +#include "ioinst.h" #ifndef CONFIG_USER_ONLY +void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len, + int is_write); +void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len, + int is_write); +static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb) +{ + hwaddr addr = 0; + uint8_t reg; + + reg = ipb >> 28; + if (reg > 0) { + addr = env->regs[reg]; + } + addr += (ipb >> 16) & 0xfff; + + return addr; +} + void s390x_tod_timer(void *opaque); void s390x_cpu_timer(void *opaque); -int s390_virtio_hypercall(CPUS390XState *env, uint64_t mem, uint64_t hypercall); +int s390_virtio_hypercall(CPUS390XState *env); #ifdef CONFIG_KVM -void kvm_s390_interrupt(CPUS390XState *env, int type, uint32_t code); -void kvm_s390_virtio_irq(CPUS390XState *env, int config_change, uint64_t token); -void kvm_s390_interrupt_internal(CPUS390XState *env, int type, uint32_t parm, +void kvm_s390_interrupt(S390CPU *cpu, int type, uint32_t code); +void kvm_s390_virtio_irq(S390CPU *cpu, int config_change, uint64_t token); +void kvm_s390_interrupt_internal(S390CPU *cpu, int type, uint32_t parm, uint64_t parm64, int vm); #else -static inline void kvm_s390_interrupt(CPUS390XState *env, int type, uint32_t code) +static inline void kvm_s390_interrupt(S390CPU *cpu, int type, uint32_t code) { } -static inline void kvm_s390_virtio_irq(CPUS390XState *env, int config_change, +static inline void kvm_s390_virtio_irq(S390CPU *cpu, int config_change, uint64_t token) { } -static inline void kvm_s390_interrupt_internal(CPUS390XState *env, int type, +static inline void kvm_s390_interrupt_internal(S390CPU *cpu, int type, uint32_t parm, uint64_t parm64, int vm) { } #endif S390CPU *s390_cpu_addr2state(uint16_t cpu_addr); -void s390_add_running_cpu(CPUS390XState *env); -unsigned s390_del_running_cpu(CPUS390XState *env); +void s390_add_running_cpu(S390CPU *cpu); +unsigned s390_del_running_cpu(S390CPU *cpu); /* service interrupts are floating therefore we must not pass an cpustate */ void s390_sclp_extint(uint32_t parm); @@ -327,11 +383,11 @@ void s390_sclp_extint(uint32_t parm); extern const hwaddr virtio_size; #else -static inline void s390_add_running_cpu(CPUS390XState *env) +static inline void s390_add_running_cpu(S390CPU *cpu) { } -static inline unsigned s390_del_running_cpu(CPUS390XState *env) +static inline unsigned s390_del_running_cpu(S390CPU *cpu) { return 0; } @@ -339,6 +395,114 @@ static inline unsigned s390_del_running_cpu(CPUS390XState *env) void cpu_lock(void); void cpu_unlock(void); +typedef struct SubchDev SubchDev; + +#ifndef CONFIG_USER_ONLY +SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, + uint16_t schid); +bool css_subch_visible(SubchDev *sch); +void css_conditional_io_interrupt(SubchDev *sch); +int css_do_stsch(SubchDev *sch, SCHIB *schib); +bool css_schid_final(int m, uint8_t cssid, uint8_t ssid, uint16_t schid); +int css_do_msch(SubchDev *sch, SCHIB *schib); +int css_do_xsch(SubchDev *sch); +int css_do_csch(SubchDev *sch); +int css_do_hsch(SubchDev *sch); +int css_do_ssch(SubchDev *sch, ORB *orb); +int css_do_tsch(SubchDev *sch, IRB *irb); +int css_do_stcrw(CRW *crw); +int css_do_tpi(IOIntCode *int_code, int lowcore); +int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, uint8_t l_chpid, + int rfmt, void *buf); +void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo); +int css_enable_mcsse(void); +int css_enable_mss(void); +int css_do_rsch(SubchDev *sch); +int css_do_rchp(uint8_t cssid, uint8_t chpid); +bool css_present(uint8_t cssid); +#else +static inline SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, + uint16_t schid) +{ + return NULL; +} +static inline bool css_subch_visible(SubchDev *sch) +{ + return false; +} +static inline void css_conditional_io_interrupt(SubchDev *sch) +{ +} +static inline int css_do_stsch(SubchDev *sch, SCHIB *schib) +{ + return -ENODEV; +} +static inline bool css_schid_final(uint8_t cssid, uint8_t ssid, uint16_t schid) +{ + return true; +} +static inline int css_do_msch(SubchDev *sch, SCHIB *schib) +{ + return -ENODEV; +} +static inline int css_do_xsch(SubchDev *sch) +{ + return -ENODEV; +} +static inline int css_do_csch(SubchDev *sch) +{ + return -ENODEV; +} +static inline int css_do_hsch(SubchDev *sch) +{ + return -ENODEV; +} +static inline int css_do_ssch(SubchDev *sch, ORB *orb) +{ + return -ENODEV; +} +static inline int css_do_tsch(SubchDev *sch, IRB *irb) +{ + return -ENODEV; +} +static inline int css_do_stcrw(CRW *crw) +{ + return 1; +} +static inline int css_do_tpi(IOIntCode *int_code, int lowcore) +{ + return 0; +} +static inline int css_collect_chp_desc(int m, uint8_t cssid, uint8_t f_chpid, + int rfmt, uint8_t l_chpid, void *buf) +{ + return 0; +} +static inline void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo) +{ +} +static inline int css_enable_mss(void) +{ + return -EINVAL; +} +static inline int css_enable_mcsse(void) +{ + return -EINVAL; +} +static inline int css_do_rsch(SubchDev *sch) +{ + return -ENODEV; +} +static inline int css_do_rchp(uint8_t cssid, uint8_t chpid) +{ + return -ENODEV; +} +static inline bool css_present(uint8_t cssid) +{ + return false; +} +#endif + static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls) { env->aregs[0] = newtls >> 32; @@ -350,26 +514,22 @@ static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls) #define cpu_gen_code cpu_s390x_gen_code #define cpu_signal_handler cpu_s390x_signal_handler +void s390_cpu_list(FILE *f, fprintf_function cpu_fprintf); +#define cpu_list s390_cpu_list + #include "exec/exec-all.h" -#ifdef CONFIG_USER_ONLY - -#define EXCP_OPEX 1 /* operation exception (sigill) */ -#define EXCP_SVC 2 /* supervisor call (syscall) */ -#define EXCP_ADDR 5 /* addressing exception */ -#define EXCP_SPEC 6 /* specification exception */ - -#else - #define EXCP_EXT 1 /* external interrupt */ #define EXCP_SVC 2 /* supervisor call (syscall) */ #define EXCP_PGM 3 /* program interruption */ - -#endif /* CONFIG_USER_ONLY */ +#define EXCP_IO 7 /* I/O interrupt */ +#define EXCP_MCHK 8 /* machine check */ #define INTERRUPT_EXT (1 << 0) #define INTERRUPT_TOD (1 << 1) #define INTERRUPT_CPUTIMER (1 << 2) +#define INTERRUPT_IO (1 << 3) +#define INTERRUPT_MCHK (1 << 4) /* Program Status Word. */ #define S390_PSWM_REGNUM 0 @@ -430,79 +590,6 @@ static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls) /* Total. */ #define S390_NUM_REGS 51 -/* Pseudo registers -- PC and condition code. */ -#define S390_PC_REGNUM S390_NUM_REGS -#define S390_CC_REGNUM (S390_NUM_REGS+1) -#define S390_NUM_PSEUDO_REGS 2 -#define S390_NUM_TOTAL_REGS (S390_NUM_REGS+2) - - - -/* Program Status Word. */ -#define S390_PSWM_REGNUM 0 -#define S390_PSWA_REGNUM 1 -/* General Purpose Registers. */ -#define S390_R0_REGNUM 2 -#define S390_R1_REGNUM 3 -#define S390_R2_REGNUM 4 -#define S390_R3_REGNUM 5 -#define S390_R4_REGNUM 6 -#define S390_R5_REGNUM 7 -#define S390_R6_REGNUM 8 -#define S390_R7_REGNUM 9 -#define S390_R8_REGNUM 10 -#define S390_R9_REGNUM 11 -#define S390_R10_REGNUM 12 -#define S390_R11_REGNUM 13 -#define S390_R12_REGNUM 14 -#define S390_R13_REGNUM 15 -#define S390_R14_REGNUM 16 -#define S390_R15_REGNUM 17 -/* Access Registers. */ -#define S390_A0_REGNUM 18 -#define S390_A1_REGNUM 19 -#define S390_A2_REGNUM 20 -#define S390_A3_REGNUM 21 -#define S390_A4_REGNUM 22 -#define S390_A5_REGNUM 23 -#define S390_A6_REGNUM 24 -#define S390_A7_REGNUM 25 -#define S390_A8_REGNUM 26 -#define S390_A9_REGNUM 27 -#define S390_A10_REGNUM 28 -#define S390_A11_REGNUM 29 -#define S390_A12_REGNUM 30 -#define S390_A13_REGNUM 31 -#define S390_A14_REGNUM 32 -#define S390_A15_REGNUM 33 -/* Floating Point Control Word. */ -#define S390_FPC_REGNUM 34 -/* Floating Point Registers. */ -#define S390_F0_REGNUM 35 -#define S390_F1_REGNUM 36 -#define S390_F2_REGNUM 37 -#define S390_F3_REGNUM 38 -#define S390_F4_REGNUM 39 -#define S390_F5_REGNUM 40 -#define S390_F6_REGNUM 41 -#define S390_F7_REGNUM 42 -#define S390_F8_REGNUM 43 -#define S390_F9_REGNUM 44 -#define S390_F10_REGNUM 45 -#define S390_F11_REGNUM 46 -#define S390_F12_REGNUM 47 -#define S390_F13_REGNUM 48 -#define S390_F14_REGNUM 49 -#define S390_F15_REGNUM 50 -/* Total. */ -#define S390_NUM_REGS 51 - -/* Pseudo registers -- PC and condition code. */ -#define S390_PC_REGNUM S390_NUM_REGS -#define S390_CC_REGNUM (S390_NUM_REGS+1) -#define S390_NUM_PSEUDO_REGS 2 -#define S390_NUM_TOTAL_REGS (S390_NUM_REGS+2) - /* CC optimization */ enum cc_op { @@ -524,15 +611,19 @@ enum cc_op { CC_OP_ADD_64, /* overflow on add (64bit) */ CC_OP_ADDU_64, /* overflow on unsigned add (64bit) */ + CC_OP_ADDC_64, /* overflow on unsigned add-carry (64bit) */ CC_OP_SUB_64, /* overflow on subtraction (64bit) */ CC_OP_SUBU_64, /* overflow on unsigned subtraction (64bit) */ + CC_OP_SUBB_64, /* overflow on unsigned sub-borrow (64bit) */ CC_OP_ABS_64, /* sign eval on abs (64bit) */ CC_OP_NABS_64, /* sign eval on nabs (64bit) */ CC_OP_ADD_32, /* overflow on add (32bit) */ CC_OP_ADDU_32, /* overflow on unsigned add (32bit) */ + CC_OP_ADDC_32, /* overflow on unsigned add-carry (32bit) */ CC_OP_SUB_32, /* overflow on subtraction (32bit) */ CC_OP_SUBU_32, /* overflow on unsigned subtraction (32bit) */ + CC_OP_SUBB_32, /* overflow on unsigned sub-borrow (32bit) */ CC_OP_ABS_32, /* sign eval on abs (64bit) */ CC_OP_NABS_32, /* sign eval on nabs (64bit) */ @@ -542,14 +633,14 @@ enum cc_op { CC_OP_TM_32, /* test under mask (32bit) */ CC_OP_TM_64, /* test under mask (64bit) */ - CC_OP_LTGT_F32, /* FP compare (32bit) */ - CC_OP_LTGT_F64, /* FP compare (64bit) */ - CC_OP_NZ_F32, /* FP dst != 0 (32bit) */ CC_OP_NZ_F64, /* FP dst != 0 (64bit) */ + CC_OP_NZ_F128, /* FP dst != 0 (128bit) */ CC_OP_ICM, /* insert characters under mask */ - CC_OP_SLAG, /* Calculate shift left signed */ + CC_OP_SLA_32, /* Calculate shift left signed (32bit) */ + CC_OP_SLA_64, /* Calculate shift left signed (64bit) */ + CC_OP_FLOGR, /* find leftmost one */ CC_OP_MAX }; @@ -569,26 +660,31 @@ static const char *cc_names[] = { [CC_OP_LTGT0_64] = "CC_OP_LTGT0_64", [CC_OP_ADD_64] = "CC_OP_ADD_64", [CC_OP_ADDU_64] = "CC_OP_ADDU_64", + [CC_OP_ADDC_64] = "CC_OP_ADDC_64", [CC_OP_SUB_64] = "CC_OP_SUB_64", [CC_OP_SUBU_64] = "CC_OP_SUBU_64", + [CC_OP_SUBB_64] = "CC_OP_SUBB_64", [CC_OP_ABS_64] = "CC_OP_ABS_64", [CC_OP_NABS_64] = "CC_OP_NABS_64", [CC_OP_ADD_32] = "CC_OP_ADD_32", [CC_OP_ADDU_32] = "CC_OP_ADDU_32", + [CC_OP_ADDC_32] = "CC_OP_ADDC_32", [CC_OP_SUB_32] = "CC_OP_SUB_32", [CC_OP_SUBU_32] = "CC_OP_SUBU_32", + [CC_OP_SUBB_32] = "CC_OP_SUBB_32", [CC_OP_ABS_32] = "CC_OP_ABS_32", [CC_OP_NABS_32] = "CC_OP_NABS_32", [CC_OP_COMP_32] = "CC_OP_COMP_32", [CC_OP_COMP_64] = "CC_OP_COMP_64", [CC_OP_TM_32] = "CC_OP_TM_32", [CC_OP_TM_64] = "CC_OP_TM_64", - [CC_OP_LTGT_F32] = "CC_OP_LTGT_F32", - [CC_OP_LTGT_F64] = "CC_OP_LTGT_F64", [CC_OP_NZ_F32] = "CC_OP_NZ_F32", [CC_OP_NZ_F64] = "CC_OP_NZ_F64", + [CC_OP_NZ_F128] = "CC_OP_NZ_F128", [CC_OP_ICM] = "CC_OP_ICM", - [CC_OP_SLAG] = "CC_OP_SLAG", + [CC_OP_SLA_32] = "CC_OP_SLA_32", + [CC_OP_SLA_64] = "CC_OP_SLA_64", + [CC_OP_FLOGR] = "CC_OP_FLOGR", }; static inline const char *cc_name(int cc_op) @@ -605,9 +701,9 @@ typedef struct LowCore uint32_t ext_params; /* 0x080 */ uint16_t cpu_addr; /* 0x084 */ uint16_t ext_int_code; /* 0x086 */ - uint16_t svc_ilc; /* 0x088 */ + uint16_t svc_ilen; /* 0x088 */ uint16_t svc_code; /* 0x08a */ - uint16_t pgm_ilc; /* 0x08c */ + uint16_t pgm_ilen; /* 0x08c */ uint16_t pgm_code; /* 0x08e */ uint32_t data_exc_code; /* 0x090 */ uint16_t mon_class_num; /* 0x094 */ @@ -835,87 +931,6 @@ struct sysib_322 { #define SK_F (0x1 << 3) #define SK_ACC_MASK (0xf << 4) - -/* EBCDIC handling */ -static const uint8_t ebcdic2ascii[] = { - 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, - 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, - 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, - 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, - 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, - 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, - 0x87, 0xA4, 0x5B, 0x2E, 0x3C, 0x28, 0x2B, 0x21, - 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, - 0x8D, 0xE1, 0x5D, 0x24, 0x2A, 0x29, 0x3B, 0x5E, - 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, - 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, - 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, - 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, - 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, - 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, - 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, - 0x9B, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, - 0xAB, 0x07, 0xAA, 0x7C, 0x07, 0x07, 0x07, 0x07, - 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, - 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, - 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, - 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07, -}; - -static const uint8_t ascii2ebcdic [] = { - 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, - 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, - 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, - 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, - 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, - 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, - 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, - 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, - 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, - 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, - 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF -}; - -static inline void ebcdic_put(uint8_t *p, const char *ascii, int len) -{ - int i; - - for (i = 0; i < len; i++) { - p[i] = ascii2ebcdic[(int)ascii[i]]; - } -} - #define SIGP_SENSE 0x01 #define SIGP_EXTERNAL_CALL 0x02 #define SIGP_EMERGENCY 0x03 @@ -958,9 +973,11 @@ static inline uint64_t time2tod(uint64_t ns) { return (ns << 9) / 125; } -static inline void cpu_inject_ext(CPUS390XState *env, uint32_t code, uint32_t param, +static inline void cpu_inject_ext(S390CPU *cpu, uint32_t code, uint32_t param, uint64_t param64) { + CPUS390XState *env = &cpu->env; + if (env->ext_index == MAX_EXT_QUEUE - 1) { /* ugh - can't queue anymore. Let's drop. */ return; @@ -974,14 +991,57 @@ static inline void cpu_inject_ext(CPUS390XState *env, uint32_t code, uint32_t pa env->ext_queue[env->ext_index].param64 = param64; env->pending_int |= INTERRUPT_EXT; - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); +} + +static inline void cpu_inject_io(S390CPU *cpu, uint16_t subchannel_id, + uint16_t subchannel_number, + uint32_t io_int_parm, uint32_t io_int_word) +{ + CPUS390XState *env = &cpu->env; + int isc = IO_INT_WORD_ISC(io_int_word); + + if (env->io_index[isc] == MAX_IO_QUEUE - 1) { + /* ugh - can't queue anymore. Let's drop. */ + return; + } + + env->io_index[isc]++; + assert(env->io_index[isc] < MAX_IO_QUEUE); + + env->io_queue[env->io_index[isc]][isc].id = subchannel_id; + env->io_queue[env->io_index[isc]][isc].nr = subchannel_number; + env->io_queue[env->io_index[isc]][isc].parm = io_int_parm; + env->io_queue[env->io_index[isc]][isc].word = io_int_word; + + env->pending_int |= INTERRUPT_IO; + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); +} + +static inline void cpu_inject_crw_mchk(S390CPU *cpu) +{ + CPUS390XState *env = &cpu->env; + + if (env->mchk_index == MAX_MCHK_QUEUE - 1) { + /* ugh - can't queue anymore. Let's drop. */ + return; + } + + env->mchk_index++; + assert(env->mchk_index < MAX_MCHK_QUEUE); + + env->mchk_queue[env->mchk_index].type = 1; + + env->pending_int |= INTERRUPT_MCHK; + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); } static inline bool cpu_has_work(CPUState *cpu) { - CPUS390XState *env = &S390_CPU(cpu)->env; + S390CPU *s390_cpu = S390_CPU(cpu); + CPUS390XState *env = &s390_cpu->env; - return (env->interrupt_request & CPU_INTERRUPT_HARD) && + return (cpu->interrupt_request & CPU_INTERRUPT_HARD) && (env->psw.mask & PSW_MASK_EXT); } @@ -991,12 +1051,61 @@ static inline void cpu_pc_from_tb(CPUS390XState *env, TranslationBlock* tb) } /* fpu_helper.c */ -uint32_t set_cc_f32(CPUS390XState *env, float32 v1, float32 v2); -uint32_t set_cc_f64(CPUS390XState *env, float64 v1, float64 v2); uint32_t set_cc_nz_f32(float32 v); uint32_t set_cc_nz_f64(float64 v); +uint32_t set_cc_nz_f128(float128 v); /* misc_helper.c */ -void program_interrupt(CPUS390XState *env, uint32_t code, int ilc); +void program_interrupt(CPUS390XState *env, uint32_t code, int ilen); +void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp, + uintptr_t retaddr); + +#include + +#ifdef CONFIG_KVM +void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id, + uint16_t subchannel_nr, uint32_t io_int_parm, + uint32_t io_int_word); +void kvm_s390_crw_mchk(S390CPU *cpu); +void kvm_s390_enable_css_support(S390CPU *cpu); +#else +static inline void kvm_s390_io_interrupt(S390CPU *cpu, + uint16_t subchannel_id, + uint16_t subchannel_nr, + uint32_t io_int_parm, + uint32_t io_int_word) +{ +} +static inline void kvm_s390_crw_mchk(S390CPU *cpu) +{ +} +static inline void kvm_s390_enable_css_support(S390CPU *cpu) +{ +} +#endif + +static inline void s390_io_interrupt(S390CPU *cpu, + uint16_t subchannel_id, + uint16_t subchannel_nr, + uint32_t io_int_parm, + uint32_t io_int_word) +{ + if (kvm_enabled()) { + kvm_s390_io_interrupt(cpu, subchannel_id, subchannel_nr, io_int_parm, + io_int_word); + } else { + cpu_inject_io(cpu, subchannel_id, subchannel_nr, io_int_parm, + io_int_word); + } +} + +static inline void s390_crw_mchk(S390CPU *cpu) +{ + if (kvm_enabled()) { + kvm_s390_crw_mchk(cpu); + } else { + cpu_inject_crw_mchk(cpu); + } +} #endif diff --git a/target-s390x/fpu_helper.c b/target-s390x/fpu_helper.c index 173f820428..94375b6a63 100644 --- a/target-s390x/fpu_helper.c +++ b/target-s390x/fpu_helper.c @@ -32,6 +32,52 @@ #define HELPER_LOG(x...) #endif +#define RET128(F) (env->retxl = F.low, F.high) + +#define convert_bit(mask, from, to) \ + (to < from \ + ? (mask / (from / to)) & to \ + : (mask & from) * (to / from)) + +static void ieee_exception(CPUS390XState *env, uint32_t dxc, uintptr_t retaddr) +{ + /* Install the DXC code. */ + env->fpc = (env->fpc & ~0xff00) | (dxc << 8); + /* Trap. */ + runtime_exception(env, PGM_DATA, retaddr); +} + +/* Should be called after any operation that may raise IEEE exceptions. */ +static void handle_exceptions(CPUS390XState *env, uintptr_t retaddr) +{ + unsigned s390_exc, qemu_exc; + + /* Get the exceptions raised by the current operation. Reset the + fpu_status contents so that the next operation has a clean slate. */ + qemu_exc = env->fpu_status.float_exception_flags; + if (qemu_exc == 0) { + return; + } + env->fpu_status.float_exception_flags = 0; + + /* Convert softfloat exception bits to s390 exception bits. */ + s390_exc = 0; + s390_exc |= convert_bit(qemu_exc, float_flag_invalid, 0x80); + s390_exc |= convert_bit(qemu_exc, float_flag_divbyzero, 0x40); + s390_exc |= convert_bit(qemu_exc, float_flag_overflow, 0x20); + s390_exc |= convert_bit(qemu_exc, float_flag_underflow, 0x10); + s390_exc |= convert_bit(qemu_exc, float_flag_inexact, 0x08); + + /* Install the exceptions that we raised. */ + env->fpc |= s390_exc << 16; + + /* Send signals for enabled exceptions. */ + s390_exc &= env->fpc >> 24; + if (s390_exc) { + ieee_exception(env, s390_exc, retaddr); + } +} + static inline int float_comp_to_cc(CPUS390XState *env, int float_compare) { switch (float_compare) { @@ -48,19 +94,6 @@ static inline int float_comp_to_cc(CPUS390XState *env, int float_compare) } } -/* condition codes for binary FP ops */ -uint32_t set_cc_f32(CPUS390XState *env, float32 v1, float32 v2) -{ - return float_comp_to_cc(env, float32_compare_quiet(v1, v2, - &env->fpu_status)); -} - -uint32_t set_cc_f64(CPUS390XState *env, float64 v1, float64 v2) -{ - return float_comp_to_cc(env, float64_compare_quiet(v1, v2, - &env->fpu_status)); -} - /* condition codes for unary FP ops */ uint32_t set_cc_nz_f32(float32 v) { @@ -88,7 +121,7 @@ uint32_t set_cc_nz_f64(float64 v) } } -static uint32_t set_cc_nz_f128(float128 v) +uint32_t set_cc_nz_f128(float128 v) { if (float128_is_any_nan(v)) { return 3; @@ -101,433 +134,211 @@ static uint32_t set_cc_nz_f128(float128 v) } } -/* convert 32-bit int to 64-bit float */ -void HELPER(cdfbr)(CPUS390XState *env, uint32_t f1, int32_t v2) +/* 32-bit FP addition */ +uint64_t HELPER(aeb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { - HELPER_LOG("%s: converting %d to f%d\n", __func__, v2, f1); - env->fregs[f1].d = int32_to_float64(v2, &env->fpu_status); + float32 ret = float32_add(f1, f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* convert 32-bit int to 128-bit float */ -void HELPER(cxfbr)(CPUS390XState *env, uint32_t f1, int32_t v2) +/* 64-bit FP addition */ +uint64_t HELPER(adb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { - CPU_QuadU v1; - - v1.q = int32_to_float128(v2, &env->fpu_status); - env->fregs[f1].ll = v1.ll.upper; - env->fregs[f1 + 2].ll = v1.ll.lower; + float64 ret = float64_add(f1, f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* convert 64-bit int to 32-bit float */ -void HELPER(cegbr)(CPUS390XState *env, uint32_t f1, int64_t v2) +/* 128-bit FP addition */ +uint64_t HELPER(axb)(CPUS390XState *env, uint64_t ah, uint64_t al, + uint64_t bh, uint64_t bl) { - HELPER_LOG("%s: converting %ld to f%d\n", __func__, v2, f1); - env->fregs[f1].l.upper = int64_to_float32(v2, &env->fpu_status); + float128 ret = float128_add(make_float128(ah, al), + make_float128(bh, bl), + &env->fpu_status); + handle_exceptions(env, GETPC()); + return RET128(ret); } -/* convert 64-bit int to 64-bit float */ -void HELPER(cdgbr)(CPUS390XState *env, uint32_t f1, int64_t v2) +/* 32-bit FP subtraction */ +uint64_t HELPER(seb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { - HELPER_LOG("%s: converting %ld to f%d\n", __func__, v2, f1); - env->fregs[f1].d = int64_to_float64(v2, &env->fpu_status); + float32 ret = float32_sub(f1, f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* convert 64-bit int to 128-bit float */ -void HELPER(cxgbr)(CPUS390XState *env, uint32_t f1, int64_t v2) +/* 64-bit FP subtraction */ +uint64_t HELPER(sdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { - CPU_QuadU x1; - - x1.q = int64_to_float128(v2, &env->fpu_status); - HELPER_LOG("%s: converted %ld to 0x%lx and 0x%lx\n", __func__, v2, - x1.ll.upper, x1.ll.lower); - env->fregs[f1].ll = x1.ll.upper; - env->fregs[f1 + 2].ll = x1.ll.lower; + float64 ret = float64_sub(f1, f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* convert 32-bit int to 32-bit float */ -void HELPER(cefbr)(CPUS390XState *env, uint32_t f1, int32_t v2) +/* 128-bit FP subtraction */ +uint64_t HELPER(sxb)(CPUS390XState *env, uint64_t ah, uint64_t al, + uint64_t bh, uint64_t bl) { - env->fregs[f1].l.upper = int32_to_float32(v2, &env->fpu_status); - HELPER_LOG("%s: converting %d to 0x%d in f%d\n", __func__, v2, - env->fregs[f1].l.upper, f1); + float128 ret = float128_sub(make_float128(ah, al), + make_float128(bh, bl), + &env->fpu_status); + handle_exceptions(env, GETPC()); + return RET128(ret); } -/* 32-bit FP addition RR */ -uint32_t HELPER(aebr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* 32-bit FP division */ +uint64_t HELPER(deb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { - env->fregs[f1].l.upper = float32_add(env->fregs[f1].l.upper, - env->fregs[f2].l.upper, - &env->fpu_status); - HELPER_LOG("%s: adding 0x%d resulting in 0x%d in f%d\n", __func__, - env->fregs[f2].l.upper, env->fregs[f1].l.upper, f1); - - return set_cc_nz_f32(env->fregs[f1].l.upper); + float32 ret = float32_div(f1, f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* 64-bit FP addition RR */ -uint32_t HELPER(adbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* 64-bit FP division */ +uint64_t HELPER(ddb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { - env->fregs[f1].d = float64_add(env->fregs[f1].d, env->fregs[f2].d, - &env->fpu_status); - HELPER_LOG("%s: adding 0x%ld resulting in 0x%ld in f%d\n", __func__, - env->fregs[f2].d, env->fregs[f1].d, f1); - - return set_cc_nz_f64(env->fregs[f1].d); + float64 ret = float64_div(f1, f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* 32-bit FP subtraction RR */ -uint32_t HELPER(sebr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* 128-bit FP division */ +uint64_t HELPER(dxb)(CPUS390XState *env, uint64_t ah, uint64_t al, + uint64_t bh, uint64_t bl) { - env->fregs[f1].l.upper = float32_sub(env->fregs[f1].l.upper, - env->fregs[f2].l.upper, - &env->fpu_status); - HELPER_LOG("%s: adding 0x%d resulting in 0x%d in f%d\n", __func__, - env->fregs[f2].l.upper, env->fregs[f1].l.upper, f1); - - return set_cc_nz_f32(env->fregs[f1].l.upper); + float128 ret = float128_div(make_float128(ah, al), + make_float128(bh, bl), + &env->fpu_status); + handle_exceptions(env, GETPC()); + return RET128(ret); } -/* 64-bit FP subtraction RR */ -uint32_t HELPER(sdbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* 32-bit FP multiplication */ +uint64_t HELPER(meeb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { - env->fregs[f1].d = float64_sub(env->fregs[f1].d, env->fregs[f2].d, - &env->fpu_status); - HELPER_LOG("%s: subtracting 0x%ld resulting in 0x%ld in f%d\n", - __func__, env->fregs[f2].d, env->fregs[f1].d, f1); - - return set_cc_nz_f64(env->fregs[f1].d); + float32 ret = float32_mul(f1, f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* 32-bit FP division RR */ -void HELPER(debr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* 64-bit FP multiplication */ +uint64_t HELPER(mdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { - env->fregs[f1].l.upper = float32_div(env->fregs[f1].l.upper, - env->fregs[f2].l.upper, - &env->fpu_status); + float64 ret = float64_mul(f1, f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* 128-bit FP division RR */ -void HELPER(dxbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* 64/32-bit FP multiplication */ +uint64_t HELPER(mdeb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { - CPU_QuadU v1; - CPU_QuadU v2; - CPU_QuadU res; - - v1.ll.upper = env->fregs[f1].ll; - v1.ll.lower = env->fregs[f1 + 2].ll; - v2.ll.upper = env->fregs[f2].ll; - v2.ll.lower = env->fregs[f2 + 2].ll; - res.q = float128_div(v1.q, v2.q, &env->fpu_status); - env->fregs[f1].ll = res.ll.upper; - env->fregs[f1 + 2].ll = res.ll.lower; + float64 ret = float32_to_float64(f2, &env->fpu_status); + ret = float64_mul(f1, ret, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* 64-bit FP multiplication RR */ -void HELPER(mdbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* 128-bit FP multiplication */ +uint64_t HELPER(mxb)(CPUS390XState *env, uint64_t ah, uint64_t al, + uint64_t bh, uint64_t bl) { - env->fregs[f1].d = float64_mul(env->fregs[f1].d, env->fregs[f2].d, - &env->fpu_status); + float128 ret = float128_mul(make_float128(ah, al), + make_float128(bh, bl), + &env->fpu_status); + handle_exceptions(env, GETPC()); + return RET128(ret); } -/* 128-bit FP multiplication RR */ -void HELPER(mxbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* 128/64-bit FP multiplication */ +uint64_t HELPER(mxdb)(CPUS390XState *env, uint64_t ah, uint64_t al, + uint64_t f2) { - CPU_QuadU v1; - CPU_QuadU v2; - CPU_QuadU res; - - v1.ll.upper = env->fregs[f1].ll; - v1.ll.lower = env->fregs[f1 + 2].ll; - v2.ll.upper = env->fregs[f2].ll; - v2.ll.lower = env->fregs[f2 + 2].ll; - res.q = float128_mul(v1.q, v2.q, &env->fpu_status); - env->fregs[f1].ll = res.ll.upper; - env->fregs[f1 + 2].ll = res.ll.lower; + float128 ret = float64_to_float128(f2, &env->fpu_status); + ret = float128_mul(make_float128(ah, al), ret, &env->fpu_status); + handle_exceptions(env, GETPC()); + return RET128(ret); } /* convert 32-bit float to 64-bit float */ -void HELPER(ldebr)(CPUS390XState *env, uint32_t r1, uint32_t r2) +uint64_t HELPER(ldeb)(CPUS390XState *env, uint64_t f2) { - env->fregs[r1].d = float32_to_float64(env->fregs[r2].l.upper, - &env->fpu_status); + float64 ret = float32_to_float64(f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } /* convert 128-bit float to 64-bit float */ -void HELPER(ldxbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al) { - CPU_QuadU x2; - - x2.ll.upper = env->fregs[f2].ll; - x2.ll.lower = env->fregs[f2 + 2].ll; - env->fregs[f1].d = float128_to_float64(x2.q, &env->fpu_status); - HELPER_LOG("%s: to 0x%ld\n", __func__, env->fregs[f1].d); + float64 ret = float128_to_float64(make_float128(ah, al), &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } /* convert 64-bit float to 128-bit float */ -void HELPER(lxdbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2) { - CPU_QuadU res; + float128 ret = float64_to_float128(f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return RET128(ret); +} - res.q = float64_to_float128(env->fregs[f2].d, &env->fpu_status); - env->fregs[f1].ll = res.ll.upper; - env->fregs[f1 + 2].ll = res.ll.lower; +/* convert 32-bit float to 128-bit float */ +uint64_t HELPER(lxeb)(CPUS390XState *env, uint64_t f2) +{ + float128 ret = float32_to_float128(f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return RET128(ret); } /* convert 64-bit float to 32-bit float */ -void HELPER(ledbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2) { - float64 d2 = env->fregs[f2].d; - - env->fregs[f1].l.upper = float64_to_float32(d2, &env->fpu_status); + float32 ret = float64_to_float32(f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } /* convert 128-bit float to 32-bit float */ -void HELPER(lexbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al) { - CPU_QuadU x2; - - x2.ll.upper = env->fregs[f2].ll; - x2.ll.lower = env->fregs[f2 + 2].ll; - env->fregs[f1].l.upper = float128_to_float32(x2.q, &env->fpu_status); - HELPER_LOG("%s: to 0x%d\n", __func__, env->fregs[f1].l.upper); + float32 ret = float128_to_float32(make_float128(ah, al), &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* absolute value of 32-bit float */ -uint32_t HELPER(lpebr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* 32-bit FP compare */ +uint32_t HELPER(ceb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { - float32 v1; - float32 v2 = env->fregs[f2].d; - - v1 = float32_abs(v2); - env->fregs[f1].d = v1; - return set_cc_nz_f32(v1); + int cmp = float32_compare_quiet(f1, f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return float_comp_to_cc(env, cmp); } -/* absolute value of 64-bit float */ -uint32_t HELPER(lpdbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* 64-bit FP compare */ +uint32_t HELPER(cdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { - float64 v1; - float64 v2 = env->fregs[f2].d; - - v1 = float64_abs(v2); - env->fregs[f1].d = v1; - return set_cc_nz_f64(v1); + int cmp = float64_compare_quiet(f1, f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return float_comp_to_cc(env, cmp); } -/* absolute value of 128-bit float */ -uint32_t HELPER(lpxbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* 128-bit FP compare */ +uint32_t HELPER(cxb)(CPUS390XState *env, uint64_t ah, uint64_t al, + uint64_t bh, uint64_t bl) { - CPU_QuadU v1; - CPU_QuadU v2; - - v2.ll.upper = env->fregs[f2].ll; - v2.ll.lower = env->fregs[f2 + 2].ll; - v1.q = float128_abs(v2.q); - env->fregs[f1].ll = v1.ll.upper; - env->fregs[f1 + 2].ll = v1.ll.lower; - return set_cc_nz_f128(v1.q); + int cmp = float128_compare_quiet(make_float128(ah, al), + make_float128(bh, bl), + &env->fpu_status); + handle_exceptions(env, GETPC()); + return float_comp_to_cc(env, cmp); } -/* load and test 64-bit float */ -uint32_t HELPER(ltdbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) -{ - env->fregs[f1].d = env->fregs[f2].d; - return set_cc_nz_f64(env->fregs[f1].d); -} - -/* load and test 32-bit float */ -uint32_t HELPER(ltebr)(CPUS390XState *env, uint32_t f1, uint32_t f2) -{ - env->fregs[f1].l.upper = env->fregs[f2].l.upper; - return set_cc_nz_f32(env->fregs[f1].l.upper); -} - -/* load and test 128-bit float */ -uint32_t HELPER(ltxbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) -{ - CPU_QuadU x; - - x.ll.upper = env->fregs[f2].ll; - x.ll.lower = env->fregs[f2 + 2].ll; - env->fregs[f1].ll = x.ll.upper; - env->fregs[f1 + 2].ll = x.ll.lower; - return set_cc_nz_f128(x.q); -} - -/* load complement of 32-bit float */ -uint32_t HELPER(lcebr)(CPUS390XState *env, uint32_t f1, uint32_t f2) -{ - env->fregs[f1].l.upper = float32_chs(env->fregs[f2].l.upper); - - return set_cc_nz_f32(env->fregs[f1].l.upper); -} - -/* load complement of 64-bit float */ -uint32_t HELPER(lcdbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) -{ - env->fregs[f1].d = float64_chs(env->fregs[f2].d); - - return set_cc_nz_f64(env->fregs[f1].d); -} - -/* load complement of 128-bit float */ -uint32_t HELPER(lcxbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) -{ - CPU_QuadU x1, x2; - - x2.ll.upper = env->fregs[f2].ll; - x2.ll.lower = env->fregs[f2 + 2].ll; - x1.q = float128_chs(x2.q); - env->fregs[f1].ll = x1.ll.upper; - env->fregs[f1 + 2].ll = x1.ll.lower; - return set_cc_nz_f128(x1.q); -} - -/* 32-bit FP addition RM */ -void HELPER(aeb)(CPUS390XState *env, uint32_t f1, uint32_t val) -{ - float32 v1 = env->fregs[f1].l.upper; - CPU_FloatU v2; - - v2.l = val; - HELPER_LOG("%s: adding 0x%d from f%d and 0x%d\n", __func__, - v1, f1, v2.f); - env->fregs[f1].l.upper = float32_add(v1, v2.f, &env->fpu_status); -} - -/* 32-bit FP division RM */ -void HELPER(deb)(CPUS390XState *env, uint32_t f1, uint32_t val) -{ - float32 v1 = env->fregs[f1].l.upper; - CPU_FloatU v2; - - v2.l = val; - HELPER_LOG("%s: dividing 0x%d from f%d by 0x%d\n", __func__, - v1, f1, v2.f); - env->fregs[f1].l.upper = float32_div(v1, v2.f, &env->fpu_status); -} - -/* 32-bit FP multiplication RM */ -void HELPER(meeb)(CPUS390XState *env, uint32_t f1, uint32_t val) -{ - float32 v1 = env->fregs[f1].l.upper; - CPU_FloatU v2; - - v2.l = val; - HELPER_LOG("%s: multiplying 0x%d from f%d and 0x%d\n", __func__, - v1, f1, v2.f); - env->fregs[f1].l.upper = float32_mul(v1, v2.f, &env->fpu_status); -} - -/* 32-bit FP compare RR */ -uint32_t HELPER(cebr)(CPUS390XState *env, uint32_t f1, uint32_t f2) -{ - float32 v1 = env->fregs[f1].l.upper; - float32 v2 = env->fregs[f2].l.upper; - - HELPER_LOG("%s: comparing 0x%d from f%d and 0x%d\n", __func__, - v1, f1, v2); - return set_cc_f32(env, v1, v2); -} - -/* 64-bit FP compare RR */ -uint32_t HELPER(cdbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) -{ - float64 v1 = env->fregs[f1].d; - float64 v2 = env->fregs[f2].d; - - HELPER_LOG("%s: comparing 0x%ld from f%d and 0x%ld\n", __func__, - v1, f1, v2); - return set_cc_f64(env, v1, v2); -} - -/* 128-bit FP compare RR */ -uint32_t HELPER(cxbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) -{ - CPU_QuadU v1; - CPU_QuadU v2; - - v1.ll.upper = env->fregs[f1].ll; - v1.ll.lower = env->fregs[f1 + 2].ll; - v2.ll.upper = env->fregs[f2].ll; - v2.ll.lower = env->fregs[f2 + 2].ll; - - return float_comp_to_cc(env, float128_compare_quiet(v1.q, v2.q, - &env->fpu_status)); -} - -/* 64-bit FP compare RM */ -uint32_t HELPER(cdb)(CPUS390XState *env, uint32_t f1, uint64_t a2) -{ - float64 v1 = env->fregs[f1].d; - CPU_DoubleU v2; - - v2.ll = cpu_ldq_data(env, a2); - HELPER_LOG("%s: comparing 0x%ld from f%d and 0x%lx\n", __func__, v1, - f1, v2.d); - return set_cc_f64(env, v1, v2.d); -} - -/* 64-bit FP addition RM */ -uint32_t HELPER(adb)(CPUS390XState *env, uint32_t f1, uint64_t a2) -{ - float64 v1 = env->fregs[f1].d; - CPU_DoubleU v2; - - v2.ll = cpu_ldq_data(env, a2); - HELPER_LOG("%s: adding 0x%lx from f%d and 0x%lx\n", __func__, - v1, f1, v2.d); - env->fregs[f1].d = v1 = float64_add(v1, v2.d, &env->fpu_status); - return set_cc_nz_f64(v1); -} - -/* 32-bit FP subtraction RM */ -void HELPER(seb)(CPUS390XState *env, uint32_t f1, uint32_t val) -{ - float32 v1 = env->fregs[f1].l.upper; - CPU_FloatU v2; - - v2.l = val; - env->fregs[f1].l.upper = float32_sub(v1, v2.f, &env->fpu_status); -} - -/* 64-bit FP subtraction RM */ -uint32_t HELPER(sdb)(CPUS390XState *env, uint32_t f1, uint64_t a2) -{ - float64 v1 = env->fregs[f1].d; - CPU_DoubleU v2; - - v2.ll = cpu_ldq_data(env, a2); - env->fregs[f1].d = v1 = float64_sub(v1, v2.d, &env->fpu_status); - return set_cc_nz_f64(v1); -} - -/* 64-bit FP multiplication RM */ -void HELPER(mdb)(CPUS390XState *env, uint32_t f1, uint64_t a2) -{ - float64 v1 = env->fregs[f1].d; - CPU_DoubleU v2; - - v2.ll = cpu_ldq_data(env, a2); - HELPER_LOG("%s: multiplying 0x%lx from f%d and 0x%ld\n", __func__, - v1, f1, v2.d); - env->fregs[f1].d = float64_mul(v1, v2.d, &env->fpu_status); -} - -/* 64-bit FP division RM */ -void HELPER(ddb)(CPUS390XState *env, uint32_t f1, uint64_t a2) -{ - float64 v1 = env->fregs[f1].d; - CPU_DoubleU v2; - - v2.ll = cpu_ldq_data(env, a2); - HELPER_LOG("%s: dividing 0x%lx from f%d by 0x%ld\n", __func__, - v1, f1, v2.d); - env->fregs[f1].d = float64_div(v1, v2.d, &env->fpu_status); -} - -static void set_round_mode(CPUS390XState *env, int m3) +static int swap_round_mode(CPUS390XState *env, int m3) { + int ret = env->fpu_status.float_rounding_mode; switch (m3) { case 0: /* current mode */ @@ -551,232 +362,242 @@ static void set_round_mode(CPUS390XState *env, int m3) set_float_rounding_mode(float_round_down, &env->fpu_status); break; } + return ret; +} + +/* convert 64-bit int to 32-bit float */ +uint64_t HELPER(cegb)(CPUS390XState *env, int64_t v2, uint32_t m3) +{ + int hold = swap_round_mode(env, m3); + float32 ret = int64_to_float32(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; +} + +/* convert 64-bit int to 64-bit float */ +uint64_t HELPER(cdgb)(CPUS390XState *env, int64_t v2, uint32_t m3) +{ + int hold = swap_round_mode(env, m3); + float64 ret = int64_to_float64(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; +} + +/* convert 64-bit int to 128-bit float */ +uint64_t HELPER(cxgb)(CPUS390XState *env, int64_t v2, uint32_t m3) +{ + int hold = swap_round_mode(env, m3); + float128 ret = int64_to_float128(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return RET128(ret); +} + +/* convert 64-bit uint to 32-bit float */ +uint64_t HELPER(celgb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +{ + int hold = swap_round_mode(env, m3); + float32 ret = uint64_to_float32(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; +} + +/* convert 64-bit uint to 64-bit float */ +uint64_t HELPER(cdlgb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +{ + int hold = swap_round_mode(env, m3); + float64 ret = uint64_to_float64(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; +} + +/* convert 64-bit uint to 128-bit float */ +uint64_t HELPER(cxlgb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +{ + int hold = swap_round_mode(env, m3); + float128 ret = uint64_to_float128(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return RET128(ret); } /* convert 32-bit float to 64-bit int */ -uint32_t HELPER(cgebr)(CPUS390XState *env, uint32_t r1, uint32_t f2, - uint32_t m3) +uint64_t HELPER(cgeb)(CPUS390XState *env, uint64_t v2, uint32_t m3) { - float32 v2 = env->fregs[f2].l.upper; - - set_round_mode(env, m3); - env->regs[r1] = float32_to_int64(v2, &env->fpu_status); - return set_cc_nz_f32(v2); + int hold = swap_round_mode(env, m3); + int64_t ret = float32_to_int64(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } /* convert 64-bit float to 64-bit int */ -uint32_t HELPER(cgdbr)(CPUS390XState *env, uint32_t r1, uint32_t f2, - uint32_t m3) +uint64_t HELPER(cgdb)(CPUS390XState *env, uint64_t v2, uint32_t m3) { - float64 v2 = env->fregs[f2].d; - - set_round_mode(env, m3); - env->regs[r1] = float64_to_int64(v2, &env->fpu_status); - return set_cc_nz_f64(v2); + int hold = swap_round_mode(env, m3); + int64_t ret = float64_to_int64(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } /* convert 128-bit float to 64-bit int */ -uint32_t HELPER(cgxbr)(CPUS390XState *env, uint32_t r1, uint32_t f2, - uint32_t m3) +uint64_t HELPER(cgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3) { - CPU_QuadU v2; - - v2.ll.upper = env->fregs[f2].ll; - v2.ll.lower = env->fregs[f2 + 2].ll; - set_round_mode(env, m3); - env->regs[r1] = float128_to_int64(v2.q, &env->fpu_status); - if (float128_is_any_nan(v2.q)) { - return 3; - } else if (float128_is_zero(v2.q)) { - return 0; - } else if (float128_is_neg(v2.q)) { - return 1; - } else { - return 2; - } + int hold = swap_round_mode(env, m3); + float128 v2 = make_float128(h, l); + int64_t ret = float128_to_int64(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } /* convert 32-bit float to 32-bit int */ -uint32_t HELPER(cfebr)(CPUS390XState *env, uint32_t r1, uint32_t f2, - uint32_t m3) +uint64_t HELPER(cfeb)(CPUS390XState *env, uint64_t v2, uint32_t m3) { - float32 v2 = env->fregs[f2].l.upper; - - set_round_mode(env, m3); - env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | - float32_to_int32(v2, &env->fpu_status); - return set_cc_nz_f32(v2); + int hold = swap_round_mode(env, m3); + int32_t ret = float32_to_int32(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } /* convert 64-bit float to 32-bit int */ -uint32_t HELPER(cfdbr)(CPUS390XState *env, uint32_t r1, uint32_t f2, - uint32_t m3) +uint64_t HELPER(cfdb)(CPUS390XState *env, uint64_t v2, uint32_t m3) { - float64 v2 = env->fregs[f2].d; - - set_round_mode(env, m3); - env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | - float64_to_int32(v2, &env->fpu_status); - return set_cc_nz_f64(v2); + int hold = swap_round_mode(env, m3); + int32_t ret = float64_to_int32(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } /* convert 128-bit float to 32-bit int */ -uint32_t HELPER(cfxbr)(CPUS390XState *env, uint32_t r1, uint32_t f2, - uint32_t m3) +uint64_t HELPER(cfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3) { - CPU_QuadU v2; - - v2.ll.upper = env->fregs[f2].ll; - v2.ll.lower = env->fregs[f2 + 2].ll; - env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | - float128_to_int32(v2.q, &env->fpu_status); - return set_cc_nz_f128(v2.q); + int hold = swap_round_mode(env, m3); + float128 v2 = make_float128(h, l); + int32_t ret = float128_to_int32(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* load 32-bit FP zero */ -void HELPER(lzer)(CPUS390XState *env, uint32_t f1) +/* convert 32-bit float to 64-bit uint */ +uint64_t HELPER(clgeb)(CPUS390XState *env, uint64_t v2, uint32_t m3) { - env->fregs[f1].l.upper = float32_zero; + int hold = swap_round_mode(env, m3); + uint64_t ret; + v2 = float32_to_float64(v2, &env->fpu_status); + ret = float64_to_uint64(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* load 64-bit FP zero */ -void HELPER(lzdr)(CPUS390XState *env, uint32_t f1) +/* convert 64-bit float to 64-bit uint */ +uint64_t HELPER(clgdb)(CPUS390XState *env, uint64_t v2, uint32_t m3) { - env->fregs[f1].d = float64_zero; + int hold = swap_round_mode(env, m3); + uint64_t ret = float64_to_uint64(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* load 128-bit FP zero */ -void HELPER(lzxr)(CPUS390XState *env, uint32_t f1) +/* convert 128-bit float to 64-bit uint */ +uint64_t HELPER(clgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3) { - CPU_QuadU x; - - x.q = float64_to_float128(float64_zero, &env->fpu_status); - env->fregs[f1].ll = x.ll.upper; - env->fregs[f1 + 1].ll = x.ll.lower; + int hold = swap_round_mode(env, m3); + float128 v2 = make_float128(h, l); + /* ??? Not 100% correct. */ + uint64_t ret = float128_to_int64(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* 128-bit FP subtraction RR */ -uint32_t HELPER(sxbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* convert 32-bit float to 32-bit uint */ +uint64_t HELPER(clfeb)(CPUS390XState *env, uint64_t v2, uint32_t m3) { - CPU_QuadU v1; - CPU_QuadU v2; - CPU_QuadU res; - - v1.ll.upper = env->fregs[f1].ll; - v1.ll.lower = env->fregs[f1 + 2].ll; - v2.ll.upper = env->fregs[f2].ll; - v2.ll.lower = env->fregs[f2 + 2].ll; - res.q = float128_sub(v1.q, v2.q, &env->fpu_status); - env->fregs[f1].ll = res.ll.upper; - env->fregs[f1 + 2].ll = res.ll.lower; - return set_cc_nz_f128(res.q); + int hold = swap_round_mode(env, m3); + uint32_t ret = float32_to_uint32(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* 128-bit FP addition RR */ -uint32_t HELPER(axbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* convert 64-bit float to 32-bit uint */ +uint64_t HELPER(clfdb)(CPUS390XState *env, uint64_t v2, uint32_t m3) { - CPU_QuadU v1; - CPU_QuadU v2; - CPU_QuadU res; - - v1.ll.upper = env->fregs[f1].ll; - v1.ll.lower = env->fregs[f1 + 2].ll; - v2.ll.upper = env->fregs[f2].ll; - v2.ll.lower = env->fregs[f2 + 2].ll; - res.q = float128_add(v1.q, v2.q, &env->fpu_status); - env->fregs[f1].ll = res.ll.upper; - env->fregs[f1 + 2].ll = res.ll.lower; - return set_cc_nz_f128(res.q); + int hold = swap_round_mode(env, m3); + uint32_t ret = float64_to_uint32(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* 32-bit FP multiplication RR */ -void HELPER(meebr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* convert 128-bit float to 32-bit uint */ +uint64_t HELPER(clfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3) { - env->fregs[f1].l.upper = float32_mul(env->fregs[f1].l.upper, - env->fregs[f2].l.upper, - &env->fpu_status); + int hold = swap_round_mode(env, m3); + float128 v2 = make_float128(h, l); + /* Not 100% correct. */ + uint32_t ret = float128_to_int64(v2, &env->fpu_status); + set_float_rounding_mode(hold, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* 64-bit FP division RR */ -void HELPER(ddbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* 32-bit FP multiply and add */ +uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1, + uint64_t f2, uint64_t f3) { - env->fregs[f1].d = float64_div(env->fregs[f1].d, env->fregs[f2].d, - &env->fpu_status); + float32 ret = float32_muladd(f2, f3, f1, 0, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* 64-bit FP multiply and add RM */ -void HELPER(madb)(CPUS390XState *env, uint32_t f1, uint64_t a2, uint32_t f3) +/* 64-bit FP multiply and add */ +uint64_t HELPER(madb)(CPUS390XState *env, uint64_t f1, + uint64_t f2, uint64_t f3) { - CPU_DoubleU v2; - - HELPER_LOG("%s: f1 %d a2 0x%lx f3 %d\n", __func__, f1, a2, f3); - v2.ll = cpu_ldq_data(env, a2); - env->fregs[f1].d = float64_add(env->fregs[f1].d, - float64_mul(v2.d, env->fregs[f3].d, - &env->fpu_status), - &env->fpu_status); + float64 ret = float64_muladd(f2, f3, f1, 0, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* 64-bit FP multiply and add RR */ -void HELPER(madbr)(CPUS390XState *env, uint32_t f1, uint32_t f3, uint32_t f2) +/* 32-bit FP multiply and subtract */ +uint64_t HELPER(mseb)(CPUS390XState *env, uint64_t f1, + uint64_t f2, uint64_t f3) { - HELPER_LOG("%s: f1 %d f2 %d f3 %d\n", __func__, f1, f2, f3); - env->fregs[f1].d = float64_add(float64_mul(env->fregs[f2].d, - env->fregs[f3].d, - &env->fpu_status), - env->fregs[f1].d, &env->fpu_status); + float32 ret = float32_muladd(f2, f3, f1, float_muladd_negate_c, + &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } -/* 64-bit FP multiply and subtract RR */ -void HELPER(msdbr)(CPUS390XState *env, uint32_t f1, uint32_t f3, uint32_t f2) +/* 64-bit FP multiply and subtract */ +uint64_t HELPER(msdb)(CPUS390XState *env, uint64_t f1, + uint64_t f2, uint64_t f3) { - HELPER_LOG("%s: f1 %d f2 %d f3 %d\n", __func__, f1, f2, f3); - env->fregs[f1].d = float64_sub(float64_mul(env->fregs[f2].d, - env->fregs[f3].d, - &env->fpu_status), - env->fregs[f1].d, &env->fpu_status); -} - -/* 32-bit FP multiply and add RR */ -void HELPER(maebr)(CPUS390XState *env, uint32_t f1, uint32_t f3, uint32_t f2) -{ - env->fregs[f1].l.upper = float32_add(env->fregs[f1].l.upper, - float32_mul(env->fregs[f2].l.upper, - env->fregs[f3].l.upper, - &env->fpu_status), - &env->fpu_status); -} - -/* convert 32-bit float to 64-bit float */ -void HELPER(ldeb)(CPUS390XState *env, uint32_t f1, uint64_t a2) -{ - uint32_t v2; - - v2 = cpu_ldl_data(env, a2); - env->fregs[f1].d = float32_to_float64(v2, - &env->fpu_status); -} - -/* convert 64-bit float to 128-bit float */ -void HELPER(lxdb)(CPUS390XState *env, uint32_t f1, uint64_t a2) -{ - CPU_DoubleU v2; - CPU_QuadU v1; - - v2.ll = cpu_ldq_data(env, a2); - v1.q = float64_to_float128(v2.d, &env->fpu_status); - env->fregs[f1].ll = v1.ll.upper; - env->fregs[f1 + 2].ll = v1.ll.lower; + float64 ret = float64_muladd(f2, f3, f1, float_muladd_negate_c, + &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; } /* test data class 32-bit */ -uint32_t HELPER(tceb)(CPUS390XState *env, uint32_t f1, uint64_t m2) +uint32_t HELPER(tceb)(uint64_t f1, uint64_t m2) { - float32 v1 = env->fregs[f1].l.upper; + float32 v1 = f1; int neg = float32_is_neg(v1); uint32_t cc = 0; - HELPER_LOG("%s: v1 0x%lx m2 0x%lx neg %d\n", __func__, (long)v1, m2, neg); if ((float32_is_zero(v1) && (m2 & (1 << (11-neg)))) || (float32_is_infinity(v1) && (m2 & (1 << (5-neg)))) || (float32_is_any_nan(v1) && (m2 & (1 << (3-neg)))) || @@ -786,19 +607,16 @@ uint32_t HELPER(tceb)(CPUS390XState *env, uint32_t f1, uint64_t m2) /* assume normalized number */ cc = 1; } - /* FIXME: denormalized? */ return cc; } /* test data class 64-bit */ -uint32_t HELPER(tcdb)(CPUS390XState *env, uint32_t f1, uint64_t m2) +uint32_t HELPER(tcdb)(uint64_t v1, uint64_t m2) { - float64 v1 = env->fregs[f1].d; int neg = float64_is_neg(v1); uint32_t cc = 0; - HELPER_LOG("%s: v1 0x%lx m2 0x%lx neg %d\n", __func__, v1, m2, neg); if ((float64_is_zero(v1) && (m2 & (1 << (11-neg)))) || (float64_is_infinity(v1) && (m2 & (1 << (5-neg)))) || (float64_is_any_nan(v1) && (m2 & (1 << (3-neg)))) || @@ -813,20 +631,16 @@ uint32_t HELPER(tcdb)(CPUS390XState *env, uint32_t f1, uint64_t m2) } /* test data class 128-bit */ -uint32_t HELPER(tcxb)(CPUS390XState *env, uint32_t f1, uint64_t m2) +uint32_t HELPER(tcxb)(uint64_t ah, uint64_t al, uint64_t m2) { - CPU_QuadU v1; + float128 v1 = make_float128(ah, al); + int neg = float128_is_neg(v1); uint32_t cc = 0; - int neg; - v1.ll.upper = env->fregs[f1].ll; - v1.ll.lower = env->fregs[f1 + 2].ll; - - neg = float128_is_neg(v1.q); - if ((float128_is_zero(v1.q) && (m2 & (1 << (11-neg)))) || - (float128_is_infinity(v1.q) && (m2 & (1 << (5-neg)))) || - (float128_is_any_nan(v1.q) && (m2 & (1 << (3-neg)))) || - (float128_is_signaling_nan(v1.q) && (m2 & (1 << (1-neg))))) { + if ((float128_is_zero(v1) && (m2 & (1 << (11-neg)))) || + (float128_is_infinity(v1) && (m2 & (1 << (5-neg)))) || + (float128_is_any_nan(v1) && (m2 & (1 << (3-neg)))) || + (float128_is_signaling_nan(v1) && (m2 & (1 << (1-neg))))) { cc = 1; } else if (m2 & (1 << (9-neg))) { /* assume normalized number */ @@ -836,8 +650,64 @@ uint32_t HELPER(tcxb)(CPUS390XState *env, uint32_t f1, uint64_t m2) return cc; } -/* square root 64-bit RR */ -void HELPER(sqdbr)(CPUS390XState *env, uint32_t f1, uint32_t f2) +/* square root 32-bit */ +uint64_t HELPER(sqeb)(CPUS390XState *env, uint64_t f2) { - env->fregs[f1].d = float64_sqrt(env->fregs[f2].d, &env->fpu_status); + float32 ret = float32_sqrt(f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; +} + +/* square root 64-bit */ +uint64_t HELPER(sqdb)(CPUS390XState *env, uint64_t f2) +{ + float64 ret = float64_sqrt(f2, &env->fpu_status); + handle_exceptions(env, GETPC()); + return ret; +} + +/* square root 128-bit */ +uint64_t HELPER(sqxb)(CPUS390XState *env, uint64_t ah, uint64_t al) +{ + float128 ret = float128_sqrt(make_float128(ah, al), &env->fpu_status); + handle_exceptions(env, GETPC()); + return RET128(ret); +} + +static const int fpc_to_rnd[4] = { + float_round_nearest_even, + float_round_to_zero, + float_round_up, + float_round_down +}; + +/* set fpc */ +void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc) +{ + /* Install everything in the main FPC. */ + env->fpc = fpc; + + /* Install the rounding mode in the shadow fpu_status. */ + set_float_rounding_mode(fpc_to_rnd[fpc & 3], &env->fpu_status); +} + +/* set fpc and signal */ +void HELPER(sfas)(CPUS390XState *env, uint64_t val) +{ + uint32_t signalling = env->fpc; + uint32_t source = val; + uint32_t s390_exc; + + /* The contents of the source operand are placed in the FPC register; + then the flags in the FPC register are set to the logical OR of the + signalling flags and the source flags. */ + env->fpc = source | (signalling & 0x00ff0000); + set_float_rounding_mode(fpc_to_rnd[source & 3], &env->fpu_status); + + /* If any signalling flag is 1 and the corresponding source mask + is also 1, a simulated-iee-exception trap occurs. */ + s390_exc = (signalling >> 16) & (source >> 24); + if (s390_exc) { + ieee_exception(env, s390_exc | 3, GETPC()); + } } diff --git a/target-s390x/helper.c b/target-s390x/helper.c index 42e06eb85e..b425054be8 100644 --- a/target-s390x/helper.c +++ b/target-s390x/helper.c @@ -57,7 +57,7 @@ void s390x_tod_timer(void *opaque) CPUS390XState *env = &cpu->env; env->pending_int |= INTERRUPT_TOD; - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); } void s390x_cpu_timer(void *opaque) @@ -66,7 +66,7 @@ void s390x_cpu_timer(void *opaque) CPUS390XState *env = &cpu->env; env->pending_int |= INTERRUPT_CPUTIMER; - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); } #endif @@ -74,35 +74,33 @@ S390CPU *cpu_s390x_init(const char *cpu_model) { S390CPU *cpu; CPUS390XState *env; - static int inited; cpu = S390_CPU(object_new(TYPE_S390_CPU)); env = &cpu->env; - - if (tcg_enabled() && !inited) { - inited = 1; - s390x_translate_init(); - } - env->cpu_model_str = cpu_model; - qemu_init_vcpu(env); + + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + return cpu; } #if defined(CONFIG_USER_ONLY) -void do_interrupt(CPUS390XState *env) +void s390_cpu_do_interrupt(CPUState *cs) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + env->exception_index = -1; } int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong address, int rw, int mmu_idx) { - /* fprintf(stderr, "%s: address 0x%lx rw %d mmu_idx %d\n", - __func__, address, rw, mmu_idx); */ - env->exception_index = EXCP_ADDR; - /* FIXME: find out how this works on a real machine */ + env->exception_index = EXCP_PGM; + env->int_pgm_code = PGM_ADDRESSING; + /* On real machines this value is dropped into LowMem. Since this + is userland, simply put this someplace that cpu_loop can find it. */ env->__excp_addr = address; return 1; } @@ -111,11 +109,11 @@ int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong address, /* Ensure to exit the TB after this call! */ static void trigger_pgm_exception(CPUS390XState *env, uint32_t code, - uint32_t ilc) + uint32_t ilen) { env->exception_index = EXCP_PGM; env->int_pgm_code = code; - env->int_pgm_ilc = ilc; + env->int_pgm_ilen = ilen; } static int trans_bits(CPUS390XState *env, uint64_t mode) @@ -143,30 +141,30 @@ static int trans_bits(CPUS390XState *env, uint64_t mode) static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr, uint64_t mode) { - int ilc = ILC_LATER_INC_2; + int ilen = ILEN_LATER_INC; int bits = trans_bits(env, mode) | 4; DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits); stq_phys(env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits); - trigger_pgm_exception(env, PGM_PROTECTION, ilc); + trigger_pgm_exception(env, PGM_PROTECTION, ilen); } static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr, uint32_t type, uint64_t asc, int rw) { - int ilc = ILC_LATER; + int ilen = ILEN_LATER; int bits = trans_bits(env, asc); + /* Code accesses have an undefined ilc. */ if (rw == 2) { - /* code has is undefined ilc */ - ilc = 2; + ilen = 2; } DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits); stq_phys(env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits); - trigger_pgm_exception(env, type, ilc); + trigger_pgm_exception(env, type, ilen); } static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr, @@ -387,7 +385,7 @@ int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong orig_vaddr, int prot; DPRINTF("%s: address 0x%" PRIx64 " rw %d mmu_idx %d\n", - __func__, _vaddr, rw, mmu_idx); + __func__, orig_vaddr, rw, mmu_idx); orig_vaddr &= TARGET_PAGE_MASK; vaddr = orig_vaddr; @@ -404,9 +402,9 @@ int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong orig_vaddr, /* check out of RAM access */ if (raddr > (ram_size + virtio_size)) { - DPRINTF("%s: aaddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__, - (uint64_t)aaddr, (uint64_t)ram_size); - trigger_pgm_exception(env, PGM_ADDRESSING, ILC_LATER); + DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__, + (uint64_t)raddr, (uint64_t)ram_size); + trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_LATER); return 1; } @@ -441,51 +439,97 @@ hwaddr cpu_get_phys_page_debug(CPUS390XState *env, void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) { if (mask & PSW_MASK_WAIT) { + S390CPU *cpu = s390_env_get_cpu(env); + CPUState *cs = CPU(cpu); if (!(mask & (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK))) { - if (s390_del_running_cpu(env) == 0) { + if (s390_del_running_cpu(cpu) == 0) { #ifndef CONFIG_USER_ONLY qemu_system_shutdown_request(); #endif } } - env->halted = 1; + cs->halted = 1; env->exception_index = EXCP_HLT; } env->psw.addr = addr; env->psw.mask = mask; - env->cc_op = (mask >> 13) & 3; + env->cc_op = (mask >> 44) & 3; } static uint64_t get_psw_mask(CPUS390XState *env) { - uint64_t r = env->psw.mask; + uint64_t r; env->cc_op = calc_cc(env, env->cc_op, env->cc_src, env->cc_dst, env->cc_vr); - r &= ~(3ULL << 13); + r = env->psw.mask; + r &= ~PSW_MASK_CC; assert(!(env->cc_op & ~3)); - r |= env->cc_op << 13; + r |= (uint64_t)env->cc_op << 44; return r; } +static LowCore *cpu_map_lowcore(CPUS390XState *env) +{ + LowCore *lowcore; + hwaddr len = sizeof(LowCore); + + lowcore = cpu_physical_memory_map(env->psa, &len, 1); + + if (len < sizeof(LowCore)) { + cpu_abort(env, "Could not map lowcore\n"); + } + + return lowcore; +} + +static void cpu_unmap_lowcore(LowCore *lowcore) +{ + cpu_physical_memory_unmap(lowcore, sizeof(LowCore), 1, sizeof(LowCore)); +} + +void *s390_cpu_physical_memory_map(CPUS390XState *env, hwaddr addr, hwaddr *len, + int is_write) +{ + hwaddr start = addr; + + /* Mind the prefix area. */ + if (addr < 8192) { + /* Map the lowcore. */ + start += env->psa; + *len = MIN(*len, 8192 - addr); + } else if ((addr >= env->psa) && (addr < env->psa + 8192)) { + /* Map the 0 page. */ + start -= env->psa; + *len = MIN(*len, 8192 - start); + } + + return cpu_physical_memory_map(start, len, is_write); +} + +void s390_cpu_physical_memory_unmap(CPUS390XState *env, void *addr, hwaddr len, + int is_write) +{ + cpu_physical_memory_unmap(addr, len, is_write, len); +} + static void do_svc_interrupt(CPUS390XState *env) { uint64_t mask, addr; LowCore *lowcore; - hwaddr len = TARGET_PAGE_SIZE; - lowcore = cpu_physical_memory_map(env->psa, &len, 1); + lowcore = cpu_map_lowcore(env); lowcore->svc_code = cpu_to_be16(env->int_svc_code); - lowcore->svc_ilc = cpu_to_be16(env->int_svc_ilc); + lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen); lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env)); - lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + (env->int_svc_ilc)); + lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + env->int_svc_ilen); mask = be64_to_cpu(lowcore->svc_new_psw.mask); addr = be64_to_cpu(lowcore->svc_new_psw.addr); - cpu_physical_memory_unmap(lowcore, len, 1, len); + cpu_unmap_lowcore(lowcore); load_psw(env, mask, addr); } @@ -494,39 +538,36 @@ static void do_program_interrupt(CPUS390XState *env) { uint64_t mask, addr; LowCore *lowcore; - hwaddr len = TARGET_PAGE_SIZE; - int ilc = env->int_pgm_ilc; + int ilen = env->int_pgm_ilen; - switch (ilc) { - case ILC_LATER: - ilc = get_ilc(cpu_ldub_code(env, env->psw.addr)); + switch (ilen) { + case ILEN_LATER: + ilen = get_ilen(cpu_ldub_code(env, env->psw.addr)); break; - case ILC_LATER_INC: - ilc = get_ilc(cpu_ldub_code(env, env->psw.addr)); - env->psw.addr += ilc * 2; - break; - case ILC_LATER_INC_2: - ilc = get_ilc(cpu_ldub_code(env, env->psw.addr)) * 2; - env->psw.addr += ilc; + case ILEN_LATER_INC: + ilen = get_ilen(cpu_ldub_code(env, env->psw.addr)); + env->psw.addr += ilen; break; + default: + assert(ilen == 2 || ilen == 4 || ilen == 6); } - qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilc=%d\n", - __func__, env->int_pgm_code, ilc); + qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n", + __func__, env->int_pgm_code, ilen); - lowcore = cpu_physical_memory_map(env->psa, &len, 1); + lowcore = cpu_map_lowcore(env); - lowcore->pgm_ilc = cpu_to_be16(ilc); + lowcore->pgm_ilen = cpu_to_be16(ilen); lowcore->pgm_code = cpu_to_be16(env->int_pgm_code); lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env)); lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr); mask = be64_to_cpu(lowcore->program_new_psw.mask); addr = be64_to_cpu(lowcore->program_new_psw.addr); - cpu_physical_memory_unmap(lowcore, len, 1, len); + cpu_unmap_lowcore(lowcore); DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__, - env->int_pgm_code, ilc, env->psw.mask, + env->int_pgm_code, ilen, env->psw.mask, env->psw.addr); load_psw(env, mask, addr); @@ -538,7 +579,6 @@ static void do_ext_interrupt(CPUS390XState *env) { uint64_t mask, addr; LowCore *lowcore; - hwaddr len = TARGET_PAGE_SIZE; ExtQueue *q; if (!(env->psw.mask & PSW_MASK_EXT)) { @@ -550,7 +590,7 @@ static void do_ext_interrupt(CPUS390XState *env) } q = &env->ext_queue[env->ext_index]; - lowcore = cpu_physical_memory_map(env->psa, &len, 1); + lowcore = cpu_map_lowcore(env); lowcore->ext_int_code = cpu_to_be16(q->code); lowcore->ext_params = cpu_to_be32(q->param); @@ -561,7 +601,7 @@ static void do_ext_interrupt(CPUS390XState *env) mask = be64_to_cpu(lowcore->external_new_psw.mask); addr = be64_to_cpu(lowcore->external_new_psw.addr); - cpu_physical_memory_unmap(lowcore, len, 1, len); + cpu_unmap_lowcore(lowcore); env->ext_index--; if (env->ext_index == -1) { @@ -574,12 +614,148 @@ static void do_ext_interrupt(CPUS390XState *env) load_psw(env, mask, addr); } -void do_interrupt(CPUS390XState *env) +static void do_io_interrupt(CPUS390XState *env) { + LowCore *lowcore; + IOIntQueue *q; + uint8_t isc; + int disable = 1; + int found = 0; + + if (!(env->psw.mask & PSW_MASK_IO)) { + cpu_abort(env, "I/O int w/o I/O mask\n"); + } + + for (isc = 0; isc < ARRAY_SIZE(env->io_index); isc++) { + uint64_t isc_bits; + + if (env->io_index[isc] < 0) { + continue; + } + if (env->io_index[isc] > MAX_IO_QUEUE) { + cpu_abort(env, "I/O queue overrun for isc %d: %d\n", + isc, env->io_index[isc]); + } + + q = &env->io_queue[env->io_index[isc]][isc]; + isc_bits = ISC_TO_ISC_BITS(IO_INT_WORD_ISC(q->word)); + if (!(env->cregs[6] & isc_bits)) { + disable = 0; + continue; + } + if (!found) { + uint64_t mask, addr; + + found = 1; + lowcore = cpu_map_lowcore(env); + + lowcore->subchannel_id = cpu_to_be16(q->id); + lowcore->subchannel_nr = cpu_to_be16(q->nr); + lowcore->io_int_parm = cpu_to_be32(q->parm); + lowcore->io_int_word = cpu_to_be32(q->word); + lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env)); + lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr); + mask = be64_to_cpu(lowcore->io_new_psw.mask); + addr = be64_to_cpu(lowcore->io_new_psw.addr); + + cpu_unmap_lowcore(lowcore); + + env->io_index[isc]--; + + DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, + env->psw.mask, env->psw.addr); + load_psw(env, mask, addr); + } + if (env->io_index[isc] >= 0) { + disable = 0; + } + continue; + } + + if (disable) { + env->pending_int &= ~INTERRUPT_IO; + } + +} + +static void do_mchk_interrupt(CPUS390XState *env) +{ + uint64_t mask, addr; + LowCore *lowcore; + MchkQueue *q; + int i; + + if (!(env->psw.mask & PSW_MASK_MCHECK)) { + cpu_abort(env, "Machine check w/o mchk mask\n"); + } + + if (env->mchk_index < 0 || env->mchk_index > MAX_MCHK_QUEUE) { + cpu_abort(env, "Mchk queue overrun: %d\n", env->mchk_index); + } + + q = &env->mchk_queue[env->mchk_index]; + + if (q->type != 1) { + /* Don't know how to handle this... */ + cpu_abort(env, "Unknown machine check type %d\n", q->type); + } + if (!(env->cregs[14] & (1 << 28))) { + /* CRW machine checks disabled */ + return; + } + + lowcore = cpu_map_lowcore(env); + + for (i = 0; i < 16; i++) { + lowcore->floating_pt_save_area[i] = cpu_to_be64(env->fregs[i].ll); + lowcore->gpregs_save_area[i] = cpu_to_be64(env->regs[i]); + lowcore->access_regs_save_area[i] = cpu_to_be32(env->aregs[i]); + lowcore->cregs_save_area[i] = cpu_to_be64(env->cregs[i]); + } + lowcore->prefixreg_save_area = cpu_to_be32(env->psa); + lowcore->fpt_creg_save_area = cpu_to_be32(env->fpc); + lowcore->tod_progreg_save_area = cpu_to_be32(env->todpr); + lowcore->cpu_timer_save_area[0] = cpu_to_be32(env->cputm >> 32); + lowcore->cpu_timer_save_area[1] = cpu_to_be32((uint32_t)env->cputm); + lowcore->clock_comp_save_area[0] = cpu_to_be32(env->ckc >> 32); + lowcore->clock_comp_save_area[1] = cpu_to_be32((uint32_t)env->ckc); + + lowcore->mcck_interruption_code[0] = cpu_to_be32(0x00400f1d); + lowcore->mcck_interruption_code[1] = cpu_to_be32(0x40330000); + lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env)); + lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr); + mask = be64_to_cpu(lowcore->mcck_new_psw.mask); + addr = be64_to_cpu(lowcore->mcck_new_psw.addr); + + cpu_unmap_lowcore(lowcore); + + env->mchk_index--; + if (env->mchk_index == -1) { + env->pending_int &= ~INTERRUPT_MCHK; + } + + DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, + env->psw.mask, env->psw.addr); + + load_psw(env, mask, addr); +} + +void s390_cpu_do_interrupt(CPUState *cs) +{ + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n", __func__, env->exception_index, env->psw.addr); - s390_add_running_cpu(env); + s390_add_running_cpu(cpu); + /* handle machine checks */ + if ((env->psw.mask & PSW_MASK_MCHECK) && + (env->exception_index == -1)) { + if (env->pending_int & INTERRUPT_MCHK) { + env->exception_index = EXCP_MCHK; + } + } /* handle external interrupts */ if ((env->psw.mask & PSW_MASK_EXT) && env->exception_index == -1) { @@ -587,17 +763,24 @@ void do_interrupt(CPUS390XState *env) /* code is already in env */ env->exception_index = EXCP_EXT; } else if (env->pending_int & INTERRUPT_TOD) { - cpu_inject_ext(env, 0x1004, 0, 0); + cpu_inject_ext(cpu, 0x1004, 0, 0); env->exception_index = EXCP_EXT; env->pending_int &= ~INTERRUPT_EXT; env->pending_int &= ~INTERRUPT_TOD; } else if (env->pending_int & INTERRUPT_CPUTIMER) { - cpu_inject_ext(env, 0x1005, 0, 0); + cpu_inject_ext(cpu, 0x1005, 0, 0); env->exception_index = EXCP_EXT; env->pending_int &= ~INTERRUPT_EXT; env->pending_int &= ~INTERRUPT_TOD; } } + /* handle I/O interrupts */ + if ((env->psw.mask & PSW_MASK_IO) && + (env->exception_index == -1)) { + if (env->pending_int & INTERRUPT_IO) { + env->exception_index = EXCP_IO; + } + } switch (env->exception_index) { case EXCP_PGM: @@ -609,11 +792,17 @@ void do_interrupt(CPUS390XState *env) case EXCP_EXT: do_ext_interrupt(env); break; + case EXCP_IO: + do_io_interrupt(env); + break; + case EXCP_MCHK: + do_mchk_interrupt(env); + break; } env->exception_index = -1; if (!env->pending_int) { - env->interrupt_request &= ~CPU_INTERRUPT_HARD; + cs->interrupt_request &= ~CPU_INTERRUPT_HARD; } } diff --git a/target-s390x/helper.h b/target-s390x/helper.h index c4926c52ad..0d80aa046f 100644 --- a/target-s390x/helper.h +++ b/target-s390x/helper.h @@ -1,152 +1,119 @@ #include "exec/def-helper.h" -DEF_HELPER_2(exception, void, env, i32) -DEF_HELPER_4(nc, i32, env, i32, i64, i64) -DEF_HELPER_4(oc, i32, env, i32, i64, i64) -DEF_HELPER_4(xc, i32, env, i32, i64, i64) -DEF_HELPER_4(mvc, void, env, i32, i64, i64) -DEF_HELPER_4(clc, i32, env, i32, i64, i64) +DEF_HELPER_2(exception, noreturn, env, i32) +DEF_HELPER_FLAGS_4(nc, TCG_CALL_NO_WG, i32, env, i32, i64, i64) +DEF_HELPER_FLAGS_4(oc, TCG_CALL_NO_WG, i32, env, i32, i64, i64) +DEF_HELPER_FLAGS_4(xc, TCG_CALL_NO_WG, i32, env, i32, i64, i64) +DEF_HELPER_FLAGS_4(mvc, TCG_CALL_NO_WG, void, env, i32, i64, i64) +DEF_HELPER_FLAGS_4(clc, TCG_CALL_NO_WG, i32, env, i32, i64, i64) DEF_HELPER_3(mvcl, i32, env, i32, i32) -DEF_HELPER_FLAGS_1(set_cc_comp_s32, TCG_CALL_NO_RWG_SE, i32, s32) -DEF_HELPER_FLAGS_1(set_cc_comp_s64, TCG_CALL_NO_RWG_SE, i32, s64) -DEF_HELPER_FLAGS_2(set_cc_icm, TCG_CALL_NO_RWG_SE, i32, i32, i32) -DEF_HELPER_4(clm, i32, env, i32, i32, i64) -DEF_HELPER_4(stcm, void, env, i32, i32, i64) -DEF_HELPER_3(mlg, void, env, i32, i64) -DEF_HELPER_3(dlg, void, env, i32, i64) -DEF_HELPER_FLAGS_3(set_cc_add64, TCG_CALL_NO_RWG_SE, i32, s64, s64, s64) -DEF_HELPER_FLAGS_3(set_cc_addu64, TCG_CALL_NO_RWG_SE, i32, i64, i64, i64) -DEF_HELPER_FLAGS_3(set_cc_add32, TCG_CALL_NO_RWG_SE, i32, s32, s32, s32) -DEF_HELPER_FLAGS_3(set_cc_addu32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) -DEF_HELPER_FLAGS_3(set_cc_sub64, TCG_CALL_NO_RWG_SE, i32, s64, s64, s64) -DEF_HELPER_FLAGS_3(set_cc_subu64, TCG_CALL_NO_RWG_SE, i32, i64, i64, i64) -DEF_HELPER_FLAGS_3(set_cc_sub32, TCG_CALL_NO_RWG_SE, i32, s32, s32, s32) -DEF_HELPER_FLAGS_3(set_cc_subu32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) -DEF_HELPER_4(srst, i32, env, i32, i32, i32) -DEF_HELPER_4(clst, i32, env, i32, i32, i32) +DEF_HELPER_FLAGS_4(clm, TCG_CALL_NO_WG, i32, env, i32, i32, i64) +DEF_HELPER_FLAGS_3(divs32, TCG_CALL_NO_WG, s64, env, s64, s64) +DEF_HELPER_FLAGS_3(divu32, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(divs64, TCG_CALL_NO_WG, s64, env, s64, s64) +DEF_HELPER_FLAGS_4(divu64, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_4(srst, i64, env, i64, i64, i64) +DEF_HELPER_4(clst, i64, env, i64, i64, i64) DEF_HELPER_4(mvpg, void, env, i64, i64, i64) -DEF_HELPER_4(mvst, void, env, i32, i32, i32) -DEF_HELPER_4(csg, i32, env, i32, i64, i32) -DEF_HELPER_4(cdsg, i32, env, i32, i64, i32) -DEF_HELPER_4(cs, i32, env, i32, i64, i32) +DEF_HELPER_4(mvst, i64, env, i64, i64, i64) DEF_HELPER_5(ex, i32, env, i32, i64, i64, i64) DEF_HELPER_FLAGS_1(abs_i32, TCG_CALL_NO_RWG_SE, i32, s32) DEF_HELPER_FLAGS_1(nabs_i32, TCG_CALL_NO_RWG_SE, s32, s32) DEF_HELPER_FLAGS_1(abs_i64, TCG_CALL_NO_RWG_SE, i64, s64) DEF_HELPER_FLAGS_1(nabs_i64, TCG_CALL_NO_RWG_SE, s64, s64) -DEF_HELPER_4(stcmh, void, env, i32, i64, i32) -DEF_HELPER_4(icmh, i32, env, i32, i64, i32) -DEF_HELPER_3(ipm, void, env, i32, i32) -DEF_HELPER_FLAGS_3(addc_u32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) -DEF_HELPER_FLAGS_3(set_cc_addc_u64, TCG_CALL_NO_RWG_SE, i32, i64, i64, i64) -DEF_HELPER_4(stam, void, env, i32, i64, i32) -DEF_HELPER_4(lam, void, env, i32, i64, i32) +DEF_HELPER_FLAGS_4(stam, TCG_CALL_NO_WG, void, env, i32, i64, i32) +DEF_HELPER_FLAGS_4(lam, TCG_CALL_NO_WG, void, env, i32, i64, i32) DEF_HELPER_4(mvcle, i32, env, i32, i64, i32) DEF_HELPER_4(clcle, i32, env, i32, i64, i32) -DEF_HELPER_4(slb, i32, env, i32, i32, i32) -DEF_HELPER_5(slbg, i32, env, i32, i32, i64, i64) -DEF_HELPER_3(cefbr, void, env, i32, s32) -DEF_HELPER_3(cdfbr, void, env, i32, s32) -DEF_HELPER_3(cxfbr, void, env, i32, s32) -DEF_HELPER_3(cegbr, void, env, i32, s64) -DEF_HELPER_3(cdgbr, void, env, i32, s64) -DEF_HELPER_3(cxgbr, void, env, i32, s64) -DEF_HELPER_3(adbr, i32, env, i32, i32) -DEF_HELPER_3(aebr, i32, env, i32, i32) -DEF_HELPER_3(sebr, i32, env, i32, i32) -DEF_HELPER_3(sdbr, i32, env, i32, i32) -DEF_HELPER_3(debr, void, env, i32, i32) -DEF_HELPER_3(dxbr, void, env, i32, i32) -DEF_HELPER_3(mdbr, void, env, i32, i32) -DEF_HELPER_3(mxbr, void, env, i32, i32) -DEF_HELPER_3(ldebr, void, env, i32, i32) -DEF_HELPER_3(ldxbr, void, env, i32, i32) -DEF_HELPER_3(lxdbr, void, env, i32, i32) -DEF_HELPER_3(ledbr, void, env, i32, i32) -DEF_HELPER_3(lexbr, void, env, i32, i32) -DEF_HELPER_3(lpebr, i32, env, i32, i32) -DEF_HELPER_3(lpdbr, i32, env, i32, i32) -DEF_HELPER_3(lpxbr, i32, env, i32, i32) -DEF_HELPER_3(ltebr, i32, env, i32, i32) -DEF_HELPER_3(ltdbr, i32, env, i32, i32) -DEF_HELPER_3(ltxbr, i32, env, i32, i32) -DEF_HELPER_3(lcebr, i32, env, i32, i32) -DEF_HELPER_3(lcdbr, i32, env, i32, i32) -DEF_HELPER_3(lcxbr, i32, env, i32, i32) -DEF_HELPER_3(aeb, void, env, i32, i32) -DEF_HELPER_3(deb, void, env, i32, i32) -DEF_HELPER_3(meeb, void, env, i32, i32) -DEF_HELPER_3(cdb, i32, env, i32, i64) -DEF_HELPER_3(adb, i32, env, i32, i64) -DEF_HELPER_3(seb, void, env, i32, i32) -DEF_HELPER_3(sdb, i32, env, i32, i64) -DEF_HELPER_3(mdb, void, env, i32, i64) -DEF_HELPER_3(ddb, void, env, i32, i64) -DEF_HELPER_FLAGS_3(cebr, TCG_CALL_NO_SE, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(cdbr, TCG_CALL_NO_SE, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(cxbr, TCG_CALL_NO_SE, i32, env, i32, i32) -DEF_HELPER_4(cgebr, i32, env, i32, i32, i32) -DEF_HELPER_4(cgdbr, i32, env, i32, i32, i32) -DEF_HELPER_4(cgxbr, i32, env, i32, i32, i32) -DEF_HELPER_2(lzer, void, env, i32) -DEF_HELPER_2(lzdr, void, env, i32) -DEF_HELPER_2(lzxr, void, env, i32) -DEF_HELPER_4(cfebr, i32, env, i32, i32, i32) -DEF_HELPER_4(cfdbr, i32, env, i32, i32, i32) -DEF_HELPER_4(cfxbr, i32, env, i32, i32, i32) -DEF_HELPER_3(axbr, i32, env, i32, i32) -DEF_HELPER_3(sxbr, i32, env, i32, i32) -DEF_HELPER_3(meebr, void, env, i32, i32) -DEF_HELPER_3(ddbr, void, env, i32, i32) -DEF_HELPER_4(madb, void, env, i32, i64, i32) -DEF_HELPER_4(maebr, void, env, i32, i32, i32) -DEF_HELPER_4(madbr, void, env, i32, i32, i32) -DEF_HELPER_4(msdbr, void, env, i32, i32, i32) -DEF_HELPER_3(ldeb, void, env, i32, i64) -DEF_HELPER_3(lxdb, void, env, i32, i64) -DEF_HELPER_FLAGS_3(tceb, TCG_CALL_NO_SE, i32, env, i32, i64) -DEF_HELPER_FLAGS_3(tcdb, TCG_CALL_NO_SE, i32, env, i32, i64) -DEF_HELPER_FLAGS_3(tcxb, TCG_CALL_NO_SE, i32, env, i32, i64) -DEF_HELPER_3(flogr, i32, env, i32, i64) -DEF_HELPER_3(sqdbr, void, env, i32, i32) +DEF_HELPER_3(cegb, i64, env, s64, i32) +DEF_HELPER_3(cdgb, i64, env, s64, i32) +DEF_HELPER_3(cxgb, i64, env, s64, i32) +DEF_HELPER_3(celgb, i64, env, i64, i32) +DEF_HELPER_3(cdlgb, i64, env, i64, i32) +DEF_HELPER_3(cxlgb, i64, env, i64, i32) +DEF_HELPER_FLAGS_3(aeb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(adb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_5(axb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_3(seb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(sdb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_5(sxb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_3(deb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(ddb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_5(dxb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_3(meeb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(mdeb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(mdb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_5(mxb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_4(mxdb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_2(ldeb, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_3(ldxb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(lxdb, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(lxeb, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(ledb, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_3(lexb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(ceb, TCG_CALL_NO_WG_SE, i32, env, i64, i64) +DEF_HELPER_FLAGS_3(cdb, TCG_CALL_NO_WG_SE, i32, env, i64, i64) +DEF_HELPER_FLAGS_5(cxb, TCG_CALL_NO_WG_SE, i32, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_3(cgeb, TCG_CALL_NO_WG, i64, env, i64, i32) +DEF_HELPER_FLAGS_3(cgdb, TCG_CALL_NO_WG, i64, env, i64, i32) +DEF_HELPER_FLAGS_4(cgxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) +DEF_HELPER_FLAGS_3(cfeb, TCG_CALL_NO_WG, i64, env, i64, i32) +DEF_HELPER_FLAGS_3(cfdb, TCG_CALL_NO_WG, i64, env, i64, i32) +DEF_HELPER_FLAGS_4(cfxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) +DEF_HELPER_FLAGS_3(clgeb, TCG_CALL_NO_WG, i64, env, i64, i32) +DEF_HELPER_FLAGS_3(clgdb, TCG_CALL_NO_WG, i64, env, i64, i32) +DEF_HELPER_FLAGS_4(clgxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) +DEF_HELPER_FLAGS_3(clfeb, TCG_CALL_NO_WG, i64, env, i64, i32) +DEF_HELPER_FLAGS_3(clfdb, TCG_CALL_NO_WG, i64, env, i64, i32) +DEF_HELPER_FLAGS_4(clfxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) +DEF_HELPER_FLAGS_4(maeb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(madb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(mseb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(msdb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_2(tceb, TCG_CALL_NO_RWG_SE, i32, i64, i64) +DEF_HELPER_FLAGS_2(tcdb, TCG_CALL_NO_RWG_SE, i32, i64, i64) +DEF_HELPER_FLAGS_3(tcxb, TCG_CALL_NO_RWG_SE, i32, i64, i64, i64) +DEF_HELPER_FLAGS_1(clz, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_2(sqeb, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(sqdb, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_3(sqxb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_1(cvd, TCG_CALL_NO_RWG_SE, i64, s32) -DEF_HELPER_4(unpk, void, env, i32, i64, i64) -DEF_HELPER_4(tr, void, env, i32, i64, i64) +DEF_HELPER_FLAGS_4(unpk, TCG_CALL_NO_WG, void, env, i32, i64, i64) +DEF_HELPER_FLAGS_4(tr, TCG_CALL_NO_WG, void, env, i32, i64, i64) +DEF_HELPER_4(cksm, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_5(calc_cc, TCG_CALL_NO_RWG_SE, i32, env, i32, i64, i64, i64) +DEF_HELPER_FLAGS_2(sfpc, TCG_CALL_NO_RWG, void, env, i64) +DEF_HELPER_FLAGS_2(sfas, TCG_CALL_NO_WG, void, env, i64) +DEF_HELPER_FLAGS_1(popcnt, TCG_CALL_NO_RWG_SE, i64, i64) -DEF_HELPER_3(servc, i32, env, i32, i64) +#ifndef CONFIG_USER_ONLY +DEF_HELPER_3(servc, i32, env, i64, i64) DEF_HELPER_4(diag, i64, env, i32, i64, i64) -DEF_HELPER_3(load_psw, void, env, i64, i64) -DEF_HELPER_1(program_interrupt, void, i32) -DEF_HELPER_FLAGS_2(stidp, TCG_CALL_NO_RWG, void, env, i64) +DEF_HELPER_3(load_psw, noreturn, env, i64, i64) DEF_HELPER_FLAGS_2(spx, TCG_CALL_NO_RWG, void, env, i64) -DEF_HELPER_FLAGS_1(sck, TCG_CALL_NO_RWG, i32, i64) -DEF_HELPER_2(stck, i32, env, i64) -DEF_HELPER_2(stcke, i32, env, i64) +DEF_HELPER_FLAGS_1(stck, TCG_CALL_NO_RWG_SE, i64, env) DEF_HELPER_FLAGS_2(sckc, TCG_CALL_NO_RWG, void, env, i64) -DEF_HELPER_FLAGS_2(stckc, TCG_CALL_NO_RWG, void, env, i64) +DEF_HELPER_FLAGS_1(stckc, TCG_CALL_NO_RWG, i64, env) DEF_HELPER_FLAGS_2(spt, TCG_CALL_NO_RWG, void, env, i64) -DEF_HELPER_FLAGS_2(stpt, TCG_CALL_NO_RWG, void, env, i64) -DEF_HELPER_4(stsi, i32, env, i64, i32, i32) -DEF_HELPER_4(lctl, void, env, i32, i64, i32) -DEF_HELPER_4(lctlg, void, env, i32, i64, i32) -DEF_HELPER_4(stctl, void, env, i32, i64, i32) -DEF_HELPER_4(stctg, void, env, i32, i64, i32) +DEF_HELPER_FLAGS_1(stpt, TCG_CALL_NO_RWG, i64, env) +DEF_HELPER_4(stsi, i32, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(lctl, TCG_CALL_NO_WG, void, env, i32, i64, i32) +DEF_HELPER_FLAGS_4(lctlg, TCG_CALL_NO_WG, void, env, i32, i64, i32) +DEF_HELPER_FLAGS_4(stctl, TCG_CALL_NO_WG, void, env, i32, i64, i32) +DEF_HELPER_FLAGS_4(stctg, TCG_CALL_NO_WG, void, env, i32, i64, i32) DEF_HELPER_FLAGS_2(tprot, TCG_CALL_NO_RWG, i32, i64, i64) DEF_HELPER_FLAGS_2(iske, TCG_CALL_NO_RWG_SE, i64, env, i64) -DEF_HELPER_FLAGS_3(sske, TCG_CALL_NO_RWG, void, env, i32, i64) -DEF_HELPER_FLAGS_3(rrbe, TCG_CALL_NO_RWG, i32, env, i32, i64) -DEF_HELPER_3(csp, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(sske, TCG_CALL_NO_RWG, void, env, i64, i64) +DEF_HELPER_FLAGS_2(rrbe, TCG_CALL_NO_RWG, i32, env, i64) +DEF_HELPER_3(csp, i32, env, i32, i64) DEF_HELPER_4(mvcs, i32, env, i64, i64, i64) DEF_HELPER_4(mvcp, i32, env, i64, i64, i64) DEF_HELPER_4(sigp, i32, env, i64, i32, i64) -DEF_HELPER_2(sacf, void, env, i64) +DEF_HELPER_FLAGS_2(sacf, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_3(ipte, TCG_CALL_NO_RWG, void, env, i64, i64) DEF_HELPER_FLAGS_1(ptlb, TCG_CALL_NO_RWG, void, env) -DEF_HELPER_3(lra, i32, env, i64, i32) -DEF_HELPER_3(stura, void, env, i64, i32) -DEF_HELPER_3(cksm, void, env, i32, i32) - -DEF_HELPER_FLAGS_5(calc_cc, TCG_CALL_NO_RWG_SE, - i32, env, i32, i64, i64, i64) +DEF_HELPER_2(lra, i64, env, i64) +DEF_HELPER_FLAGS_3(stura, TCG_CALL_NO_WG, void, env, i64, i64) +#endif #include "exec/def-helper.h" diff --git a/target-s390x/insn-data.def b/target-s390x/insn-data.def new file mode 100644 index 0000000000..b42ebb6a1a --- /dev/null +++ b/target-s390x/insn-data.def @@ -0,0 +1,813 @@ +/* ADD */ + C(0x1a00, AR, RR_a, Z, r1, r2, new, r1_32, add, adds32) + C(0xb9f8, ARK, RRF_a, DO, r2, r3, new, r1_32, add, adds32) + C(0x5a00, A, RX_a, Z, r1, m2_32s, new, r1_32, add, adds32) + C(0xe35a, AY, RXY_a, LD, r1, m2_32s, new, r1_32, add, adds32) + C(0xb908, AGR, RRE, Z, r1, r2, r1, 0, add, adds64) + C(0xb918, AGFR, RRE, Z, r1, r2_32s, r1, 0, add, adds64) + C(0xb9e8, AGRK, RRF_a, DO, r2, r3, r1, 0, add, adds64) + C(0xe308, AG, RXY_a, Z, r1, m2_64, r1, 0, add, adds64) + C(0xe318, AGF, RXY_a, Z, r1, m2_32s, r1, 0, add, adds64) + C(0xb30a, AEBR, RRE, Z, e1, e2, new, e1, aeb, f32) + C(0xb31a, ADBR, RRE, Z, f1_o, f2_o, f1, 0, adb, f64) + C(0xb34a, AXBR, RRE, Z, 0, x2_o, x1, 0, axb, f128) + C(0xed0a, AEB, RXE, Z, e1, m2_32u, new, e1, aeb, f32) + C(0xed1a, ADB, RXE, Z, f1_o, m2_64, f1, 0, adb, f64) +/* ADD IMMEDIATE */ + C(0xc209, AFI, RIL_a, EI, r1, i2, new, r1_32, add, adds32) + C(0xeb6a, ASI, SIY, GIE, m1_32s, i2, new, m1_32, add, adds32) + C(0xecd8, AHIK, RIE_d, DO, r3, i2, new, r1_32, add, adds32) + C(0xc208, AGFI, RIL_a, EI, r1, i2, r1, 0, add, adds64) + C(0xeb7a, AGSI, SIY, GIE, m1_64, i2, new, m1_64, add, adds64) + C(0xecd9, AGHIK, RIE_d, DO, r3, i2, r1, 0, add, adds64) +/* ADD HALFWORD */ + C(0x4a00, AH, RX_a, Z, r1, m2_16s, new, r1_32, add, adds32) + C(0xe37a, AHY, RXY_a, LD, r1, m2_16s, new, r1_32, add, adds32) +/* ADD HALFWORD IMMEDIATE */ + C(0xa70a, AHI, RI_a, Z, r1, i2, new, r1_32, add, adds32) + C(0xa70b, AGHI, RI_a, Z, r1, i2, r1, 0, add, adds64) + +/* ADD LOGICAL */ + C(0x1e00, ALR, RR_a, Z, r1, r2, new, r1_32, add, addu32) + C(0xb9fa, ALRK, RRF_a, DO, r2, r3, new, r1_32, add, addu32) + C(0x5e00, AL, RX_a, Z, r1, m2_32u, new, r1_32, add, addu32) + C(0xe35e, ALY, RXY_a, LD, r1, m2_32u, new, r1_32, add, addu32) + C(0xb90a, ALGR, RRE, Z, r1, r2, r1, 0, add, addu64) + C(0xb91a, ALGFR, RRE, Z, r1, r2_32u, r1, 0, add, addu64) + C(0xb9ea, ALGRK, RRF_a, DO, r2, r3, r1, 0, add, addu64) + C(0xe30a, ALG, RXY_a, Z, r1, m2_64, r1, 0, add, addu64) + C(0xe31a, ALGF, RXY_a, Z, r1, m2_32u, r1, 0, add, addu64) +/* ADD LOGICAL IMMEDIATE */ + C(0xc20b, ALFI, RIL_a, EI, r1, i2_32u, new, r1_32, add, addu32) + C(0xc20a, ALGFI, RIL_a, EI, r1, i2_32u, r1, 0, add, addu64) +/* ADD LOGICAL WITH SIGNED IMMEDIATE */ + C(0xeb6e, ALSI, SIY, GIE, m1_32u, i2, new, m1_32, add, addu32) + C(0xecda, ALHSIK, RIE_d, DO, r3, i2, new, r1_32, add, addu32) + C(0xeb7e, ALGSI, SIY, GIE, m1_64, i2, new, m1_64, add, addu64) + C(0xecdb, ALGHSIK, RIE_d, DO, r3, i2, r1, 0, add, addu64) +/* ADD LOGICAL WITH CARRY */ + C(0xb998, ALCR, RRE, Z, r1, r2, new, r1_32, addc, addc32) + C(0xb988, ALCGR, RRE, Z, r1, r2, r1, 0, addc, addc64) + C(0xe398, ALC, RXY_a, Z, r1, m2_32u, new, r1_32, addc, addc32) + C(0xe388, ALCG, RXY_a, Z, r1, m2_64, r1, 0, addc, addc64) + +/* AND */ + C(0x1400, NR, RR_a, Z, r1, r2, new, r1_32, and, nz32) + C(0xb9f4, NRK, RRF_a, DO, r2, r3, new, r1_32, and, nz32) + C(0x5400, N, RX_a, Z, r1, m2_32s, new, r1_32, and, nz32) + C(0xe354, NY, RXY_a, LD, r1, m2_32s, new, r1_32, and, nz32) + C(0xb980, NGR, RRE, Z, r1, r2, r1, 0, and, nz64) + C(0xb9e4, NGRK, RRF_a, DO, r2, r3, r1, 0, and, nz64) + C(0xe380, NG, RXY_a, Z, r1, m2_64, r1, 0, and, nz64) + C(0xd400, NC, SS_a, Z, la1, a2, 0, 0, nc, 0) +/* AND IMMEDIATE */ + D(0xc00a, NIHF, RIL_a, EI, r1_o, i2_32u, r1, 0, andi, 0, 0x2020) + D(0xc00b, NILF, RIL_a, EI, r1_o, i2_32u, r1, 0, andi, 0, 0x2000) + D(0xa504, NIHH, RI_a, Z, r1_o, i2_16u, r1, 0, andi, 0, 0x1030) + D(0xa505, NIHL, RI_a, Z, r1_o, i2_16u, r1, 0, andi, 0, 0x1020) + D(0xa506, NILH, RI_a, Z, r1_o, i2_16u, r1, 0, andi, 0, 0x1010) + D(0xa507, NILL, RI_a, Z, r1_o, i2_16u, r1, 0, andi, 0, 0x1000) + C(0x9400, NI, SI, Z, m1_8u, i2_8u, new, m1_8, and, nz64) + C(0xeb54, NIY, SIY, LD, m1_8u, i2_8u, new, m1_8, and, nz64) + +/* BRANCH AND SAVE */ + C(0x0d00, BASR, RR_a, Z, 0, r2_nz, r1, 0, bas, 0) + C(0x4d00, BAS, RX_a, Z, 0, a2, r1, 0, bas, 0) +/* BRANCH RELATIVE AND SAVE */ + C(0xa705, BRAS, RI_b, Z, 0, 0, r1, 0, basi, 0) + C(0xc005, BRASL, RIL_b, Z, 0, 0, r1, 0, basi, 0) +/* BRANCH ON CONDITION */ + C(0x0700, BCR, RR_b, Z, 0, r2_nz, 0, 0, bc, 0) + C(0x4700, BC, RX_b, Z, 0, a2, 0, 0, bc, 0) +/* BRANCH RELATIVE ON CONDITION */ + C(0xa704, BRC, RI_c, Z, 0, 0, 0, 0, bc, 0) + C(0xc004, BRCL, RIL_c, Z, 0, 0, 0, 0, bc, 0) +/* BRANCH ON COUNT */ + C(0x0600, BCTR, RR_a, Z, 0, r2_nz, 0, 0, bct32, 0) + C(0xb946, BCTGR, RRE, Z, 0, r2_nz, 0, 0, bct64, 0) + C(0x4600, BCT, RX_a, Z, 0, a2, 0, 0, bct32, 0) + C(0xe346, BCTG, RXY_a, Z, 0, a2, 0, 0, bct64, 0) +/* BRANCH RELATIVE ON COUNT */ + C(0xa706, BRCT, RI_b, Z, 0, 0, 0, 0, bct32, 0) + C(0xa707, BRCTG, RI_b, Z, 0, 0, 0, 0, bct64, 0) +/* BRANCH ON INDEX */ + D(0x8600, BXH, RS_a, Z, 0, a2, 0, 0, bx32, 0, 0) + D(0x8700, BXLE, RS_a, Z, 0, a2, 0, 0, bx32, 0, 1) + D(0xeb44, BXHG, RSY_a, Z, 0, a2, 0, 0, bx64, 0, 0) + D(0xeb45, BXLEG, RSY_a, Z, 0, a2, 0, 0, bx64, 0, 1) +/* BRANCH RELATIVE ON INDEX */ + D(0x8400, BRXH, RSI, Z, 0, 0, 0, 0, bx32, 0, 0) + D(0x8500, BRXLE, RSI, Z, 0, 0, 0, 0, bx32, 0, 1) + D(0xec44, BRXHG, RIE_e, Z, 0, 0, 0, 0, bx64, 0, 0) + D(0xec45, BRXHLE, RIE_e, Z, 0, 0, 0, 0, bx64, 0, 1) + +/* CHECKSUM */ + C(0xb241, CKSM, RRE, Z, r1_o, ra2, new, r1_32, cksm, 0) + +/* COPY SIGN */ + C(0xb372, CPSDR, RRF_b, FPSSH, f3_o, f2_o, f1, 0, cps, 0) + +/* COMPARE */ + C(0x1900, CR, RR_a, Z, r1_o, r2_o, 0, 0, 0, cmps32) + C(0x5900, C, RX_a, Z, r1_o, m2_32s, 0, 0, 0, cmps32) + C(0xe359, CY, RXY_a, LD, r1_o, m2_32s, 0, 0, 0, cmps32) + C(0xb920, CGR, RRE, Z, r1_o, r2_o, 0, 0, 0, cmps64) + C(0xb930, CGFR, RRE, Z, r1_o, r2_32s, 0, 0, 0, cmps64) + C(0xe320, CG, RXY_a, Z, r1_o, m2_64, 0, 0, 0, cmps64) + C(0xe330, CGF, RXY_a, Z, r1_o, m2_32s, 0, 0, 0, cmps64) + C(0xb309, CEBR, RRE, Z, e1, e2, 0, 0, ceb, 0) + C(0xb319, CDBR, RRE, Z, f1_o, f2_o, 0, 0, cdb, 0) + C(0xb349, CXBR, RRE, Z, x1_o, x2_o, 0, 0, cxb, 0) + C(0xed09, CEB, RXE, Z, e1, m2_32u, 0, 0, ceb, 0) + C(0xed19, CDB, RXE, Z, f1_o, m2_64, 0, 0, cdb, 0) +/* COMPARE IMMEDIATE */ + C(0xc20d, CFI, RIL_a, EI, r1, i2, 0, 0, 0, cmps32) + C(0xc20c, CGFI, RIL_a, EI, r1, i2, 0, 0, 0, cmps64) +/* COMPARE RELATIVE LONG */ + C(0xc60d, CRL, RIL_b, GIE, r1, mri2_32s, 0, 0, 0, cmps32) + C(0xc608, CGRL, RIL_b, GIE, r1, mri2_64, 0, 0, 0, cmps64) + C(0xc60c, CGFRL, RIL_b, GIE, r1, mri2_32s, 0, 0, 0, cmps64) +/* COMPARE HALFWORD */ + C(0x4900, CH, RX_a, Z, r1_o, m2_16s, 0, 0, 0, cmps32) + C(0xe379, CHY, RXY_a, LD, r1_o, m2_16s, 0, 0, 0, cmps32) + C(0xe334, CGH, RXY_a, GIE, r1_o, m2_16s, 0, 0, 0, cmps64) +/* COMPARE HALFWORD IMMEDIATE */ + C(0xa70e, CHI, RI_a, Z, r1_o, i2, 0, 0, 0, cmps32) + C(0xa70f, CGHI, RI_a, Z, r1_o, i2, 0, 0, 0, cmps64) + C(0xe554, CHHSI, SIL, GIE, m1_16s, i2, 0, 0, 0, cmps64) + C(0xe55c, CHSI, SIL, GIE, m1_32s, i2, 0, 0, 0, cmps64) + C(0xe558, CGHSI, SIL, GIE, m1_64, i2, 0, 0, 0, cmps64) +/* COMPARE HALFWORD RELATIVE LONG */ + C(0xc605, CHRL, RIL_a, GIE, r1_o, mri2_32s, 0, 0, 0, cmps32) + C(0xc604, CGHRL, RIL_a, GIE, r1_o, mri2_64, 0, 0, 0, cmps64) + +/* COMPARE LOGICAL */ + C(0x1500, CLR, RR_a, Z, r1, r2, 0, 0, 0, cmpu32) + C(0x5500, CL, RX_a, Z, r1, m2_32s, 0, 0, 0, cmpu32) + C(0xe355, CLY, RXY_a, LD, r1, m2_32s, 0, 0, 0, cmpu32) + C(0xb921, CLGR, RRE, Z, r1, r2, 0, 0, 0, cmpu64) + C(0xb931, CLGFR, RRE, Z, r1, r2_32u, 0, 0, 0, cmpu64) + C(0xe321, CLG, RXY_a, Z, r1, m2_64, 0, 0, 0, cmpu64) + C(0xe331, CLGF, RXY_a, Z, r1, m2_32u, 0, 0, 0, cmpu64) + C(0xd500, CLC, SS_a, Z, la1, a2, 0, 0, clc, 0) +/* COMPARE LOGICAL IMMEDIATE */ + C(0xc20f, CLFI, RIL_a, EI, r1, i2, 0, 0, 0, cmpu32) + C(0xc20e, CLGFI, RIL_a, EI, r1, i2_32u, 0, 0, 0, cmpu64) + C(0x9500, CLI, SI, Z, m1_8u, i2_8u, 0, 0, 0, cmpu64) + C(0xeb55, CLIY, SIY, LD, m1_8u, i2_8u, 0, 0, 0, cmpu64) + C(0xe555, CLHHSI, SIL, GIE, m1_16u, i2_16u, 0, 0, 0, cmpu64) + C(0xe55d, CLFHSI, SIL, GIE, m1_32u, i2_16u, 0, 0, 0, cmpu64) + C(0xe559, CLGHSI, SIL, GIE, m1_64, i2_16u, 0, 0, 0, cmpu64) +/* COMPARE LOGICAL RELATIVE LONG */ + C(0xc60f, CLRL, RIL_b, GIE, r1_o, mri2_32u, 0, 0, 0, cmpu32) + C(0xc60a, CLGRL, RIL_b, GIE, r1_o, mri2_64, 0, 0, 0, cmpu64) + C(0xc60e, CLGFRL, RIL_b, GIE, r1_o, mri2_32u, 0, 0, 0, cmpu64) + C(0xc607, CLHRL, RIL_b, GIE, r1_o, mri2_16u, 0, 0, 0, cmpu32) + C(0xc606, CLGHRL, RIL_b, GIE, r1_o, mri2_16u, 0, 0, 0, cmpu64) +/* COMPARE LOGICAL LONG EXTENDED */ + C(0xa900, CLCLE, RS_a, Z, 0, a2, 0, 0, clcle, 0) +/* COMPARE LOGICAL CHARACTERS UNDER MASK */ + C(0xbd00, CLM, RS_b, Z, r1_o, a2, 0, 0, clm, 0) + C(0xeb21, CLMY, RSY_b, LD, r1_o, a2, 0, 0, clm, 0) + C(0xeb20, CLMH, RSY_b, Z, r1_sr32, a2, 0, 0, clm, 0) +/* COMPARE LOGICAL STRING */ + C(0xb25d, CLST, RRE, Z, r1_o, r2_o, 0, 0, clst, 0) + +/* COMPARE AND BRANCH */ + D(0xecf6, CRB, RRS, GIE, r1_32s, r2_32s, 0, 0, cj, 0, 0) + D(0xece4, CGRB, RRS, GIE, r1_o, r2_o, 0, 0, cj, 0, 0) + D(0xec76, CRJ, RIE_b, GIE, r1_32s, r2_32s, 0, 0, cj, 0, 0) + D(0xec64, CGRJ, RIE_b, GIE, r1_o, r2_o, 0, 0, cj, 0, 0) + D(0xecfe, CIB, RIS, GIE, r1_32s, i2, 0, 0, cj, 0, 0) + D(0xecfc, CGIB, RIS, GIE, r1_o, i2, 0, 0, cj, 0, 0) + D(0xec7e, CIJ, RIE_c, GIE, r1_32s, i2, 0, 0, cj, 0, 0) + D(0xec7c, CGIJ, RIE_c, GIE, r1_o, i2, 0, 0, cj, 0, 0) +/* COMPARE LOGICAL AND BRANCH */ + D(0xecf7, CLRB, RRS, GIE, r1_32u, r2_32u, 0, 0, cj, 0, 1) + D(0xece5, CLGRB, RRS, GIE, r1_o, r2_o, 0, 0, cj, 0, 1) + D(0xec77, CLRJ, RIE_b, GIE, r1_32u, r2_32u, 0, 0, cj, 0, 1) + D(0xec65, CLGRJ, RIE_b, GIE, r1_o, r2_o, 0, 0, cj, 0, 1) + D(0xecff, CLIB, RIS, GIE, r1_32u, i2_8u, 0, 0, cj, 0, 1) + D(0xecfd, CLGIB, RIS, GIE, r1_o, i2_8u, 0, 0, cj, 0, 1) + D(0xec7f, CLIJ, RIE_c, GIE, r1_32u, i2_8u, 0, 0, cj, 0, 1) + D(0xec7d, CLGIJ, RIE_c, GIE, r1_o, i2_8u, 0, 0, cj, 0, 1) + +/* COMPARE AND SWAP */ + D(0xba00, CS, RS_a, Z, r3_32u, r1_32u, new, r1_32, cs, 0, 0) + D(0xeb14, CSY, RSY_a, LD, r3_32u, r1_32u, new, r1_32, cs, 0, 0) + D(0xeb30, CSG, RSY_a, Z, r3_o, r1_o, new, r1, cs, 0, 1) +/* COMPARE DOUBLE AND SWAP */ + D(0xbb00, CDS, RS_a, Z, r3_D32, r1_D32, new, r1_D32, cs, 0, 1) + D(0xeb31, CDSY, RSY_a, LD, r3_D32, r1_D32, new, r1_D32, cs, 0, 1) + C(0xeb3e, CDSG, RSY_a, Z, 0, 0, 0, 0, cdsg, 0) + +/* COMPARE AND TRAP */ + D(0xb972, CRT, RRF_c, GIE, r1_32s, r2_32s, 0, 0, ct, 0, 0) + D(0xb960, CGRT, RRF_c, GIE, r1_o, r2_o, 0, 0, ct, 0, 0) + D(0xec72, CIT, RIE_a, GIE, r1_32s, i2, 0, 0, ct, 0, 0) + D(0xec70, CGIT, RIE_a, GIE, r1_o, i2, 0, 0, ct, 0, 0) +/* COMPARE LOGICAL AND TRAP */ + D(0xb973, CLRT, RRF_c, GIE, r1_32u, r2_32u, 0, 0, ct, 0, 1) + D(0xb961, CLGRT, RRF_c, GIE, r1_o, r2_o, 0, 0, ct, 0, 1) + D(0xec73, CLFIT, RIE_a, GIE, r1_32u, i2_32u, 0, 0, ct, 0, 1) + D(0xec71, CLGIT, RIE_a, GIE, r1_o, i2_32u, 0, 0, ct, 0, 0) + +/* CONVERT TO DECIMAL */ + C(0x4e00, CVD, RX_a, Z, r1_o, a2, 0, 0, cvd, 0) + C(0xe326, CVDY, RXY_a, LD, r1_o, a2, 0, 0, cvd, 0) +/* CONVERT TO FIXED */ + C(0xb398, CFEBR, RRF_e, Z, 0, e2, new, r1_32, cfeb, 0) + C(0xb399, CFDBR, RRF_e, Z, 0, f2_o, new, r1_32, cfdb, 0) + C(0xb39a, CFXBR, RRF_e, Z, 0, x2_o, new, r1_32, cfxb, 0) + C(0xb3a8, CGEBR, RRF_e, Z, 0, e2, r1, 0, cgeb, 0) + C(0xb3a9, CGDBR, RRF_e, Z, 0, f2_o, r1, 0, cgdb, 0) + C(0xb3aa, CGXBR, RRF_e, Z, 0, x2_o, r1, 0, cgxb, 0) +/* CONVERT FROM FIXED */ + C(0xb394, CEFBR, RRF_e, Z, 0, r2_32s, new, e1, cegb, 0) + C(0xb395, CDFBR, RRF_e, Z, 0, r2_32s, f1, 0, cdgb, 0) + C(0xb396, CXFBR, RRF_e, Z, 0, r2_32s, x1, 0, cxgb, 0) + C(0xb3a4, CEGBR, RRF_e, Z, 0, r2_o, new, e1, cegb, 0) + C(0xb3a5, CDGBR, RRF_e, Z, 0, r2_o, f1, 0, cdgb, 0) + C(0xb3a6, CXGBR, RRF_e, Z, 0, r2_o, x1, 0, cxgb, 0) +/* CONVERT TO LOGICAL */ + C(0xb39c, CLFEBR, RRF_e, FPE, 0, e2, new, r1_32, clfeb, 0) + C(0xb39d, CLFDBR, RRF_e, FPE, 0, f2_o, new, r1_32, clfdb, 0) + C(0xb39e, CLFXBR, RRF_e, FPE, 0, x2_o, new, r1_32, clfxb, 0) + C(0xb3ac, CLGEBR, RRF_e, FPE, 0, e2, r1, 0, clgeb, 0) + C(0xb3ad, CLGDBR, RRF_e, FPE, 0, f2_o, r1, 0, clgdb, 0) + C(0xb3ae, CLGXBR, RRF_e, FPE, 0, x2_o, r1, 0, clgxb, 0) +/* CONVERT FROM LOGICAL */ + C(0xb390, CELFBR, RRF_e, FPE, 0, r2_32u, new, e1, celgb, 0) + C(0xb391, CDLFBR, RRF_e, FPE, 0, r2_32u, f1, 0, cdlgb, 0) + C(0xb392, CXLFBR, RRF_e, FPE, 0, r2_32u, x1, 0, cxlgb, 0) + C(0xb3a0, CELGBR, RRF_e, FPE, 0, r2_o, new, e1, celgb, 0) + C(0xb3a1, CDLGBR, RRF_e, FPE, 0, r2_o, f1, 0, cdlgb, 0) + C(0xb3a2, CXLGBR, RRF_e, FPE, 0, r2_o, x1, 0, cxlgb, 0) + +/* DIVIDE */ + C(0x1d00, DR, RR_a, Z, r1_D32, r2_32s, new_P, r1_P32, divs32, 0) + C(0x5d00, D, RX_a, Z, r1_D32, m2_32s, new_P, r1_P32, divs32, 0) + C(0xb30d, DEBR, RRE, Z, e1, e2, new, e1, deb, 0) + C(0xb31d, DDBR, RRE, Z, f1_o, f2_o, f1, 0, ddb, 0) + C(0xb34d, DXBR, RRE, Z, 0, x2_o, x1, 0, dxb, 0) + C(0xed0d, DEB, RXE, Z, e1, m2_32u, new, e1, deb, 0) + C(0xed1d, DDB, RXE, Z, f1_o, m2_64, f1, 0, ddb, 0) +/* DIVIDE LOGICAL */ + C(0xb997, DLR, RRE, Z, r1_D32, r2_32u, new_P, r1_P32, divu32, 0) + C(0xe397, DL, RXY_a, Z, r1_D32, m2_32u, new_P, r1_P32, divu32, 0) + C(0xb987, DLGR, RRE, Z, 0, r2_o, r1_P, 0, divu64, 0) + C(0xe387, DLG, RXY_a, Z, 0, m2_64, r1_P, 0, divu64, 0) +/* DIVIDE SINGLE */ + C(0xb90d, DSGR, RRE, Z, r1p1, r2, r1_P, 0, divs64, 0) + C(0xb91d, DSGFR, RRE, Z, r1p1, r2_32s, r1_P, 0, divs64, 0) + C(0xe30d, DSG, RXY_a, Z, r1p1, m2_64, r1_P, 0, divs64, 0) + C(0xe31d, DSGF, RXY_a, Z, r1p1, m2_32s, r1_P, 0, divs64, 0) + +/* EXCLUSIVE OR */ + C(0x1700, XR, RR_a, Z, r1, r2, new, r1_32, xor, nz32) + C(0xb9f7, XRK, RRF_a, DO, r2, r3, new, r1_32, xor, nz32) + C(0x5700, X, RX_a, Z, r1, m2_32s, new, r1_32, xor, nz32) + C(0xe357, XY, RXY_a, LD, r1, m2_32s, new, r1_32, xor, nz32) + C(0xb982, XGR, RRE, Z, r1, r2, r1, 0, xor, nz64) + C(0xb9e7, XGRK, RRF_a, DO, r2, r3, r1, 0, xor, nz64) + C(0xe382, XG, RXY_a, Z, r1, m2_64, r1, 0, xor, nz64) + C(0xd700, XC, SS_a, Z, 0, 0, 0, 0, xc, 0) +/* EXCLUSIVE OR IMMEDIATE */ + D(0xc006, XIHF, RIL_a, EI, r1_o, i2_32u, r1, 0, xori, 0, 0x2020) + D(0xc007, XILF, RIL_a, EI, r1_o, i2_32u, r1, 0, xori, 0, 0x2000) + C(0x9700, XI, SI, Z, m1_8u, i2_8u, new, m1_8, xor, nz64) + C(0xeb57, XIY, SIY, LD, m1_8u, i2_8u, new, m1_8, xor, nz64) + +/* EXECUTE */ + C(0x4400, EX, RX_a, Z, r1_o, a2, 0, 0, ex, 0) +/* EXECUTE RELATIVE LONG */ + C(0xc600, EXRL, RIL_b, EE, r1_o, ri2, 0, 0, ex, 0) + +/* EXTRACT ACCESS */ + C(0xb24f, EAR, RRE, Z, 0, 0, new, r1_32, ear, 0) +/* EXTRACT FPC */ + C(0xb38c, EFPC, RRE, Z, 0, 0, new, r1_32, efpc, 0) + +/* FIND LEFTMOST ONE */ + C(0xb983, FLOGR, RRE, EI, 0, r2_o, r1_P, 0, flogr, 0) + +/* INSERT CHARACTER */ + C(0x4300, IC, RX_a, Z, 0, m2_8u, 0, r1_8, mov2, 0) + C(0xe373, ICY, RXY_a, LD, 0, m2_8u, 0, r1_8, mov2, 0) +/* INSERT CHARACTERS UNDER MASK */ + D(0xbf00, ICM, RS_b, Z, 0, a2, r1, 0, icm, 0, 0) + D(0xeb81, ICMY, RSY_b, LD, 0, a2, r1, 0, icm, 0, 0) + D(0xeb80, ICMH, RSY_b, Z, 0, a2, r1, 0, icm, 0, 32) +/* INSERT IMMEDIATE */ + D(0xc008, IIHF, RIL_a, EI, r1_o, i2_32u, r1, 0, insi, 0, 0x2020) + D(0xc009, IILF, RIL_a, EI, r1_o, i2_32u, r1, 0, insi, 0, 0x2000) + D(0xa500, IIHH, RI_a, Z, r1_o, i2_16u, r1, 0, insi, 0, 0x1030) + D(0xa501, IIHL, RI_a, Z, r1_o, i2_16u, r1, 0, insi, 0, 0x1020) + D(0xa502, IILH, RI_a, Z, r1_o, i2_16u, r1, 0, insi, 0, 0x1010) + D(0xa503, IILL, RI_a, Z, r1_o, i2_16u, r1, 0, insi, 0, 0x1000) +/* INSERT PROGRAM MASK */ + C(0xb222, IPM, RRE, Z, 0, 0, r1, 0, ipm, 0) + +/* LOAD */ + C(0x1800, LR, RR_a, Z, 0, r2_o, 0, cond_r1r2_32, mov2, 0) + C(0x5800, L, RX_a, Z, 0, a2, new, r1_32, ld32s, 0) + C(0xe358, LY, RXY_a, Z, 0, a2, new, r1_32, ld32s, 0) + C(0xb904, LGR, RRE, Z, 0, r2_o, 0, r1, mov2, 0) + C(0xb914, LGFR, RRE, Z, 0, r2_32s, 0, r1, mov2, 0) + C(0xe304, LG, RXY_a, Z, 0, a2, r1, 0, ld64, 0) + C(0xe314, LGF, RXY_a, Z, 0, a2, r1, 0, ld32s, 0) + C(0x2800, LDR, RR_a, Z, 0, f2_o, 0, f1, mov2, 0) + C(0x6800, LD, RX_a, Z, 0, m2_64, 0, f1, mov2, 0) + C(0xed65, LDY, RXY_a, LD, 0, m2_64, 0, f1, mov2, 0) + C(0x3800, LER, RR_a, Z, 0, e2, 0, cond_e1e2, mov2, 0) + C(0x7800, LE, RX_a, Z, 0, m2_32u, 0, e1, mov2, 0) + C(0xed64, LEY, RXY_a, LD, 0, m2_32u, 0, e1, mov2, 0) + C(0xb365, LXR, RRE, Z, 0, x2_o, 0, x1, movx, 0) +/* LOAD IMMEDIATE */ + C(0xc001, LGFI, RIL_a, EI, 0, i2, 0, r1, mov2, 0) +/* LOAD RELATIVE LONG */ + C(0xc40d, LRL, RIL_b, GIE, 0, ri2, new, r1_32, ld32s, 0) + C(0xc408, LGRL, RIL_b, GIE, 0, ri2, r1, 0, ld64, 0) + C(0xc40c, LGFRL, RIL_b, GIE, 0, ri2, r1, 0, ld32s, 0) +/* LOAD ADDRESS */ + C(0x4100, LA, RX_a, Z, 0, a2, 0, r1, mov2, 0) + C(0xe371, LAY, RXY_a, LD, 0, a2, 0, r1, mov2, 0) +/* LOAD ADDRESS RELATIVE LONG */ + C(0xc000, LARL, RIL_b, Z, 0, ri2, 0, r1, mov2, 0) +/* LOAD AND TEST */ + C(0x1200, LTR, RR_a, Z, 0, r2_o, 0, cond_r1r2_32, mov2, s32) + C(0xb902, LTGR, RRE, Z, 0, r2_o, 0, r1, mov2, s64) + C(0xb912, LTGFR, RRE, Z, 0, r2_32s, 0, r1, mov2, s64) + C(0xe312, LT, RXY_a, EI, 0, a2, new, r1_32, ld32s, s64) + C(0xe302, LTG, RXY_a, EI, 0, a2, r1, 0, ld64, s64) + C(0xe332, LTGF, RXY_a, GIE, 0, a2, r1, 0, ld32s, s64) + C(0xb302, LTEBR, RRE, Z, 0, e2, 0, cond_e1e2, mov2, f32) + C(0xb312, LTDBR, RRE, Z, 0, f2_o, 0, f1, mov2, f64) + C(0xb342, LTXBR, RRE, Z, 0, x2_o, 0, x1, movx, f128) +/* LOAD BYTE */ + C(0xb926, LBR, RRE, EI, 0, r2_8s, 0, r1_32, mov2, 0) + C(0xb906, LGBR, RRE, EI, 0, r2_8s, 0, r1, mov2, 0) + C(0xe376, LB, RXY_a, LD, 0, a2, new, r1_32, ld8s, 0) + C(0xe377, LGB, RXY_a, LD, 0, a2, r1, 0, ld8s, 0) +/* LOAD COMPLEMENT */ + C(0x1300, LCR, RR_a, Z, 0, r2, new, r1_32, neg, neg32) + C(0xb903, LCGR, RRE, Z, 0, r2, r1, 0, neg, neg64) + C(0xb913, LCGFR, RRE, Z, 0, r2_32s, r1, 0, neg, neg64) + C(0xb303, LCEBR, RRE, Z, 0, e2, new, e1, negf32, f32) + C(0xb313, LCDBR, RRE, Z, 0, f2_o, f1, 0, negf64, f64) + C(0xb343, LCXBR, RRE, Z, 0, x2_o, x1, 0, negf128, f128) + C(0xb373, LCDFR, RRE, FPSSH, 0, f2_o, f1, 0, negf64, 0) +/* LOAD HALFWORD */ + C(0xb927, LHR, RRE, EI, 0, r2_16s, 0, r1_32, mov2, 0) + C(0xb907, LGHR, RRE, EI, 0, r2_16s, 0, r1, mov2, 0) + C(0x4800, LH, RX_a, Z, 0, a2, new, r1_32, ld16s, 0) + C(0xe378, LHY, RXY_a, LD, 0, a2, new, r1_32, ld16s, 0) + C(0xe315, LGH, RXY_a, Z, 0, a2, r1, 0, ld16s, 0) +/* LOAD HALFWORD IMMEDIATE */ + C(0xa708, LHI, RI_a, Z, 0, i2, 0, r1_32, mov2, 0) + C(0xa709, LGHI, RI_a, Z, 0, i2, 0, r1, mov2, 0) +/* LOAD HALFWORD RELATIVE LONG */ + C(0xc405, LHRL, RIL_b, GIE, 0, ri2, new, r1_32, ld16s, 0) + C(0xc404, LGHRL, RIL_b, GIE, 0, ri2, r1, 0, ld16s, 0) +/* LOAD LOGICAL */ + C(0xb916, LLGFR, RRE, Z, 0, r2_32u, 0, r1, mov2, 0) + C(0xe316, LLGF, RXY_a, Z, 0, a2, r1, 0, ld32u, 0) +/* LOAD LOGICAL RELATIVE LONG */ + C(0xc40e, LLGFRL, RIL_b, GIE, 0, ri2, r1, 0, ld32u, 0) +/* LOAD LOGICAL CHARACTER */ + C(0xb994, LLCR, RRE, EI, 0, r2_8u, 0, r1_32, mov2, 0) + C(0xb984, LLGCR, RRE, EI, 0, r2_8u, 0, r1, mov2, 0) + C(0xe394, LLC, RXY_a, EI, 0, a2, new, r1_32, ld8u, 0) + C(0xe390, LLGC, RXY_a, Z, 0, a2, r1, 0, ld8u, 0) +/* LOAD LOGICAL HALFWORD */ + C(0xb995, LLHR, RRE, EI, 0, r2_16u, 0, r1_32, mov2, 0) + C(0xb985, LLGHR, RRE, EI, 0, r2_16u, 0, r1, mov2, 0) + C(0xe395, LLH, RXY_a, EI, 0, a2, new, r1_32, ld16u, 0) + C(0xe391, LLGH, RXY_a, Z, 0, a2, r1, 0, ld16u, 0) +/* LOAD LOGICAL HALFWORD RELATIVE LONG */ + C(0xc402, LLHRL, RIL_b, GIE, 0, ri2, new, r1_32, ld16u, 0) + C(0xc406, LLGHRL, RIL_b, GIE, 0, ri2, r1, 0, ld16u, 0) +/* LOAD LOGICAL IMMEDATE */ + D(0xc00e, LLIHF, RIL_a, EI, 0, i2_32u_shl, 0, r1, mov2, 0, 32) + D(0xc00f, LLILF, RIL_a, EI, 0, i2_32u_shl, 0, r1, mov2, 0, 0) + D(0xa50c, LLIHH, RI_a, Z, 0, i2_16u_shl, 0, r1, mov2, 0, 48) + D(0xa50d, LLIHL, RI_a, Z, 0, i2_16u_shl, 0, r1, mov2, 0, 32) + D(0xa50e, LLILH, RI_a, Z, 0, i2_16u_shl, 0, r1, mov2, 0, 16) + D(0xa50f, LLILL, RI_a, Z, 0, i2_16u_shl, 0, r1, mov2, 0, 0) +/* LOAD LOGICAL THIRTY ONE BITS */ + C(0xb917, LLGTR, RRE, Z, 0, r2_o, r1, 0, llgt, 0) + C(0xe317, LLGT, RXY_a, Z, 0, m2_32u, r1, 0, llgt, 0) +/* LOAD FPR FROM GR */ + C(0xb3c1, LDGR, RRE, FPRGR, 0, r2_o, 0, f1, mov2, 0) +/* LOAD GR FROM FPR */ + C(0xb3cd, LGDR, RRE, FPRGR, 0, f2_o, 0, r1, mov2, 0) +/* LOAD NEGATIVE */ + C(0x1100, LNR, RR_a, Z, 0, r2_32s, new, r1_32, nabs, nabs32) + C(0xb901, LNGR, RRE, Z, 0, r2, r1, 0, nabs, nabs64) + C(0xb911, LNGFR, RRE, Z, 0, r2_32s, r1, 0, nabs, nabs64) + C(0xb301, LNEBR, RRE, Z, 0, e2, new, e1, nabsf32, f32) + C(0xb311, LNDBR, RRE, Z, 0, f2_o, f1, 0, nabsf64, f64) + C(0xb341, LNXBR, RRE, Z, 0, x2_o, x1, 0, nabsf128, f128) +/* LOAD ON CONDITION */ + C(0xb9f2, LOCR, RRF_c, LOC, r1, r2, new, r1_32, loc, 0) + C(0xb9e2, LOCGR, RRF_c, LOC, r1, r2, r1, 0, loc, 0) + C(0xebf2, LOC, RSY_b, LOC, r1, m2_32u, new, r1_32, loc, 0) + C(0xebe2, LOCG, RSY_b, LOC, r1, m2_64, r1, 0, loc, 0) +/* LOAD POSITIVE */ + C(0x1000, LPR, RR_a, Z, 0, r2_32s, new, r1_32, abs, abs32) + C(0xb900, LPGR, RRE, Z, 0, r2, r1, 0, abs, abs64) + C(0xb910, LPGFR, RRE, Z, 0, r2_32s, r1, 0, abs, abs64) + C(0xb300, LPEBR, RRE, Z, 0, e2, new, e1, absf32, f32) + C(0xb310, LPDBR, RRE, Z, 0, f2_o, f1, 0, absf64, f64) + C(0xb340, LPXBR, RRE, Z, 0, x2_o, x1, 0, absf128, f128) +/* LOAD REVERSED */ + C(0xb91f, LRVR, RRE, Z, 0, r2_32u, new, r1_32, rev32, 0) + C(0xb90f, LRVGR, RRE, Z, 0, r2_o, r1, 0, rev64, 0) + C(0xe31f, LRVH, RXY_a, Z, 0, m2_16u, new, r1_16, rev16, 0) + C(0xe31e, LRV, RXY_a, Z, 0, m2_32u, new, r1_32, rev32, 0) + C(0xe30f, LRVG, RXY_a, Z, 0, m2_64, r1, 0, rev64, 0) +/* LOAD ZERO */ + C(0xb374, LZER, RRE, Z, 0, 0, 0, e1, zero, 0) + C(0xb375, LZDR, RRE, Z, 0, 0, 0, f1, zero, 0) + C(0xb376, LZXR, RRE, Z, 0, 0, 0, x1, zero2, 0) + +/* LOAD FPC */ + C(0xb29d, LFPC, S, Z, 0, m2_32u, 0, 0, sfpc, 0) +/* LOAD FPC AND SIGNAL */ + C(0xb2bd, LFAS, S, IEEEE_SIM, 0, m2_32u, 0, 0, sfas, 0) + +/* LOAD LENGTHENED */ + C(0xb304, LDEBR, RRE, Z, 0, e2, f1, 0, ldeb, 0) + C(0xb305, LXDBR, RRE, Z, 0, f2_o, x1, 0, lxdb, 0) + C(0xb306, LXEBR, RRE, Z, 0, e2, x1, 0, lxeb, 0) + C(0xed04, LDEB, RXE, Z, 0, m2_32u, f1, 0, ldeb, 0) + C(0xed05, LXDB, RXE, Z, 0, m2_64, x1, 0, lxdb, 0) + C(0xed06, LXEB, RXE, Z, 0, m2_32u, x1, 0, lxeb, 0) +/* LOAD ROUNDED */ + C(0xb344, LEDBR, RRE, Z, 0, f2_o, new, e1, ledb, 0) + C(0xb345, LDXBR, RRE, Z, 0, x2_o, f1, 0, ldxb, 0) + C(0xb346, LEXBR, RRE, Z, 0, x2_o, new, e1, lexb, 0) + +/* LOAD MULTIPLE */ + C(0x9800, LM, RS_a, Z, 0, a2, 0, 0, lm32, 0) + C(0xeb98, LMY, RSY_a, LD, 0, a2, 0, 0, lm32, 0) + C(0xeb04, LMG, RSY_a, Z, 0, a2, 0, 0, lm64, 0) +/* LOAD MULTIPLE HIGH */ + C(0xeb96, LMH, RSY_a, Z, 0, a2, 0, 0, lmh, 0) +/* LOAD ACCESS MULTIPLE */ + C(0x9a00, LAM, RS_a, Z, 0, a2, 0, 0, lam, 0) + C(0xeb9a, LAMY, RSY_a, LD, 0, a2, 0, 0, lam, 0) + +/* MOVE */ + C(0xd200, MVC, SS_a, Z, la1, a2, 0, 0, mvc, 0) + C(0xe544, MVHHI, SIL, GIE, la1, i2, 0, m1_16, mov2, 0) + C(0xe54c, MVHI, SIL, GIE, la1, i2, 0, m1_32, mov2, 0) + C(0xe548, MVGHI, SIL, GIE, la1, i2, 0, m1_64, mov2, 0) + C(0x9200, MVI, SI, Z, la1, i2, 0, m1_8, mov2, 0) + C(0xeb52, MVIY, SIY, LD, la1, i2, 0, m1_8, mov2, 0) +/* MOVE LONG */ + C(0x0e00, MVCL, RR_a, Z, 0, 0, 0, 0, mvcl, 0) +/* MOVE LONG EXTENDED */ + C(0xa800, MVCLE, RS_a, Z, 0, a2, 0, 0, mvcle, 0) +/* MOVE PAGE */ + C(0xb254, MVPG, RRE, Z, r1_o, r2_o, 0, 0, mvpg, 0) +/* MOVE STRING */ + C(0xb255, MVST, RRE, Z, r1_o, r2_o, 0, 0, mvst, 0) + +/* MULTIPLY */ + C(0x1c00, MR, RR_a, Z, r1p1_32s, r2_32s, new, r1_D32, mul, 0) + C(0x5c00, M, RX_a, Z, r1p1_32s, m2_32s, new, r1_D32, mul, 0) + C(0xe35c, MFY, RXY_a, GIE, r1p1_32s, m2_32s, new, r1_D32, mul, 0) + C(0xb317, MEEBR, RRE, Z, e1, e2, new, e1, meeb, 0) + C(0xb31c, MDBR, RRE, Z, f1_o, f2_o, f1, 0, mdb, 0) + C(0xb34c, MXBR, RRE, Z, 0, x2_o, x1, 0, mxb, 0) + C(0xb30c, MDEBR, RRE, Z, f1_o, e2, f1, 0, mdeb, 0) + C(0xb307, MXDBR, RRE, Z, 0, f2_o, x1, 0, mxdb, 0) + C(0xed17, MEEB, RXE, Z, e1, m2_32u, new, e1, meeb, 0) + C(0xed1c, MDB, RXE, Z, f1_o, m2_64, f1, 0, mdb, 0) + C(0xed0c, MDEB, RXE, Z, f1_o, m2_32u, f1, 0, mdeb, 0) + C(0xed07, MXDB, RXE, Z, 0, m2_64, x1, 0, mxdb, 0) +/* MULTIPLY HALFWORD */ + C(0x4c00, MH, RX_a, Z, r1_o, m2_16s, new, r1_32, mul, 0) + C(0xe37c, MHY, RXY_a, GIE, r1_o, m2_16s, new, r1_32, mul, 0) +/* MULTIPLY HALFWORD IMMEDIATE */ + C(0xa70c, MHI, RI_a, Z, r1_o, i2, new, r1_32, mul, 0) + C(0xa70d, MGHI, RI_a, Z, r1_o, i2, r1, 0, mul, 0) +/* MULTIPLY LOGICAL */ + C(0xb996, MLR, RRE, Z, r1p1_32u, r2_32u, new, r1_D32, mul, 0) + C(0xe396, ML, RXY_a, Z, r1p1_32u, m2_32u, new, r1_D32, mul, 0) + C(0xb986, MLGR, RRE, Z, r1p1, r2_o, r1_P, 0, mul128, 0) + C(0xe386, MLG, RXY_a, Z, r1p1, m2_64, r1_P, 0, mul128, 0) +/* MULTIPLY SINGLE */ + C(0xb252, MSR, RRE, Z, r1_o, r2_o, new, r1_32, mul, 0) + C(0x7100, MS, RX_a, Z, r1_o, m2_32s, new, r1_32, mul, 0) + C(0xe351, MSY, RXY_a, LD, r1_o, m2_32s, new, r1_32, mul, 0) + C(0xb90c, MSGR, RRE, Z, r1_o, r2_o, r1, 0, mul, 0) + C(0xb91c, MSGFR, RRE, Z, r1_o, r2_32s, r1, 0, mul, 0) + C(0xe30c, MSG, RXY_a, Z, r1_o, m2_64, r1, 0, mul, 0) + C(0xe31c, MSGF, RXY_a, Z, r1_o, m2_32s, r1, 0, mul, 0) +/* MULTIPLY SINGLE IMMEDIATE */ + C(0xc201, MSFI, RIL_a, GIE, r1_o, i2, new, r1_32, mul, 0) + C(0xc200, MSGFI, RIL_a, GIE, r1_o, i2, r1, 0, mul, 0) + +/* MULTIPLY AND ADD */ + C(0xb30e, MAEBR, RRD, Z, e1, e2, new, e1, maeb, 0) + C(0xb31e, MADBR, RRD, Z, f1_o, f2_o, f1, 0, madb, 0) + C(0xed0e, MAEB, RXF, Z, e1, m2_32u, new, e1, maeb, 0) + C(0xed1e, MADB, RXF, Z, f1_o, m2_64, f1, 0, madb, 0) +/* MULTIPLY AND SUBTRACT */ + C(0xb30f, MSEBR, RRD, Z, e1, e2, new, e1, mseb, 0) + C(0xb31f, MSDBR, RRD, Z, f1_o, f2_o, f1, 0, msdb, 0) + C(0xed0f, MSEB, RXF, Z, e1, m2_32u, new, e1, mseb, 0) + C(0xed1f, MSDB, RXF, Z, f1_o, m2_64, f1, 0, msdb, 0) + +/* OR */ + C(0x1600, OR, RR_a, Z, r1, r2, new, r1_32, or, nz32) + C(0xb9f6, ORK, RRF_a, DO, r2, r3, new, r1_32, or, nz32) + C(0x5600, O, RX_a, Z, r1, m2_32s, new, r1_32, or, nz32) + C(0xe356, OY, RXY_a, LD, r1, m2_32s, new, r1_32, or, nz32) + C(0xb981, OGR, RRE, Z, r1, r2, r1, 0, or, nz64) + C(0xb9e6, OGRK, RRF_a, DO, r2, r3, r1, 0, or, nz64) + C(0xe381, OG, RXY_a, Z, r1, m2_64, r1, 0, or, nz64) + C(0xd600, OC, SS_a, Z, la1, a2, 0, 0, oc, 0) +/* OR IMMEDIATE */ + D(0xc00c, OIHF, RIL_a, EI, r1_o, i2_32u, r1, 0, ori, 0, 0x2020) + D(0xc00d, OILF, RIL_a, EI, r1_o, i2_32u, r1, 0, ori, 0, 0x2000) + D(0xa508, OIHH, RI_a, Z, r1_o, i2_16u, r1, 0, ori, 0, 0x1030) + D(0xa509, OIHL, RI_a, Z, r1_o, i2_16u, r1, 0, ori, 0, 0x1020) + D(0xa50a, OILH, RI_a, Z, r1_o, i2_16u, r1, 0, ori, 0, 0x1010) + D(0xa50b, OILL, RI_a, Z, r1_o, i2_16u, r1, 0, ori, 0, 0x1000) + C(0x9600, OI, SI, Z, m1_8u, i2_8u, new, m1_8, or, nz64) + C(0xeb56, OIY, SIY, LD, m1_8u, i2_8u, new, m1_8, or, nz64) + +/* PREFETCH */ + /* Implemented as nops of course. */ + C(0xe336, PFD, RXY_b, GIE, 0, 0, 0, 0, 0, 0) + C(0xc602, PFDRL, RIL_c, GIE, 0, 0, 0, 0, 0, 0) + +/* POPULATION COUNT */ + C(0xb9e1, POPCNT, RRE, PC, 0, r2_o, r1, 0, popcnt, nz64) + +/* ROTATE LEFT SINGLE LOGICAL */ + C(0xeb1d, RLL, RSY_a, Z, r3_o, sh32, new, r1_32, rll32, 0) + C(0xeb1c, RLLG, RSY_a, Z, r3_o, sh64, r1, 0, rll64, 0) + +/* ROTATE THEN INSERT SELECTED BITS */ + C(0xec55, RISBG, RIE_f, GIE, 0, r2, r1, 0, risbg, s64) + C(0xec5d, RISBHG, RIE_f, GIE, 0, r2, r1, 0, risbg, 0) + C(0xec51, RISBLG, RIE_f, GIE, 0, r2, r1, 0, risbg, 0) +/* ROTATE_THEN SELECTED BITS */ + C(0xec54, RNSBG, RIE_f, GIE, 0, r2, r1, 0, rosbg, 0) + C(0xec56, ROSBG, RIE_f, GIE, 0, r2, r1, 0, rosbg, 0) + C(0xec57, RXSBG, RIE_f, GIE, 0, r2, r1, 0, rosbg, 0) + +/* SEARCH STRING */ + C(0xb25e, SRST, RRE, Z, r1_o, r2_o, 0, 0, srst, 0) + +/* SET ACCESS */ + C(0xb24e, SAR, RRE, Z, 0, r2_o, 0, 0, sar, 0) +/* SET FPC */ + C(0xb384, SFPC, RRE, Z, 0, r1_o, 0, 0, sfpc, 0) +/* SET FPC AND SIGNAL */ + C(0xb385, SFASR, RRE, IEEEE_SIM, 0, r1_o, 0, 0, sfas, 0) +/* SET BFP ROUNDING MODE */ + C(0xb299, SRNM, S, Z, 0, 0, 0, 0, srnm, 0) + C(0xb2b8, SRNMB, S, FPE, 0, 0, 0, 0, srnm, 0) +/* SET DFP ROUNDING MODE */ + C(0xb2b9, SRNMT, S, DFP, 0, 0, 0, 0, srnm, 0) + +/* SHIFT LEFT SINGLE */ + D(0x8b00, SLA, RS_a, Z, r1, sh32, new, r1_32, sla, 0, 31) + D(0xebdd, SLAK, RSY_a, DO, r3, sh32, new, r1_32, sla, 0, 31) + D(0xeb0b, SLAG, RSY_a, Z, r3, sh64, r1, 0, sla, 0, 63) +/* SHIFT LEFT SINGLE LOGICAL */ + C(0x8900, SLL, RS_a, Z, r1_o, sh32, new, r1_32, sll, 0) + C(0xebdf, SLLK, RSY_a, DO, r3_o, sh32, new, r1_32, sll, 0) + C(0xeb0d, SLLG, RSY_a, Z, r3_o, sh64, r1, 0, sll, 0) +/* SHIFT RIGHT SINGLE */ + C(0x8a00, SRA, RS_a, Z, r1_32s, sh32, new, r1_32, sra, s32) + C(0xebdc, SRAK, RSY_a, DO, r3_32s, sh32, new, r1_32, sra, s32) + C(0xeb0a, SRAG, RSY_a, Z, r3_o, sh64, r1, 0, sra, s64) +/* SHIFT RIGHT SINGLE LOGICAL */ + C(0x8800, SRL, RS_a, Z, r1_32u, sh32, new, r1_32, srl, 0) + C(0xebde, SRLK, RSY_a, DO, r3_32u, sh32, new, r1_32, srl, 0) + C(0xeb0c, SRLG, RSY_a, Z, r3_o, sh64, r1, 0, srl, 0) +/* SHIFT LEFT DOUBLE */ + D(0x8f00, SLDA, RS_a, Z, r1_D32, sh64, new, r1_D32, sla, 0, 31) +/* SHIFT LEFT DOUBLE LOGICAL */ + C(0x8d00, SLDL, RS_a, Z, r1_D32, sh64, new, r1_D32, sll, 0) +/* SHIFT RIGHT DOUBLE */ + C(0x8e00, SRDA, RS_a, Z, r1_D32, sh64, new, r1_D32, sra, s64) +/* SHIFT RIGHT DOUBLE LOGICAL */ + C(0x8c00, SRDL, RS_a, Z, r1_D32, sh64, new, r1_D32, srl, 0) + +/* SQUARE ROOT */ + C(0xb314, SQEBR, RRE, Z, 0, e2, new, e1, sqeb, 0) + C(0xb315, SQDBR, RRE, Z, 0, f2_o, f1, 0, sqdb, 0) + C(0xb316, SQXBR, RRE, Z, 0, x2_o, x1, 0, sqxb, 0) + C(0xed14, SQEB, RXE, Z, 0, m2_32u, new, e1, sqeb, 0) + C(0xed15, SQDB, RXE, Z, 0, m2_64, f1, 0, sqdb, 0) + +/* STORE */ + C(0x5000, ST, RX_a, Z, r1_o, a2, 0, 0, st32, 0) + C(0xe350, STY, RXY_a, LD, r1_o, a2, 0, 0, st32, 0) + C(0xe324, STG, RXY_a, Z, r1_o, a2, 0, 0, st64, 0) + C(0x6000, STD, RX_a, Z, f1_o, a2, 0, 0, st64, 0) + C(0xed67, STDY, RXY_a, LD, f1_o, a2, 0, 0, st64, 0) + C(0x7000, STE, RX_a, Z, e1, a2, 0, 0, st32, 0) + C(0xed66, STEY, RXY_a, LD, e1, a2, 0, 0, st32, 0) +/* STORE RELATIVE LONG */ + C(0xc40f, STRL, RIL_b, GIE, r1_o, ri2, 0, 0, st32, 0) + C(0xc40b, STGRL, RIL_b, GIE, r1_o, ri2, 0, 0, st64, 0) +/* STORE CHARACTER */ + C(0x4200, STC, RX_a, Z, r1_o, a2, 0, 0, st8, 0) + C(0xe372, STCY, RXY_a, LD, r1_o, a2, 0, 0, st8, 0) +/* STORE CHARACTERS UNDER MASK */ + D(0xbe00, STCM, RS_b, Z, r1_o, a2, 0, 0, stcm, 0, 0) + D(0xeb2d, STCMY, RSY_b, LD, r1_o, a2, 0, 0, stcm, 0, 0) + D(0xeb2c, STCMH, RSY_b, LD, r1_o, a2, 0, 0, stcm, 0, 32) +/* STORE HALFWORD */ + C(0x4000, STH, RX_a, Z, r1_o, a2, 0, 0, st16, 0) + C(0xe370, STHY, RXY_a, LD, r1_o, a2, 0, 0, st16, 0) +/* STORE HALFWORD RELATIVE LONG */ + C(0xc407, STHRL, RIL_b, GIE, r1_o, ri2, 0, 0, st16, 0) +/* STORE ON CONDITION */ + D(0xebf3, STOC, RSY_b, LOC, 0, 0, 0, 0, soc, 0, 0) + D(0xebe3, STOCG, RSY_b, LOC, 0, 0, 0, 0, soc, 0, 1) +/* STORE REVERSED */ + C(0xe33f, STRVH, RXY_a, Z, la2, r1_16u, new, m1_16, rev16, 0) + C(0xe33e, STRV, RXY_a, Z, la2, r1_32u, new, m1_32, rev32, 0) + C(0xe32f, STRVG, RXY_a, Z, la2, r1_o, new, m1_64, rev64, 0) + +/* STORE FPC */ + C(0xb29c, STFPC, S, Z, 0, a2, new, m2_32, efpc, 0) + +/* STORE MULTIPLE */ + D(0x9000, STM, RS_a, Z, 0, a2, 0, 0, stm, 0, 4) + D(0xeb90, STMY, RSY_a, LD, 0, a2, 0, 0, stm, 0, 4) + D(0xeb24, STMG, RSY_a, Z, 0, a2, 0, 0, stm, 0, 8) +/* STORE MULTIPLE HIGH */ + C(0xeb26, STMH, RSY_a, Z, 0, a2, 0, 0, stmh, 0) +/* STORE ACCESS MULTIPLE */ + C(0x9b00, STAM, RS_a, Z, 0, a2, 0, 0, stam, 0) + C(0xeb9b, STAMY, RSY_a, LD, 0, a2, 0, 0, stam, 0) + +/* SUBTRACT */ + C(0x1b00, SR, RR_a, Z, r1, r2, new, r1_32, sub, subs32) + C(0xb9f9, SRK, RRF_a, DO, r2, r3, new, r1_32, sub, subs32) + C(0x5b00, S, RX_a, Z, r1, m2_32s, new, r1_32, sub, subs32) + C(0xe35b, SY, RXY_a, LD, r1, m2_32s, new, r1_32, sub, subs32) + C(0xb909, SGR, RRE, Z, r1, r2, r1, 0, sub, subs64) + C(0xb919, SGFR, RRE, Z, r1, r2_32s, r1, 0, sub, subs64) + C(0xb9e9, SGRK, RRF_a, DO, r2, r3, r1, 0, sub, subs64) + C(0xe309, SG, RXY_a, Z, r1, m2_64, r1, 0, sub, subs64) + C(0xe319, SGF, RXY_a, Z, r1, m2_32s, r1, 0, sub, subs64) + C(0xb30b, SEBR, RRE, Z, e1, e2, new, e1, seb, f32) + C(0xb31b, SDBR, RRE, Z, f1_o, f2_o, f1, 0, sdb, f64) + C(0xb34b, SXBR, RRE, Z, 0, x2_o, x1, 0, sxb, f128) + C(0xed0b, SEB, RXE, Z, e1, m2_32u, new, e1, seb, f32) + C(0xed1b, SDB, RXE, Z, f1_o, m2_64, f1, 0, sdb, f64) +/* SUBTRACT HALFWORD */ + C(0x4b00, SH, RX_a, Z, r1, m2_16s, new, r1_32, sub, subs32) + C(0xe37b, SHY, RXY_a, LD, r1, m2_16s, new, r1_32, sub, subs32) +/* SUBTRACT LOGICAL */ + C(0x1f00, SLR, RR_a, Z, r1, r2, new, r1_32, sub, subu32) + C(0xb9fb, SLRK, RRF_a, DO, r2, r3, new, r1_32, sub, subu32) + C(0x5f00, SL, RX_a, Z, r1, m2_32u, new, r1_32, sub, subu32) + C(0xe35f, SLY, RXY_a, LD, r1, m2_32u, new, r1_32, sub, subu32) + C(0xb90b, SLGR, RRE, Z, r1, r2, r1, 0, sub, subu64) + C(0xb91b, SLGFR, RRE, Z, r1, r2_32u, r1, 0, sub, subu64) + C(0xb9eb, SLGRK, RRF_a, DO, r2, r3, r1, 0, sub, subu64) + C(0xe30b, SLG, RXY_a, Z, r1, m2_64, r1, 0, sub, subu64) + C(0xe31b, SLGF, RXY_a, Z, r1, m2_32u, r1, 0, sub, subu64) +/* SUBTRACT LOGICAL IMMEDIATE */ + C(0xc205, SLFI, RIL_a, EI, r1, i2_32u, new, r1_32, sub, subu32) + C(0xc204, SLGFI, RIL_a, EI, r1, i2_32u, r1, 0, sub, subu64) +/* SUBTRACT LOGICAL WITH BORROW */ + C(0xb999, SLBR, RRE, Z, r1, r2, new, r1_32, subb, subb32) + C(0xb989, SLBGR, RRE, Z, r1, r2, r1, 0, subb, subb64) + C(0xe399, SLB, RXY_a, Z, r1, m2_32u, new, r1_32, subb, subb32) + C(0xe389, SLBG, RXY_a, Z, r1, m2_64, r1, 0, subb, subb64) + +/* SUPERVISOR CALL */ + C(0x0a00, SVC, I, Z, 0, 0, 0, 0, svc, 0) + +/* TEST DATA CLASS */ + C(0xed10, TCEB, RXE, Z, e1, a2, 0, 0, tceb, 0) + C(0xed11, TCDB, RXE, Z, f1_o, a2, 0, 0, tcdb, 0) + C(0xed12, TCXB, RXE, Z, x1_o, a2, 0, 0, tcxb, 0) + +/* TEST UNDER MASK */ + C(0x9100, TM, SI, Z, m1_8u, i2_8u, 0, 0, 0, tm32) + C(0xeb51, TMY, SIY, LD, m1_8u, i2_8u, 0, 0, 0, tm32) + D(0xa702, TMHH, RI_a, Z, r1_o, i2_16u_shl, 0, 0, 0, tm64, 48) + D(0xa703, TMHL, RI_a, Z, r1_o, i2_16u_shl, 0, 0, 0, tm64, 32) + D(0xa700, TMLH, RI_a, Z, r1_o, i2_16u_shl, 0, 0, 0, tm64, 16) + D(0xa701, TMLL, RI_a, Z, r1_o, i2_16u_shl, 0, 0, 0, tm64, 0) + +/* TRANSLATE */ + C(0xdc00, TR, SS_a, Z, la1, a2, 0, 0, tr, 0) + +/* UNPACK */ + /* Really format SS_b, but we pack both lengths into one argument + for the helper call, so we might as well leave one 8-bit field. */ + C(0xf300, UNPK, SS_a, Z, la1, a2, 0, 0, unpk, 0) + +#ifndef CONFIG_USER_ONLY +/* COMPARE AND SWAP AND PURGE */ + C(0xb250, CSP, RRE, Z, 0, ra2, 0, 0, csp, 0) +/* DIAGNOSE (KVM hypercall) */ + C(0x8300, DIAG, RX_a, Z, 0, 0, 0, 0, diag, 0) +/* INSERT STORAGE KEY EXTENDED */ + C(0xb229, ISKE, RRE, Z, 0, r2_o, new, r1_8, iske, 0) +/* INVALIDATE PAGE TABLE ENTRY */ + C(0xb221, IPTE, RRF_a, Z, r1_o, r2_o, 0, 0, ipte, 0) +/* LOAD CONTROL */ + C(0xb700, LCTL, RS_a, Z, 0, a2, 0, 0, lctl, 0) + C(0xeb2f, LCTLG, RSY_a, Z, 0, a2, 0, 0, lctlg, 0) +/* LOAD PSW */ + C(0x8200, LPSW, S, Z, 0, a2, 0, 0, lpsw, 0) +/* LOAD PSW EXTENDED */ + C(0xb2b2, LPSWE, S, Z, 0, a2, 0, 0, lpswe, 0) +/* LOAD REAL ADDRESS */ + C(0xb100, LRA, RX_a, Z, 0, a2, r1, 0, lra, 0) + C(0xe313, LRAY, RXY_a, LD, 0, a2, r1, 0, lra, 0) + C(0xe303, LRAG, RXY_a, Z, 0, a2, r1, 0, lra, 0) +/* MOVE TO PRIMARY */ + C(0xda00, MVCP, SS_d, Z, la1, a2, 0, 0, mvcp, 0) +/* MOVE TO SECONDARY */ + C(0xdb00, MVCS, SS_d, Z, la1, a2, 0, 0, mvcs, 0) +/* PURGE TLB */ + C(0xb20d, PTLB, S, Z, 0, 0, 0, 0, ptlb, 0) +/* RESET REFERENCE BIT EXTENDED */ + C(0xb22a, RRBE, RRE, Z, 0, r2_o, 0, 0, rrbe, 0) +/* SERVICE CALL LOGICAL PROCESSOR (PV hypercall) */ + C(0xb220, SERVC, RRE, Z, r1_o, r2_o, 0, 0, servc, 0) +/* SET ADDRESSING MODE */ + /* We only do 64-bit, so accept this as a no-op. + Let SAM24 and SAM31 signal illegal instruction. */ + C(0x010e, SAM64, E, Z, 0, 0, 0, 0, 0, 0) +/* SET ADDRESS SPACE CONTROL FAST */ + C(0xb279, SACF, S, Z, 0, a2, 0, 0, sacf, 0) +/* SET CLOCK */ + /* ??? Not implemented - is it necessary? */ + C(0xb204, SCK, S, Z, 0, 0, 0, 0, 0, 0) +/* SET CLOCK COMPARATOR */ + C(0xb206, SCKC, S, Z, 0, m2_64, 0, 0, sckc, 0) +/* SET CPU TIMER */ + C(0xb208, SPT, S, Z, 0, m2_64, 0, 0, spt, 0) +/* SET PREFIX */ + C(0xb210, SPX, S, Z, 0, m2_32u, 0, 0, spx, 0) +/* SET PSW KEY FROM ADDRESS */ + C(0xb20a, SPKA, S, Z, 0, a2, 0, 0, spka, 0) +/* SET STORAGE KEY EXTENDED */ + C(0xb22b, SSKE, RRF_c, Z, r1_o, r2_o, 0, 0, sske, 0) +/* SET SYSTEM MASK */ + C(0x8000, SSM, S, Z, 0, m2_8u, 0, 0, ssm, 0) +/* SIGNAL PROCESSOR */ + C(0xae00, SIGP, RS_a, Z, r3_o, a2, 0, 0, sigp, 0) +/* STORE CLOCK */ + C(0xb205, STCK, S, Z, la2, 0, new, m1_64, stck, 0) + C(0xb27c, STCKF, S, Z, la2, 0, new, m1_64, stck, 0) +/* STORE CLOCK EXTENDED */ + C(0xb278, STCKE, S, Z, 0, a2, 0, 0, stcke, 0) +/* STORE CLOCK COMPARATOR */ + C(0xb207, STCKC, S, Z, la2, 0, new, m1_64, stckc, 0) +/* STORE CONTROL */ + C(0xb600, STCTL, RS_a, Z, 0, a2, 0, 0, stctl, 0) + C(0xeb25, STCTG, RSY_a, Z, 0, a2, 0, 0, stctg, 0) +/* STORE CPU ADDRESS */ + C(0xb212, STAP, S, Z, la2, 0, new, m1_16, stap, 0) +/* STORE CPU ID */ + C(0xb202, STIDP, S, Z, la2, 0, new, m1_64, stidp, 0) +/* STORE CPU TIMER */ + C(0xb209, STPT, S, Z, la2, 0, new, m1_64, stpt, 0) +/* STORE FACILITY LIST */ + C(0xb2b1, STFL, S, Z, 0, 0, 0, 0, stfl, 0) +/* STORE PREFIX */ + C(0xb211, STPX, S, Z, la2, 0, new, m1_32, stpx, 0) +/* STORE SYSTEM INFORMATION */ + C(0xb27d, STSI, S, Z, 0, a2, 0, 0, stsi, 0) +/* STORE THEN AND SYSTEM MASK */ + C(0xac00, STNSM, SI, Z, la1, 0, 0, 0, stnosm, 0) +/* STORE THEN OR SYSTEM MASK */ + C(0xad00, STOSM, SI, Z, la1, 0, 0, 0, stnosm, 0) +/* STORE USING REAL ADDRESS */ + C(0xb246, STURA, RRE, Z, r1_o, r2_o, 0, 0, stura, 0) +/* TEST PROTECTION */ + C(0xe501, TPROT, SSE, Z, la1, a2, 0, 0, tprot, 0) + +/* I/O Instructions. For each we simply indicate non-operation. */ + C(0xb276, XSCH, S, Z, 0, 0, 0, 0, subchannel, 0) + C(0xb230, CSCH, S, Z, 0, 0, 0, 0, subchannel, 0) + C(0xb231, HSCH, S, Z, 0, 0, 0, 0, subchannel, 0) + C(0xb232, MSCH, S, Z, 0, 0, 0, 0, subchannel, 0) + C(0xb23b, RCHP, S, Z, 0, 0, 0, 0, subchannel, 0) + C(0xb238, RSCH, S, Z, 0, 0, 0, 0, subchannel, 0) + C(0xb233, SSCH, S, Z, 0, 0, 0, 0, subchannel, 0) + C(0xb234, STSCH, S, Z, 0, 0, 0, 0, subchannel, 0) + C(0xb235, TSCH, S, Z, 0, 0, 0, 0, subchannel, 0) + /* ??? Not listed in PoO ninth edition, but there's a linux driver that + uses it: "A CHSC subchannel is usually present on LPAR only." */ + C(0xb25f, CHSC, S, Z, 0, 0, 0, 0, subchannel, 0) +#endif /* CONFIG_USER_ONLY */ diff --git a/target-s390x/insn-format.def b/target-s390x/insn-format.def new file mode 100644 index 0000000000..0e898b90bd --- /dev/null +++ b/target-s390x/insn-format.def @@ -0,0 +1,55 @@ +/* Description of s390 insn formats. */ +/* NAME F1, F2... */ +F0(E) +F1(I, I(1, 8, 8)) +F2(RI_a, R(1, 8), I(2,16,16)) +F2(RI_b, R(1, 8), I(2,16,16)) +F2(RI_c, M(1, 8), I(2,16,16)) +F3(RIE_a, R(1, 8), I(2,16,16), M(3,32)) +F4(RIE_b, R(1, 8), R(2,12), M(3,32), I(4,16,16)) +F4(RIE_c, R(1, 8), I(2,32, 8), M(3,12), I(4,16,16)) +F3(RIE_d, R(1, 8), I(2,16,16), R(3,12)) +F3(RIE_e, R(1, 8), I(2,16,16), R(3,12)) +F5(RIE_f, R(1, 8), R(2,12), I(3,16,8), I(4,24,8), I(5,32,8)) +F2(RIL_a, R(1, 8), I(2,16,32)) +F2(RIL_b, R(1, 8), I(2,16,32)) +F2(RIL_c, M(1, 8), I(2,16,32)) +F4(RIS, R(1, 8), I(2,32, 8), M(3,12), BD(4,16,20)) +/* ??? The PoO does not call out subtypes _a and _b for RR, as it does + for e.g. RX. Our checking requires this for e.g. BCR. */ +F2(RR_a, R(1, 8), R(2,12)) +F2(RR_b, M(1, 8), R(2,12)) +F2(RRE, R(1,24), R(2,28)) +F3(RRD, R(1,16), R(2,28), R(3,24)) +F4(RRF_a, R(1,24), R(2,28), R(3,16), M(4,20)) +F4(RRF_b, R(1,24), R(2,28), R(3,16), M(4,20)) +F4(RRF_c, R(1,24), R(2,28), M(3,16), M(4,20)) +F4(RRF_d, R(1,24), R(2,28), M(3,16), M(4,20)) +F4(RRF_e, R(1,24), R(2,28), M(3,16), M(4,20)) +F4(RRS, R(1, 8), R(2,12), M(3,32), BD(4,16,20)) +F3(RS_a, R(1, 8), BD(2,16,20), R(3,12)) +F3(RS_b, R(1, 8), BD(2,16,20), M(3,12)) +F3(RSI, R(1, 8), I(2,16,16), R(3,12)) +F2(RSL, L(1, 8, 4), BD(1,16,20)) +F3(RSY_a, R(1, 8), BDL(2), R(3,12)) +F3(RSY_b, R(1, 8), BDL(2), M(3,12)) +F2(RX_a, R(1, 8), BXD(2)) +F2(RX_b, M(1, 8), BXD(2)) +F2(RXE, R(1, 8), BXD(2)) +F3(RXF, R(1,32), BXD(2), R(3, 8)) +F2(RXY_a, R(1, 8), BXDL(2)) +F2(RXY_b, M(1, 8), BXDL(2)) +F1(S, BD(2,16,20)) +F2(SI, BD(1,16,20), I(2,8,8)) +F2(SIL, BD(1,16,20), I(2,32,16)) +F2(SIY, BDL(1), I(2, 8, 8)) +F3(SS_a, L(1, 8, 8), BD(1,16,20), BD(2,32,36)) +F4(SS_b, L(1, 8, 4), BD(1,16,20), L(2,12,4), BD(2,32,36)) +F4(SS_c, L(1, 8, 4), BD(1,16,20), BD(2,32,36), I(3,12, 4)) +/* ??? Odd man out. The L1 field here is really a register, but the + easy way to compress the fields has R1 and B1 overlap. */ +F4(SS_d, L(1, 8, 4), BD(1,16,20), BD(2,32,36), R(3,12)) +F4(SS_e, R(1, 8), BD(2,16,20), R(3,12), BD(4,32,36)) +F3(SS_f, BD(1,16,20), L(2,8,8), BD(2,32,36)) +F2(SSE, BD(1,16,20), BD(2,32,36)) +F3(SSF, BD(1,16,20), BD(2,32,36), R(3,8)) diff --git a/target-s390x/int_helper.c b/target-s390x/int_helper.c index b683709860..af16b21baa 100644 --- a/target-s390x/int_helper.c +++ b/target-s390x/int_helper.c @@ -29,47 +29,90 @@ #define HELPER_LOG(x...) #endif -/* 64/64 -> 128 unsigned multiplication */ -void HELPER(mlg)(CPUS390XState *env, uint32_t r1, uint64_t v2) +/* 64/32 -> 32 signed division */ +int64_t HELPER(divs32)(CPUS390XState *env, int64_t a, int64_t b64) { -#if HOST_LONG_BITS == 64 && defined(__GNUC__) - /* assuming 64-bit hosts have __uint128_t */ - __uint128_t res = (__uint128_t)env->regs[r1 + 1]; + int32_t ret, b = b64; + int64_t q; - res *= (__uint128_t)v2; - env->regs[r1] = (uint64_t)(res >> 64); - env->regs[r1 + 1] = (uint64_t)res; -#else - mulu64(&env->regs[r1 + 1], &env->regs[r1], env->regs[r1 + 1], v2); -#endif + if (b == 0) { + runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC()); + } + + ret = q = a / b; + env->retxl = a % b; + + /* Catch non-representable quotient. */ + if (ret != q) { + runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC()); + } + + return ret; +} + +/* 64/32 -> 32 unsigned division */ +uint64_t HELPER(divu32)(CPUS390XState *env, uint64_t a, uint64_t b64) +{ + uint32_t ret, b = b64; + uint64_t q; + + if (b == 0) { + runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC()); + } + + ret = q = a / b; + env->retxl = a % b; + + /* Catch non-representable quotient. */ + if (ret != q) { + runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC()); + } + + return ret; +} + +/* 64/64 -> 64 signed division */ +int64_t HELPER(divs64)(CPUS390XState *env, int64_t a, int64_t b) +{ + /* Catch divide by zero, and non-representable quotient (MIN / -1). */ + if (b == 0 || (b == -1 && a == (1ll << 63))) { + runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC()); + } + env->retxl = a % b; + return a / b; } /* 128 -> 64/64 unsigned division */ -void HELPER(dlg)(CPUS390XState *env, uint32_t r1, uint64_t v2) +uint64_t HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al, + uint64_t b) { - uint64_t divisor = v2; - - if (!env->regs[r1]) { + uint64_t ret; + /* Signal divide by zero. */ + if (b == 0) { + runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC()); + } + if (ah == 0) { /* 64 -> 64/64 case */ - env->regs[r1] = env->regs[r1 + 1] % divisor; - env->regs[r1 + 1] = env->regs[r1 + 1] / divisor; - return; + env->retxl = al % b; + ret = al / b; } else { + /* ??? Move i386 idivq helper to host-utils. */ #if HOST_LONG_BITS == 64 && defined(__GNUC__) /* assuming 64-bit hosts have __uint128_t */ - __uint128_t dividend = (((__uint128_t)env->regs[r1]) << 64) | - (env->regs[r1 + 1]); - __uint128_t quotient = dividend / divisor; - __uint128_t remainder = dividend % divisor; - - env->regs[r1 + 1] = quotient; - env->regs[r1] = remainder; + __uint128_t a = ((__uint128_t)ah << 64) | al; + __uint128_t q = a / b; + env->retxl = a % b; + ret = q; + if (ret != q) { + runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC()); + } #else /* 32-bit hosts would need special wrapper functionality - just abort if we encounter such a case; it's very unlikely anyways. */ cpu_abort(env, "128 -> 64/64 division not implemented\n"); #endif } + return ret; } /* absolute value 32-bit */ @@ -114,69 +157,10 @@ int64_t HELPER(nabs_i64)(int64_t val) } } -/* add with carry 32-bit unsigned */ -uint32_t HELPER(addc_u32)(uint32_t cc, uint32_t v1, uint32_t v2) +/* count leading zeros, for find leftmost one */ +uint64_t HELPER(clz)(uint64_t v) { - uint32_t res; - - res = v1 + v2; - if (cc & 2) { - res++; - } - - return res; -} - -/* subtract unsigned v2 from v1 with borrow */ -uint32_t HELPER(slb)(CPUS390XState *env, uint32_t cc, uint32_t r1, uint32_t v2) -{ - uint32_t v1 = env->regs[r1]; - uint32_t res = v1 + (~v2) + (cc >> 1); - - env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | res; - if (cc & 2) { - /* borrow */ - return v1 ? 1 : 0; - } else { - return v1 ? 3 : 2; - } -} - -/* subtract unsigned v2 from v1 with borrow */ -uint32_t HELPER(slbg)(CPUS390XState *env, uint32_t cc, uint32_t r1, - uint64_t v1, uint64_t v2) -{ - uint64_t res = v1 + (~v2) + (cc >> 1); - - env->regs[r1] = res; - if (cc & 2) { - /* borrow */ - return v1 ? 1 : 0; - } else { - return v1 ? 3 : 2; - } -} - -/* find leftmost one */ -uint32_t HELPER(flogr)(CPUS390XState *env, uint32_t r1, uint64_t v2) -{ - uint64_t res = 0; - uint64_t ov2 = v2; - - while (!(v2 & 0x8000000000000000ULL) && v2) { - v2 <<= 1; - res++; - } - - if (!v2) { - env->regs[r1] = 64; - env->regs[r1 + 1] = 0; - return 0; - } else { - env->regs[r1] = res; - env->regs[r1 + 1] = ov2 & ~(0x8000000000000000ULL >> res); - return 2; - } + return clz64(v); } uint64_t HELPER(cvd)(int32_t bin) @@ -199,3 +183,15 @@ uint64_t HELPER(cvd)(int32_t bin) return dec; } + +uint64_t HELPER(popcnt)(uint64_t r2) +{ + uint64_t ret = 0; + int i; + + for (i = 0; i < 64; i += 8) { + uint64_t t = ctpop32((r2 >> i) & 0xff); + ret |= t << i; + } + return ret; +} diff --git a/target-s390x/interrupt.c b/target-s390x/interrupt.c index 6c0024b426..6d6580de3a 100644 --- a/target-s390x/interrupt.c +++ b/target-s390x/interrupt.c @@ -19,11 +19,12 @@ void s390_sclp_extint(uint32_t parm) if (kvm_enabled()) { #ifdef CONFIG_KVM - kvm_s390_interrupt_internal(env, KVM_S390_INT_SERVICE, parm, 0, 1); + kvm_s390_interrupt_internal(dummy_cpu, KVM_S390_INT_SERVICE, parm, + 0, 1); #endif } else { env->psw.addr += 4; - cpu_inject_ext(env, EXT_SERVICE, parm, 0); + cpu_inject_ext(dummy_cpu, EXT_SERVICE, parm, 0); } } #endif diff --git a/target-s390x/ioinst.c b/target-s390x/ioinst.c new file mode 100644 index 0000000000..28c508d541 --- /dev/null +++ b/target-s390x/ioinst.c @@ -0,0 +1,761 @@ +/* + * I/O instructions for S/390 + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. + */ + +#include + +#include "cpu.h" +#include "ioinst.h" +#include "trace.h" + +int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid, + int *schid) +{ + if (!IOINST_SCHID_ONE(value)) { + return -EINVAL; + } + if (!IOINST_SCHID_M(value)) { + if (IOINST_SCHID_CSSID(value)) { + return -EINVAL; + } + *cssid = 0; + *m = 0; + } else { + *cssid = IOINST_SCHID_CSSID(value); + *m = 1; + } + *ssid = IOINST_SCHID_SSID(value); + *schid = IOINST_SCHID_NR(value); + return 0; +} + +int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + int ret = -ENODEV; + int cc; + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("xsch", cssid, ssid, schid); + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_xsch(sch); + } + switch (ret) { + case -ENODEV: + cc = 3; + break; + case -EBUSY: + cc = 2; + break; + case 0: + cc = 0; + break; + default: + cc = 1; + break; + } + + return cc; +} + +int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + int ret = -ENODEV; + int cc; + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("csch", cssid, ssid, schid); + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_csch(sch); + } + if (ret == -ENODEV) { + cc = 3; + } else { + cc = 0; + } + return cc; +} + +int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + int ret = -ENODEV; + int cc; + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("hsch", cssid, ssid, schid); + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_hsch(sch); + } + switch (ret) { + case -ENODEV: + cc = 3; + break; + case -EBUSY: + cc = 2; + break; + case 0: + cc = 0; + break; + default: + cc = 1; + break; + } + + return cc; +} + +static int ioinst_schib_valid(SCHIB *schib) +{ + if ((schib->pmcw.flags & PMCW_FLAGS_MASK_INVALID) || + (schib->pmcw.chars & PMCW_CHARS_MASK_INVALID)) { + return 0; + } + /* Disallow extended measurements for now. */ + if (schib->pmcw.chars & PMCW_CHARS_MASK_XMWME) { + return 0; + } + return 1; +} + +int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + SCHIB *schib; + uint64_t addr; + int ret = -ENODEV; + int cc; + hwaddr len = sizeof(*schib); + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("msch", cssid, ssid, schid); + addr = decode_basedisp_s(env, ipb); + schib = s390_cpu_physical_memory_map(env, addr, &len, 0); + if (!schib || len != sizeof(*schib)) { + program_interrupt(env, PGM_SPECIFICATION, 2); + cc = -EIO; + goto out; + } + if (!ioinst_schib_valid(schib)) { + program_interrupt(env, PGM_OPERAND, 2); + cc = -EIO; + goto out; + } + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_msch(sch, schib); + } + switch (ret) { + case -ENODEV: + cc = 3; + break; + case -EBUSY: + cc = 2; + break; + case 0: + cc = 0; + break; + default: + cc = 1; + break; + } +out: + s390_cpu_physical_memory_unmap(env, schib, len, 0); + return cc; +} + +static void copy_orb_from_guest(ORB *dest, const ORB *src) +{ + dest->intparm = be32_to_cpu(src->intparm); + dest->ctrl0 = be16_to_cpu(src->ctrl0); + dest->lpm = src->lpm; + dest->ctrl1 = src->ctrl1; + dest->cpa = be32_to_cpu(src->cpa); +} + +static int ioinst_orb_valid(ORB *orb) +{ + if ((orb->ctrl0 & ORB_CTRL0_MASK_INVALID) || + (orb->ctrl1 & ORB_CTRL1_MASK_INVALID)) { + return 0; + } + if ((orb->cpa & HIGH_ORDER_BIT) != 0) { + return 0; + } + return 1; +} + +int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + ORB *orig_orb, orb; + uint64_t addr; + int ret = -ENODEV; + int cc; + hwaddr len = sizeof(*orig_orb); + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("ssch", cssid, ssid, schid); + addr = decode_basedisp_s(env, ipb); + orig_orb = s390_cpu_physical_memory_map(env, addr, &len, 0); + if (!orig_orb || len != sizeof(*orig_orb)) { + program_interrupt(env, PGM_SPECIFICATION, 2); + cc = -EIO; + goto out; + } + copy_orb_from_guest(&orb, orig_orb); + if (!ioinst_orb_valid(&orb)) { + program_interrupt(env, PGM_OPERAND, 2); + cc = -EIO; + goto out; + } + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_ssch(sch, &orb); + } + switch (ret) { + case -ENODEV: + cc = 3; + break; + case -EBUSY: + cc = 2; + break; + case 0: + cc = 0; + break; + default: + cc = 1; + break; + } + +out: + s390_cpu_physical_memory_unmap(env, orig_orb, len, 0); + return cc; +} + +int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb) +{ + CRW *crw; + uint64_t addr; + int cc; + hwaddr len = sizeof(*crw); + + addr = decode_basedisp_s(env, ipb); + crw = s390_cpu_physical_memory_map(env, addr, &len, 1); + if (!crw || len != sizeof(*crw)) { + program_interrupt(env, PGM_SPECIFICATION, 2); + cc = -EIO; + goto out; + } + cc = css_do_stcrw(crw); + /* 0 - crw stored, 1 - zeroes stored */ +out: + s390_cpu_physical_memory_unmap(env, crw, len, 1); + return cc; +} + +int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + uint64_t addr; + int cc; + SCHIB *schib; + hwaddr len = sizeof(*schib); + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("stsch", cssid, ssid, schid); + addr = decode_basedisp_s(env, ipb); + schib = s390_cpu_physical_memory_map(env, addr, &len, 1); + if (!schib || len != sizeof(*schib)) { + program_interrupt(env, PGM_SPECIFICATION, 2); + cc = -EIO; + goto out; + } + sch = css_find_subch(m, cssid, ssid, schid); + if (sch) { + if (css_subch_visible(sch)) { + css_do_stsch(sch, schib); + cc = 0; + } else { + /* Indicate no more subchannels in this css/ss */ + cc = 3; + } + } else { + if (css_schid_final(m, cssid, ssid, schid)) { + cc = 3; /* No more subchannels in this css/ss */ + } else { + /* Store an empty schib. */ + memset(schib, 0, sizeof(*schib)); + cc = 0; + } + } +out: + s390_cpu_physical_memory_unmap(env, schib, len, 1); + return cc; +} + +int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + IRB *irb; + uint64_t addr; + int ret = -ENODEV; + int cc; + hwaddr len = sizeof(*irb); + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("tsch", cssid, ssid, schid); + addr = decode_basedisp_s(env, ipb); + irb = s390_cpu_physical_memory_map(env, addr, &len, 1); + if (!irb || len != sizeof(*irb)) { + program_interrupt(env, PGM_SPECIFICATION, 2); + cc = -EIO; + goto out; + } + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_tsch(sch, irb); + /* 0 - status pending, 1 - not status pending */ + cc = ret; + } else { + cc = 3; + } +out: + s390_cpu_physical_memory_unmap(env, irb, sizeof(*irb), 1); + return cc; +} + +typedef struct ChscReq { + uint16_t len; + uint16_t command; + uint32_t param0; + uint32_t param1; + uint32_t param2; +} QEMU_PACKED ChscReq; + +typedef struct ChscResp { + uint16_t len; + uint16_t code; + uint32_t param; + char data[0]; +} QEMU_PACKED ChscResp; + +#define CHSC_MIN_RESP_LEN 0x0008 + +#define CHSC_SCPD 0x0002 +#define CHSC_SCSC 0x0010 +#define CHSC_SDA 0x0031 + +#define CHSC_SCPD_0_M 0x20000000 +#define CHSC_SCPD_0_C 0x10000000 +#define CHSC_SCPD_0_FMT 0x0f000000 +#define CHSC_SCPD_0_CSSID 0x00ff0000 +#define CHSC_SCPD_0_RFMT 0x00000f00 +#define CHSC_SCPD_0_RES 0xc000f000 +#define CHSC_SCPD_1_RES 0xffffff00 +#define CHSC_SCPD_01_CHPID 0x000000ff +static void ioinst_handle_chsc_scpd(ChscReq *req, ChscResp *res) +{ + uint16_t len = be16_to_cpu(req->len); + uint32_t param0 = be32_to_cpu(req->param0); + uint32_t param1 = be32_to_cpu(req->param1); + uint16_t resp_code; + int rfmt; + uint16_t cssid; + uint8_t f_chpid, l_chpid; + int desc_size; + int m; + + rfmt = (param0 & CHSC_SCPD_0_RFMT) >> 8; + if ((rfmt == 0) || (rfmt == 1)) { + rfmt = !!(param0 & CHSC_SCPD_0_C); + } + if ((len != 0x0010) || (param0 & CHSC_SCPD_0_RES) || + (param1 & CHSC_SCPD_1_RES) || req->param2) { + resp_code = 0x0003; + goto out_err; + } + if (param0 & CHSC_SCPD_0_FMT) { + resp_code = 0x0007; + goto out_err; + } + cssid = (param0 & CHSC_SCPD_0_CSSID) >> 16; + m = param0 & CHSC_SCPD_0_M; + if (cssid != 0) { + if (!m || !css_present(cssid)) { + resp_code = 0x0008; + goto out_err; + } + } + f_chpid = param0 & CHSC_SCPD_01_CHPID; + l_chpid = param1 & CHSC_SCPD_01_CHPID; + if (l_chpid < f_chpid) { + resp_code = 0x0003; + goto out_err; + } + /* css_collect_chp_desc() is endian-aware */ + desc_size = css_collect_chp_desc(m, cssid, f_chpid, l_chpid, rfmt, + &res->data); + res->code = cpu_to_be16(0x0001); + res->len = cpu_to_be16(8 + desc_size); + res->param = cpu_to_be32(rfmt); + return; + + out_err: + res->code = cpu_to_be16(resp_code); + res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); + res->param = cpu_to_be32(rfmt); +} + +#define CHSC_SCSC_0_M 0x20000000 +#define CHSC_SCSC_0_FMT 0x000f0000 +#define CHSC_SCSC_0_CSSID 0x0000ff00 +#define CHSC_SCSC_0_RES 0xdff000ff +static void ioinst_handle_chsc_scsc(ChscReq *req, ChscResp *res) +{ + uint16_t len = be16_to_cpu(req->len); + uint32_t param0 = be32_to_cpu(req->param0); + uint8_t cssid; + uint16_t resp_code; + uint32_t general_chars[510]; + uint32_t chsc_chars[508]; + + if (len != 0x0010) { + resp_code = 0x0003; + goto out_err; + } + + if (param0 & CHSC_SCSC_0_FMT) { + resp_code = 0x0007; + goto out_err; + } + cssid = (param0 & CHSC_SCSC_0_CSSID) >> 8; + if (cssid != 0) { + if (!(param0 & CHSC_SCSC_0_M) || !css_present(cssid)) { + resp_code = 0x0008; + goto out_err; + } + } + if ((param0 & CHSC_SCSC_0_RES) || req->param1 || req->param2) { + resp_code = 0x0003; + goto out_err; + } + res->code = cpu_to_be16(0x0001); + res->len = cpu_to_be16(4080); + res->param = 0; + + memset(general_chars, 0, sizeof(general_chars)); + memset(chsc_chars, 0, sizeof(chsc_chars)); + + general_chars[0] = cpu_to_be32(0x03000000); + general_chars[1] = cpu_to_be32(0x00059000); + + chsc_chars[0] = cpu_to_be32(0x40000000); + chsc_chars[3] = cpu_to_be32(0x00040000); + + memcpy(res->data, general_chars, sizeof(general_chars)); + memcpy(res->data + sizeof(general_chars), chsc_chars, sizeof(chsc_chars)); + return; + + out_err: + res->code = cpu_to_be16(resp_code); + res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); + res->param = 0; +} + +#define CHSC_SDA_0_FMT 0x0f000000 +#define CHSC_SDA_0_OC 0x0000ffff +#define CHSC_SDA_0_RES 0xf0ff0000 +#define CHSC_SDA_OC_MCSSE 0x0 +#define CHSC_SDA_OC_MSS 0x2 +static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res) +{ + uint16_t resp_code = 0x0001; + uint16_t len = be16_to_cpu(req->len); + uint32_t param0 = be32_to_cpu(req->param0); + uint16_t oc; + int ret; + + if ((len != 0x0400) || (param0 & CHSC_SDA_0_RES)) { + resp_code = 0x0003; + goto out; + } + + if (param0 & CHSC_SDA_0_FMT) { + resp_code = 0x0007; + goto out; + } + + oc = param0 & CHSC_SDA_0_OC; + switch (oc) { + case CHSC_SDA_OC_MCSSE: + ret = css_enable_mcsse(); + if (ret == -EINVAL) { + resp_code = 0x0101; + goto out; + } + break; + case CHSC_SDA_OC_MSS: + ret = css_enable_mss(); + if (ret == -EINVAL) { + resp_code = 0x0101; + goto out; + } + break; + default: + resp_code = 0x0003; + goto out; + } + +out: + res->code = cpu_to_be16(resp_code); + res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); + res->param = 0; +} + +static void ioinst_handle_chsc_unimplemented(ChscResp *res) +{ + res->len = cpu_to_be16(CHSC_MIN_RESP_LEN); + res->code = cpu_to_be16(0x0004); + res->param = 0; +} + +int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb) +{ + ChscReq *req; + ChscResp *res; + uint64_t addr; + int reg; + uint16_t len; + uint16_t command; + hwaddr map_size = TARGET_PAGE_SIZE; + int ret = 0; + + trace_ioinst("chsc"); + reg = (ipb >> 20) & 0x00f; + addr = env->regs[reg]; + /* Page boundary? */ + if (addr & 0xfff) { + program_interrupt(env, PGM_SPECIFICATION, 2); + return -EIO; + } + req = s390_cpu_physical_memory_map(env, addr, &map_size, 1); + if (!req || map_size != TARGET_PAGE_SIZE) { + program_interrupt(env, PGM_SPECIFICATION, 2); + ret = -EIO; + goto out; + } + len = be16_to_cpu(req->len); + /* Length field valid? */ + if ((len < 16) || (len > 4088) || (len & 7)) { + program_interrupt(env, PGM_OPERAND, 2); + ret = -EIO; + goto out; + } + memset((char *)req + len, 0, TARGET_PAGE_SIZE - len); + res = (void *)((char *)req + len); + command = be16_to_cpu(req->command); + trace_ioinst_chsc_cmd(command, len); + switch (command) { + case CHSC_SCSC: + ioinst_handle_chsc_scsc(req, res); + break; + case CHSC_SCPD: + ioinst_handle_chsc_scpd(req, res); + break; + case CHSC_SDA: + ioinst_handle_chsc_sda(req, res); + break; + default: + ioinst_handle_chsc_unimplemented(res); + break; + } + +out: + s390_cpu_physical_memory_unmap(env, req, map_size, 1); + return ret; +} + +int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb) +{ + uint64_t addr; + int lowcore; + IOIntCode *int_code; + hwaddr len, orig_len; + int ret; + + trace_ioinst("tpi"); + addr = decode_basedisp_s(env, ipb); + lowcore = addr ? 0 : 1; + len = lowcore ? 8 /* two words */ : 12 /* three words */; + orig_len = len; + int_code = s390_cpu_physical_memory_map(env, addr, &len, 1); + if (!int_code || (len != orig_len)) { + program_interrupt(env, PGM_SPECIFICATION, 2); + ret = -EIO; + goto out; + } + ret = css_do_tpi(int_code, lowcore); +out: + s390_cpu_physical_memory_unmap(env, int_code, len, 1); + return ret; +} + +#define SCHM_REG1_RES(_reg) (_reg & 0x000000000ffffffc) +#define SCHM_REG1_MBK(_reg) ((_reg & 0x00000000f0000000) >> 28) +#define SCHM_REG1_UPD(_reg) ((_reg & 0x0000000000000002) >> 1) +#define SCHM_REG1_DCT(_reg) (_reg & 0x0000000000000001) + +int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2, + uint32_t ipb) +{ + uint8_t mbk; + int update; + int dct; + + trace_ioinst("schm"); + + if (SCHM_REG1_RES(reg1)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + + mbk = SCHM_REG1_MBK(reg1); + update = SCHM_REG1_UPD(reg1); + dct = SCHM_REG1_DCT(reg1); + + if (update && (reg2 & 0x0000000000000fff)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + + css_do_schm(mbk, update, dct, update ? reg2 : 0); + + return 0; +} + +int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1) +{ + int cssid, ssid, schid, m; + SubchDev *sch; + int ret = -ENODEV; + int cc; + + if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + trace_ioinst_sch_id("rsch", cssid, ssid, schid); + sch = css_find_subch(m, cssid, ssid, schid); + if (sch && css_subch_visible(sch)) { + ret = css_do_rsch(sch); + } + switch (ret) { + case -ENODEV: + cc = 3; + break; + case -EINVAL: + cc = 2; + break; + case 0: + cc = 0; + break; + default: + cc = 1; + break; + } + + return cc; + +} + +#define RCHP_REG1_RES(_reg) (_reg & 0x00000000ff00ff00) +#define RCHP_REG1_CSSID(_reg) ((_reg & 0x0000000000ff0000) >> 16) +#define RCHP_REG1_CHPID(_reg) (_reg & 0x00000000000000ff) +int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1) +{ + int cc; + uint8_t cssid; + uint8_t chpid; + int ret; + + if (RCHP_REG1_RES(reg1)) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + + cssid = RCHP_REG1_CSSID(reg1); + chpid = RCHP_REG1_CHPID(reg1); + + trace_ioinst_chp_id("rchp", cssid, chpid); + + ret = css_do_rchp(cssid, chpid); + + switch (ret) { + case -ENODEV: + cc = 3; + break; + case -EBUSY: + cc = 2; + break; + case 0: + cc = 0; + break; + default: + /* Invalid channel subsystem. */ + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + + return cc; +} + +#define SAL_REG1_INVALID(_reg) (_reg & 0x0000000080000000) +int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1) +{ + /* We do not provide address limit checking, so let's suppress it. */ + if (SAL_REG1_INVALID(reg1) || reg1 & 0x000000000000ffff) { + program_interrupt(env, PGM_OPERAND, 2); + return -EIO; + } + return 0; +} diff --git a/target-s390x/ioinst.h b/target-s390x/ioinst.h new file mode 100644 index 0000000000..7bed2910dc --- /dev/null +++ b/target-s390x/ioinst.h @@ -0,0 +1,233 @@ +/* + * S/390 channel I/O instructions + * + * Copyright 2012 IBM Corp. + * Author(s): Cornelia Huck + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at + * your option) any later version. See the COPYING file in the top-level + * directory. +*/ + +#ifndef IOINST_S390X_H +#define IOINST_S390X_H +/* + * Channel I/O related definitions, as defined in the Principles + * Of Operation (and taken from the Linux implementation). + */ + +/* subchannel status word (command mode only) */ +typedef struct SCSW { + uint16_t flags; + uint16_t ctrl; + uint32_t cpa; + uint8_t dstat; + uint8_t cstat; + uint16_t count; +} QEMU_PACKED SCSW; + +#define SCSW_FLAGS_MASK_KEY 0xf000 +#define SCSW_FLAGS_MASK_SCTL 0x0800 +#define SCSW_FLAGS_MASK_ESWF 0x0400 +#define SCSW_FLAGS_MASK_CC 0x0300 +#define SCSW_FLAGS_MASK_FMT 0x0080 +#define SCSW_FLAGS_MASK_PFCH 0x0040 +#define SCSW_FLAGS_MASK_ISIC 0x0020 +#define SCSW_FLAGS_MASK_ALCC 0x0010 +#define SCSW_FLAGS_MASK_SSI 0x0008 +#define SCSW_FLAGS_MASK_ZCC 0x0004 +#define SCSW_FLAGS_MASK_ECTL 0x0002 +#define SCSW_FLAGS_MASK_PNO 0x0001 + +#define SCSW_CTRL_MASK_FCTL 0x7000 +#define SCSW_CTRL_MASK_ACTL 0x0fe0 +#define SCSW_CTRL_MASK_STCTL 0x001f + +#define SCSW_FCTL_CLEAR_FUNC 0x1000 +#define SCSW_FCTL_HALT_FUNC 0x2000 +#define SCSW_FCTL_START_FUNC 0x4000 + +#define SCSW_ACTL_SUSP 0x0020 +#define SCSW_ACTL_DEVICE_ACTIVE 0x0040 +#define SCSW_ACTL_SUBCH_ACTIVE 0x0080 +#define SCSW_ACTL_CLEAR_PEND 0x0100 +#define SCSW_ACTL_HALT_PEND 0x0200 +#define SCSW_ACTL_START_PEND 0x0400 +#define SCSW_ACTL_RESUME_PEND 0x0800 + +#define SCSW_STCTL_STATUS_PEND 0x0001 +#define SCSW_STCTL_SECONDARY 0x0002 +#define SCSW_STCTL_PRIMARY 0x0004 +#define SCSW_STCTL_INTERMEDIATE 0x0008 +#define SCSW_STCTL_ALERT 0x0010 + +#define SCSW_DSTAT_ATTENTION 0x80 +#define SCSW_DSTAT_STAT_MOD 0x40 +#define SCSW_DSTAT_CU_END 0x20 +#define SCSW_DSTAT_BUSY 0x10 +#define SCSW_DSTAT_CHANNEL_END 0x08 +#define SCSW_DSTAT_DEVICE_END 0x04 +#define SCSW_DSTAT_UNIT_CHECK 0x02 +#define SCSW_DSTAT_UNIT_EXCEP 0x01 + +#define SCSW_CSTAT_PCI 0x80 +#define SCSW_CSTAT_INCORR_LEN 0x40 +#define SCSW_CSTAT_PROG_CHECK 0x20 +#define SCSW_CSTAT_PROT_CHECK 0x10 +#define SCSW_CSTAT_DATA_CHECK 0x08 +#define SCSW_CSTAT_CHN_CTRL_CHK 0x04 +#define SCSW_CSTAT_INTF_CTRL_CHK 0x02 +#define SCSW_CSTAT_CHAIN_CHECK 0x01 + +/* path management control word */ +typedef struct PMCW { + uint32_t intparm; + uint16_t flags; + uint16_t devno; + uint8_t lpm; + uint8_t pnom; + uint8_t lpum; + uint8_t pim; + uint16_t mbi; + uint8_t pom; + uint8_t pam; + uint8_t chpid[8]; + uint32_t chars; +} QEMU_PACKED PMCW; + +#define PMCW_FLAGS_MASK_QF 0x8000 +#define PMCW_FLAGS_MASK_W 0x4000 +#define PMCW_FLAGS_MASK_ISC 0x3800 +#define PMCW_FLAGS_MASK_ENA 0x0080 +#define PMCW_FLAGS_MASK_LM 0x0060 +#define PMCW_FLAGS_MASK_MME 0x0018 +#define PMCW_FLAGS_MASK_MP 0x0004 +#define PMCW_FLAGS_MASK_TF 0x0002 +#define PMCW_FLAGS_MASK_DNV 0x0001 +#define PMCW_FLAGS_MASK_INVALID 0x0700 + +#define PMCW_CHARS_MASK_ST 0x00e00000 +#define PMCW_CHARS_MASK_MBFC 0x00000004 +#define PMCW_CHARS_MASK_XMWME 0x00000002 +#define PMCW_CHARS_MASK_CSENSE 0x00000001 +#define PMCW_CHARS_MASK_INVALID 0xff1ffff8 + +/* subchannel information block */ +typedef struct SCHIB { + PMCW pmcw; + SCSW scsw; + uint64_t mba; + uint8_t mda[4]; +} QEMU_PACKED SCHIB; + +/* interruption response block */ +typedef struct IRB { + SCSW scsw; + uint32_t esw[5]; + uint32_t ecw[8]; + uint32_t emw[8]; +} QEMU_PACKED IRB; + +/* operation request block */ +typedef struct ORB { + uint32_t intparm; + uint16_t ctrl0; + uint8_t lpm; + uint8_t ctrl1; + uint32_t cpa; +} QEMU_PACKED ORB; + +#define ORB_CTRL0_MASK_KEY 0xf000 +#define ORB_CTRL0_MASK_SPND 0x0800 +#define ORB_CTRL0_MASK_STR 0x0400 +#define ORB_CTRL0_MASK_MOD 0x0200 +#define ORB_CTRL0_MASK_SYNC 0x0100 +#define ORB_CTRL0_MASK_FMT 0x0080 +#define ORB_CTRL0_MASK_PFCH 0x0040 +#define ORB_CTRL0_MASK_ISIC 0x0020 +#define ORB_CTRL0_MASK_ALCC 0x0010 +#define ORB_CTRL0_MASK_SSIC 0x0008 +#define ORB_CTRL0_MASK_C64 0x0002 +#define ORB_CTRL0_MASK_I2K 0x0001 +#define ORB_CTRL0_MASK_INVALID 0x0004 + +#define ORB_CTRL1_MASK_ILS 0x80 +#define ORB_CTRL1_MASK_MIDAW 0x40 +#define ORB_CTRL1_MASK_ORBX 0x01 +#define ORB_CTRL1_MASK_INVALID 0x3e + +/* channel command word (type 1) */ +typedef struct CCW1 { + uint8_t cmd_code; + uint8_t flags; + uint16_t count; + uint32_t cda; +} QEMU_PACKED CCW1; + +#define CCW_FLAG_DC 0x80 +#define CCW_FLAG_CC 0x40 +#define CCW_FLAG_SLI 0x20 +#define CCW_FLAG_SKIP 0x10 +#define CCW_FLAG_PCI 0x08 +#define CCW_FLAG_IDA 0x04 +#define CCW_FLAG_SUSPEND 0x02 + +#define CCW_CMD_NOOP 0x03 +#define CCW_CMD_BASIC_SENSE 0x04 +#define CCW_CMD_TIC 0x08 +#define CCW_CMD_SENSE_ID 0xe4 + +typedef struct CRW { + uint16_t flags; + uint16_t rsid; +} QEMU_PACKED CRW; + +#define CRW_FLAGS_MASK_S 0x4000 +#define CRW_FLAGS_MASK_R 0x2000 +#define CRW_FLAGS_MASK_C 0x1000 +#define CRW_FLAGS_MASK_RSC 0x0f00 +#define CRW_FLAGS_MASK_A 0x0080 +#define CRW_FLAGS_MASK_ERC 0x003f + +#define CRW_ERC_INIT 0x02 +#define CRW_ERC_IPI 0x04 + +#define CRW_RSC_SUBCH 0x3 +#define CRW_RSC_CHP 0x4 + +/* I/O interruption code */ +typedef struct IOIntCode { + uint32_t subsys_id; + uint32_t intparm; + uint32_t interrupt_id; +} QEMU_PACKED IOIntCode; + +/* schid disintegration */ +#define IOINST_SCHID_ONE(_schid) ((_schid & 0x00010000) >> 16) +#define IOINST_SCHID_M(_schid) ((_schid & 0x00080000) >> 19) +#define IOINST_SCHID_CSSID(_schid) ((_schid & 0xff000000) >> 24) +#define IOINST_SCHID_SSID(_schid) ((_schid & 0x00060000) >> 17) +#define IOINST_SCHID_NR(_schid) (_schid & 0x0000ffff) + +#define IO_INT_WORD_ISC(_int_word) ((_int_word & 0x38000000) >> 24) +#define ISC_TO_ISC_BITS(_isc) ((0x80 >> _isc) << 24) + +int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid, + int *schid); +int ioinst_handle_xsch(CPUS390XState *env, uint64_t reg1); +int ioinst_handle_csch(CPUS390XState *env, uint64_t reg1); +int ioinst_handle_hsch(CPUS390XState *env, uint64_t reg1); +int ioinst_handle_msch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); +int ioinst_handle_ssch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); +int ioinst_handle_stcrw(CPUS390XState *env, uint32_t ipb); +int ioinst_handle_stsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); +int ioinst_handle_tsch(CPUS390XState *env, uint64_t reg1, uint32_t ipb); +int ioinst_handle_chsc(CPUS390XState *env, uint32_t ipb); +int ioinst_handle_tpi(CPUS390XState *env, uint32_t ipb); +int ioinst_handle_schm(CPUS390XState *env, uint64_t reg1, uint64_t reg2, + uint32_t ipb); +int ioinst_handle_rsch(CPUS390XState *env, uint64_t reg1); +int ioinst_handle_rchp(CPUS390XState *env, uint64_t reg1); +int ioinst_handle_sal(CPUS390XState *env, uint64_t reg1); + +#endif diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 762231d845..8f111ae732 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -2,6 +2,7 @@ * QEMU S390x KVM implementation * * Copyright (c) 2009 Alexander Graf + * Copyright IBM Corp. 2012 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -13,7 +14,10 @@ * 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 + * Contributions after 2012-10-29 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + * + * You should have received a copy of the GNU (Lesser) General Public * License along with this library; if not, see . */ @@ -43,9 +47,29 @@ #define IPA0_DIAG 0x8300 #define IPA0_SIGP 0xae00 -#define IPA0_PRIV 0xb200 +#define IPA0_B2 0xb200 +#define IPA0_B9 0xb900 +#define IPA0_EB 0xeb00 #define PRIV_SCLP_CALL 0x20 +#define PRIV_CSCH 0x30 +#define PRIV_HSCH 0x31 +#define PRIV_MSCH 0x32 +#define PRIV_SSCH 0x33 +#define PRIV_STSCH 0x34 +#define PRIV_TSCH 0x35 +#define PRIV_TPI 0x36 +#define PRIV_SAL 0x37 +#define PRIV_RSCH 0x38 +#define PRIV_STCRW 0x39 +#define PRIV_STCPS 0x3a +#define PRIV_RCHP 0x3b +#define PRIV_SCHM 0x3c +#define PRIV_CHSC 0x5f +#define PRIV_SIGA 0x74 +#define PRIV_XSCH 0x76 +#define PRIV_SQBS 0x8a +#define PRIV_EQBS 0x9c #define DIAG_KVM_HYPERCALL 0x500 #define DIAG_KVM_BREAKPOINT 0x501 @@ -72,43 +96,52 @@ int kvm_arch_init(KVMState *s) return 0; } -int kvm_arch_init_vcpu(CPUS390XState *env) +unsigned long kvm_arch_vcpu_id(CPUState *cpu) { - int ret = 0; + return cpu->cpu_index; +} - if (kvm_vcpu_ioctl(env, KVM_S390_INITIAL_RESET, NULL) < 0) { - perror("cannot init reset vcpu"); +int kvm_arch_init_vcpu(CPUState *cpu) +{ + /* nothing todo yet */ + return 0; +} + +void kvm_arch_reset_vcpu(CPUState *cpu) +{ + /* The initial reset call is needed here to reset in-kernel + * vcpu data that we can't access directly from QEMU + * (i.e. with older kernels which don't support sync_regs/ONE_REG). + * Before this ioctl cpu_synchronize_state() is called in common kvm + * code (kvm-all) */ + if (kvm_vcpu_ioctl(cpu, KVM_S390_INITIAL_RESET, NULL)) { + perror("Can't reset vcpu\n"); } - - return ret; } -void kvm_arch_reset_vcpu(CPUS390XState *env) -{ - /* FIXME: add code to reset vcpu. */ -} - -int kvm_arch_put_registers(CPUS390XState *env, int level) +int kvm_arch_put_registers(CPUState *cs, int level) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; struct kvm_sregs sregs; struct kvm_regs regs; int ret; int i; /* always save the PSW and the GPRS*/ - env->kvm_run->psw_addr = env->psw.addr; - env->kvm_run->psw_mask = env->psw.mask; + cs->kvm_run->psw_addr = env->psw.addr; + cs->kvm_run->psw_mask = env->psw.mask; - if (cap_sync_regs && env->kvm_run->kvm_valid_regs & KVM_SYNC_GPRS) { + if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_GPRS) { for (i = 0; i < 16; i++) { - env->kvm_run->s.regs.gprs[i] = env->regs[i]; - env->kvm_run->kvm_dirty_regs |= KVM_SYNC_GPRS; + cs->kvm_run->s.regs.gprs[i] = env->regs[i]; + cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_GPRS; } } else { for (i = 0; i < 16; i++) { regs.gprs[i] = env->regs[i]; } - ret = kvm_vcpu_ioctl(env, KVM_SET_REGS, ®s); + ret = kvm_vcpu_ioctl(cs, KVM_SET_REGS, ®s); if (ret < 0) { return ret; } @@ -120,53 +153,55 @@ int kvm_arch_put_registers(CPUS390XState *env, int level) } if (cap_sync_regs && - env->kvm_run->kvm_valid_regs & KVM_SYNC_ACRS && - env->kvm_run->kvm_valid_regs & KVM_SYNC_CRS) { + cs->kvm_run->kvm_valid_regs & KVM_SYNC_ACRS && + cs->kvm_run->kvm_valid_regs & KVM_SYNC_CRS) { for (i = 0; i < 16; i++) { - env->kvm_run->s.regs.acrs[i] = env->aregs[i]; - env->kvm_run->s.regs.crs[i] = env->cregs[i]; + cs->kvm_run->s.regs.acrs[i] = env->aregs[i]; + cs->kvm_run->s.regs.crs[i] = env->cregs[i]; } - env->kvm_run->kvm_dirty_regs |= KVM_SYNC_ACRS; - env->kvm_run->kvm_dirty_regs |= KVM_SYNC_CRS; + cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_ACRS; + cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_CRS; } else { for (i = 0; i < 16; i++) { sregs.acrs[i] = env->aregs[i]; sregs.crs[i] = env->cregs[i]; } - ret = kvm_vcpu_ioctl(env, KVM_SET_SREGS, &sregs); + ret = kvm_vcpu_ioctl(cs, KVM_SET_SREGS, &sregs); if (ret < 0) { return ret; } } /* Finally the prefix */ - if (cap_sync_regs && env->kvm_run->kvm_valid_regs & KVM_SYNC_PREFIX) { - env->kvm_run->s.regs.prefix = env->psa; - env->kvm_run->kvm_dirty_regs |= KVM_SYNC_PREFIX; + if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_PREFIX) { + cs->kvm_run->s.regs.prefix = env->psa; + cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_PREFIX; } else { /* prefix is only supported via sync regs */ } return 0; } -int kvm_arch_get_registers(CPUS390XState *env) +int kvm_arch_get_registers(CPUState *cs) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; struct kvm_sregs sregs; struct kvm_regs regs; int ret; int i; /* get the PSW */ - env->psw.addr = env->kvm_run->psw_addr; - env->psw.mask = env->kvm_run->psw_mask; + env->psw.addr = cs->kvm_run->psw_addr; + env->psw.mask = cs->kvm_run->psw_mask; /* the GPRS */ - if (cap_sync_regs && env->kvm_run->kvm_valid_regs & KVM_SYNC_GPRS) { + if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_GPRS) { for (i = 0; i < 16; i++) { - env->regs[i] = env->kvm_run->s.regs.gprs[i]; + env->regs[i] = cs->kvm_run->s.regs.gprs[i]; } } else { - ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, ®s); + ret = kvm_vcpu_ioctl(cs, KVM_GET_REGS, ®s); if (ret < 0) { return ret; } @@ -177,14 +212,14 @@ int kvm_arch_get_registers(CPUS390XState *env) /* The ACRS and CRS */ if (cap_sync_regs && - env->kvm_run->kvm_valid_regs & KVM_SYNC_ACRS && - env->kvm_run->kvm_valid_regs & KVM_SYNC_CRS) { + cs->kvm_run->kvm_valid_regs & KVM_SYNC_ACRS && + cs->kvm_run->kvm_valid_regs & KVM_SYNC_CRS) { for (i = 0; i < 16; i++) { - env->aregs[i] = env->kvm_run->s.regs.acrs[i]; - env->cregs[i] = env->kvm_run->s.regs.crs[i]; + env->aregs[i] = cs->kvm_run->s.regs.acrs[i]; + env->cregs[i] = cs->kvm_run->s.regs.crs[i]; } } else { - ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); + ret = kvm_vcpu_ioctl(cs, KVM_GET_SREGS, &sregs); if (ret < 0) { return ret; } @@ -195,8 +230,8 @@ int kvm_arch_get_registers(CPUS390XState *env) } /* Finally the prefix */ - if (cap_sync_regs && env->kvm_run->kvm_valid_regs & KVM_SYNC_PREFIX) { - env->psa = env->kvm_run->s.regs.prefix; + if (cap_sync_regs && cs->kvm_run->kvm_valid_regs & KVM_SYNC_PREFIX) { + env->psa = cs->kvm_run->s.regs.prefix; } else { /* no prefix without sync regs */ } @@ -239,8 +274,10 @@ void *kvm_arch_vmalloc(ram_addr_t size) } } -int kvm_arch_insert_sw_breakpoint(CPUS390XState *env, struct kvm_sw_breakpoint *bp) +int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01}; if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) || @@ -250,8 +287,10 @@ int kvm_arch_insert_sw_breakpoint(CPUS390XState *env, struct kvm_sw_breakpoint * return 0; } -int kvm_arch_remove_sw_breakpoint(CPUS390XState *env, struct kvm_sw_breakpoint *bp) +int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) { + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; uint8_t t[4]; static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01}; @@ -266,26 +305,28 @@ int kvm_arch_remove_sw_breakpoint(CPUS390XState *env, struct kvm_sw_breakpoint * return 0; } -void kvm_arch_pre_run(CPUS390XState *env, struct kvm_run *run) +void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) { } -void kvm_arch_post_run(CPUS390XState *env, struct kvm_run *run) +void kvm_arch_post_run(CPUState *cpu, struct kvm_run *run) { } -int kvm_arch_process_async_events(CPUS390XState *env) +int kvm_arch_process_async_events(CPUState *cs) { - return env->halted; + S390CPU *cpu = S390_CPU(cs); + return cpu->env.halted; } -void kvm_s390_interrupt_internal(CPUS390XState *env, int type, uint32_t parm, +void kvm_s390_interrupt_internal(S390CPU *cpu, int type, uint32_t parm, uint64_t parm64, int vm) { + CPUState *cs = CPU(cpu); struct kvm_s390_interrupt kvmint; int r; - if (!env->kvm_state) { + if (!cs->kvm_state) { return; } @@ -294,9 +335,9 @@ void kvm_s390_interrupt_internal(CPUS390XState *env, int type, uint32_t parm, kvmint.parm64 = parm64; if (vm) { - r = kvm_vm_ioctl(env->kvm_state, KVM_S390_INTERRUPT, &kvmint); + r = kvm_vm_ioctl(cs->kvm_state, KVM_S390_INTERRUPT, &kvmint); } else { - r = kvm_vcpu_ioctl(env, KVM_S390_INTERRUPT, &kvmint); + r = kvm_vcpu_ioctl(cs, KVM_S390_INTERRUPT, &kvmint); } if (r < 0) { @@ -305,34 +346,38 @@ void kvm_s390_interrupt_internal(CPUS390XState *env, int type, uint32_t parm, } } -void kvm_s390_virtio_irq(CPUS390XState *env, int config_change, uint64_t token) +void kvm_s390_virtio_irq(S390CPU *cpu, int config_change, uint64_t token) { - kvm_s390_interrupt_internal(env, KVM_S390_INT_VIRTIO, config_change, + kvm_s390_interrupt_internal(cpu, KVM_S390_INT_VIRTIO, config_change, token, 1); } -void kvm_s390_interrupt(CPUS390XState *env, int type, uint32_t code) +void kvm_s390_interrupt(S390CPU *cpu, int type, uint32_t code) { - kvm_s390_interrupt_internal(env, type, code, 0, 0); + kvm_s390_interrupt_internal(cpu, type, code, 0, 0); } -static void enter_pgmcheck(CPUS390XState *env, uint16_t code) +static void enter_pgmcheck(S390CPU *cpu, uint16_t code) { - kvm_s390_interrupt(env, KVM_S390_PROGRAM_INT, code); + kvm_s390_interrupt(cpu, KVM_S390_PROGRAM_INT, code); } -static inline void setcc(CPUS390XState *env, uint64_t cc) +static inline void setcc(S390CPU *cpu, uint64_t cc) { - env->kvm_run->psw_mask &= ~(3ull << 44); - env->kvm_run->psw_mask |= (cc & 3) << 44; + CPUS390XState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + cs->kvm_run->psw_mask &= ~(3ull << 44); + cs->kvm_run->psw_mask |= (cc & 3) << 44; env->psw.mask &= ~(3ul << 44); env->psw.mask |= (cc & 3) << 44; } -static int kvm_sclp_service_call(CPUS390XState *env, struct kvm_run *run, +static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, uint16_t ipbh0) { + CPUS390XState *env = &cpu->env; uint32_t sccb; uint64_t code; int r = 0; @@ -343,26 +388,147 @@ static int kvm_sclp_service_call(CPUS390XState *env, struct kvm_run *run, r = sclp_service_call(sccb, code); if (r < 0) { - enter_pgmcheck(env, -r); + enter_pgmcheck(cpu, -r); } - setcc(env, r); + setcc(cpu, r); return 0; } -static int handle_priv(CPUS390XState *env, struct kvm_run *run, uint8_t ipa1) +static int kvm_handle_css_inst(S390CPU *cpu, struct kvm_run *run, + uint8_t ipa0, uint8_t ipa1, uint8_t ipb) +{ + int r = 0; + int no_cc = 0; + CPUS390XState *env = &cpu->env; + + if (ipa0 != 0xb2) { + /* Not handled for now. */ + return -1; + } + cpu_synchronize_state(env); + switch (ipa1) { + case PRIV_XSCH: + r = ioinst_handle_xsch(env, env->regs[1]); + break; + case PRIV_CSCH: + r = ioinst_handle_csch(env, env->regs[1]); + break; + case PRIV_HSCH: + r = ioinst_handle_hsch(env, env->regs[1]); + break; + case PRIV_MSCH: + r = ioinst_handle_msch(env, env->regs[1], run->s390_sieic.ipb); + break; + case PRIV_SSCH: + r = ioinst_handle_ssch(env, env->regs[1], run->s390_sieic.ipb); + break; + case PRIV_STCRW: + r = ioinst_handle_stcrw(env, run->s390_sieic.ipb); + break; + case PRIV_STSCH: + r = ioinst_handle_stsch(env, env->regs[1], run->s390_sieic.ipb); + break; + case PRIV_TSCH: + /* We should only get tsch via KVM_EXIT_S390_TSCH. */ + fprintf(stderr, "Spurious tsch intercept\n"); + break; + case PRIV_CHSC: + r = ioinst_handle_chsc(env, run->s390_sieic.ipb); + break; + case PRIV_TPI: + /* This should have been handled by kvm already. */ + fprintf(stderr, "Spurious tpi intercept\n"); + break; + case PRIV_SCHM: + no_cc = 1; + r = ioinst_handle_schm(env, env->regs[1], env->regs[2], + run->s390_sieic.ipb); + break; + case PRIV_RSCH: + r = ioinst_handle_rsch(env, env->regs[1]); + break; + case PRIV_RCHP: + r = ioinst_handle_rchp(env, env->regs[1]); + break; + case PRIV_STCPS: + /* We do not provide this instruction, it is suppressed. */ + no_cc = 1; + r = 0; + break; + case PRIV_SAL: + no_cc = 1; + r = ioinst_handle_sal(env, env->regs[1]); + break; + default: + r = -1; + break; + } + + if (r >= 0) { + if (!no_cc) { + setcc(cpu, r); + } + r = 0; + } else if (r < -1) { + r = 0; + } + return r; +} + +static int is_ioinst(uint8_t ipa0, uint8_t ipa1, uint8_t ipb) +{ + int ret = 0; + uint16_t ipa = (ipa0 << 8) | ipa1; + + switch (ipa) { + case IPA0_B2 | PRIV_CSCH: + case IPA0_B2 | PRIV_HSCH: + case IPA0_B2 | PRIV_MSCH: + case IPA0_B2 | PRIV_SSCH: + case IPA0_B2 | PRIV_STSCH: + case IPA0_B2 | PRIV_TPI: + case IPA0_B2 | PRIV_SAL: + case IPA0_B2 | PRIV_RSCH: + case IPA0_B2 | PRIV_STCRW: + case IPA0_B2 | PRIV_STCPS: + case IPA0_B2 | PRIV_RCHP: + case IPA0_B2 | PRIV_SCHM: + case IPA0_B2 | PRIV_CHSC: + case IPA0_B2 | PRIV_SIGA: + case IPA0_B2 | PRIV_XSCH: + case IPA0_B9 | PRIV_EQBS: + case IPA0_EB | PRIV_SQBS: + ret = 1; + break; + } + + return ret; +} + +static int handle_priv(S390CPU *cpu, struct kvm_run *run, + uint8_t ipa0, uint8_t ipa1) { int r = 0; uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16; + uint8_t ipb = run->s390_sieic.ipb & 0xff; dprintf("KVM: PRIV: %d\n", ipa1); switch (ipa1) { case PRIV_SCLP_CALL: - r = kvm_sclp_service_call(env, run, ipbh0); + r = kvm_sclp_service_call(cpu, run, ipbh0); break; default: - dprintf("KVM: unknown PRIV: 0x%x\n", ipa1); - r = -1; + if (is_ioinst(ipa0, ipa1, ipb)) { + r = kvm_handle_css_inst(cpu, run, ipa0, ipa1, ipb); + if (r == -1) { + setcc(cpu, 3); + r = 0; + } + } else { + dprintf("KVM: unknown PRIV: 0x%x\n", ipa1); + r = -1; + } break; } @@ -372,7 +538,7 @@ static int handle_priv(CPUS390XState *env, struct kvm_run *run, uint8_t ipa1) static int handle_hypercall(CPUS390XState *env, struct kvm_run *run) { cpu_synchronize_state(env); - env->regs[2] = s390_virtio_hypercall(env, env->regs[2], env->regs[1]); + env->regs[2] = s390_virtio_hypercall(env); return 0; } @@ -399,12 +565,10 @@ static int handle_diag(CPUS390XState *env, struct kvm_run *run, int ipb_code) static int s390_cpu_restart(S390CPU *cpu) { - CPUS390XState *env = &cpu->env; - - kvm_s390_interrupt(env, KVM_S390_RESTART, 0); - s390_add_running_cpu(env); + kvm_s390_interrupt(cpu, KVM_S390_RESTART, 0); + s390_add_running_cpu(cpu); qemu_cpu_kick(CPU(cpu)); - dprintf("DONE: SIGP cpu restart: %p\n", env); + dprintf("DONE: SIGP cpu restart: %p\n", &cpu->env); return 0; } @@ -415,12 +579,13 @@ static int s390_store_status(CPUS390XState *env, uint32_t parameter) return -1; } -static int s390_cpu_initial_reset(CPUS390XState *env) +static int s390_cpu_initial_reset(S390CPU *cpu) { + CPUS390XState *env = &cpu->env; int i; - s390_del_running_cpu(env); - if (kvm_vcpu_ioctl(env, KVM_S390_INITIAL_RESET, NULL) < 0) { + s390_del_running_cpu(cpu); + if (kvm_vcpu_ioctl(CPU(cpu), KVM_S390_INITIAL_RESET, NULL) < 0) { perror("cannot init reset vcpu"); } @@ -434,8 +599,9 @@ static int s390_cpu_initial_reset(CPUS390XState *env) return 0; } -static int handle_sigp(CPUS390XState *env, struct kvm_run *run, uint8_t ipa1) +static int handle_sigp(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) { + CPUS390XState *env = &cpu->env; uint8_t order_code; uint32_t parameter; uint16_t cpu_addr; @@ -479,7 +645,7 @@ static int handle_sigp(CPUS390XState *env, struct kvm_run *run, uint8_t ipa1) /* make the caller panic */ return -1; case SIGP_INITIAL_CPU_RESET: - r = s390_cpu_initial_reset(target_env); + r = s390_cpu_initial_reset(target_cpu); break; default: fprintf(stderr, "KVM: unknown SIGP: 0x%x\n", order_code); @@ -487,12 +653,13 @@ static int handle_sigp(CPUS390XState *env, struct kvm_run *run, uint8_t ipa1) } out: - setcc(env, r ? 3 : 0); + setcc(cpu, r ? 3 : 0); return 0; } -static int handle_instruction(CPUS390XState *env, struct kvm_run *run) +static int handle_instruction(S390CPU *cpu, struct kvm_run *run) { + CPUS390XState *env = &cpu->env; unsigned int ipa0 = (run->s390_sieic.ipa & 0xff00); uint8_t ipa1 = run->s390_sieic.ipa & 0x00ff; int ipb_code = (run->s390_sieic.ipb & 0x0fff0000) >> 16; @@ -500,50 +667,53 @@ static int handle_instruction(CPUS390XState *env, struct kvm_run *run) dprintf("handle_instruction 0x%x 0x%x\n", run->s390_sieic.ipa, run->s390_sieic.ipb); switch (ipa0) { - case IPA0_PRIV: - r = handle_priv(env, run, ipa1); - break; - case IPA0_DIAG: - r = handle_diag(env, run, ipb_code); - break; - case IPA0_SIGP: - r = handle_sigp(env, run, ipa1); - break; + case IPA0_B2: + case IPA0_B9: + case IPA0_EB: + r = handle_priv(cpu, run, ipa0 >> 8, ipa1); + break; + case IPA0_DIAG: + r = handle_diag(env, run, ipb_code); + break; + case IPA0_SIGP: + r = handle_sigp(cpu, run, ipa1); + break; } if (r < 0) { - enter_pgmcheck(env, 0x0001); + enter_pgmcheck(cpu, 0x0001); } return 0; } -static bool is_special_wait_psw(CPUS390XState *env) +static bool is_special_wait_psw(CPUState *cs) { /* signal quiesce */ - return env->kvm_run->psw_addr == 0xfffUL; + return cs->kvm_run->psw_addr == 0xfffUL; } -static int handle_intercept(CPUS390XState *env) +static int handle_intercept(S390CPU *cpu) { - struct kvm_run *run = env->kvm_run; + CPUState *cs = CPU(cpu); + struct kvm_run *run = cs->kvm_run; int icpt_code = run->s390_sieic.icptcode; int r = 0; dprintf("intercept: 0x%x (at 0x%lx)\n", icpt_code, - (long)env->kvm_run->psw_addr); + (long)cs->kvm_run->psw_addr); switch (icpt_code) { case ICPT_INSTRUCTION: - r = handle_instruction(env, run); + r = handle_instruction(cpu, run); break; case ICPT_WAITPSW: - if (s390_del_running_cpu(env) == 0 && - is_special_wait_psw(env)) { + if (s390_del_running_cpu(cpu) == 0 && + is_special_wait_psw(cs)) { qemu_system_shutdown_request(); } r = EXCP_HALTED; break; case ICPT_CPU_STOP: - if (s390_del_running_cpu(env) == 0) { + if (s390_del_running_cpu(cpu) == 0) { qemu_system_shutdown_request(); } r = EXCP_HALTED; @@ -565,17 +735,58 @@ static int handle_intercept(CPUS390XState *env) return r; } -int kvm_arch_handle_exit(CPUS390XState *env, struct kvm_run *run) +static int handle_tsch(S390CPU *cpu) { + CPUS390XState *env = &cpu->env; + CPUState *cs = CPU(cpu); + struct kvm_run *run = cs->kvm_run; + int ret; + + cpu_synchronize_state(env); + ret = ioinst_handle_tsch(env, env->regs[1], run->s390_tsch.ipb); + if (ret >= 0) { + /* Success; set condition code. */ + setcc(cpu, ret); + ret = 0; + } else if (ret < -1) { + /* + * Failure. + * If an I/O interrupt had been dequeued, we have to reinject it. + */ + if (run->s390_tsch.dequeued) { + uint16_t subchannel_id = run->s390_tsch.subchannel_id; + uint16_t subchannel_nr = run->s390_tsch.subchannel_nr; + uint32_t io_int_parm = run->s390_tsch.io_int_parm; + uint32_t io_int_word = run->s390_tsch.io_int_word; + uint32_t type = ((subchannel_id & 0xff00) << 24) | + ((subchannel_id & 0x00060) << 22) | (subchannel_nr << 16); + + kvm_s390_interrupt_internal(cpu, type, + ((uint32_t)subchannel_id << 16) + | subchannel_nr, + ((uint64_t)io_int_parm << 32) + | io_int_word, 1); + } + ret = 0; + } + return ret; +} + +int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) +{ + S390CPU *cpu = S390_CPU(cs); int ret = 0; switch (run->exit_reason) { case KVM_EXIT_S390_SIEIC: - ret = handle_intercept(env); + ret = handle_intercept(cpu); break; case KVM_EXIT_S390_RESET: qemu_system_reset_request(); break; + case KVM_EXIT_S390_TSCH: + ret = handle_tsch(cpu); + break; default: fprintf(stderr, "Unknown KVM exit: %d\n", run->exit_reason); break; @@ -587,12 +798,12 @@ int kvm_arch_handle_exit(CPUS390XState *env, struct kvm_run *run) return ret; } -bool kvm_arch_stop_on_emulation_error(CPUS390XState *env) +bool kvm_arch_stop_on_emulation_error(CPUState *cpu) { return true; } -int kvm_arch_on_sigbus_vcpu(CPUS390XState *env, int code, void *addr) +int kvm_arch_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) { return 1; } @@ -601,3 +812,33 @@ int kvm_arch_on_sigbus(int code, void *addr) { return 1; } + +void kvm_s390_io_interrupt(S390CPU *cpu, uint16_t subchannel_id, + uint16_t subchannel_nr, uint32_t io_int_parm, + uint32_t io_int_word) +{ + uint32_t type; + + type = ((subchannel_id & 0xff00) << 24) | + ((subchannel_id & 0x00060) << 22) | (subchannel_nr << 16); + kvm_s390_interrupt_internal(cpu, type, + ((uint32_t)subchannel_id << 16) | subchannel_nr, + ((uint64_t)io_int_parm << 32) | io_int_word, 1); +} + +void kvm_s390_crw_mchk(S390CPU *cpu) +{ + kvm_s390_interrupt_internal(cpu, KVM_S390_MCHK, 1 << 28, + 0x00400f1d40330000, 1); +} + +void kvm_s390_enable_css_support(S390CPU *cpu) +{ + struct kvm_enable_cap cap = {}; + int r; + + /* Activate host kernel channel subsystem support. */ + cap.cap = KVM_CAP_S390_CSS_SUPPORT; + r = kvm_vcpu_ioctl(CPU(cpu), KVM_ENABLE_CAP, &cap); + assert(r == 0); +} diff --git a/target-s390x/machine.c b/target-s390x/machine.c deleted file mode 100644 index 3e79be6d56..0000000000 --- a/target-s390x/machine.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * QEMU S390x machine definitions - * - * Copyright (c) 2009 Alexander Graf - * - * 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 . - */ - -#include "hw/hw.h" -#include "hw/boards.h" - -void cpu_save(QEMUFile *f, void *opaque) -{ -} - -int cpu_load(QEMUFile *f, void *opaque, int version_id) -{ - return 0; -} diff --git a/target-s390x/mem_helper.c b/target-s390x/mem_helper.c index bed21e6e1c..372334b3c8 100644 --- a/target-s390x/mem_helper.c +++ b/target-s390x/mem_helper.c @@ -304,214 +304,142 @@ uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask, return cc; } -/* store character under mask */ -void HELPER(stcm)(CPUS390XState *env, uint32_t r1, uint32_t mask, - uint64_t addr) +static inline uint64_t fix_address(CPUS390XState *env, uint64_t a) { - uint8_t r; - - HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%lx\n", __func__, r1, mask, - addr); - while (mask) { - if (mask & 8) { - r = (r1 & 0xff000000UL) >> 24; - cpu_stb_data(env, addr, r); - HELPER_LOG("mask 0x%x %02x (0x%lx) ", mask, r, addr); - addr++; - } - mask = (mask << 1) & 0xf; - r1 <<= 8; + /* 31-Bit mode */ + if (!(env->psw.mask & PSW_MASK_64)) { + a &= 0x7fffffff; } - HELPER_LOG("\n"); + return a; } static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2) { uint64_t r = d2; - if (x2) { r += env->regs[x2]; } - if (b2) { r += env->regs[b2]; } - - /* 31-Bit mode */ - if (!(env->psw.mask & PSW_MASK_64)) { - r &= 0x7fffffff; - } - - return r; + return fix_address(env, r); } static inline uint64_t get_address_31fix(CPUS390XState *env, int reg) { - uint64_t r = env->regs[reg]; - - /* 31-Bit mode */ - if (!(env->psw.mask & PSW_MASK_64)) { - r &= 0x7fffffff; - } - - return r; + return fix_address(env, env->regs[reg]); } /* search string (c is byte to search, r2 is string, r1 end of string) */ -uint32_t HELPER(srst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2) +uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end, + uint64_t str) { - uint64_t i; - uint32_t cc = 2; - uint64_t str = get_address_31fix(env, r2); - uint64_t end = get_address_31fix(env, r1); + uint32_t len; + uint8_t v, c = r0; - HELPER_LOG("%s: c %d *r1 0x%" PRIx64 " *r2 0x%" PRIx64 "\n", __func__, - c, env->regs[r1], env->regs[r2]); + str = fix_address(env, str); + end = fix_address(env, end); - for (i = str; i != end; i++) { - if (cpu_ldub_data(env, i) == c) { - env->regs[r1] = i; - cc = 1; - break; + /* Assume for now that R2 is unmodified. */ + env->retxl = str; + + /* Lest we fail to service interrupts in a timely manner, limit the + amount of work we're willing to do. For now, lets cap at 8k. */ + for (len = 0; len < 0x2000; ++len) { + if (str + len == end) { + /* Character not found. R1 & R2 are unmodified. */ + env->cc_op = 2; + return end; + } + v = cpu_ldub_data(env, str + len); + if (v == c) { + /* Character found. Set R1 to the location; R2 is unmodified. */ + env->cc_op = 1; + return str + len; } } - return cc; + /* CPU-determined bytes processed. Advance R2 to next byte to process. */ + env->retxl = str + len; + env->cc_op = 3; + return end; } /* unsigned string compare (c is string terminator) */ -uint32_t HELPER(clst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2) +uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2) { - uint64_t s1 = get_address_31fix(env, r1); - uint64_t s2 = get_address_31fix(env, r2); - uint8_t v1, v2; - uint32_t cc; + uint32_t len; c = c & 0xff; -#ifdef CONFIG_USER_ONLY - if (!c) { - HELPER_LOG("%s: comparing '%s' and '%s'\n", - __func__, (char *)g2h(s1), (char *)g2h(s2)); - } -#endif - for (;;) { - v1 = cpu_ldub_data(env, s1); - v2 = cpu_ldub_data(env, s2); - if ((v1 == c || v2 == c) || (v1 != v2)) { - break; + s1 = fix_address(env, s1); + s2 = fix_address(env, s2); + + /* Lest we fail to service interrupts in a timely manner, limit the + amount of work we're willing to do. For now, lets cap at 8k. */ + for (len = 0; len < 0x2000; ++len) { + uint8_t v1 = cpu_ldub_data(env, s1 + len); + uint8_t v2 = cpu_ldub_data(env, s2 + len); + if (v1 == v2) { + if (v1 == c) { + /* Equal. CC=0, and don't advance the registers. */ + env->cc_op = 0; + env->retxl = s2; + return s1; + } + } else { + /* Unequal. CC={1,2}, and advance the registers. Note that + the terminator need not be zero, but the string that contains + the terminator is by definition "low". */ + env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2); + env->retxl = s2 + len; + return s1 + len; } - s1++; - s2++; } - if (v1 == v2) { - cc = 0; - } else { - cc = (v1 < v2) ? 1 : 2; - /* FIXME: 31-bit mode! */ - env->regs[r1] = s1; - env->regs[r2] = s2; - } - return cc; + /* CPU-determined bytes equal; advance the registers. */ + env->cc_op = 3; + env->retxl = s2 + len; + return s1 + len; } /* move page */ void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2) { /* XXX missing r0 handling */ + env->cc_op = 0; #ifdef CONFIG_USER_ONLY - int i; - - for (i = 0; i < TARGET_PAGE_SIZE; i++) { - cpu_stb_data(env, r1 + i, cpu_ldub_data(env, r2 + i)); - } + memmove(g2h(r1), g2h(r2), TARGET_PAGE_SIZE); #else mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2); #endif } /* string copy (c is string terminator) */ -void HELPER(mvst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2) +uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s) { - uint64_t dest = get_address_31fix(env, r1); - uint64_t src = get_address_31fix(env, r2); - uint8_t v; + uint32_t len; c = c & 0xff; -#ifdef CONFIG_USER_ONLY - if (!c) { - HELPER_LOG("%s: copy '%s' to 0x%lx\n", __func__, (char *)g2h(src), - dest); - } -#endif - for (;;) { - v = cpu_ldub_data(env, src); - cpu_stb_data(env, dest, v); + d = fix_address(env, d); + s = fix_address(env, s); + + /* Lest we fail to service interrupts in a timely manner, limit the + amount of work we're willing to do. For now, lets cap at 8k. */ + for (len = 0; len < 0x2000; ++len) { + uint8_t v = cpu_ldub_data(env, s + len); + cpu_stb_data(env, d + len, v); if (v == c) { - break; + /* Complete. Set CC=1 and advance R1. */ + env->cc_op = 1; + env->retxl = s; + return d + len; } - src++; - dest++; - } - env->regs[r1] = dest; /* FIXME: 31-bit mode! */ -} - -/* compare and swap 64-bit */ -uint32_t HELPER(csg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) -{ - /* FIXME: locking? */ - uint32_t cc; - uint64_t v2 = cpu_ldq_data(env, a2); - - if (env->regs[r1] == v2) { - cc = 0; - cpu_stq_data(env, a2, env->regs[r3]); - } else { - cc = 1; - env->regs[r1] = v2; - } - return cc; -} - -/* compare double and swap 64-bit */ -uint32_t HELPER(cdsg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) -{ - /* FIXME: locking? */ - uint32_t cc; - uint64_t v2_hi = cpu_ldq_data(env, a2); - uint64_t v2_lo = cpu_ldq_data(env, a2 + 8); - uint64_t v1_hi = env->regs[r1]; - uint64_t v1_lo = env->regs[r1 + 1]; - - if ((v1_hi == v2_hi) && (v1_lo == v2_lo)) { - cc = 0; - cpu_stq_data(env, a2, env->regs[r3]); - cpu_stq_data(env, a2 + 8, env->regs[r3 + 1]); - } else { - cc = 1; - env->regs[r1] = v2_hi; - env->regs[r1 + 1] = v2_lo; } - return cc; -} - -/* compare and swap 32-bit */ -uint32_t HELPER(cs)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) -{ - /* FIXME: locking? */ - uint32_t cc; - uint32_t v2 = cpu_ldl_data(env, a2); - - HELPER_LOG("%s: r1 %d a2 0x%lx r3 %d\n", __func__, r1, a2, r3); - if (((uint32_t)env->regs[r1]) == v2) { - cc = 0; - cpu_stl_data(env, a2, (uint32_t)env->regs[r3]); - } else { - cc = 1; - env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | v2; - } - return cc; + /* Incomplete. Set CC=3 and signal to advance R1 and R2. */ + env->cc_op = 3; + env->retxl = s + len; + return d + len; } static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address, @@ -594,7 +522,7 @@ uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1, HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff); env->psw.addr = ret - 4; env->int_svc_code = (insn | v1) & 0xff; - env->int_svc_ilc = 4; + env->int_svc_ilen = 4; helper_exception(env, EXCP_SVC); } else if ((insn & 0xff00) == 0xbf00) { uint32_t insn2, r1, r3, b2, d2; @@ -613,55 +541,6 @@ uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1, return cc; } -/* store character under mask high operates on the upper half of r1 */ -void HELPER(stcmh)(CPUS390XState *env, uint32_t r1, uint64_t address, - uint32_t mask) -{ - int pos = 56; /* top of the upper half of r1 */ - - while (mask) { - if (mask & 8) { - cpu_stb_data(env, address, (env->regs[r1] >> pos) & 0xff); - address++; - } - mask = (mask << 1) & 0xf; - pos -= 8; - } -} - -/* insert character under mask high; same as icm, but operates on the - upper half of r1 */ -uint32_t HELPER(icmh)(CPUS390XState *env, uint32_t r1, uint64_t address, - uint32_t mask) -{ - int pos = 56; /* top of the upper half of r1 */ - uint64_t rmask = 0xff00000000000000ULL; - uint8_t val = 0; - int ccd = 0; - uint32_t cc = 0; - - while (mask) { - if (mask & 8) { - env->regs[r1] &= ~rmask; - val = cpu_ldub_data(env, address); - if ((val & 0x80) && !ccd) { - cc = 1; - } - ccd = 1; - if (val && cc == 0) { - cc = 2; - } - env->regs[r1] |= (uint64_t)val << pos; - address++; - } - mask = (mask << 1) & 0xf; - pos -= 8; - rmask >>= 8; - } - - return cc; -} - /* load access registers r1 to r3 from memory at a2 */ void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) { @@ -822,42 +701,49 @@ uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2, } /* checksum */ -void HELPER(cksm)(CPUS390XState *env, uint32_t r1, uint32_t r2) +uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1, + uint64_t src, uint64_t src_len) { - uint64_t src = get_address_31fix(env, r2); - uint64_t src_len = env->regs[(r2 + 1) & 15]; - uint64_t cksm = (uint32_t)env->regs[r1]; + uint64_t max_len, len; + uint64_t cksm = (uint32_t)r1; - while (src_len >= 4) { - cksm += cpu_ldl_data(env, src); + /* Lest we fail to service interrupts in a timely manner, limit the + amount of work we're willing to do. For now, lets cap at 8k. */ + max_len = (src_len > 0x2000 ? 0x2000 : src_len); - /* move to next word */ - src_len -= 4; - src += 4; + /* Process full words as available. */ + for (len = 0; len + 4 <= max_len; len += 4, src += 4) { + cksm += (uint32_t)cpu_ldl_data(env, src); } - switch (src_len) { - case 0: - break; + switch (max_len - len) { case 1: cksm += cpu_ldub_data(env, src) << 24; + len += 1; break; case 2: cksm += cpu_lduw_data(env, src) << 16; + len += 2; break; case 3: cksm += cpu_lduw_data(env, src) << 16; cksm += cpu_ldub_data(env, src + 2) << 8; + len += 3; break; } - /* indicate we've processed everything */ - env->regs[r2] = src + src_len; - env->regs[(r2 + 1) & 15] = 0; + /* Fold the carry from the checksum. Note that we can see carry-out + during folding more than once (but probably not more than twice). */ + while (cksm > 0xffffffffull) { + cksm = (uint32_t)cksm + (cksm >> 32); + } - /* store result */ - env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | - ((uint32_t)cksm + (cksm >> 32)); + /* Indicate whether or not we've processed everything. */ + env->cc_op = (len == src_len ? 0 : 3); + + /* Return both cksm and processed length. */ + env->retxl = cksm; + return len; } void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest, @@ -1007,7 +893,7 @@ uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2) } /* set storage key extended */ -void HELPER(sske)(CPUS390XState *env, uint32_t r1, uint64_t r2) +void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2) { uint64_t addr = get_address(env, 0, 0, r2); @@ -1019,7 +905,7 @@ void HELPER(sske)(CPUS390XState *env, uint32_t r1, uint64_t r2) } /* reset reference bit extended */ -uint32_t HELPER(rrbe)(CPUS390XState *env, uint32_t r1, uint64_t r2) +uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2) { uint8_t re; uint8_t key; @@ -1045,16 +931,16 @@ uint32_t HELPER(rrbe)(CPUS390XState *env, uint32_t r1, uint64_t r2) } /* compare and swap and purge */ -uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint32_t r2) +uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2) { uint32_t cc; uint32_t o1 = env->regs[r1]; - uint64_t a2 = get_address_31fix(env, r2) & ~3ULL; + uint64_t a2 = r2 & ~3ULL; uint32_t o2 = cpu_ldl_data(env, a2); if (o1 == o2) { cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]); - if (env->regs[r2] & 0x3) { + if (r2 & 0x3) { /* flush TLB / ALB */ tlb_flush(env, 1); } @@ -1154,13 +1040,13 @@ void HELPER(ptlb)(CPUS390XState *env) } /* store using real address */ -void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint32_t v1) +void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1) { - stw_phys(get_address(env, 0, 0, addr), v1); + stw_phys(get_address(env, 0, 0, addr), (uint32_t)v1); } /* load real address */ -uint32_t HELPER(lra)(CPUS390XState *env, uint64_t addr, uint32_t r1) +uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr) { uint32_t cc = 0; int old_exc = env->exception_index; @@ -1184,14 +1070,7 @@ uint32_t HELPER(lra)(CPUS390XState *env, uint64_t addr, uint32_t r1) } env->exception_index = old_exc; - if (!(env->psw.mask & PSW_MASK_64)) { - env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | - (ret & 0xffffffffULL); - } else { - env->regs[r1] = ret; - } - - return cc; + env->cc_op = cc; + return ret; } - #endif diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c index e521ed55cc..09301d0a6f 100644 --- a/target-s390x/misc_helper.c +++ b/target-s390x/misc_helper.c @@ -41,7 +41,27 @@ #define HELPER_LOG(x...) #endif -/* raise an exception */ +/* Raise an exception dynamically from a helper function. */ +void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp, + uintptr_t retaddr) +{ + int t; + + env->exception_index = EXCP_PGM; + env->int_pgm_code = excp; + + /* Use the (ultimate) callers address to find the insn that trapped. */ + cpu_restore_state(env, retaddr); + + /* Advance past the insn. */ + t = cpu_ldub_code(env, env->psw.addr); + env->int_pgm_ilen = t = get_ilen(t); + env->psw.addr += 2 * t; + + cpu_loop_exit(env); +} + +/* Raise an exception statically from a TB. */ void HELPER(exception)(CPUS390XState *env, uint32_t excp) { HELPER_LOG("%s: exception %d\n", __func__, excp); @@ -50,29 +70,108 @@ void HELPER(exception)(CPUS390XState *env, uint32_t excp) } #ifndef CONFIG_USER_ONLY -void program_interrupt(CPUS390XState *env, uint32_t code, int ilc) + +/* EBCDIC handling */ +static const uint8_t ebcdic2ascii[] = { + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, + 0x87, 0xA4, 0x5B, 0x2E, 0x3C, 0x28, 0x2B, 0x21, + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, + 0x8D, 0xE1, 0x5D, 0x24, 0x2A, 0x29, 0x3B, 0x5E, + 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, + 0x9B, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, + 0xAB, 0x07, 0xAA, 0x7C, 0x07, 0x07, 0x07, 0x07, + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, + 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07, +}; + +static const uint8_t ascii2ebcdic[] = { + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF +}; + +static inline void ebcdic_put(uint8_t *p, const char *ascii, int len) +{ + int i; + + for (i = 0; i < len; i++) { + p[i] = ascii2ebcdic[(uint8_t)ascii[i]]; + } +} + +void program_interrupt(CPUS390XState *env, uint32_t code, int ilen) { qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", env->psw.addr); if (kvm_enabled()) { #ifdef CONFIG_KVM - kvm_s390_interrupt(env, KVM_S390_PROGRAM_INT, code); + kvm_s390_interrupt(s390_env_get_cpu(env), KVM_S390_PROGRAM_INT, code); #endif } else { env->int_pgm_code = code; - env->int_pgm_ilc = ilc; + env->int_pgm_ilen = ilen; env->exception_index = EXCP_PGM; cpu_loop_exit(env); } } /* SCLP service call */ -uint32_t HELPER(servc)(CPUS390XState *env, uint32_t r1, uint64_t r2) +uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2) { - int r; - - r = sclp_service_call(r1, r2); + int r = sclp_service_call(r1, r2); if (r < 0) { program_interrupt(env, -r, 4); return 0; @@ -89,7 +188,7 @@ uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem, switch (num) { case 0x500: /* KVM hypercall */ - r = s390_virtio_hypercall(env, mem, code); + r = s390_virtio_hypercall(env); break; case 0x44: /* yield */ @@ -105,38 +204,22 @@ uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem, } if (r) { - program_interrupt(env, PGM_OPERATION, ILC_LATER_INC); + program_interrupt(env, PGM_OPERATION, ILEN_LATER_INC); } return r; } -/* Store CPU ID */ -void HELPER(stidp)(CPUS390XState *env, uint64_t a1) -{ - cpu_stq_data(env, a1, env->cpu_num); -} - /* Set Prefix */ void HELPER(spx)(CPUS390XState *env, uint64_t a1) { - uint32_t prefix; - - prefix = cpu_ldl_data(env, a1); - env->psa = prefix & 0xfffff000; + uint32_t prefix = a1 & 0x7fffe000; + env->psa = prefix; qemu_log("prefix: %#x\n", prefix); tlb_flush_page(env, 0); tlb_flush_page(env, TARGET_PAGE_SIZE); } -/* Set Clock */ -uint32_t HELPER(sck)(uint64_t a1) -{ - /* XXX not implemented - is it necessary? */ - - return 0; -} - static inline uint64_t clock_value(CPUS390XState *env) { uint64_t time; @@ -148,32 +231,14 @@ static inline uint64_t clock_value(CPUS390XState *env) } /* Store Clock */ -uint32_t HELPER(stck)(CPUS390XState *env, uint64_t a1) +uint64_t HELPER(stck)(CPUS390XState *env) { - cpu_stq_data(env, a1, clock_value(env)); - - return 0; -} - -/* Store Clock Extended */ -uint32_t HELPER(stcke)(CPUS390XState *env, uint64_t a1) -{ - cpu_stb_data(env, a1, 0); - /* basically the same value as stck */ - cpu_stq_data(env, a1 + 1, clock_value(env) | env->cpu_num); - /* more fine grained than stck */ - cpu_stq_data(env, a1 + 9, 0); - /* XXX programmable fields */ - cpu_stw_data(env, a1 + 17, 0); - - return 0; + return clock_value(env); } /* Set Clock Comparator */ -void HELPER(sckc)(CPUS390XState *env, uint64_t a1) +void HELPER(sckc)(CPUS390XState *env, uint64_t time) { - uint64_t time = cpu_ldq_data(env, a1); - if (time == -1ULL) { return; } @@ -187,17 +252,15 @@ void HELPER(sckc)(CPUS390XState *env, uint64_t a1) } /* Store Clock Comparator */ -void HELPER(stckc)(CPUS390XState *env, uint64_t a1) +uint64_t HELPER(stckc)(CPUS390XState *env) { /* XXX implement */ - cpu_stq_data(env, a1, 0); + return 0; } /* Set CPU Timer */ -void HELPER(spt)(CPUS390XState *env, uint64_t a1) +void HELPER(spt)(CPUS390XState *env, uint64_t time) { - uint64_t time = cpu_ldq_data(env, a1); - if (time == -1ULL) { return; } @@ -209,15 +272,15 @@ void HELPER(spt)(CPUS390XState *env, uint64_t a1) } /* Store CPU Timer */ -void HELPER(stpt)(CPUS390XState *env, uint64_t a1) +uint64_t HELPER(stpt)(CPUS390XState *env) { /* XXX implement */ - cpu_stq_data(env, a1, 0); + return 0; } /* Store System Information */ -uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint32_t r0, - uint32_t r1) +uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, + uint64_t r0, uint64_t r1) { int cc = 0; int sel1, sel2; diff --git a/target-s390x/translate.c b/target-s390x/translate.c index 9e34741311..88e481cdbc 100644 --- a/target-s390x/translate.c +++ b/target-s390x/translate.c @@ -18,7 +18,6 @@ * License along with this library; if not, see . */ -/* #define DEBUG_ILLEGAL_INSTRUCTIONS */ /* #define DEBUG_INLINE_BRANCHES */ #define S390X_DEBUG_DISAS /* #define S390X_DEBUG_DISAS_VERBOSE */ @@ -33,6 +32,7 @@ #include "disas/disas.h" #include "tcg-op.h" #include "qemu/log.h" +#include "qemu/host-utils.h" /* global register indexes */ static TCGv_ptr cpu_env; @@ -42,29 +42,41 @@ static TCGv_ptr cpu_env; #define GEN_HELPER 1 #include "helper.h" + +/* Information that (most) every instruction needs to manipulate. */ typedef struct DisasContext DisasContext; +typedef struct DisasInsn DisasInsn; +typedef struct DisasFields DisasFields; + struct DisasContext { - uint64_t pc; - int is_jmp; - enum cc_op cc_op; struct TranslationBlock *tb; + const DisasInsn *insn; + DisasFields *fields; + uint64_t pc, next_pc; + enum cc_op cc_op; + bool singlestep_enabled; }; -#define DISAS_EXCP 4 +/* Information carried about a condition to be evaluated. */ +typedef struct { + TCGCond cond:8; + bool is_64; + bool g1; + bool g2; + union { + struct { TCGv_i64 a, b; } s64; + struct { TCGv_i32 a, b; } s32; + } u; +} DisasCompare; -static void gen_op_calc_cc(DisasContext *s); +#define DISAS_EXCP 4 #ifdef DEBUG_INLINE_BRANCHES static uint64_t inline_branch_hit[CC_OP_MAX]; static uint64_t inline_branch_miss[CC_OP_MAX]; #endif -static inline void debug_insn(uint64_t insn) -{ - LOG_DISAS("insn: 0x%" PRIx64 "\n", insn); -} - -static inline uint64_t pc_to_link_info(DisasContext *s, uint64_t pc) +static uint64_t pc_to_link_info(DisasContext *s, uint64_t pc) { if (!(s->tb->flags & FLAG_MASK_64)) { if (s->tb->flags & FLAG_MASK_32) { @@ -97,7 +109,7 @@ void cpu_dump_state(CPUS390XState *env, FILE *f, fprintf_function cpu_fprintf, } for (i = 0; i < 16; i++) { - cpu_fprintf(f, "F%02d=%016" PRIx64, i, *(uint64_t *)&env->fregs[i]); + cpu_fprintf(f, "F%02d=%016" PRIx64, i, env->fregs[i].ll); if ((i % 4) == 3) { cpu_fprintf(f, "\n"); } else { @@ -134,21 +146,22 @@ static TCGv_i64 cc_src; static TCGv_i64 cc_dst; static TCGv_i64 cc_vr; -static char cpu_reg_names[10*3 + 6*4]; +static char cpu_reg_names[32][4]; static TCGv_i64 regs[16]; +static TCGv_i64 fregs[16]; static uint8_t gen_opc_cc_op[OPC_BUF_SIZE]; void s390x_translate_init(void) { int i; - size_t cpu_reg_names_size = sizeof(cpu_reg_names); - char *p; cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); - psw_addr = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUS390XState, psw.addr), + psw_addr = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUS390XState, psw.addr), "psw_addr"); - psw_mask = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUS390XState, psw.mask), + psw_mask = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUS390XState, psw.mask), "psw_mask"); cc_op = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUS390XState, cc_op), @@ -160,116 +173,87 @@ void s390x_translate_init(void) cc_vr = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUS390XState, cc_vr), "cc_vr"); - p = cpu_reg_names; for (i = 0; i < 16; i++) { - snprintf(p, cpu_reg_names_size, "r%d", i); + snprintf(cpu_reg_names[i], sizeof(cpu_reg_names[0]), "r%d", i); regs[i] = tcg_global_mem_new(TCG_AREG0, - offsetof(CPUS390XState, regs[i]), p); - p += (i < 10) ? 3 : 4; - cpu_reg_names_size -= (i < 10) ? 3 : 4; + offsetof(CPUS390XState, regs[i]), + cpu_reg_names[i]); } + + for (i = 0; i < 16; i++) { + snprintf(cpu_reg_names[i + 16], sizeof(cpu_reg_names[0]), "f%d", i); + fregs[i] = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUS390XState, fregs[i].d), + cpu_reg_names[i + 16]); + } + + /* register helpers */ +#define GEN_HELPER 2 +#include "helper.h" } -static inline TCGv_i64 load_reg(int reg) +static TCGv_i64 load_reg(int reg) { TCGv_i64 r = tcg_temp_new_i64(); tcg_gen_mov_i64(r, regs[reg]); return r; } -static inline TCGv_i64 load_freg(int reg) +static TCGv_i64 load_freg32_i64(int reg) { TCGv_i64 r = tcg_temp_new_i64(); - tcg_gen_ld_i64(r, cpu_env, offsetof(CPUS390XState, fregs[reg].d)); + tcg_gen_shri_i64(r, fregs[reg], 32); return r; } -static inline TCGv_i32 load_freg32(int reg) -{ - TCGv_i32 r = tcg_temp_new_i32(); - tcg_gen_ld_i32(r, cpu_env, offsetof(CPUS390XState, fregs[reg].l.upper)); - return r; -} - -static inline TCGv_i32 load_reg32(int reg) -{ - TCGv_i32 r = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(r, regs[reg]); - return r; -} - -static inline TCGv_i64 load_reg32_i64(int reg) -{ - TCGv_i64 r = tcg_temp_new_i64(); - tcg_gen_ext32s_i64(r, regs[reg]); - return r; -} - -static inline void store_reg(int reg, TCGv_i64 v) +static void store_reg(int reg, TCGv_i64 v) { tcg_gen_mov_i64(regs[reg], v); } -static inline void store_freg(int reg, TCGv_i64 v) +static void store_freg(int reg, TCGv_i64 v) { - tcg_gen_st_i64(v, cpu_env, offsetof(CPUS390XState, fregs[reg].d)); + tcg_gen_mov_i64(fregs[reg], v); } -static inline void store_reg32(int reg, TCGv_i32 v) -{ -#if HOST_LONG_BITS == 32 - tcg_gen_mov_i32(TCGV_LOW(regs[reg]), v); -#else - TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(tmp, v); - /* 32 bit register writes keep the upper half */ - tcg_gen_deposit_i64(regs[reg], regs[reg], tmp, 0, 32); - tcg_temp_free_i64(tmp); -#endif -} - -static inline void store_reg32_i64(int reg, TCGv_i64 v) +static void store_reg32_i64(int reg, TCGv_i64 v) { /* 32 bit register writes keep the upper half */ -#if HOST_LONG_BITS == 32 - tcg_gen_mov_i32(TCGV_LOW(regs[reg]), TCGV_LOW(v)); -#else tcg_gen_deposit_i64(regs[reg], regs[reg], v, 0, 32); -#endif } -static inline void store_reg16(int reg, TCGv_i32 v) +static void store_reg32h_i64(int reg, TCGv_i64 v) { - TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(tmp, v); - /* 16 bit register writes keep the upper bytes */ - tcg_gen_deposit_i64(regs[reg], regs[reg], tmp, 0, 16); - tcg_temp_free_i64(tmp); + tcg_gen_deposit_i64(regs[reg], regs[reg], v, 32, 32); } -static inline void store_reg8(int reg, TCGv_i64 v) +static void store_freg32_i64(int reg, TCGv_i64 v) { - /* 8 bit register writes keep the upper bytes */ - tcg_gen_deposit_i64(regs[reg], regs[reg], v, 0, 8); + tcg_gen_deposit_i64(fregs[reg], fregs[reg], v, 32, 32); } -static inline void store_freg32(int reg, TCGv_i32 v) +static void return_low128(TCGv_i64 dest) { - tcg_gen_st_i32(v, cpu_env, offsetof(CPUS390XState, fregs[reg].l.upper)); + tcg_gen_ld_i64(dest, cpu_env, offsetof(CPUS390XState, retxl)); } -static inline void update_psw_addr(DisasContext *s) +static void update_psw_addr(DisasContext *s) { /* psw.addr */ tcg_gen_movi_i64(psw_addr, s->pc); } -static inline void potential_page_fault(DisasContext *s) +static void update_cc_op(DisasContext *s) +{ + if (s->cc_op != CC_OP_DYNAMIC && s->cc_op != CC_OP_STATIC) { + tcg_gen_movi_i32(cc_op, s->cc_op); + } +} + +static void potential_page_fault(DisasContext *s) { -#ifndef CONFIG_USER_ONLY update_psw_addr(s); - gen_op_calc_cc(s); -#endif + update_cc_op(s); } static inline uint64_t ld_code2(CPUS390XState *env, uint64_t pc) @@ -279,18 +263,15 @@ static inline uint64_t ld_code2(CPUS390XState *env, uint64_t pc) static inline uint64_t ld_code4(CPUS390XState *env, uint64_t pc) { - return (uint64_t)cpu_ldl_code(env, pc); + return (uint64_t)(uint32_t)cpu_ldl_code(env, pc); } static inline uint64_t ld_code6(CPUS390XState *env, uint64_t pc) { - uint64_t opc; - opc = (uint64_t)cpu_lduw_code(env, pc) << 32; - opc |= (uint64_t)(uint32_t)cpu_ldl_code(env, pc + 2); - return opc; + return (ld_code2(env, pc) << 32) | ld_code4(env, pc + 2); } -static inline int get_mem_index(DisasContext *s) +static int get_mem_index(DisasContext *s) { switch (s->tb->flags & FLAG_MASK_ASC) { case PSW_ASC_PRIMARY >> 32: @@ -305,179 +286,114 @@ static inline int get_mem_index(DisasContext *s) } } -static inline void gen_debug(DisasContext *s) +static void gen_exception(int excp) { - TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG); - update_psw_addr(s); - gen_op_calc_cc(s); + TCGv_i32 tmp = tcg_const_i32(excp); gen_helper_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); - s->is_jmp = DISAS_EXCP; } -#ifdef CONFIG_USER_ONLY - -static void gen_illegal_opcode(CPUS390XState *env, DisasContext *s, int ilc) -{ - TCGv_i32 tmp = tcg_const_i32(EXCP_SPEC); - update_psw_addr(s); - gen_op_calc_cc(s); - gen_helper_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); - s->is_jmp = DISAS_EXCP; -} - -#else /* CONFIG_USER_ONLY */ - -static void debug_print_inst(CPUS390XState *env, DisasContext *s, int ilc) -{ -#ifdef DEBUG_ILLEGAL_INSTRUCTIONS - uint64_t inst = 0; - - switch (ilc & 3) { - case 1: - inst = ld_code2(env, s->pc); - break; - case 2: - inst = ld_code4(env, s->pc); - break; - case 3: - inst = ld_code6(env, s->pc); - break; - } - - fprintf(stderr, "Illegal instruction [%d at %016" PRIx64 "]: 0x%016" - PRIx64 "\n", ilc, s->pc, inst); -#endif -} - -static void gen_program_exception(CPUS390XState *env, DisasContext *s, int ilc, - int code) +static void gen_program_exception(DisasContext *s, int code) { TCGv_i32 tmp; - debug_print_inst(env, s, ilc); - - /* remember what pgm exeption this was */ + /* Remember what pgm exeption this was. */ tmp = tcg_const_i32(code); tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUS390XState, int_pgm_code)); tcg_temp_free_i32(tmp); - tmp = tcg_const_i32(ilc); - tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUS390XState, int_pgm_ilc)); + tmp = tcg_const_i32(s->next_pc - s->pc); + tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUS390XState, int_pgm_ilen)); tcg_temp_free_i32(tmp); - /* advance past instruction */ - s->pc += (ilc * 2); + /* Advance past instruction. */ + s->pc = s->next_pc; update_psw_addr(s); - /* save off cc */ - gen_op_calc_cc(s); + /* Save off cc. */ + update_cc_op(s); - /* trigger exception */ - tmp = tcg_const_i32(EXCP_PGM); - gen_helper_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); - - /* end TB here */ - s->is_jmp = DISAS_EXCP; + /* Trigger exception. */ + gen_exception(EXCP_PGM); } - -static void gen_illegal_opcode(CPUS390XState *env, DisasContext *s, int ilc) +static inline void gen_illegal_opcode(DisasContext *s) { - gen_program_exception(env, s, ilc, PGM_SPECIFICATION); + gen_program_exception(s, PGM_SPECIFICATION); } -static void gen_privileged_exception(CPUS390XState *env, DisasContext *s, - int ilc) -{ - gen_program_exception(env, s, ilc, PGM_PRIVILEGED); -} - -static void check_privileged(CPUS390XState *env, DisasContext *s, int ilc) +static inline void check_privileged(DisasContext *s) { if (s->tb->flags & (PSW_MASK_PSTATE >> 32)) { - gen_privileged_exception(env, s, ilc); + gen_program_exception(s, PGM_PRIVILEGED); } } -#endif /* CONFIG_USER_ONLY */ - static TCGv_i64 get_address(DisasContext *s, int x2, int b2, int d2) { - TCGv_i64 tmp; + TCGv_i64 tmp = tcg_temp_new_i64(); + bool need_31 = !(s->tb->flags & FLAG_MASK_64); - /* 31-bitify the immediate part; register contents are dealt with below */ - if (!(s->tb->flags & FLAG_MASK_64)) { - d2 &= 0x7fffffffUL; - } + /* Note that d2 is limited to 20 bits, signed. If we crop negative + displacements early we create larger immedate addends. */ - if (x2) { - if (d2) { - tmp = tcg_const_i64(d2); - tcg_gen_add_i64(tmp, tmp, regs[x2]); - } else { - tmp = load_reg(x2); - } - if (b2) { - tcg_gen_add_i64(tmp, tmp, regs[b2]); - } + /* Note that addi optimizes the imm==0 case. */ + if (b2 && x2) { + tcg_gen_add_i64(tmp, regs[b2], regs[x2]); + tcg_gen_addi_i64(tmp, tmp, d2); } else if (b2) { - if (d2) { - tmp = tcg_const_i64(d2); - tcg_gen_add_i64(tmp, tmp, regs[b2]); - } else { - tmp = load_reg(b2); - } + tcg_gen_addi_i64(tmp, regs[b2], d2); + } else if (x2) { + tcg_gen_addi_i64(tmp, regs[x2], d2); } else { - tmp = tcg_const_i64(d2); + if (need_31) { + d2 &= 0x7fffffff; + need_31 = false; + } + tcg_gen_movi_i64(tmp, d2); } - - /* 31-bit mode mask if there are values loaded from registers */ - if (!(s->tb->flags & FLAG_MASK_64) && (x2 || b2)) { - tcg_gen_andi_i64(tmp, tmp, 0x7fffffffUL); + if (need_31) { + tcg_gen_andi_i64(tmp, tmp, 0x7fffffff); } return tmp; } -static void gen_op_movi_cc(DisasContext *s, uint32_t val) +static inline bool live_cc_data(DisasContext *s) { + return (s->cc_op != CC_OP_DYNAMIC + && s->cc_op != CC_OP_STATIC + && s->cc_op > 3); +} + +static inline void gen_op_movi_cc(DisasContext *s, uint32_t val) +{ + if (live_cc_data(s)) { + tcg_gen_discard_i64(cc_src); + tcg_gen_discard_i64(cc_dst); + tcg_gen_discard_i64(cc_vr); + } s->cc_op = CC_OP_CONST0 + val; } static void gen_op_update1_cc_i64(DisasContext *s, enum cc_op op, TCGv_i64 dst) { - tcg_gen_discard_i64(cc_src); + if (live_cc_data(s)) { + tcg_gen_discard_i64(cc_src); + tcg_gen_discard_i64(cc_vr); + } tcg_gen_mov_i64(cc_dst, dst); - tcg_gen_discard_i64(cc_vr); - s->cc_op = op; -} - -static void gen_op_update1_cc_i32(DisasContext *s, enum cc_op op, TCGv_i32 dst) -{ - tcg_gen_discard_i64(cc_src); - tcg_gen_extu_i32_i64(cc_dst, dst); - tcg_gen_discard_i64(cc_vr); s->cc_op = op; } static void gen_op_update2_cc_i64(DisasContext *s, enum cc_op op, TCGv_i64 src, TCGv_i64 dst) { + if (live_cc_data(s)) { + tcg_gen_discard_i64(cc_vr); + } tcg_gen_mov_i64(cc_src, src); tcg_gen_mov_i64(cc_dst, dst); - tcg_gen_discard_i64(cc_vr); - s->cc_op = op; -} - -static void gen_op_update2_cc_i32(DisasContext *s, enum cc_op op, TCGv_i32 src, - TCGv_i32 dst) -{ - tcg_gen_extu_i32_i64(cc_src, src); - tcg_gen_extu_i32_i64(cc_dst, dst); - tcg_gen_discard_i64(cc_vr); s->cc_op = op; } @@ -490,214 +406,71 @@ static void gen_op_update3_cc_i64(DisasContext *s, enum cc_op op, TCGv_i64 src, s->cc_op = op; } -static void gen_op_update3_cc_i32(DisasContext *s, enum cc_op op, TCGv_i32 src, - TCGv_i32 dst, TCGv_i32 vr) -{ - tcg_gen_extu_i32_i64(cc_src, src); - tcg_gen_extu_i32_i64(cc_dst, dst); - tcg_gen_extu_i32_i64(cc_vr, vr); - s->cc_op = op; -} - -static inline void set_cc_nz_u32(DisasContext *s, TCGv_i32 val) -{ - gen_op_update1_cc_i32(s, CC_OP_NZ, val); -} - -static inline void set_cc_nz_u64(DisasContext *s, TCGv_i64 val) +static void set_cc_nz_u64(DisasContext *s, TCGv_i64 val) { gen_op_update1_cc_i64(s, CC_OP_NZ, val); } -static inline void cmp_32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2, - enum cc_op cond) +static void gen_set_cc_nz_f32(DisasContext *s, TCGv_i64 val) { - gen_op_update2_cc_i32(s, cond, v1, v2); + gen_op_update1_cc_i64(s, CC_OP_NZ_F32, val); } -static inline void cmp_64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2, - enum cc_op cond) +static void gen_set_cc_nz_f64(DisasContext *s, TCGv_i64 val) { - gen_op_update2_cc_i64(s, cond, v1, v2); + gen_op_update1_cc_i64(s, CC_OP_NZ_F64, val); } -static inline void cmp_s32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2) +static void gen_set_cc_nz_f128(DisasContext *s, TCGv_i64 vh, TCGv_i64 vl) { - cmp_32(s, v1, v2, CC_OP_LTGT_32); -} - -static inline void cmp_u32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2) -{ - cmp_32(s, v1, v2, CC_OP_LTUGTU_32); -} - -static inline void cmp_s32c(DisasContext *s, TCGv_i32 v1, int32_t v2) -{ - /* XXX optimize for the constant? put it in s? */ - TCGv_i32 tmp = tcg_const_i32(v2); - cmp_32(s, v1, tmp, CC_OP_LTGT_32); - tcg_temp_free_i32(tmp); -} - -static inline void cmp_u32c(DisasContext *s, TCGv_i32 v1, uint32_t v2) -{ - TCGv_i32 tmp = tcg_const_i32(v2); - cmp_32(s, v1, tmp, CC_OP_LTUGTU_32); - tcg_temp_free_i32(tmp); -} - -static inline void cmp_s64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2) -{ - cmp_64(s, v1, v2, CC_OP_LTGT_64); -} - -static inline void cmp_u64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2) -{ - cmp_64(s, v1, v2, CC_OP_LTUGTU_64); -} - -static inline void cmp_s64c(DisasContext *s, TCGv_i64 v1, int64_t v2) -{ - TCGv_i64 tmp = tcg_const_i64(v2); - cmp_s64(s, v1, tmp); - tcg_temp_free_i64(tmp); -} - -static inline void cmp_u64c(DisasContext *s, TCGv_i64 v1, uint64_t v2) -{ - TCGv_i64 tmp = tcg_const_i64(v2); - cmp_u64(s, v1, tmp); - tcg_temp_free_i64(tmp); -} - -static inline void set_cc_s32(DisasContext *s, TCGv_i32 val) -{ - gen_op_update1_cc_i32(s, CC_OP_LTGT0_32, val); -} - -static inline void set_cc_s64(DisasContext *s, TCGv_i64 val) -{ - gen_op_update1_cc_i64(s, CC_OP_LTGT0_64, val); -} - -static void set_cc_add64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2, TCGv_i64 vr) -{ - gen_op_update3_cc_i64(s, CC_OP_ADD_64, v1, v2, vr); -} - -static void set_cc_addu64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2, - TCGv_i64 vr) -{ - gen_op_update3_cc_i64(s, CC_OP_ADDU_64, v1, v2, vr); -} - -static void set_cc_sub64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2, TCGv_i64 vr) -{ - gen_op_update3_cc_i64(s, CC_OP_SUB_64, v1, v2, vr); -} - -static void set_cc_subu64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2, - TCGv_i64 vr) -{ - gen_op_update3_cc_i64(s, CC_OP_SUBU_64, v1, v2, vr); -} - -static void set_cc_abs64(DisasContext *s, TCGv_i64 v1) -{ - gen_op_update1_cc_i64(s, CC_OP_ABS_64, v1); -} - -static void set_cc_nabs64(DisasContext *s, TCGv_i64 v1) -{ - gen_op_update1_cc_i64(s, CC_OP_NABS_64, v1); -} - -static void set_cc_add32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2, TCGv_i32 vr) -{ - gen_op_update3_cc_i32(s, CC_OP_ADD_32, v1, v2, vr); -} - -static void set_cc_addu32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2, - TCGv_i32 vr) -{ - gen_op_update3_cc_i32(s, CC_OP_ADDU_32, v1, v2, vr); -} - -static void set_cc_sub32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2, TCGv_i32 vr) -{ - gen_op_update3_cc_i32(s, CC_OP_SUB_32, v1, v2, vr); -} - -static void set_cc_subu32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2, - TCGv_i32 vr) -{ - gen_op_update3_cc_i32(s, CC_OP_SUBU_32, v1, v2, vr); -} - -static void set_cc_abs32(DisasContext *s, TCGv_i32 v1) -{ - gen_op_update1_cc_i32(s, CC_OP_ABS_32, v1); -} - -static void set_cc_nabs32(DisasContext *s, TCGv_i32 v1) -{ - gen_op_update1_cc_i32(s, CC_OP_NABS_32, v1); -} - -static void set_cc_comp32(DisasContext *s, TCGv_i32 v1) -{ - gen_op_update1_cc_i32(s, CC_OP_COMP_32, v1); -} - -static void set_cc_comp64(DisasContext *s, TCGv_i64 v1) -{ - gen_op_update1_cc_i64(s, CC_OP_COMP_64, v1); -} - -static void set_cc_icm(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2) -{ - gen_op_update2_cc_i32(s, CC_OP_ICM, v1, v2); -} - -static void set_cc_cmp_f32_i64(DisasContext *s, TCGv_i32 v1, TCGv_i64 v2) -{ - tcg_gen_extu_i32_i64(cc_src, v1); - tcg_gen_mov_i64(cc_dst, v2); - tcg_gen_discard_i64(cc_vr); - s->cc_op = CC_OP_LTGT_F32; -} - -static void gen_set_cc_nz_f32(DisasContext *s, TCGv_i32 v1) -{ - gen_op_update1_cc_i32(s, CC_OP_NZ_F32, v1); + gen_op_update2_cc_i64(s, CC_OP_NZ_F128, vh, vl); } /* CC value is in env->cc_op */ -static inline void set_cc_static(DisasContext *s) +static void set_cc_static(DisasContext *s) { - tcg_gen_discard_i64(cc_src); - tcg_gen_discard_i64(cc_dst); - tcg_gen_discard_i64(cc_vr); - s->cc_op = CC_OP_STATIC; -} - -static inline void gen_op_set_cc_op(DisasContext *s) -{ - if (s->cc_op != CC_OP_DYNAMIC && s->cc_op != CC_OP_STATIC) { - tcg_gen_movi_i32(cc_op, s->cc_op); + if (live_cc_data(s)) { + tcg_gen_discard_i64(cc_src); + tcg_gen_discard_i64(cc_dst); + tcg_gen_discard_i64(cc_vr); } -} - -static inline void gen_update_cc_op(DisasContext *s) -{ - gen_op_set_cc_op(s); + s->cc_op = CC_OP_STATIC; } /* calculates cc into cc_op */ static void gen_op_calc_cc(DisasContext *s) { - TCGv_i32 local_cc_op = tcg_const_i32(s->cc_op); - TCGv_i64 dummy = tcg_const_i64(0); + TCGv_i32 local_cc_op; + TCGv_i64 dummy; + + TCGV_UNUSED_I32(local_cc_op); + TCGV_UNUSED_I64(dummy); + switch (s->cc_op) { + default: + dummy = tcg_const_i64(0); + /* FALLTHRU */ + case CC_OP_ADD_64: + case CC_OP_ADDU_64: + case CC_OP_ADDC_64: + case CC_OP_SUB_64: + case CC_OP_SUBU_64: + case CC_OP_SUBB_64: + case CC_OP_ADD_32: + case CC_OP_ADDU_32: + case CC_OP_ADDC_32: + case CC_OP_SUB_32: + case CC_OP_SUBU_32: + case CC_OP_SUBB_32: + local_cc_op = tcg_const_i32(s->cc_op); + break; + case CC_OP_CONST0: + case CC_OP_CONST1: + case CC_OP_CONST2: + case CC_OP_CONST3: + case CC_OP_STATIC: + case CC_OP_DYNAMIC: + break; + } switch (s->cc_op) { case CC_OP_CONST0: @@ -721,6 +494,7 @@ static void gen_op_calc_cc(DisasContext *s) case CC_OP_COMP_64: case CC_OP_NZ_F32: case CC_OP_NZ_F64: + case CC_OP_FLOGR: /* 1 argument */ gen_helper_calc_cc(cc_op, cpu_env, local_cc_op, dummy, cc_dst, dummy); break; @@ -731,20 +505,24 @@ static void gen_op_calc_cc(DisasContext *s) case CC_OP_LTUGTU_64: case CC_OP_TM_32: case CC_OP_TM_64: - case CC_OP_LTGT_F32: - case CC_OP_LTGT_F64: - case CC_OP_SLAG: + case CC_OP_SLA_32: + case CC_OP_SLA_64: + case CC_OP_NZ_F128: /* 2 arguments */ gen_helper_calc_cc(cc_op, cpu_env, local_cc_op, cc_src, cc_dst, dummy); break; case CC_OP_ADD_64: case CC_OP_ADDU_64: + case CC_OP_ADDC_64: case CC_OP_SUB_64: case CC_OP_SUBU_64: + case CC_OP_SUBB_64: case CC_OP_ADD_32: case CC_OP_ADDU_32: + case CC_OP_ADDC_32: case CC_OP_SUB_32: case CC_OP_SUBU_32: + case CC_OP_SUBB_32: /* 3 arguments */ gen_helper_calc_cc(cc_op, cpu_env, local_cc_op, cc_src, cc_dst, cc_vr); break; @@ -756,4358 +534,4203 @@ static void gen_op_calc_cc(DisasContext *s) tcg_abort(); } - tcg_temp_free_i32(local_cc_op); + if (!TCGV_IS_UNUSED_I32(local_cc_op)) { + tcg_temp_free_i32(local_cc_op); + } + if (!TCGV_IS_UNUSED_I64(dummy)) { + tcg_temp_free_i64(dummy); + } /* We now have cc in cc_op as constant */ set_cc_static(s); } -static inline void decode_rr(DisasContext *s, uint64_t insn, int *r1, int *r2) +static int use_goto_tb(DisasContext *s, uint64_t dest) { - debug_insn(insn); - - *r1 = (insn >> 4) & 0xf; - *r2 = insn & 0xf; -} - -static inline TCGv_i64 decode_rx(DisasContext *s, uint64_t insn, int *r1, - int *x2, int *b2, int *d2) -{ - debug_insn(insn); - - *r1 = (insn >> 20) & 0xf; - *x2 = (insn >> 16) & 0xf; - *b2 = (insn >> 12) & 0xf; - *d2 = insn & 0xfff; - - return get_address(s, *x2, *b2, *d2); -} - -static inline void decode_rs(DisasContext *s, uint64_t insn, int *r1, int *r3, - int *b2, int *d2) -{ - debug_insn(insn); - - *r1 = (insn >> 20) & 0xf; - /* aka m3 */ - *r3 = (insn >> 16) & 0xf; - *b2 = (insn >> 12) & 0xf; - *d2 = insn & 0xfff; -} - -static inline TCGv_i64 decode_si(DisasContext *s, uint64_t insn, int *i2, - int *b1, int *d1) -{ - debug_insn(insn); - - *i2 = (insn >> 16) & 0xff; - *b1 = (insn >> 12) & 0xf; - *d1 = insn & 0xfff; - - return get_address(s, 0, *b1, *d1); -} - -static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong pc) -{ - TranslationBlock *tb; - - gen_update_cc_op(s); - - tb = s->tb; /* NOTE: we handle the case where the TB spans two pages here */ - if ((pc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) || - (pc & TARGET_PAGE_MASK) == ((s->pc - 1) & TARGET_PAGE_MASK)) { - /* jump to same page: we can use a direct jump */ - tcg_gen_goto_tb(tb_num); - tcg_gen_movi_i64(psw_addr, pc); - tcg_gen_exit_tb((tcg_target_long)tb + tb_num); - } else { - /* jump to another page: currently not optimized */ - tcg_gen_movi_i64(psw_addr, pc); - tcg_gen_exit_tb(0); - } + return (((dest & TARGET_PAGE_MASK) == (s->tb->pc & TARGET_PAGE_MASK) + || (dest & TARGET_PAGE_MASK) == ((s->pc - 1) & TARGET_PAGE_MASK)) + && !s->singlestep_enabled + && !(s->tb->cflags & CF_LAST_IO)); } -static inline void account_noninline_branch(DisasContext *s, int cc_op) +static void account_noninline_branch(DisasContext *s, int cc_op) { #ifdef DEBUG_INLINE_BRANCHES inline_branch_miss[cc_op]++; #endif } -static inline void account_inline_branch(DisasContext *s) +static void account_inline_branch(DisasContext *s, int cc_op) { #ifdef DEBUG_INLINE_BRANCHES - inline_branch_hit[s->cc_op]++; + inline_branch_hit[cc_op]++; #endif } -static void gen_jcc(DisasContext *s, uint32_t mask, int skip) +/* Table of mask values to comparison codes, given a comparison as input. + For such, CC=3 should not be possible. */ +static const TCGCond ltgt_cond[16] = { + TCG_COND_NEVER, TCG_COND_NEVER, /* | | | x */ + TCG_COND_GT, TCG_COND_GT, /* | | GT | x */ + TCG_COND_LT, TCG_COND_LT, /* | LT | | x */ + TCG_COND_NE, TCG_COND_NE, /* | LT | GT | x */ + TCG_COND_EQ, TCG_COND_EQ, /* EQ | | | x */ + TCG_COND_GE, TCG_COND_GE, /* EQ | | GT | x */ + TCG_COND_LE, TCG_COND_LE, /* EQ | LT | | x */ + TCG_COND_ALWAYS, TCG_COND_ALWAYS, /* EQ | LT | GT | x */ +}; + +/* Table of mask values to comparison codes, given a logic op as input. + For such, only CC=0 and CC=1 should be possible. */ +static const TCGCond nz_cond[16] = { + TCG_COND_NEVER, TCG_COND_NEVER, /* | | x | x */ + TCG_COND_NEVER, TCG_COND_NEVER, + TCG_COND_NE, TCG_COND_NE, /* | NE | x | x */ + TCG_COND_NE, TCG_COND_NE, + TCG_COND_EQ, TCG_COND_EQ, /* EQ | | x | x */ + TCG_COND_EQ, TCG_COND_EQ, + TCG_COND_ALWAYS, TCG_COND_ALWAYS, /* EQ | NE | x | x */ + TCG_COND_ALWAYS, TCG_COND_ALWAYS, +}; + +/* Interpret MASK in terms of S->CC_OP, and fill in C with all the + details required to generate a TCG comparison. */ +static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) { - TCGv_i32 tmp, tmp2, r; - TCGv_i64 tmp64; - int old_cc_op; + TCGCond cond; + enum cc_op old_cc_op = s->cc_op; - switch (s->cc_op) { + if (mask == 15 || mask == 0) { + c->cond = (mask ? TCG_COND_ALWAYS : TCG_COND_NEVER); + c->u.s32.a = cc_op; + c->u.s32.b = cc_op; + c->g1 = c->g2 = true; + c->is_64 = false; + return; + } + + /* Find the TCG condition for the mask + cc op. */ + switch (old_cc_op) { case CC_OP_LTGT0_32: - tmp = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tmp, cc_dst); - switch (mask) { - case 0x8 | 0x4: /* dst <= 0 */ - tcg_gen_brcondi_i32(TCG_COND_GT, tmp, 0, skip); - break; - case 0x8 | 0x2: /* dst >= 0 */ - tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, skip); - break; - case 0x8: /* dst == 0 */ - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, skip); - break; - case 0x7: /* dst != 0 */ - case 0x6: /* dst != 0 */ - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, skip); - break; - case 0x4: /* dst < 0 */ - tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, skip); - break; - case 0x2: /* dst > 0 */ - tcg_gen_brcondi_i32(TCG_COND_LE, tmp, 0, skip); - break; - default: - tcg_temp_free_i32(tmp); - goto do_dynamic; - } - account_inline_branch(s); - tcg_temp_free_i32(tmp); - break; case CC_OP_LTGT0_64: - switch (mask) { - case 0x8 | 0x4: /* dst <= 0 */ - tcg_gen_brcondi_i64(TCG_COND_GT, cc_dst, 0, skip); - break; - case 0x8 | 0x2: /* dst >= 0 */ - tcg_gen_brcondi_i64(TCG_COND_LT, cc_dst, 0, skip); - break; - case 0x8: /* dst == 0 */ - tcg_gen_brcondi_i64(TCG_COND_NE, cc_dst, 0, skip); - break; - case 0x7: /* dst != 0 */ - case 0x6: /* dst != 0 */ - tcg_gen_brcondi_i64(TCG_COND_EQ, cc_dst, 0, skip); - break; - case 0x4: /* dst < 0 */ - tcg_gen_brcondi_i64(TCG_COND_GE, cc_dst, 0, skip); - break; - case 0x2: /* dst > 0 */ - tcg_gen_brcondi_i64(TCG_COND_LE, cc_dst, 0, skip); - break; - default: - goto do_dynamic; - } - account_inline_branch(s); - break; case CC_OP_LTGT_32: - tmp = tcg_temp_new_i32(); - tmp2 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tmp, cc_src); - tcg_gen_trunc_i64_i32(tmp2, cc_dst); - switch (mask) { - case 0x8 | 0x4: /* src <= dst */ - tcg_gen_brcond_i32(TCG_COND_GT, tmp, tmp2, skip); - break; - case 0x8 | 0x2: /* src >= dst */ - tcg_gen_brcond_i32(TCG_COND_LT, tmp, tmp2, skip); - break; - case 0x8: /* src == dst */ - tcg_gen_brcond_i32(TCG_COND_NE, tmp, tmp2, skip); - break; - case 0x7: /* src != dst */ - case 0x6: /* src != dst */ - tcg_gen_brcond_i32(TCG_COND_EQ, tmp, tmp2, skip); - break; - case 0x4: /* src < dst */ - tcg_gen_brcond_i32(TCG_COND_GE, tmp, tmp2, skip); - break; - case 0x2: /* src > dst */ - tcg_gen_brcond_i32(TCG_COND_LE, tmp, tmp2, skip); - break; - default: - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(tmp2); - goto do_dynamic; - } - account_inline_branch(s); - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(tmp2); - break; case CC_OP_LTGT_64: - switch (mask) { - case 0x8 | 0x4: /* src <= dst */ - tcg_gen_brcond_i64(TCG_COND_GT, cc_src, cc_dst, skip); - break; - case 0x8 | 0x2: /* src >= dst */ - tcg_gen_brcond_i64(TCG_COND_LT, cc_src, cc_dst, skip); - break; - case 0x8: /* src == dst */ - tcg_gen_brcond_i64(TCG_COND_NE, cc_src, cc_dst, skip); - break; - case 0x7: /* src != dst */ - case 0x6: /* src != dst */ - tcg_gen_brcond_i64(TCG_COND_EQ, cc_src, cc_dst, skip); - break; - case 0x4: /* src < dst */ - tcg_gen_brcond_i64(TCG_COND_GE, cc_src, cc_dst, skip); - break; - case 0x2: /* src > dst */ - tcg_gen_brcond_i64(TCG_COND_LE, cc_src, cc_dst, skip); - break; - default: + cond = ltgt_cond[mask]; + if (cond == TCG_COND_NEVER) { goto do_dynamic; } - account_inline_branch(s); + account_inline_branch(s, old_cc_op); break; + case CC_OP_LTUGTU_32: - tmp = tcg_temp_new_i32(); - tmp2 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tmp, cc_src); - tcg_gen_trunc_i64_i32(tmp2, cc_dst); - switch (mask) { - case 0x8 | 0x4: /* src <= dst */ - tcg_gen_brcond_i32(TCG_COND_GTU, tmp, tmp2, skip); - break; - case 0x8 | 0x2: /* src >= dst */ - tcg_gen_brcond_i32(TCG_COND_LTU, tmp, tmp2, skip); - break; - case 0x8: /* src == dst */ - tcg_gen_brcond_i32(TCG_COND_NE, tmp, tmp2, skip); - break; - case 0x7: /* src != dst */ - case 0x6: /* src != dst */ - tcg_gen_brcond_i32(TCG_COND_EQ, tmp, tmp2, skip); - break; - case 0x4: /* src < dst */ - tcg_gen_brcond_i32(TCG_COND_GEU, tmp, tmp2, skip); - break; - case 0x2: /* src > dst */ - tcg_gen_brcond_i32(TCG_COND_LEU, tmp, tmp2, skip); - break; - default: - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(tmp2); - goto do_dynamic; - } - account_inline_branch(s); - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(tmp2); - break; case CC_OP_LTUGTU_64: - switch (mask) { - case 0x8 | 0x4: /* src <= dst */ - tcg_gen_brcond_i64(TCG_COND_GTU, cc_src, cc_dst, skip); - break; - case 0x8 | 0x2: /* src >= dst */ - tcg_gen_brcond_i64(TCG_COND_LTU, cc_src, cc_dst, skip); - break; - case 0x8: /* src == dst */ - tcg_gen_brcond_i64(TCG_COND_NE, cc_src, cc_dst, skip); - break; - case 0x7: /* src != dst */ - case 0x6: /* src != dst */ - tcg_gen_brcond_i64(TCG_COND_EQ, cc_src, cc_dst, skip); - break; - case 0x4: /* src < dst */ - tcg_gen_brcond_i64(TCG_COND_GEU, cc_src, cc_dst, skip); - break; - case 0x2: /* src > dst */ - tcg_gen_brcond_i64(TCG_COND_LEU, cc_src, cc_dst, skip); - break; - default: + cond = tcg_unsigned_cond(ltgt_cond[mask]); + if (cond == TCG_COND_NEVER) { goto do_dynamic; } - account_inline_branch(s); + account_inline_branch(s, old_cc_op); break; + case CC_OP_NZ: - switch (mask) { - /* dst == 0 || dst != 0 */ - case 0x8 | 0x4: - case 0x8 | 0x4 | 0x2: - case 0x8 | 0x4 | 0x2 | 0x1: - case 0x8 | 0x4 | 0x1: - break; - /* dst == 0 */ - case 0x8: - case 0x8 | 0x2: - case 0x8 | 0x2 | 0x1: - case 0x8 | 0x1: - tcg_gen_brcondi_i64(TCG_COND_NE, cc_dst, 0, skip); - break; - /* dst != 0 */ - case 0x4: - case 0x4 | 0x2: - case 0x4 | 0x2 | 0x1: - case 0x4 | 0x1: - tcg_gen_brcondi_i64(TCG_COND_EQ, cc_dst, 0, skip); - break; - default: + cond = nz_cond[mask]; + if (cond == TCG_COND_NEVER) { goto do_dynamic; } - account_inline_branch(s); + account_inline_branch(s, old_cc_op); break; + case CC_OP_TM_32: - tmp = tcg_temp_new_i32(); - tmp2 = tcg_temp_new_i32(); - - tcg_gen_trunc_i64_i32(tmp, cc_src); - tcg_gen_trunc_i64_i32(tmp2, cc_dst); - tcg_gen_and_i32(tmp, tmp, tmp2); - switch (mask) { - case 0x8: /* val & mask == 0 */ - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, skip); - break; - case 0x4 | 0x2 | 0x1: /* val & mask != 0 */ - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, skip); - break; - default: - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(tmp2); - goto do_dynamic; - } - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(tmp2); - account_inline_branch(s); - break; case CC_OP_TM_64: - tmp64 = tcg_temp_new_i64(); - - tcg_gen_and_i64(tmp64, cc_src, cc_dst); switch (mask) { - case 0x8: /* val & mask == 0 */ - tcg_gen_brcondi_i64(TCG_COND_NE, tmp64, 0, skip); + case 8: + cond = TCG_COND_EQ; break; - case 0x4 | 0x2 | 0x1: /* val & mask != 0 */ - tcg_gen_brcondi_i64(TCG_COND_EQ, tmp64, 0, skip); + case 4 | 2 | 1: + cond = TCG_COND_NE; break; default: - tcg_temp_free_i64(tmp64); goto do_dynamic; } - tcg_temp_free_i64(tmp64); - account_inline_branch(s); + account_inline_branch(s, old_cc_op); break; + case CC_OP_ICM: switch (mask) { - case 0x8: /* val == 0 */ - tcg_gen_brcondi_i64(TCG_COND_NE, cc_dst, 0, skip); + case 8: + cond = TCG_COND_EQ; break; - case 0x4 | 0x2 | 0x1: /* val != 0 */ - case 0x4 | 0x2: /* val != 0 */ - tcg_gen_brcondi_i64(TCG_COND_EQ, cc_dst, 0, skip); + case 4 | 2 | 1: + case 4 | 2: + cond = TCG_COND_NE; break; default: goto do_dynamic; } - account_inline_branch(s); + account_inline_branch(s, old_cc_op); break; - case CC_OP_STATIC: - old_cc_op = s->cc_op; - goto do_dynamic_nocccalc; - case CC_OP_DYNAMIC: - default: -do_dynamic: - old_cc_op = s->cc_op; - /* calculate cc value */ - gen_op_calc_cc(s); -do_dynamic_nocccalc: - /* jump based on cc */ - account_noninline_branch(s, old_cc_op); - - switch (mask) { - case 0x8 | 0x4 | 0x2 | 0x1: - /* always true */ + case CC_OP_FLOGR: + switch (mask & 0xa) { + case 8: /* src == 0 -> no one bit found */ + cond = TCG_COND_EQ; break; + case 2: /* src != 0 -> one bit found */ + cond = TCG_COND_NE; + break; + default: + goto do_dynamic; + } + account_inline_branch(s, old_cc_op); + break; + + case CC_OP_ADDU_32: + case CC_OP_ADDU_64: + switch (mask) { + case 8 | 2: /* vr == 0 */ + cond = TCG_COND_EQ; + break; + case 4 | 1: /* vr != 0 */ + cond = TCG_COND_NE; + break; + case 8 | 4: /* no carry -> vr >= src */ + cond = TCG_COND_GEU; + break; + case 2 | 1: /* carry -> vr < src */ + cond = TCG_COND_LTU; + break; + default: + goto do_dynamic; + } + account_inline_branch(s, old_cc_op); + break; + + case CC_OP_SUBU_32: + case CC_OP_SUBU_64: + /* Note that CC=0 is impossible; treat it as dont-care. */ + switch (mask & 7) { + case 2: /* zero -> op1 == op2 */ + cond = TCG_COND_EQ; + break; + case 4 | 1: /* !zero -> op1 != op2 */ + cond = TCG_COND_NE; + break; + case 4: /* borrow (!carry) -> op1 < op2 */ + cond = TCG_COND_LTU; + break; + case 2 | 1: /* !borrow (carry) -> op1 >= op2 */ + cond = TCG_COND_GEU; + break; + default: + goto do_dynamic; + } + account_inline_branch(s, old_cc_op); + break; + + default: + do_dynamic: + /* Calculate cc value. */ + gen_op_calc_cc(s); + /* FALLTHRU */ + + case CC_OP_STATIC: + /* Jump based on CC. We'll load up the real cond below; + the assignment here merely avoids a compiler warning. */ + account_noninline_branch(s, old_cc_op); + old_cc_op = CC_OP_STATIC; + cond = TCG_COND_NEVER; + break; + } + + /* Load up the arguments of the comparison. */ + c->is_64 = true; + c->g1 = c->g2 = false; + switch (old_cc_op) { + case CC_OP_LTGT0_32: + c->is_64 = false; + c->u.s32.a = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(c->u.s32.a, cc_dst); + c->u.s32.b = tcg_const_i32(0); + break; + case CC_OP_LTGT_32: + case CC_OP_LTUGTU_32: + case CC_OP_SUBU_32: + c->is_64 = false; + c->u.s32.a = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(c->u.s32.a, cc_src); + c->u.s32.b = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(c->u.s32.b, cc_dst); + break; + + case CC_OP_LTGT0_64: + case CC_OP_NZ: + case CC_OP_FLOGR: + c->u.s64.a = cc_dst; + c->u.s64.b = tcg_const_i64(0); + c->g1 = true; + break; + case CC_OP_LTGT_64: + case CC_OP_LTUGTU_64: + case CC_OP_SUBU_64: + c->u.s64.a = cc_src; + c->u.s64.b = cc_dst; + c->g1 = c->g2 = true; + break; + + case CC_OP_TM_32: + case CC_OP_TM_64: + case CC_OP_ICM: + c->u.s64.a = tcg_temp_new_i64(); + c->u.s64.b = tcg_const_i64(0); + tcg_gen_and_i64(c->u.s64.a, cc_src, cc_dst); + break; + + case CC_OP_ADDU_32: + c->is_64 = false; + c->u.s32.a = tcg_temp_new_i32(); + c->u.s32.b = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(c->u.s32.a, cc_vr); + if (cond == TCG_COND_EQ || cond == TCG_COND_NE) { + tcg_gen_movi_i32(c->u.s32.b, 0); + } else { + tcg_gen_trunc_i64_i32(c->u.s32.b, cc_src); + } + break; + + case CC_OP_ADDU_64: + c->u.s64.a = cc_vr; + c->g1 = true; + if (cond == TCG_COND_EQ || cond == TCG_COND_NE) { + c->u.s64.b = tcg_const_i64(0); + } else { + c->u.s64.b = cc_src; + c->g2 = true; + } + break; + + case CC_OP_STATIC: + c->is_64 = false; + c->u.s32.a = cc_op; + c->g1 = true; + switch (mask) { case 0x8 | 0x4 | 0x2: /* cc != 3 */ - tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 3, skip); + cond = TCG_COND_NE; + c->u.s32.b = tcg_const_i32(3); break; case 0x8 | 0x4 | 0x1: /* cc != 2 */ - tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 2, skip); + cond = TCG_COND_NE; + c->u.s32.b = tcg_const_i32(2); break; case 0x8 | 0x2 | 0x1: /* cc != 1 */ - tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 1, skip); + cond = TCG_COND_NE; + c->u.s32.b = tcg_const_i32(1); break; - case 0x8 | 0x2: /* cc == 0 || cc == 2 */ - tmp = tcg_temp_new_i32(); - tcg_gen_andi_i32(tmp, cc_op, 1); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, skip); - tcg_temp_free_i32(tmp); + case 0x8 | 0x2: /* cc == 0 || cc == 2 => (cc & 1) == 0 */ + cond = TCG_COND_EQ; + c->g1 = false; + c->u.s32.a = tcg_temp_new_i32(); + c->u.s32.b = tcg_const_i32(0); + tcg_gen_andi_i32(c->u.s32.a, cc_op, 1); break; case 0x8 | 0x4: /* cc < 2 */ - tcg_gen_brcondi_i32(TCG_COND_GEU, cc_op, 2, skip); + cond = TCG_COND_LTU; + c->u.s32.b = tcg_const_i32(2); break; case 0x8: /* cc == 0 */ - tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 0, skip); + cond = TCG_COND_EQ; + c->u.s32.b = tcg_const_i32(0); break; case 0x4 | 0x2 | 0x1: /* cc != 0 */ - tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 0, skip); + cond = TCG_COND_NE; + c->u.s32.b = tcg_const_i32(0); break; - case 0x4 | 0x1: /* cc == 1 || cc == 3 */ - tmp = tcg_temp_new_i32(); - tcg_gen_andi_i32(tmp, cc_op, 1); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, skip); - tcg_temp_free_i32(tmp); + case 0x4 | 0x1: /* cc == 1 || cc == 3 => (cc & 1) != 0 */ + cond = TCG_COND_NE; + c->g1 = false; + c->u.s32.a = tcg_temp_new_i32(); + c->u.s32.b = tcg_const_i32(0); + tcg_gen_andi_i32(c->u.s32.a, cc_op, 1); break; case 0x4: /* cc == 1 */ - tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 1, skip); + cond = TCG_COND_EQ; + c->u.s32.b = tcg_const_i32(1); break; case 0x2 | 0x1: /* cc > 1 */ - tcg_gen_brcondi_i32(TCG_COND_LEU, cc_op, 1, skip); + cond = TCG_COND_GTU; + c->u.s32.b = tcg_const_i32(1); break; case 0x2: /* cc == 2 */ - tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 2, skip); + cond = TCG_COND_EQ; + c->u.s32.b = tcg_const_i32(2); break; case 0x1: /* cc == 3 */ - tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 3, skip); + cond = TCG_COND_EQ; + c->u.s32.b = tcg_const_i32(3); break; - default: /* cc is masked by something else */ - tmp = tcg_const_i32(3); - /* 3 - cc */ - tcg_gen_sub_i32(tmp, tmp, cc_op); - tmp2 = tcg_const_i32(1); - /* 1 << (3 - cc) */ - tcg_gen_shl_i32(tmp2, tmp2, tmp); - r = tcg_const_i32(mask); - /* mask & (1 << (3 - cc)) */ - tcg_gen_and_i32(r, r, tmp2); - tcg_temp_free_i32(tmp); - tcg_temp_free_i32(tmp2); - - tcg_gen_brcondi_i32(TCG_COND_EQ, r, 0, skip); - tcg_temp_free_i32(r); + default: + /* CC is masked by something else: (8 >> cc) & mask. */ + cond = TCG_COND_NE; + c->g1 = false; + c->u.s32.a = tcg_const_i32(8); + c->u.s32.b = tcg_const_i32(0); + tcg_gen_shr_i32(c->u.s32.a, c->u.s32.a, cc_op); + tcg_gen_andi_i32(c->u.s32.a, c->u.s32.a, mask); break; } break; - } -} -static void gen_bcr(DisasContext *s, uint32_t mask, TCGv_i64 target, - uint64_t offset) -{ - int skip; - - if (mask == 0xf) { - /* unconditional */ - tcg_gen_mov_i64(psw_addr, target); - tcg_gen_exit_tb(0); - } else if (mask == 0) { - /* ignore cc and never match */ - gen_goto_tb(s, 0, offset + 2); - } else { - TCGv_i64 new_addr = tcg_temp_local_new_i64(); - - tcg_gen_mov_i64(new_addr, target); - skip = gen_new_label(); - gen_jcc(s, mask, skip); - tcg_gen_mov_i64(psw_addr, new_addr); - tcg_temp_free_i64(new_addr); - tcg_gen_exit_tb(0); - gen_set_label(skip); - tcg_temp_free_i64(new_addr); - gen_goto_tb(s, 1, offset + 2); - } -} - -static void gen_brc(uint32_t mask, DisasContext *s, int32_t offset) -{ - int skip; - - if (mask == 0xf) { - /* unconditional */ - gen_goto_tb(s, 0, s->pc + offset); - } else if (mask == 0) { - /* ignore cc and never match */ - gen_goto_tb(s, 0, s->pc + 4); - } else { - skip = gen_new_label(); - gen_jcc(s, mask, skip); - gen_goto_tb(s, 0, s->pc + offset); - gen_set_label(skip); - gen_goto_tb(s, 1, s->pc + 4); - } - s->is_jmp = DISAS_TB_JUMP; -} - -static void gen_op_mvc(DisasContext *s, int l, TCGv_i64 s1, TCGv_i64 s2) -{ - TCGv_i64 tmp, tmp2; - int i; - int l_memset = gen_new_label(); - int l_out = gen_new_label(); - TCGv_i64 dest = tcg_temp_local_new_i64(); - TCGv_i64 src = tcg_temp_local_new_i64(); - TCGv_i32 vl; - - /* Find out if we should use the inline version of mvc */ - switch (l) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - case 11: - case 15: - /* use inline */ - break; default: - /* Fall back to helper */ - vl = tcg_const_i32(l); - potential_page_fault(s); - gen_helper_mvc(cpu_env, vl, s1, s2); - tcg_temp_free_i32(vl); - return; + abort(); + } + c->cond = cond; +} + +static void free_compare(DisasCompare *c) +{ + if (!c->g1) { + if (c->is_64) { + tcg_temp_free_i64(c->u.s64.a); + } else { + tcg_temp_free_i32(c->u.s32.a); + } + } + if (!c->g2) { + if (c->is_64) { + tcg_temp_free_i64(c->u.s64.b); + } else { + tcg_temp_free_i32(c->u.s32.b); + } + } +} + +/* ====================================================================== */ +/* Define the insn format enumeration. */ +#define F0(N) FMT_##N, +#define F1(N, X1) F0(N) +#define F2(N, X1, X2) F0(N) +#define F3(N, X1, X2, X3) F0(N) +#define F4(N, X1, X2, X3, X4) F0(N) +#define F5(N, X1, X2, X3, X4, X5) F0(N) + +typedef enum { +#include "insn-format.def" +} DisasFormat; + +#undef F0 +#undef F1 +#undef F2 +#undef F3 +#undef F4 +#undef F5 + +/* Define a structure to hold the decoded fields. We'll store each inside + an array indexed by an enum. In order to conserve memory, we'll arrange + for fields that do not exist at the same time to overlap, thus the "C" + for compact. For checking purposes there is an "O" for original index + as well that will be applied to availability bitmaps. */ + +enum DisasFieldIndexO { + FLD_O_r1, + FLD_O_r2, + FLD_O_r3, + FLD_O_m1, + FLD_O_m3, + FLD_O_m4, + FLD_O_b1, + FLD_O_b2, + FLD_O_b4, + FLD_O_d1, + FLD_O_d2, + FLD_O_d4, + FLD_O_x2, + FLD_O_l1, + FLD_O_l2, + FLD_O_i1, + FLD_O_i2, + FLD_O_i3, + FLD_O_i4, + FLD_O_i5 +}; + +enum DisasFieldIndexC { + FLD_C_r1 = 0, + FLD_C_m1 = 0, + FLD_C_b1 = 0, + FLD_C_i1 = 0, + + FLD_C_r2 = 1, + FLD_C_b2 = 1, + FLD_C_i2 = 1, + + FLD_C_r3 = 2, + FLD_C_m3 = 2, + FLD_C_i3 = 2, + + FLD_C_m4 = 3, + FLD_C_b4 = 3, + FLD_C_i4 = 3, + FLD_C_l1 = 3, + + FLD_C_i5 = 4, + FLD_C_d1 = 4, + + FLD_C_d2 = 5, + + FLD_C_d4 = 6, + FLD_C_x2 = 6, + FLD_C_l2 = 6, + + NUM_C_FIELD = 7 +}; + +struct DisasFields { + unsigned op:8; + unsigned op2:8; + unsigned presentC:16; + unsigned int presentO; + int c[NUM_C_FIELD]; +}; + +/* This is the way fields are to be accessed out of DisasFields. */ +#define have_field(S, F) have_field1((S), FLD_O_##F) +#define get_field(S, F) get_field1((S), FLD_O_##F, FLD_C_##F) + +static bool have_field1(const DisasFields *f, enum DisasFieldIndexO c) +{ + return (f->presentO >> c) & 1; +} + +static int get_field1(const DisasFields *f, enum DisasFieldIndexO o, + enum DisasFieldIndexC c) +{ + assert(have_field1(f, o)); + return f->c[c]; +} + +/* Describe the layout of each field in each format. */ +typedef struct DisasField { + unsigned int beg:8; + unsigned int size:8; + unsigned int type:2; + unsigned int indexC:6; + enum DisasFieldIndexO indexO:8; +} DisasField; + +typedef struct DisasFormatInfo { + DisasField op[NUM_C_FIELD]; +} DisasFormatInfo; + +#define R(N, B) { B, 4, 0, FLD_C_r##N, FLD_O_r##N } +#define M(N, B) { B, 4, 0, FLD_C_m##N, FLD_O_m##N } +#define BD(N, BB, BD) { BB, 4, 0, FLD_C_b##N, FLD_O_b##N }, \ + { BD, 12, 0, FLD_C_d##N, FLD_O_d##N } +#define BXD(N) { 16, 4, 0, FLD_C_b##N, FLD_O_b##N }, \ + { 12, 4, 0, FLD_C_x##N, FLD_O_x##N }, \ + { 20, 12, 0, FLD_C_d##N, FLD_O_d##N } +#define BDL(N) { 16, 4, 0, FLD_C_b##N, FLD_O_b##N }, \ + { 20, 20, 2, FLD_C_d##N, FLD_O_d##N } +#define BXDL(N) { 16, 4, 0, FLD_C_b##N, FLD_O_b##N }, \ + { 12, 4, 0, FLD_C_x##N, FLD_O_x##N }, \ + { 20, 20, 2, FLD_C_d##N, FLD_O_d##N } +#define I(N, B, S) { B, S, 1, FLD_C_i##N, FLD_O_i##N } +#define L(N, B, S) { B, S, 0, FLD_C_l##N, FLD_O_l##N } + +#define F0(N) { { } }, +#define F1(N, X1) { { X1 } }, +#define F2(N, X1, X2) { { X1, X2 } }, +#define F3(N, X1, X2, X3) { { X1, X2, X3 } }, +#define F4(N, X1, X2, X3, X4) { { X1, X2, X3, X4 } }, +#define F5(N, X1, X2, X3, X4, X5) { { X1, X2, X3, X4, X5 } }, + +static const DisasFormatInfo format_info[] = { +#include "insn-format.def" +}; + +#undef F0 +#undef F1 +#undef F2 +#undef F3 +#undef F4 +#undef F5 +#undef R +#undef M +#undef BD +#undef BXD +#undef BDL +#undef BXDL +#undef I +#undef L + +/* Generally, we'll extract operands into this structures, operate upon + them, and store them back. See the "in1", "in2", "prep", "wout" sets + of routines below for more details. */ +typedef struct { + bool g_out, g_out2, g_in1, g_in2; + TCGv_i64 out, out2, in1, in2; + TCGv_i64 addr1; +} DisasOps; + +/* Instructions can place constraints on their operands, raising specification + exceptions if they are violated. To make this easy to automate, each "in1", + "in2", "prep", "wout" helper will have a SPEC_ define that equals one + of the following, or 0. To make this easy to document, we'll put the + SPEC_ defines next to . */ + +#define SPEC_r1_even 1 +#define SPEC_r2_even 2 +#define SPEC_r3_even 4 +#define SPEC_r1_f128 8 +#define SPEC_r2_f128 16 + +/* Return values from translate_one, indicating the state of the TB. */ +typedef enum { + /* Continue the TB. */ + NO_EXIT, + /* We have emitted one or more goto_tb. No fixup required. */ + EXIT_GOTO_TB, + /* We are not using a goto_tb (for whatever reason), but have updated + the PC (for whatever reason), so there's no need to do it again on + exiting the TB. */ + EXIT_PC_UPDATED, + /* We are exiting the TB, but have neither emitted a goto_tb, nor + updated the PC for the next instruction to be executed. */ + EXIT_PC_STALE, + /* We are ending the TB with a noreturn function call, e.g. longjmp. + No following code will be executed. */ + EXIT_NORETURN, +} ExitStatus; + +typedef enum DisasFacility { + FAC_Z, /* zarch (default) */ + FAC_CASS, /* compare and swap and store */ + FAC_CASS2, /* compare and swap and store 2*/ + FAC_DFP, /* decimal floating point */ + FAC_DFPR, /* decimal floating point rounding */ + FAC_DO, /* distinct operands */ + FAC_EE, /* execute extensions */ + FAC_EI, /* extended immediate */ + FAC_FPE, /* floating point extension */ + FAC_FPSSH, /* floating point support sign handling */ + FAC_FPRGR, /* FPR-GR transfer */ + FAC_GIE, /* general instructions extension */ + FAC_HFP_MA, /* HFP multiply-and-add/subtract */ + FAC_HW, /* high-word */ + FAC_IEEEE_SIM, /* IEEE exception sumilation */ + FAC_LOC, /* load/store on condition */ + FAC_LD, /* long displacement */ + FAC_PC, /* population count */ + FAC_SCF, /* store clock fast */ + FAC_SFLE, /* store facility list extended */ +} DisasFacility; + +struct DisasInsn { + unsigned opc:16; + DisasFormat fmt:8; + DisasFacility fac:8; + unsigned spec:8; + + const char *name; + + void (*help_in1)(DisasContext *, DisasFields *, DisasOps *); + void (*help_in2)(DisasContext *, DisasFields *, DisasOps *); + void (*help_prep)(DisasContext *, DisasFields *, DisasOps *); + void (*help_wout)(DisasContext *, DisasFields *, DisasOps *); + void (*help_cout)(DisasContext *, DisasOps *); + ExitStatus (*help_op)(DisasContext *, DisasOps *); + + uint64_t data; +}; + +/* ====================================================================== */ +/* Miscelaneous helpers, used by several operations. */ + +static void help_l2_shift(DisasContext *s, DisasFields *f, + DisasOps *o, int mask) +{ + int b2 = get_field(f, b2); + int d2 = get_field(f, d2); + + if (b2 == 0) { + o->in2 = tcg_const_i64(d2 & mask); + } else { + o->in2 = get_address(s, 0, b2, d2); + tcg_gen_andi_i64(o->in2, o->in2, mask); + } +} + +static ExitStatus help_goto_direct(DisasContext *s, uint64_t dest) +{ + if (dest == s->next_pc) { + return NO_EXIT; + } + if (use_goto_tb(s, dest)) { + update_cc_op(s); + tcg_gen_goto_tb(0); + tcg_gen_movi_i64(psw_addr, dest); + tcg_gen_exit_tb((tcg_target_long)s->tb); + return EXIT_GOTO_TB; + } else { + tcg_gen_movi_i64(psw_addr, dest); + return EXIT_PC_UPDATED; + } +} + +static ExitStatus help_branch(DisasContext *s, DisasCompare *c, + bool is_imm, int imm, TCGv_i64 cdest) +{ + ExitStatus ret; + uint64_t dest = s->pc + 2 * imm; + int lab; + + /* Take care of the special cases first. */ + if (c->cond == TCG_COND_NEVER) { + ret = NO_EXIT; + goto egress; + } + if (is_imm) { + if (dest == s->next_pc) { + /* Branch to next. */ + ret = NO_EXIT; + goto egress; + } + if (c->cond == TCG_COND_ALWAYS) { + ret = help_goto_direct(s, dest); + goto egress; + } + } else { + if (TCGV_IS_UNUSED_I64(cdest)) { + /* E.g. bcr %r0 -> no branch. */ + ret = NO_EXIT; + goto egress; + } + if (c->cond == TCG_COND_ALWAYS) { + tcg_gen_mov_i64(psw_addr, cdest); + ret = EXIT_PC_UPDATED; + goto egress; + } } - tcg_gen_mov_i64(dest, s1); - tcg_gen_mov_i64(src, s2); + if (use_goto_tb(s, s->next_pc)) { + if (is_imm && use_goto_tb(s, dest)) { + /* Both exits can use goto_tb. */ + update_cc_op(s); - if (!(s->tb->flags & FLAG_MASK_64)) { - /* XXX what if we overflow while moving? */ - tcg_gen_andi_i64(dest, dest, 0x7fffffffUL); - tcg_gen_andi_i64(src, src, 0x7fffffffUL); - } + lab = gen_new_label(); + if (c->is_64) { + tcg_gen_brcond_i64(c->cond, c->u.s64.a, c->u.s64.b, lab); + } else { + tcg_gen_brcond_i32(c->cond, c->u.s32.a, c->u.s32.b, lab); + } - tmp = tcg_temp_new_i64(); - tcg_gen_addi_i64(tmp, src, 1); - tcg_gen_brcond_i64(TCG_COND_EQ, dest, tmp, l_memset); - tcg_temp_free_i64(tmp); + /* Branch not taken. */ + tcg_gen_goto_tb(0); + tcg_gen_movi_i64(psw_addr, s->next_pc); + tcg_gen_exit_tb((tcg_target_long)s->tb + 0); - switch (l) { - case 0: - tmp = tcg_temp_new_i64(); + /* Branch taken. */ + gen_set_label(lab); + tcg_gen_goto_tb(1); + tcg_gen_movi_i64(psw_addr, dest); + tcg_gen_exit_tb((tcg_target_long)s->tb + 1); - tcg_gen_qemu_ld8u(tmp, src, get_mem_index(s)); - tcg_gen_qemu_st8(tmp, dest, get_mem_index(s)); + ret = EXIT_GOTO_TB; + } else { + /* Fallthru can use goto_tb, but taken branch cannot. */ + /* Store taken branch destination before the brcond. This + avoids having to allocate a new local temp to hold it. + We'll overwrite this in the not taken case anyway. */ + if (!is_imm) { + tcg_gen_mov_i64(psw_addr, cdest); + } - tcg_temp_free_i64(tmp); - break; - case 1: - tmp = tcg_temp_new_i64(); + lab = gen_new_label(); + if (c->is_64) { + tcg_gen_brcond_i64(c->cond, c->u.s64.a, c->u.s64.b, lab); + } else { + tcg_gen_brcond_i32(c->cond, c->u.s32.a, c->u.s32.b, lab); + } - tcg_gen_qemu_ld16u(tmp, src, get_mem_index(s)); - tcg_gen_qemu_st16(tmp, dest, get_mem_index(s)); + /* Branch not taken. */ + update_cc_op(s); + tcg_gen_goto_tb(0); + tcg_gen_movi_i64(psw_addr, s->next_pc); + tcg_gen_exit_tb((tcg_target_long)s->tb + 0); - tcg_temp_free_i64(tmp); - break; - case 3: - tmp = tcg_temp_new_i64(); + gen_set_label(lab); + if (is_imm) { + tcg_gen_movi_i64(psw_addr, dest); + } + ret = EXIT_PC_UPDATED; + } + } else { + /* Fallthru cannot use goto_tb. This by itself is vanishingly rare. + Most commonly we're single-stepping or some other condition that + disables all use of goto_tb. Just update the PC and exit. */ - tcg_gen_qemu_ld32u(tmp, src, get_mem_index(s)); - tcg_gen_qemu_st32(tmp, dest, get_mem_index(s)); - - tcg_temp_free_i64(tmp); - break; - case 4: - tmp = tcg_temp_new_i64(); - tmp2 = tcg_temp_new_i64(); - - tcg_gen_qemu_ld32u(tmp, src, get_mem_index(s)); - tcg_gen_addi_i64(src, src, 4); - tcg_gen_qemu_ld8u(tmp2, src, get_mem_index(s)); - tcg_gen_qemu_st32(tmp, dest, get_mem_index(s)); - tcg_gen_addi_i64(dest, dest, 4); - tcg_gen_qemu_st8(tmp2, dest, get_mem_index(s)); - - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 7: - tmp = tcg_temp_new_i64(); - - tcg_gen_qemu_ld64(tmp, src, get_mem_index(s)); - tcg_gen_qemu_st64(tmp, dest, get_mem_index(s)); - - tcg_temp_free_i64(tmp); - break; - default: - /* The inline version can become too big for too uneven numbers, only - use it on known good lengths */ - tmp = tcg_temp_new_i64(); - tmp2 = tcg_const_i64(8); - for (i = 0; (i + 7) <= l; i += 8) { - tcg_gen_qemu_ld64(tmp, src, get_mem_index(s)); - tcg_gen_qemu_st64(tmp, dest, get_mem_index(s)); - - tcg_gen_add_i64(src, src, tmp2); - tcg_gen_add_i64(dest, dest, tmp2); + TCGv_i64 next = tcg_const_i64(s->next_pc); + if (is_imm) { + cdest = tcg_const_i64(dest); } - tcg_temp_free_i64(tmp2); - tmp2 = tcg_const_i64(1); - - for (; i <= l; i++) { - tcg_gen_qemu_ld8u(tmp, src, get_mem_index(s)); - tcg_gen_qemu_st8(tmp, dest, get_mem_index(s)); - - tcg_gen_add_i64(src, src, tmp2); - tcg_gen_add_i64(dest, dest, tmp2); + if (c->is_64) { + tcg_gen_movcond_i64(c->cond, psw_addr, c->u.s64.a, c->u.s64.b, + cdest, next); + } else { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 z = tcg_const_i64(0); + tcg_gen_setcond_i32(c->cond, t0, c->u.s32.a, c->u.s32.b); + tcg_gen_extu_i32_i64(t1, t0); + tcg_temp_free_i32(t0); + tcg_gen_movcond_i64(TCG_COND_NE, psw_addr, t1, z, cdest, next); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(z); } - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp); - break; + if (is_imm) { + tcg_temp_free_i64(cdest); + } + tcg_temp_free_i64(next); + + ret = EXIT_PC_UPDATED; } - tcg_gen_br(l_out); - - gen_set_label(l_memset); - /* memset case (dest == (src + 1)) */ - - tmp = tcg_temp_new_i64(); - tmp2 = tcg_temp_new_i64(); - /* fill tmp with the byte */ - tcg_gen_qemu_ld8u(tmp, src, get_mem_index(s)); - tcg_gen_shli_i64(tmp2, tmp, 8); - tcg_gen_or_i64(tmp, tmp, tmp2); - tcg_gen_shli_i64(tmp2, tmp, 16); - tcg_gen_or_i64(tmp, tmp, tmp2); - tcg_gen_shli_i64(tmp2, tmp, 32); - tcg_gen_or_i64(tmp, tmp, tmp2); - tcg_temp_free_i64(tmp2); - - tmp2 = tcg_const_i64(8); - - for (i = 0; (i + 7) <= l; i += 8) { - tcg_gen_qemu_st64(tmp, dest, get_mem_index(s)); - tcg_gen_addi_i64(dest, dest, 8); - } - - tcg_temp_free_i64(tmp2); - tmp2 = tcg_const_i64(1); - - for (; i <= l; i++) { - tcg_gen_qemu_st8(tmp, dest, get_mem_index(s)); - tcg_gen_addi_i64(dest, dest, 1); - } - - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp); - - gen_set_label(l_out); - - tcg_temp_free(dest); - tcg_temp_free(src); + egress: + free_compare(c); + return ret; } -static void gen_op_clc(DisasContext *s, int l, TCGv_i64 s1, TCGv_i64 s2) +/* ====================================================================== */ +/* The operations. These perform the bulk of the work for any insn, + usually after the operands have been loaded and output initialized. */ + +static ExitStatus op_abs(DisasContext *s, DisasOps *o) { - TCGv_i64 tmp; - TCGv_i64 tmp2; - TCGv_i32 vl; + gen_helper_abs_i64(o->out, o->in2); + return NO_EXIT; +} - /* check for simple 32bit or 64bit match */ - switch (l) { - case 0: - tmp = tcg_temp_new_i64(); - tmp2 = tcg_temp_new_i64(); +static ExitStatus op_absf32(DisasContext *s, DisasOps *o) +{ + tcg_gen_andi_i64(o->out, o->in2, 0x7fffffffull); + return NO_EXIT; +} - tcg_gen_qemu_ld8u(tmp, s1, get_mem_index(s)); - tcg_gen_qemu_ld8u(tmp2, s2, get_mem_index(s)); - cmp_u64(s, tmp, tmp2); +static ExitStatus op_absf64(DisasContext *s, DisasOps *o) +{ + tcg_gen_andi_i64(o->out, o->in2, 0x7fffffffffffffffull); + return NO_EXIT; +} - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - return; - case 1: - tmp = tcg_temp_new_i64(); - tmp2 = tcg_temp_new_i64(); +static ExitStatus op_absf128(DisasContext *s, DisasOps *o) +{ + tcg_gen_andi_i64(o->out, o->in1, 0x7fffffffffffffffull); + tcg_gen_mov_i64(o->out2, o->in2); + return NO_EXIT; +} - tcg_gen_qemu_ld16u(tmp, s1, get_mem_index(s)); - tcg_gen_qemu_ld16u(tmp2, s2, get_mem_index(s)); - cmp_u64(s, tmp, tmp2); +static ExitStatus op_add(DisasContext *s, DisasOps *o) +{ + tcg_gen_add_i64(o->out, o->in1, o->in2); + return NO_EXIT; +} - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - return; - case 3: - tmp = tcg_temp_new_i64(); - tmp2 = tcg_temp_new_i64(); +static ExitStatus op_addc(DisasContext *s, DisasOps *o) +{ + DisasCompare cmp; + TCGv_i64 carry; - tcg_gen_qemu_ld32u(tmp, s1, get_mem_index(s)); - tcg_gen_qemu_ld32u(tmp2, s2, get_mem_index(s)); - cmp_u64(s, tmp, tmp2); + tcg_gen_add_i64(o->out, o->in1, o->in2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - return; - case 7: - tmp = tcg_temp_new_i64(); - tmp2 = tcg_temp_new_i64(); - - tcg_gen_qemu_ld64(tmp, s1, get_mem_index(s)); - tcg_gen_qemu_ld64(tmp2, s2, get_mem_index(s)); - cmp_u64(s, tmp, tmp2); - - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - return; + /* The carry flag is the msb of CC, therefore the branch mask that would + create that comparison is 3. Feeding the generated comparison to + setcond produces the carry flag that we desire. */ + disas_jcc(s, &cmp, 3); + carry = tcg_temp_new_i64(); + if (cmp.is_64) { + tcg_gen_setcond_i64(cmp.cond, carry, cmp.u.s64.a, cmp.u.s64.b); + } else { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_setcond_i32(cmp.cond, t, cmp.u.s32.a, cmp.u.s32.b); + tcg_gen_extu_i32_i64(carry, t); + tcg_temp_free_i32(t); } + free_compare(&cmp); + + tcg_gen_add_i64(o->out, o->out, carry); + tcg_temp_free_i64(carry); + return NO_EXIT; +} + +static ExitStatus op_aeb(DisasContext *s, DisasOps *o) +{ + gen_helper_aeb(o->out, cpu_env, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_adb(DisasContext *s, DisasOps *o) +{ + gen_helper_adb(o->out, cpu_env, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_axb(DisasContext *s, DisasOps *o) +{ + gen_helper_axb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); + return_low128(o->out2); + return NO_EXIT; +} + +static ExitStatus op_and(DisasContext *s, DisasOps *o) +{ + tcg_gen_and_i64(o->out, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_andi(DisasContext *s, DisasOps *o) +{ + int shift = s->insn->data & 0xff; + int size = s->insn->data >> 8; + uint64_t mask = ((1ull << size) - 1) << shift; + + assert(!o->g_in2); + tcg_gen_shli_i64(o->in2, o->in2, shift); + tcg_gen_ori_i64(o->in2, o->in2, ~mask); + tcg_gen_and_i64(o->out, o->in1, o->in2); + + /* Produce the CC from only the bits manipulated. */ + tcg_gen_andi_i64(cc_dst, o->out, mask); + set_cc_nz_u64(s, cc_dst); + return NO_EXIT; +} + +static ExitStatus op_bas(DisasContext *s, DisasOps *o) +{ + tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->next_pc)); + if (!TCGV_IS_UNUSED_I64(o->in2)) { + tcg_gen_mov_i64(psw_addr, o->in2); + return EXIT_PC_UPDATED; + } else { + return NO_EXIT; + } +} + +static ExitStatus op_basi(DisasContext *s, DisasOps *o) +{ + tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->next_pc)); + return help_goto_direct(s, s->pc + 2 * get_field(s->fields, i2)); +} + +static ExitStatus op_bc(DisasContext *s, DisasOps *o) +{ + int m1 = get_field(s->fields, m1); + bool is_imm = have_field(s->fields, i2); + int imm = is_imm ? get_field(s->fields, i2) : 0; + DisasCompare c; + + disas_jcc(s, &c, m1); + return help_branch(s, &c, is_imm, imm, o->in2); +} + +static ExitStatus op_bct32(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, r1); + bool is_imm = have_field(s->fields, i2); + int imm = is_imm ? get_field(s->fields, i2) : 0; + DisasCompare c; + TCGv_i64 t; + + c.cond = TCG_COND_NE; + c.is_64 = false; + c.g1 = false; + c.g2 = false; + + t = tcg_temp_new_i64(); + tcg_gen_subi_i64(t, regs[r1], 1); + store_reg32_i64(r1, t); + c.u.s32.a = tcg_temp_new_i32(); + c.u.s32.b = tcg_const_i32(0); + tcg_gen_trunc_i64_i32(c.u.s32.a, t); + tcg_temp_free_i64(t); + + return help_branch(s, &c, is_imm, imm, o->in2); +} + +static ExitStatus op_bct64(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, r1); + bool is_imm = have_field(s->fields, i2); + int imm = is_imm ? get_field(s->fields, i2) : 0; + DisasCompare c; + + c.cond = TCG_COND_NE; + c.is_64 = true; + c.g1 = true; + c.g2 = false; + + tcg_gen_subi_i64(regs[r1], regs[r1], 1); + c.u.s64.a = regs[r1]; + c.u.s64.b = tcg_const_i64(0); + + return help_branch(s, &c, is_imm, imm, o->in2); +} + +static ExitStatus op_bx32(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, r1); + int r3 = get_field(s->fields, r3); + bool is_imm = have_field(s->fields, i2); + int imm = is_imm ? get_field(s->fields, i2) : 0; + DisasCompare c; + TCGv_i64 t; + + c.cond = (s->insn->data ? TCG_COND_LE : TCG_COND_GT); + c.is_64 = false; + c.g1 = false; + c.g2 = false; + + t = tcg_temp_new_i64(); + tcg_gen_add_i64(t, regs[r1], regs[r3]); + c.u.s32.a = tcg_temp_new_i32(); + c.u.s32.b = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(c.u.s32.a, t); + tcg_gen_trunc_i64_i32(c.u.s32.b, regs[r3 | 1]); + store_reg32_i64(r1, t); + tcg_temp_free_i64(t); + + return help_branch(s, &c, is_imm, imm, o->in2); +} + +static ExitStatus op_bx64(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, r1); + int r3 = get_field(s->fields, r3); + bool is_imm = have_field(s->fields, i2); + int imm = is_imm ? get_field(s->fields, i2) : 0; + DisasCompare c; + + c.cond = (s->insn->data ? TCG_COND_LE : TCG_COND_GT); + c.is_64 = true; + + if (r1 == (r3 | 1)) { + c.u.s64.b = load_reg(r3 | 1); + c.g2 = false; + } else { + c.u.s64.b = regs[r3 | 1]; + c.g2 = true; + } + + tcg_gen_add_i64(regs[r1], regs[r1], regs[r3]); + c.u.s64.a = regs[r1]; + c.g1 = true; + + return help_branch(s, &c, is_imm, imm, o->in2); +} + +static ExitStatus op_cj(DisasContext *s, DisasOps *o) +{ + int imm, m3 = get_field(s->fields, m3); + bool is_imm; + DisasCompare c; + + c.cond = ltgt_cond[m3]; + if (s->insn->data) { + c.cond = tcg_unsigned_cond(c.cond); + } + c.is_64 = c.g1 = c.g2 = true; + c.u.s64.a = o->in1; + c.u.s64.b = o->in2; + + is_imm = have_field(s->fields, i4); + if (is_imm) { + imm = get_field(s->fields, i4); + } else { + imm = 0; + o->out = get_address(s, 0, get_field(s->fields, b4), + get_field(s->fields, d4)); + } + + return help_branch(s, &c, is_imm, imm, o->out); +} + +static ExitStatus op_ceb(DisasContext *s, DisasOps *o) +{ + gen_helper_ceb(cc_op, cpu_env, o->in1, o->in2); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_cdb(DisasContext *s, DisasOps *o) +{ + gen_helper_cdb(cc_op, cpu_env, o->in1, o->in2); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_cxb(DisasContext *s, DisasOps *o) +{ + gen_helper_cxb(cc_op, cpu_env, o->out, o->out2, o->in1, o->in2); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_cfeb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_cfeb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + gen_set_cc_nz_f32(s, o->in2); + return NO_EXIT; +} + +static ExitStatus op_cfdb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_cfdb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + gen_set_cc_nz_f64(s, o->in2); + return NO_EXIT; +} + +static ExitStatus op_cfxb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_cfxb(o->out, cpu_env, o->in1, o->in2, m3); + tcg_temp_free_i32(m3); + gen_set_cc_nz_f128(s, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_cgeb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_cgeb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + gen_set_cc_nz_f32(s, o->in2); + return NO_EXIT; +} + +static ExitStatus op_cgdb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_cgdb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + gen_set_cc_nz_f64(s, o->in2); + return NO_EXIT; +} + +static ExitStatus op_cgxb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_cgxb(o->out, cpu_env, o->in1, o->in2, m3); + tcg_temp_free_i32(m3); + gen_set_cc_nz_f128(s, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_clfeb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_clfeb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + gen_set_cc_nz_f32(s, o->in2); + return NO_EXIT; +} + +static ExitStatus op_clfdb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_clfdb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + gen_set_cc_nz_f64(s, o->in2); + return NO_EXIT; +} + +static ExitStatus op_clfxb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_clfxb(o->out, cpu_env, o->in1, o->in2, m3); + tcg_temp_free_i32(m3); + gen_set_cc_nz_f128(s, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_clgeb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_clgeb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + gen_set_cc_nz_f32(s, o->in2); + return NO_EXIT; +} + +static ExitStatus op_clgdb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_clgdb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + gen_set_cc_nz_f64(s, o->in2); + return NO_EXIT; +} + +static ExitStatus op_clgxb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_clgxb(o->out, cpu_env, o->in1, o->in2, m3); + tcg_temp_free_i32(m3); + gen_set_cc_nz_f128(s, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_cegb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_cegb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + return NO_EXIT; +} + +static ExitStatus op_cdgb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_cdgb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + return NO_EXIT; +} + +static ExitStatus op_cxgb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_cxgb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + return_low128(o->out2); + return NO_EXIT; +} + +static ExitStatus op_celgb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_celgb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + return NO_EXIT; +} + +static ExitStatus op_cdlgb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_cdlgb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + return NO_EXIT; +} + +static ExitStatus op_cxlgb(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + gen_helper_cxlgb(o->out, cpu_env, o->in2, m3); + tcg_temp_free_i32(m3); + return_low128(o->out2); + return NO_EXIT; +} + +static ExitStatus op_cksm(DisasContext *s, DisasOps *o) +{ + int r2 = get_field(s->fields, r2); + TCGv_i64 len = tcg_temp_new_i64(); potential_page_fault(s); - vl = tcg_const_i32(l); - gen_helper_clc(cc_op, cpu_env, vl, s1, s2); - tcg_temp_free_i32(vl); + gen_helper_cksm(len, cpu_env, o->in1, o->in2, regs[r2 + 1]); set_cc_static(s); + return_low128(o->out); + + tcg_gen_add_i64(regs[r2], regs[r2], len); + tcg_gen_sub_i64(regs[r2 + 1], regs[r2 + 1], len); + tcg_temp_free_i64(len); + + return NO_EXIT; } -static void disas_e3(CPUS390XState *env, DisasContext* s, int op, int r1, - int x2, int b2, int d2) +static ExitStatus op_clc(DisasContext *s, DisasOps *o) { - TCGv_i64 addr, tmp, tmp2, tmp3, tmp4; - TCGv_i32 tmp32_1, tmp32_2, tmp32_3; + int l = get_field(s->fields, l1); + TCGv_i32 vl; - LOG_DISAS("disas_e3: op 0x%x r1 %d x2 %d b2 %d d2 %d\n", - op, r1, x2, b2, d2); - addr = get_address(s, x2, b2, d2); - switch (op) { - case 0x2: /* LTG R1,D2(X2,B2) [RXY] */ - case 0x4: /* lg r1,d2(x2,b2) */ - tcg_gen_qemu_ld64(regs[r1], addr, get_mem_index(s)); - if (op == 0x2) { - set_cc_s64(s, regs[r1]); - } + switch (l + 1) { + case 1: + tcg_gen_qemu_ld8u(cc_src, o->addr1, get_mem_index(s)); + tcg_gen_qemu_ld8u(cc_dst, o->in2, get_mem_index(s)); break; - case 0x12: /* LT R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_1, tmp2); - store_reg32(r1, tmp32_1); - set_cc_s32(s, tmp32_1); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); + case 2: + tcg_gen_qemu_ld16u(cc_src, o->addr1, get_mem_index(s)); + tcg_gen_qemu_ld16u(cc_dst, o->in2, get_mem_index(s)); break; - case 0xc: /* MSG R1,D2(X2,B2) [RXY] */ - case 0x1c: /* MSGF R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - if (op == 0xc) { - tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); - } else { - tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); - } - tcg_gen_mul_i64(regs[r1], regs[r1], tmp2); - tcg_temp_free_i64(tmp2); + case 4: + tcg_gen_qemu_ld32u(cc_src, o->addr1, get_mem_index(s)); + tcg_gen_qemu_ld32u(cc_dst, o->in2, get_mem_index(s)); break; - case 0xd: /* DSG R1,D2(X2,B2) [RXY] */ - case 0x1d: /* DSGF R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - if (op == 0x1d) { - tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); - } else { - tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); - } - tmp4 = load_reg(r1 + 1); - tmp3 = tcg_temp_new_i64(); - tcg_gen_div_i64(tmp3, tmp4, tmp2); - store_reg(r1 + 1, tmp3); - tcg_gen_rem_i64(tmp3, tmp4, tmp2); - store_reg(r1, tmp3); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - tcg_temp_free_i64(tmp4); - break; - case 0x8: /* AG R1,D2(X2,B2) [RXY] */ - case 0xa: /* ALG R1,D2(X2,B2) [RXY] */ - case 0x18: /* AGF R1,D2(X2,B2) [RXY] */ - case 0x1a: /* ALGF R1,D2(X2,B2) [RXY] */ - if (op == 0x1a) { - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); - } else if (op == 0x18) { - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); - } else { - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); - } - tmp4 = load_reg(r1); - tmp3 = tcg_temp_new_i64(); - tcg_gen_add_i64(tmp3, tmp4, tmp2); - store_reg(r1, tmp3); - switch (op) { - case 0x8: - case 0x18: - set_cc_add64(s, tmp4, tmp2, tmp3); - break; - case 0xa: - case 0x1a: - set_cc_addu64(s, tmp4, tmp2, tmp3); - break; - default: - tcg_abort(); - } - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - tcg_temp_free_i64(tmp4); - break; - case 0x9: /* SG R1,D2(X2,B2) [RXY] */ - case 0xb: /* SLG R1,D2(X2,B2) [RXY] */ - case 0x19: /* SGF R1,D2(X2,B2) [RXY] */ - case 0x1b: /* SLGF R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - if (op == 0x19) { - tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); - } else if (op == 0x1b) { - tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); - } else { - tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); - } - tmp4 = load_reg(r1); - tmp3 = tcg_temp_new_i64(); - tcg_gen_sub_i64(tmp3, tmp4, tmp2); - store_reg(r1, tmp3); - switch (op) { - case 0x9: - case 0x19: - set_cc_sub64(s, tmp4, tmp2, tmp3); - break; - case 0xb: - case 0x1b: - set_cc_subu64(s, tmp4, tmp2, tmp3); - break; - default: - tcg_abort(); - } - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - tcg_temp_free_i64(tmp4); - break; - case 0xf: /* LRVG R1,D2(X2,B2) [RXE] */ - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); - tcg_gen_bswap64_i64(tmp2, tmp2); - store_reg(r1, tmp2); - tcg_temp_free_i64(tmp2); - break; - case 0x14: /* LGF R1,D2(X2,B2) [RXY] */ - case 0x16: /* LLGF R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); - if (op == 0x14) { - tcg_gen_ext32s_i64(tmp2, tmp2); - } - store_reg(r1, tmp2); - tcg_temp_free_i64(tmp2); - break; - case 0x15: /* LGH R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld16s(tmp2, addr, get_mem_index(s)); - store_reg(r1, tmp2); - tcg_temp_free_i64(tmp2); - break; - case 0x17: /* LLGT R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); - tcg_gen_andi_i64(tmp2, tmp2, 0x7fffffffULL); - store_reg(r1, tmp2); - tcg_temp_free_i64(tmp2); - break; - case 0x1e: /* LRV R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_1, tmp2); - tcg_temp_free_i64(tmp2); - tcg_gen_bswap32_i32(tmp32_1, tmp32_1); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x1f: /* LRVH R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_temp_new_i32(); - tcg_gen_qemu_ld16u(tmp2, addr, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_1, tmp2); - tcg_temp_free_i64(tmp2); - tcg_gen_bswap16_i32(tmp32_1, tmp32_1); - store_reg16(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x20: /* CG R1,D2(X2,B2) [RXY] */ - case 0x21: /* CLG R1,D2(X2,B2) */ - case 0x30: /* CGF R1,D2(X2,B2) [RXY] */ - case 0x31: /* CLGF R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - switch (op) { - case 0x20: - case 0x21: - tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); - break; - case 0x30: - tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); - break; - case 0x31: - tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); - break; - default: - tcg_abort(); - } - switch (op) { - case 0x20: - case 0x30: - cmp_s64(s, regs[r1], tmp2); - break; - case 0x21: - case 0x31: - cmp_u64(s, regs[r1], tmp2); - break; - default: - tcg_abort(); - } - tcg_temp_free_i64(tmp2); - break; - case 0x24: /* stg r1, d2(x2,b2) */ - tcg_gen_qemu_st64(regs[r1], addr, get_mem_index(s)); - break; - case 0x3e: /* STRV R1,D2(X2,B2) [RXY] */ - tmp32_1 = load_reg32(r1); - tmp2 = tcg_temp_new_i64(); - tcg_gen_bswap32_i32(tmp32_1, tmp32_1); - tcg_gen_extu_i32_i64(tmp2, tmp32_1); - tcg_temp_free_i32(tmp32_1); - tcg_gen_qemu_st32(tmp2, addr, get_mem_index(s)); - tcg_temp_free_i64(tmp2); - break; - case 0x50: /* STY R1,D2(X2,B2) [RXY] */ - tmp32_1 = load_reg32(r1); - tmp2 = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(tmp2, tmp32_1); - tcg_temp_free_i32(tmp32_1); - tcg_gen_qemu_st32(tmp2, addr, get_mem_index(s)); - tcg_temp_free_i64(tmp2); - break; - case 0x57: /* XY R1,D2(X2,B2) [RXY] */ - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_temp_new_i32(); - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_2, tmp2); - tcg_temp_free_i64(tmp2); - tcg_gen_xor_i32(tmp32_2, tmp32_1, tmp32_2); - store_reg32(r1, tmp32_2); - set_cc_nz_u32(s, tmp32_2); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x58: /* LY R1,D2(X2,B2) [RXY] */ - tmp3 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32u(tmp3, addr, get_mem_index(s)); - store_reg32_i64(r1, tmp3); - tcg_temp_free_i64(tmp3); - break; - case 0x5a: /* AY R1,D2(X2,B2) [RXY] */ - case 0x5b: /* SY R1,D2(X2,B2) [RXY] */ - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_temp_new_i32(); - tmp32_3 = tcg_temp_new_i32(); - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_2, tmp2); - tcg_temp_free_i64(tmp2); - switch (op) { - case 0x5a: - tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2); - break; - case 0x5b: - tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2); - break; - default: - tcg_abort(); - } - store_reg32(r1, tmp32_3); - switch (op) { - case 0x5a: - set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3); - break; - case 0x5b: - set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3); - break; - default: - tcg_abort(); - } - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0x71: /* LAY R1,D2(X2,B2) [RXY] */ - store_reg(r1, addr); - break; - case 0x72: /* STCY R1,D2(X2,B2) [RXY] */ - tmp32_1 = load_reg32(r1); - tmp2 = tcg_temp_new_i64(); - tcg_gen_ext_i32_i64(tmp2, tmp32_1); - tcg_gen_qemu_st8(tmp2, addr, get_mem_index(s)); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp2); - break; - case 0x73: /* ICY R1,D2(X2,B2) [RXY] */ - tmp3 = tcg_temp_new_i64(); - tcg_gen_qemu_ld8u(tmp3, addr, get_mem_index(s)); - store_reg8(r1, tmp3); - tcg_temp_free_i64(tmp3); - break; - case 0x76: /* LB R1,D2(X2,B2) [RXY] */ - case 0x77: /* LGB R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld8s(tmp2, addr, get_mem_index(s)); - switch (op) { - case 0x76: - tcg_gen_ext8s_i64(tmp2, tmp2); - store_reg32_i64(r1, tmp2); - break; - case 0x77: - tcg_gen_ext8s_i64(tmp2, tmp2); - store_reg(r1, tmp2); - break; - default: - tcg_abort(); - } - tcg_temp_free_i64(tmp2); - break; - case 0x78: /* LHY R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld16s(tmp2, addr, get_mem_index(s)); - store_reg32_i64(r1, tmp2); - tcg_temp_free_i64(tmp2); - break; - case 0x80: /* NG R1,D2(X2,B2) [RXY] */ - case 0x81: /* OG R1,D2(X2,B2) [RXY] */ - case 0x82: /* XG R1,D2(X2,B2) [RXY] */ - tmp3 = tcg_temp_new_i64(); - tcg_gen_qemu_ld64(tmp3, addr, get_mem_index(s)); - switch (op) { - case 0x80: - tcg_gen_and_i64(regs[r1], regs[r1], tmp3); - break; - case 0x81: - tcg_gen_or_i64(regs[r1], regs[r1], tmp3); - break; - case 0x82: - tcg_gen_xor_i64(regs[r1], regs[r1], tmp3); - break; - default: - tcg_abort(); - } - set_cc_nz_u64(s, regs[r1]); - tcg_temp_free_i64(tmp3); - break; - case 0x86: /* MLG R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_const_i32(r1); - tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); - gen_helper_mlg(cpu_env, tmp32_1, tmp2); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - break; - case 0x87: /* DLG R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_const_i32(r1); - tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); - gen_helper_dlg(cpu_env, tmp32_1, tmp2); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - break; - case 0x88: /* ALCG R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tmp3 = tcg_temp_new_i64(); - tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); - /* XXX possible optimization point */ - gen_op_calc_cc(s); - tcg_gen_extu_i32_i64(tmp3, cc_op); - tcg_gen_shri_i64(tmp3, tmp3, 1); - tcg_gen_andi_i64(tmp3, tmp3, 1); - tcg_gen_add_i64(tmp3, tmp2, tmp3); - tcg_gen_add_i64(tmp3, regs[r1], tmp3); - store_reg(r1, tmp3); - set_cc_addu64(s, regs[r1], tmp2, tmp3); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x89: /* SLBG R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_const_i32(r1); - tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s)); - /* XXX possible optimization point */ - gen_op_calc_cc(s); - gen_helper_slbg(cc_op, cpu_env, cc_op, tmp32_1, regs[r1], tmp2); - set_cc_static(s); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - break; - case 0x90: /* LLGC R1,D2(X2,B2) [RXY] */ - tcg_gen_qemu_ld8u(regs[r1], addr, get_mem_index(s)); - break; - case 0x91: /* LLGH R1,D2(X2,B2) [RXY] */ - tcg_gen_qemu_ld16u(regs[r1], addr, get_mem_index(s)); - break; - case 0x94: /* LLC R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld8u(tmp2, addr, get_mem_index(s)); - store_reg32_i64(r1, tmp2); - tcg_temp_free_i64(tmp2); - break; - case 0x95: /* LLH R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld16u(tmp2, addr, get_mem_index(s)); - store_reg32_i64(r1, tmp2); - tcg_temp_free_i64(tmp2); - break; - case 0x96: /* ML R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tmp3 = load_reg((r1 + 1) & 15); - tcg_gen_ext32u_i64(tmp3, tmp3); - tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); - tcg_gen_mul_i64(tmp2, tmp2, tmp3); - store_reg32_i64((r1 + 1) & 15, tmp2); - tcg_gen_shri_i64(tmp2, tmp2, 32); - store_reg32_i64(r1, tmp2); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x97: /* DL R1,D2(X2,B2) [RXY] */ - /* reg(r1) = reg(r1, r1+1) % ld32(addr) */ - /* reg(r1+1) = reg(r1, r1+1) / ld32(addr) */ - tmp = load_reg(r1); - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); - tmp3 = load_reg((r1 + 1) & 15); - tcg_gen_ext32u_i64(tmp2, tmp2); - tcg_gen_ext32u_i64(tmp3, tmp3); - tcg_gen_shli_i64(tmp, tmp, 32); - tcg_gen_or_i64(tmp, tmp, tmp3); - - tcg_gen_rem_i64(tmp3, tmp, tmp2); - tcg_gen_div_i64(tmp, tmp, tmp2); - store_reg32_i64((r1 + 1) & 15, tmp); - store_reg32_i64(r1, tmp3); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x98: /* ALC R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_temp_new_i32(); - tmp32_3 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_2, tmp2); - /* XXX possible optimization point */ - gen_op_calc_cc(s); - gen_helper_addc_u32(tmp32_3, cc_op, tmp32_1, tmp32_2); - set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3); - store_reg32(r1, tmp32_3); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0x99: /* SLB R1,D2(X2,B2) [RXY] */ - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_2, tmp2); - /* XXX possible optimization point */ - gen_op_calc_cc(s); - gen_helper_slb(cc_op, cpu_env, cc_op, tmp32_1, tmp32_2); - set_cc_static(s); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); + case 8: + tcg_gen_qemu_ld64(cc_src, o->addr1, get_mem_index(s)); + tcg_gen_qemu_ld64(cc_dst, o->in2, get_mem_index(s)); break; default: - LOG_DISAS("illegal e3 operation 0x%x\n", op); - gen_illegal_opcode(env, s, 3); - break; + potential_page_fault(s); + vl = tcg_const_i32(l); + gen_helper_clc(cc_op, cpu_env, vl, o->addr1, o->in2); + tcg_temp_free_i32(vl); + set_cc_static(s); + return NO_EXIT; } + gen_op_update2_cc_i64(s, CC_OP_LTUGTU_64, cc_src, cc_dst); + return NO_EXIT; +} + +static ExitStatus op_clcle(DisasContext *s, DisasOps *o) +{ + TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + potential_page_fault(s); + gen_helper_clcle(cc_op, cpu_env, r1, o->in2, r3); + tcg_temp_free_i32(r1); + tcg_temp_free_i32(r3); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_clm(DisasContext *s, DisasOps *o) +{ + TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); + TCGv_i32 t1 = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(t1, o->in1); + potential_page_fault(s); + gen_helper_clm(cc_op, cpu_env, t1, m3, o->in2); + set_cc_static(s); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(m3); + return NO_EXIT; +} + +static ExitStatus op_clst(DisasContext *s, DisasOps *o) +{ + potential_page_fault(s); + gen_helper_clst(o->in1, cpu_env, regs[0], o->in1, o->in2); + set_cc_static(s); + return_low128(o->in2); + return NO_EXIT; +} + +static ExitStatus op_cps(DisasContext *s, DisasOps *o) +{ + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_andi_i64(t, o->in1, 0x8000000000000000ull); + tcg_gen_andi_i64(o->out, o->in2, 0x7fffffffffffffffull); + tcg_gen_or_i64(o->out, o->out, t); + tcg_temp_free_i64(t); + return NO_EXIT; +} + +static ExitStatus op_cs(DisasContext *s, DisasOps *o) +{ + /* FIXME: needs an atomic solution for CONFIG_USER_ONLY. */ + int d2 = get_field(s->fields, d2); + int b2 = get_field(s->fields, b2); + int is_64 = s->insn->data; + TCGv_i64 addr, mem, cc, z; + + /* Note that in1 = R3 (new value) and + in2 = (zero-extended) R1 (expected value). */ + + /* Load the memory into the (temporary) output. While the PoO only talks + about moving the memory to R1 on inequality, if we include equality it + means that R1 is equal to the memory in all conditions. */ + addr = get_address(s, 0, b2, d2); + if (is_64) { + tcg_gen_qemu_ld64(o->out, addr, get_mem_index(s)); + } else { + tcg_gen_qemu_ld32u(o->out, addr, get_mem_index(s)); + } + + /* Are the memory and expected values (un)equal? Note that this setcond + produces the output CC value, thus the NE sense of the test. */ + cc = tcg_temp_new_i64(); + tcg_gen_setcond_i64(TCG_COND_NE, cc, o->in2, o->out); + + /* If the memory and expected values are equal (CC==0), copy R3 to MEM. + Recall that we are allowed to unconditionally issue the store (and + thus any possible write trap), so (re-)store the original contents + of MEM in case of inequality. */ + z = tcg_const_i64(0); + mem = tcg_temp_new_i64(); + tcg_gen_movcond_i64(TCG_COND_EQ, mem, cc, z, o->in1, o->out); + if (is_64) { + tcg_gen_qemu_st64(mem, addr, get_mem_index(s)); + } else { + tcg_gen_qemu_st32(mem, addr, get_mem_index(s)); + } + tcg_temp_free_i64(z); + tcg_temp_free_i64(mem); tcg_temp_free_i64(addr); + + /* Store CC back to cc_op. Wait until after the store so that any + exception gets the old cc_op value. */ + tcg_gen_trunc_i64_i32(cc_op, cc); + tcg_temp_free_i64(cc); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_cdsg(DisasContext *s, DisasOps *o) +{ + /* FIXME: needs an atomic solution for CONFIG_USER_ONLY. */ + int r1 = get_field(s->fields, r1); + int r3 = get_field(s->fields, r3); + int d2 = get_field(s->fields, d2); + int b2 = get_field(s->fields, b2); + TCGv_i64 addrh, addrl, memh, meml, outh, outl, cc, z; + + /* Note that R1:R1+1 = expected value and R3:R3+1 = new value. */ + + addrh = get_address(s, 0, b2, d2); + addrl = get_address(s, 0, b2, d2 + 8); + outh = tcg_temp_new_i64(); + outl = tcg_temp_new_i64(); + + tcg_gen_qemu_ld64(outh, addrh, get_mem_index(s)); + tcg_gen_qemu_ld64(outl, addrl, get_mem_index(s)); + + /* Fold the double-word compare with arithmetic. */ + cc = tcg_temp_new_i64(); + z = tcg_temp_new_i64(); + tcg_gen_xor_i64(cc, outh, regs[r1]); + tcg_gen_xor_i64(z, outl, regs[r1 + 1]); + tcg_gen_or_i64(cc, cc, z); + tcg_gen_movi_i64(z, 0); + tcg_gen_setcond_i64(TCG_COND_NE, cc, cc, z); + + memh = tcg_temp_new_i64(); + meml = tcg_temp_new_i64(); + tcg_gen_movcond_i64(TCG_COND_EQ, memh, cc, z, regs[r3], outh); + tcg_gen_movcond_i64(TCG_COND_EQ, meml, cc, z, regs[r3 + 1], outl); + tcg_temp_free_i64(z); + + tcg_gen_qemu_st64(memh, addrh, get_mem_index(s)); + tcg_gen_qemu_st64(meml, addrl, get_mem_index(s)); + tcg_temp_free_i64(memh); + tcg_temp_free_i64(meml); + tcg_temp_free_i64(addrh); + tcg_temp_free_i64(addrl); + + /* Save back state now that we've passed all exceptions. */ + tcg_gen_mov_i64(regs[r1], outh); + tcg_gen_mov_i64(regs[r1 + 1], outl); + tcg_gen_trunc_i64_i32(cc_op, cc); + tcg_temp_free_i64(outh); + tcg_temp_free_i64(outl); + tcg_temp_free_i64(cc); + set_cc_static(s); + return NO_EXIT; } #ifndef CONFIG_USER_ONLY -static void disas_e5(CPUS390XState *env, DisasContext* s, uint64_t insn) +static ExitStatus op_csp(DisasContext *s, DisasOps *o) { - TCGv_i64 tmp, tmp2; - int op = (insn >> 32) & 0xff; + TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); + check_privileged(s); + gen_helper_csp(cc_op, cpu_env, r1, o->in2); + tcg_temp_free_i32(r1); + set_cc_static(s); + return NO_EXIT; +} +#endif - tmp = get_address(s, 0, (insn >> 28) & 0xf, (insn >> 16) & 0xfff); - tmp2 = get_address(s, 0, (insn >> 12) & 0xf, insn & 0xfff); +static ExitStatus op_cvd(DisasContext *s, DisasOps *o) +{ + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i32 t2 = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(t2, o->in1); + gen_helper_cvd(t1, t2); + tcg_temp_free_i32(t2); + tcg_gen_qemu_st64(t1, o->in2, get_mem_index(s)); + tcg_temp_free_i64(t1); + return NO_EXIT; +} - LOG_DISAS("disas_e5: insn %" PRIx64 "\n", insn); - switch (op) { - case 0x01: /* TPROT D1(B1),D2(B2) [SSE] */ - /* Test Protection */ - potential_page_fault(s); - gen_helper_tprot(cc_op, tmp, tmp2); - set_cc_static(s); - break; - default: - LOG_DISAS("illegal e5 operation 0x%x\n", op); - gen_illegal_opcode(env, s, 3); - break; +static ExitStatus op_ct(DisasContext *s, DisasOps *o) +{ + int m3 = get_field(s->fields, m3); + int lab = gen_new_label(); + TCGv_i32 t; + TCGCond c; + + c = tcg_invert_cond(ltgt_cond[m3]); + if (s->insn->data) { + c = tcg_unsigned_cond(c); } + tcg_gen_brcond_i64(c, o->in1, o->in2, lab); + /* Set DXC to 0xff. */ + t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, cpu_env, offsetof(CPUS390XState, fpc)); + tcg_gen_ori_i32(t, t, 0xff00); + tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, fpc)); + tcg_temp_free_i32(t); + + /* Trap. */ + gen_program_exception(s, PGM_DATA); + + gen_set_label(lab); + return NO_EXIT; +} + +#ifndef CONFIG_USER_ONLY +static ExitStatus op_diag(DisasContext *s, DisasOps *o) +{ + TCGv_i32 tmp; + + check_privileged(s); + potential_page_fault(s); + + /* We pretend the format is RX_a so that D2 is the field we want. */ + tmp = tcg_const_i32(get_field(s->fields, d2) & 0xfff); + gen_helper_diag(regs[2], cpu_env, tmp, regs[2], regs[1]); + tcg_temp_free_i32(tmp); + return NO_EXIT; +} +#endif + +static ExitStatus op_divs32(DisasContext *s, DisasOps *o) +{ + gen_helper_divs32(o->out2, cpu_env, o->in1, o->in2); + return_low128(o->out); + return NO_EXIT; +} + +static ExitStatus op_divu32(DisasContext *s, DisasOps *o) +{ + gen_helper_divu32(o->out2, cpu_env, o->in1, o->in2); + return_low128(o->out); + return NO_EXIT; +} + +static ExitStatus op_divs64(DisasContext *s, DisasOps *o) +{ + gen_helper_divs64(o->out2, cpu_env, o->in1, o->in2); + return_low128(o->out); + return NO_EXIT; +} + +static ExitStatus op_divu64(DisasContext *s, DisasOps *o) +{ + gen_helper_divu64(o->out2, cpu_env, o->out, o->out2, o->in2); + return_low128(o->out); + return NO_EXIT; +} + +static ExitStatus op_deb(DisasContext *s, DisasOps *o) +{ + gen_helper_deb(o->out, cpu_env, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_ddb(DisasContext *s, DisasOps *o) +{ + gen_helper_ddb(o->out, cpu_env, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_dxb(DisasContext *s, DisasOps *o) +{ + gen_helper_dxb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); + return_low128(o->out2); + return NO_EXIT; +} + +static ExitStatus op_ear(DisasContext *s, DisasOps *o) +{ + int r2 = get_field(s->fields, r2); + tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, aregs[r2])); + return NO_EXIT; +} + +static ExitStatus op_efpc(DisasContext *s, DisasOps *o) +{ + tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, fpc)); + return NO_EXIT; +} + +static ExitStatus op_ex(DisasContext *s, DisasOps *o) +{ + /* ??? Perhaps a better way to implement EXECUTE is to set a bit in + tb->flags, (ab)use the tb->cs_base field as the address of + the template in memory, and grab 8 bits of tb->flags/cflags for + the contents of the register. We would then recognize all this + in gen_intermediate_code_internal, generating code for exactly + one instruction. This new TB then gets executed normally. + + On the other hand, this seems to be mostly used for modifying + MVC inside of memcpy, which needs a helper call anyway. So + perhaps this doesn't bear thinking about any further. */ + + TCGv_i64 tmp; + + update_psw_addr(s); + update_cc_op(s); + + tmp = tcg_const_i64(s->next_pc); + gen_helper_ex(cc_op, cpu_env, cc_op, o->in1, o->in2, tmp); tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); -} -#endif -static void disas_eb(CPUS390XState *env, DisasContext *s, int op, int r1, - int r3, int b2, int d2) -{ - TCGv_i64 tmp, tmp2, tmp3, tmp4; - TCGv_i32 tmp32_1, tmp32_2; - int i, stm_len; - int ilc = 3; - - LOG_DISAS("disas_eb: op 0x%x r1 %d r3 %d b2 %d d2 0x%x\n", - op, r1, r3, b2, d2); - switch (op) { - case 0xc: /* SRLG R1,R3,D2(B2) [RSY] */ - case 0xd: /* SLLG R1,R3,D2(B2) [RSY] */ - case 0xa: /* SRAG R1,R3,D2(B2) [RSY] */ - case 0xb: /* SLAG R1,R3,D2(B2) [RSY] */ - case 0x1c: /* RLLG R1,R3,D2(B2) [RSY] */ - if (b2) { - tmp = get_address(s, 0, b2, d2); - tcg_gen_andi_i64(tmp, tmp, 0x3f); - } else { - tmp = tcg_const_i64(d2 & 0x3f); - } - switch (op) { - case 0xc: - tcg_gen_shr_i64(regs[r1], regs[r3], tmp); - break; - case 0xd: - tcg_gen_shl_i64(regs[r1], regs[r3], tmp); - break; - case 0xa: - tcg_gen_sar_i64(regs[r1], regs[r3], tmp); - break; - case 0xb: - tmp2 = tcg_temp_new_i64(); - tmp3 = tcg_temp_new_i64(); - gen_op_update2_cc_i64(s, CC_OP_SLAG, regs[r3], tmp); - tcg_gen_shl_i64(tmp2, regs[r3], tmp); - /* override sign bit with source sign */ - tcg_gen_andi_i64(tmp2, tmp2, ~0x8000000000000000ULL); - tcg_gen_andi_i64(tmp3, regs[r3], 0x8000000000000000ULL); - tcg_gen_or_i64(regs[r1], tmp2, tmp3); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x1c: - tcg_gen_rotl_i64(regs[r1], regs[r3], tmp); - break; - default: - tcg_abort(); - break; - } - if (op == 0xa) { - set_cc_s64(s, regs[r1]); - } - tcg_temp_free_i64(tmp); - break; - case 0x1d: /* RLL R1,R3,D2(B2) [RSY] */ - if (b2) { - tmp = get_address(s, 0, b2, d2); - tcg_gen_andi_i64(tmp, tmp, 0x3f); - } else { - tmp = tcg_const_i64(d2 & 0x3f); - } - tmp32_1 = tcg_temp_new_i32(); - tmp32_2 = load_reg32(r3); - tcg_gen_trunc_i64_i32(tmp32_1, tmp); - switch (op) { - case 0x1d: - tcg_gen_rotl_i32(tmp32_1, tmp32_2, tmp32_1); - break; - default: - tcg_abort(); - break; - } - store_reg32(r1, tmp32_1); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x4: /* LMG R1,R3,D2(B2) [RSE] */ - case 0x24: /* STMG R1,R3,D2(B2) [RSE] */ - stm_len = 8; - goto do_mh; - case 0x26: /* STMH R1,R3,D2(B2) [RSE] */ - case 0x96: /* LMH R1,R3,D2(B2) [RSE] */ - stm_len = 4; -do_mh: - /* Apparently, unrolling lmg/stmg of any size gains performance - - even for very long ones... */ - tmp = get_address(s, 0, b2, d2); - tmp3 = tcg_const_i64(stm_len); - tmp4 = tcg_const_i64(op == 0x26 ? 32 : 4); - for (i = r1;; i = (i + 1) % 16) { - switch (op) { - case 0x4: - tcg_gen_qemu_ld64(regs[i], tmp, get_mem_index(s)); - break; - case 0x96: - tmp2 = tcg_temp_new_i64(); -#if HOST_LONG_BITS == 32 - tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); - tcg_gen_trunc_i64_i32(TCGV_HIGH(regs[i]), tmp2); -#else - tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); - tcg_gen_shl_i64(tmp2, tmp2, tmp4); - tcg_gen_ext32u_i64(regs[i], regs[i]); - tcg_gen_or_i64(regs[i], regs[i], tmp2); -#endif - tcg_temp_free_i64(tmp2); - break; - case 0x24: - tcg_gen_qemu_st64(regs[i], tmp, get_mem_index(s)); - break; - case 0x26: - tmp2 = tcg_temp_new_i64(); - tcg_gen_shr_i64(tmp2, regs[i], tmp4); - tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s)); - tcg_temp_free_i64(tmp2); - break; - default: - tcg_abort(); - } - if (i == r3) { - break; - } - tcg_gen_add_i64(tmp, tmp, tmp3); - } - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp3); - tcg_temp_free_i64(tmp4); - break; - case 0x2c: /* STCMH R1,M3,D2(B2) [RSY] */ - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - gen_helper_stcmh(cpu_env, tmp32_1, tmp, tmp32_2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; -#ifndef CONFIG_USER_ONLY - case 0x2f: /* LCTLG R1,R3,D2(B2) [RSE] */ - /* Load Control */ - check_privileged(env, s, ilc); - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - gen_helper_lctlg(cpu_env, tmp32_1, tmp, tmp32_2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x25: /* STCTG R1,R3,D2(B2) [RSE] */ - /* Store Control */ - check_privileged(env, s, ilc); - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - gen_helper_stctg(cpu_env, tmp32_1, tmp, tmp32_2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; -#endif - case 0x30: /* CSG R1,R3,D2(B2) [RSY] */ - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - /* XXX rewrite in tcg */ - gen_helper_csg(cc_op, cpu_env, tmp32_1, tmp, tmp32_2); - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x3e: /* CDSG R1,R3,D2(B2) [RSY] */ - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - /* XXX rewrite in tcg */ - gen_helper_cdsg(cc_op, cpu_env, tmp32_1, tmp, tmp32_2); - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x51: /* TMY D1(B1),I2 [SIY] */ - tmp = get_address(s, 0, b2, d2); /* SIY -> this is the destination */ - tmp2 = tcg_const_i64((r1 << 4) | r3); - tcg_gen_qemu_ld8u(tmp, tmp, get_mem_index(s)); - /* yes, this is a 32 bit operation with 64 bit tcg registers, because - that incurs less conversions */ - cmp_64(s, tmp, tmp2, CC_OP_TM_32); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x52: /* MVIY D1(B1),I2 [SIY] */ - tmp = get_address(s, 0, b2, d2); /* SIY -> this is the destination */ - tmp2 = tcg_const_i64((r1 << 4) | r3); - tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s)); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x55: /* CLIY D1(B1),I2 [SIY] */ - tmp3 = get_address(s, 0, b2, d2); /* SIY -> this is the 1st operand */ - tmp = tcg_temp_new_i64(); - tmp32_1 = tcg_temp_new_i32(); - tcg_gen_qemu_ld8u(tmp, tmp3, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_1, tmp); - cmp_u32c(s, tmp32_1, (r1 << 4) | r3); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp3); - tcg_temp_free_i32(tmp32_1); - break; - case 0x80: /* ICMH R1,M3,D2(B2) [RSY] */ - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - /* XXX split CC calculation out */ - gen_helper_icmh(cc_op, cpu_env, tmp32_1, tmp, tmp32_2); - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - default: - LOG_DISAS("illegal eb operation 0x%x\n", op); - gen_illegal_opcode(env, s, ilc); - break; - } + set_cc_static(s); + return NO_EXIT; } -static void disas_ed(CPUS390XState *env, DisasContext *s, int op, int r1, - int x2, int b2, int d2, int r1b) +static ExitStatus op_flogr(DisasContext *s, DisasOps *o) { - TCGv_i32 tmp_r1, tmp32; - TCGv_i64 addr, tmp; - addr = get_address(s, x2, b2, d2); - tmp_r1 = tcg_const_i32(r1); - switch (op) { - case 0x4: /* LDEB R1,D2(X2,B2) [RXE] */ - potential_page_fault(s); - gen_helper_ldeb(cpu_env, tmp_r1, addr); - break; - case 0x5: /* LXDB R1,D2(X2,B2) [RXE] */ - potential_page_fault(s); - gen_helper_lxdb(cpu_env, tmp_r1, addr); - break; - case 0x9: /* CEB R1,D2(X2,B2) [RXE] */ - tmp = tcg_temp_new_i64(); - tmp32 = load_freg32(r1); - tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s)); - set_cc_cmp_f32_i64(s, tmp32, tmp); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32); - break; - case 0xa: /* AEB R1,D2(X2,B2) [RXE] */ - tmp = tcg_temp_new_i64(); - tmp32 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32, tmp); - gen_helper_aeb(cpu_env, tmp_r1, tmp32); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32); + /* We'll use the original input for cc computation, since we get to + compare that against 0, which ought to be better than comparing + the real output against 64. It also lets cc_dst be a convenient + temporary during our computation. */ + gen_op_update1_cc_i64(s, CC_OP_FLOGR, o->in2); - tmp32 = load_freg32(r1); - gen_set_cc_nz_f32(s, tmp32); - tcg_temp_free_i32(tmp32); - break; - case 0xb: /* SEB R1,D2(X2,B2) [RXE] */ - tmp = tcg_temp_new_i64(); - tmp32 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32, tmp); - gen_helper_seb(cpu_env, tmp_r1, tmp32); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32); + /* R1 = IN ? CLZ(IN) : 64. */ + gen_helper_clz(o->out, o->in2); - tmp32 = load_freg32(r1); - gen_set_cc_nz_f32(s, tmp32); - tcg_temp_free_i32(tmp32); - break; - case 0xd: /* DEB R1,D2(X2,B2) [RXE] */ - tmp = tcg_temp_new_i64(); - tmp32 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32, tmp); - gen_helper_deb(cpu_env, tmp_r1, tmp32); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32); - break; - case 0x10: /* TCEB R1,D2(X2,B2) [RXE] */ - potential_page_fault(s); - gen_helper_tceb(cc_op, cpu_env, tmp_r1, addr); - set_cc_static(s); - break; - case 0x11: /* TCDB R1,D2(X2,B2) [RXE] */ - potential_page_fault(s); - gen_helper_tcdb(cc_op, cpu_env, tmp_r1, addr); - set_cc_static(s); - break; - case 0x12: /* TCXB R1,D2(X2,B2) [RXE] */ - potential_page_fault(s); - gen_helper_tcxb(cc_op, cpu_env, tmp_r1, addr); - set_cc_static(s); - break; - case 0x17: /* MEEB R1,D2(X2,B2) [RXE] */ - tmp = tcg_temp_new_i64(); - tmp32 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp, addr, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32, tmp); - gen_helper_meeb(cpu_env, tmp_r1, tmp32); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32); - break; - case 0x19: /* CDB R1,D2(X2,B2) [RXE] */ - potential_page_fault(s); - gen_helper_cdb(cc_op, cpu_env, tmp_r1, addr); - set_cc_static(s); - break; - case 0x1a: /* ADB R1,D2(X2,B2) [RXE] */ - potential_page_fault(s); - gen_helper_adb(cc_op, cpu_env, tmp_r1, addr); - set_cc_static(s); - break; - case 0x1b: /* SDB R1,D2(X2,B2) [RXE] */ - potential_page_fault(s); - gen_helper_sdb(cc_op, cpu_env, tmp_r1, addr); - set_cc_static(s); - break; - case 0x1c: /* MDB R1,D2(X2,B2) [RXE] */ - potential_page_fault(s); - gen_helper_mdb(cpu_env, tmp_r1, addr); - break; - case 0x1d: /* DDB R1,D2(X2,B2) [RXE] */ - potential_page_fault(s); - gen_helper_ddb(cpu_env, tmp_r1, addr); - break; - case 0x1e: /* MADB R1,R3,D2(X2,B2) [RXF] */ - /* for RXF insns, r1 is R3 and r1b is R1 */ - tmp32 = tcg_const_i32(r1b); - potential_page_fault(s); - gen_helper_madb(cpu_env, tmp32, addr, tmp_r1); - tcg_temp_free_i32(tmp32); - break; - default: - LOG_DISAS("illegal ed operation 0x%x\n", op); - gen_illegal_opcode(env, s, 3); - return; - } - tcg_temp_free_i32(tmp_r1); - tcg_temp_free_i64(addr); + /* R1+1 = IN & ~(found bit). Note that we may attempt to shift this + value by 64, which is undefined. But since the shift is 64 iff the + input is zero, we still get the correct result after and'ing. */ + tcg_gen_movi_i64(o->out2, 0x8000000000000000ull); + tcg_gen_shr_i64(o->out2, o->out2, o->out); + tcg_gen_andc_i64(o->out2, cc_dst, o->out2); + return NO_EXIT; } -static void disas_a5(CPUS390XState *env, DisasContext *s, int op, int r1, - int i2) +static ExitStatus op_icm(DisasContext *s, DisasOps *o) { - TCGv_i64 tmp, tmp2; - TCGv_i32 tmp32; - LOG_DISAS("disas_a5: op 0x%x r1 %d i2 0x%x\n", op, r1, i2); - switch (op) { - case 0x0: /* IIHH R1,I2 [RI] */ - tmp = tcg_const_i64(i2); - tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 48, 16); - tcg_temp_free_i64(tmp); - break; - case 0x1: /* IIHL R1,I2 [RI] */ - tmp = tcg_const_i64(i2); - tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 32, 16); - tcg_temp_free_i64(tmp); - break; - case 0x2: /* IILH R1,I2 [RI] */ - tmp = tcg_const_i64(i2); - tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 16, 16); - tcg_temp_free_i64(tmp); - break; - case 0x3: /* IILL R1,I2 [RI] */ - tmp = tcg_const_i64(i2); - tcg_gen_deposit_i64(regs[r1], regs[r1], tmp, 0, 16); - tcg_temp_free_i64(tmp); - break; - case 0x4: /* NIHH R1,I2 [RI] */ - case 0x8: /* OIHH R1,I2 [RI] */ - tmp = load_reg(r1); - tmp32 = tcg_temp_new_i32(); - switch (op) { - case 0x4: - tmp2 = tcg_const_i64((((uint64_t)i2) << 48) - | 0x0000ffffffffffffULL); - tcg_gen_and_i64(tmp, tmp, tmp2); - break; - case 0x8: - tmp2 = tcg_const_i64(((uint64_t)i2) << 48); - tcg_gen_or_i64(tmp, tmp, tmp2); - break; - default: - tcg_abort(); - } - store_reg(r1, tmp); - tcg_gen_shri_i64(tmp2, tmp, 48); - tcg_gen_trunc_i64_i32(tmp32, tmp2); - set_cc_nz_u32(s, tmp32); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32); - tcg_temp_free_i64(tmp); - break; - case 0x5: /* NIHL R1,I2 [RI] */ - case 0x9: /* OIHL R1,I2 [RI] */ - tmp = load_reg(r1); - tmp32 = tcg_temp_new_i32(); - switch (op) { - case 0x5: - tmp2 = tcg_const_i64((((uint64_t)i2) << 32) - | 0xffff0000ffffffffULL); - tcg_gen_and_i64(tmp, tmp, tmp2); - break; - case 0x9: - tmp2 = tcg_const_i64(((uint64_t)i2) << 32); - tcg_gen_or_i64(tmp, tmp, tmp2); - break; - default: - tcg_abort(); - } - store_reg(r1, tmp); - tcg_gen_shri_i64(tmp2, tmp, 32); - tcg_gen_trunc_i64_i32(tmp32, tmp2); - tcg_gen_andi_i32(tmp32, tmp32, 0xffff); - set_cc_nz_u32(s, tmp32); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32); - tcg_temp_free_i64(tmp); - break; - case 0x6: /* NILH R1,I2 [RI] */ - case 0xa: /* OILH R1,I2 [RI] */ - tmp = load_reg(r1); - tmp32 = tcg_temp_new_i32(); - switch (op) { - case 0x6: - tmp2 = tcg_const_i64((((uint64_t)i2) << 16) - | 0xffffffff0000ffffULL); - tcg_gen_and_i64(tmp, tmp, tmp2); - break; - case 0xa: - tmp2 = tcg_const_i64(((uint64_t)i2) << 16); - tcg_gen_or_i64(tmp, tmp, tmp2); - break; - default: - tcg_abort(); - } - store_reg(r1, tmp); - tcg_gen_shri_i64(tmp, tmp, 16); - tcg_gen_trunc_i64_i32(tmp32, tmp); - tcg_gen_andi_i32(tmp32, tmp32, 0xffff); - set_cc_nz_u32(s, tmp32); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32); - tcg_temp_free_i64(tmp); - break; - case 0x7: /* NILL R1,I2 [RI] */ - case 0xb: /* OILL R1,I2 [RI] */ - tmp = load_reg(r1); - tmp32 = tcg_temp_new_i32(); - switch (op) { - case 0x7: - tmp2 = tcg_const_i64(i2 | 0xffffffffffff0000ULL); - tcg_gen_and_i64(tmp, tmp, tmp2); - break; - case 0xb: - tmp2 = tcg_const_i64(i2); - tcg_gen_or_i64(tmp, tmp, tmp2); - break; - default: - tcg_abort(); - } - store_reg(r1, tmp); - tcg_gen_trunc_i64_i32(tmp32, tmp); - tcg_gen_andi_i32(tmp32, tmp32, 0xffff); - set_cc_nz_u32(s, tmp32); /* signedness should not matter here */ - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32); - tcg_temp_free_i64(tmp); - break; - case 0xc: /* LLIHH R1,I2 [RI] */ - tmp = tcg_const_i64( ((uint64_t)i2) << 48 ); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - break; - case 0xd: /* LLIHL R1,I2 [RI] */ - tmp = tcg_const_i64( ((uint64_t)i2) << 32 ); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - break; - case 0xe: /* LLILH R1,I2 [RI] */ - tmp = tcg_const_i64( ((uint64_t)i2) << 16 ); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - break; - case 0xf: /* LLILL R1,I2 [RI] */ - tmp = tcg_const_i64(i2); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - break; - default: - LOG_DISAS("illegal a5 operation 0x%x\n", op); - gen_illegal_opcode(env, s, 2); - return; - } -} + int m3 = get_field(s->fields, m3); + int pos, len, base = s->insn->data; + TCGv_i64 tmp = tcg_temp_new_i64(); + uint64_t ccm; -static void disas_a7(CPUS390XState *env, DisasContext *s, int op, int r1, - int i2) -{ - TCGv_i64 tmp, tmp2; - TCGv_i32 tmp32_1, tmp32_2, tmp32_3; - int l1; + switch (m3) { + case 0xf: + /* Effectively a 32-bit load. */ + tcg_gen_qemu_ld32u(tmp, o->in2, get_mem_index(s)); + len = 32; + goto one_insert; - LOG_DISAS("disas_a7: op 0x%x r1 %d i2 0x%x\n", op, r1, i2); - switch (op) { - case 0x0: /* TMLH or TMH R1,I2 [RI] */ - case 0x1: /* TMLL or TML R1,I2 [RI] */ - case 0x2: /* TMHH R1,I2 [RI] */ - case 0x3: /* TMHL R1,I2 [RI] */ - tmp = load_reg(r1); - tmp2 = tcg_const_i64((uint16_t)i2); - switch (op) { - case 0x0: - tcg_gen_shri_i64(tmp, tmp, 16); - break; - case 0x1: - break; - case 0x2: - tcg_gen_shri_i64(tmp, tmp, 48); - break; - case 0x3: - tcg_gen_shri_i64(tmp, tmp, 32); - break; - } - tcg_gen_andi_i64(tmp, tmp, 0xffff); - cmp_64(s, tmp, tmp2, CC_OP_TM_64); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x4: /* brc m1, i2 */ - gen_brc(r1, s, i2 * 2LL); - return; - case 0x5: /* BRAS R1,I2 [RI] */ - tmp = tcg_const_i64(pc_to_link_info(s, s->pc + 4)); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - gen_goto_tb(s, 0, s->pc + i2 * 2LL); - s->is_jmp = DISAS_TB_JUMP; - break; - case 0x6: /* BRCT R1,I2 [RI] */ - tmp32_1 = load_reg32(r1); - tcg_gen_subi_i32(tmp32_1, tmp32_1, 1); - store_reg32(r1, tmp32_1); - gen_update_cc_op(s); - l1 = gen_new_label(); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp32_1, 0, l1); - gen_goto_tb(s, 0, s->pc + (i2 * 2LL)); - gen_set_label(l1); - gen_goto_tb(s, 1, s->pc + 4); - s->is_jmp = DISAS_TB_JUMP; - tcg_temp_free_i32(tmp32_1); - break; - case 0x7: /* BRCTG R1,I2 [RI] */ - tmp = load_reg(r1); - tcg_gen_subi_i64(tmp, tmp, 1); - store_reg(r1, tmp); - gen_update_cc_op(s); - l1 = gen_new_label(); - tcg_gen_brcondi_i64(TCG_COND_EQ, tmp, 0, l1); - gen_goto_tb(s, 0, s->pc + (i2 * 2LL)); - gen_set_label(l1); - gen_goto_tb(s, 1, s->pc + 4); - s->is_jmp = DISAS_TB_JUMP; - tcg_temp_free_i64(tmp); - break; - case 0x8: /* lhi r1, i2 */ - tmp32_1 = tcg_const_i32(i2); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x9: /* lghi r1, i2 */ - tmp = tcg_const_i64(i2); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - break; - case 0xa: /* AHI R1,I2 [RI] */ - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_temp_new_i32(); - tmp32_3 = tcg_const_i32(i2); - - if (i2 < 0) { - tcg_gen_subi_i32(tmp32_2, tmp32_1, -i2); - } else { - tcg_gen_add_i32(tmp32_2, tmp32_1, tmp32_3); - } - - store_reg32(r1, tmp32_2); - set_cc_add32(s, tmp32_1, tmp32_3, tmp32_2); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0xb: /* aghi r1, i2 */ - tmp = load_reg(r1); - tmp2 = tcg_const_i64(i2); - - if (i2 < 0) { - tcg_gen_subi_i64(regs[r1], tmp, -i2); - } else { - tcg_gen_add_i64(regs[r1], tmp, tmp2); - } - set_cc_add64(s, tmp, tmp2, regs[r1]); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0xc: /* MHI R1,I2 [RI] */ - tmp32_1 = load_reg32(r1); - tcg_gen_muli_i32(tmp32_1, tmp32_1, i2); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0xd: /* MGHI R1,I2 [RI] */ - tmp = load_reg(r1); - tcg_gen_muli_i64(tmp, tmp, i2); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - break; - case 0xe: /* CHI R1,I2 [RI] */ - tmp32_1 = load_reg32(r1); - cmp_s32c(s, tmp32_1, i2); - tcg_temp_free_i32(tmp32_1); - break; - case 0xf: /* CGHI R1,I2 [RI] */ - tmp = load_reg(r1); - cmp_s64c(s, tmp, i2); - tcg_temp_free_i64(tmp); - break; - default: - LOG_DISAS("illegal a7 operation 0x%x\n", op); - gen_illegal_opcode(env, s, 2); - return; - } -} - -static void disas_b2(CPUS390XState *env, DisasContext *s, int op, - uint32_t insn) -{ - TCGv_i64 tmp, tmp2, tmp3; - TCGv_i32 tmp32_1, tmp32_2, tmp32_3; - int r1, r2; - int ilc = 2; -#ifndef CONFIG_USER_ONLY - int r3, d2, b2; -#endif - - r1 = (insn >> 4) & 0xf; - r2 = insn & 0xf; - - LOG_DISAS("disas_b2: op 0x%x r1 %d r2 %d\n", op, r1, r2); - - switch (op) { - case 0x22: /* IPM R1 [RRE] */ - tmp32_1 = tcg_const_i32(r1); - gen_op_calc_cc(s); - gen_helper_ipm(cpu_env, cc_op, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x41: /* CKSM R1,R2 [RRE] */ - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r2); - potential_page_fault(s); - gen_helper_cksm(cpu_env, tmp32_1, tmp32_2); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - gen_op_movi_cc(s, 0); - break; - case 0x4e: /* SAR R1,R2 [RRE] */ - tmp32_1 = load_reg32(r2); - tcg_gen_st_i32(tmp32_1, cpu_env, offsetof(CPUS390XState, aregs[r1])); - tcg_temp_free_i32(tmp32_1); - break; - case 0x4f: /* EAR R1,R2 [RRE] */ - tmp32_1 = tcg_temp_new_i32(); - tcg_gen_ld_i32(tmp32_1, cpu_env, offsetof(CPUS390XState, aregs[r2])); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x52: /* MSR R1,R2 [RRE] */ - tmp32_1 = load_reg32(r1); - tmp32_2 = load_reg32(r2); - tcg_gen_mul_i32(tmp32_1, tmp32_1, tmp32_2); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x54: /* MVPG R1,R2 [RRE] */ - tmp = load_reg(0); - tmp2 = load_reg(r1); - tmp3 = load_reg(r2); - potential_page_fault(s); - gen_helper_mvpg(cpu_env, tmp, tmp2, tmp3); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - /* XXX check CCO bit and set CC accordingly */ - gen_op_movi_cc(s, 0); - break; - case 0x55: /* MVST R1,R2 [RRE] */ - tmp32_1 = load_reg32(0); - tmp32_2 = tcg_const_i32(r1); - tmp32_3 = tcg_const_i32(r2); - potential_page_fault(s); - gen_helper_mvst(cpu_env, tmp32_1, tmp32_2, tmp32_3); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - gen_op_movi_cc(s, 1); - break; - case 0x5d: /* CLST R1,R2 [RRE] */ - tmp32_1 = load_reg32(0); - tmp32_2 = tcg_const_i32(r1); - tmp32_3 = tcg_const_i32(r2); - potential_page_fault(s); - gen_helper_clst(cc_op, cpu_env, tmp32_1, tmp32_2, tmp32_3); - set_cc_static(s); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0x5e: /* SRST R1,R2 [RRE] */ - tmp32_1 = load_reg32(0); - tmp32_2 = tcg_const_i32(r1); - tmp32_3 = tcg_const_i32(r2); - potential_page_fault(s); - gen_helper_srst(cc_op, cpu_env, tmp32_1, tmp32_2, tmp32_3); - set_cc_static(s); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - -#ifndef CONFIG_USER_ONLY - case 0x02: /* STIDP D2(B2) [S] */ - /* Store CPU ID */ - check_privileged(env, s, ilc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - potential_page_fault(s); - gen_helper_stidp(cpu_env, tmp); - tcg_temp_free_i64(tmp); - break; - case 0x04: /* SCK D2(B2) [S] */ - /* Set Clock */ - check_privileged(env, s, ilc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - potential_page_fault(s); - gen_helper_sck(cc_op, tmp); - set_cc_static(s); - tcg_temp_free_i64(tmp); - break; - case 0x05: /* STCK D2(B2) [S] */ - /* Store Clock */ - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - potential_page_fault(s); - gen_helper_stck(cc_op, cpu_env, tmp); - set_cc_static(s); - tcg_temp_free_i64(tmp); - break; - case 0x06: /* SCKC D2(B2) [S] */ - /* Set Clock Comparator */ - check_privileged(env, s, ilc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - potential_page_fault(s); - gen_helper_sckc(cpu_env, tmp); - tcg_temp_free_i64(tmp); - break; - case 0x07: /* STCKC D2(B2) [S] */ - /* Store Clock Comparator */ - check_privileged(env, s, ilc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - potential_page_fault(s); - gen_helper_stckc(cpu_env, tmp); - tcg_temp_free_i64(tmp); - break; - case 0x08: /* SPT D2(B2) [S] */ - /* Set CPU Timer */ - check_privileged(env, s, ilc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - potential_page_fault(s); - gen_helper_spt(cpu_env, tmp); - tcg_temp_free_i64(tmp); - break; - case 0x09: /* STPT D2(B2) [S] */ - /* Store CPU Timer */ - check_privileged(env, s, ilc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - potential_page_fault(s); - gen_helper_stpt(cpu_env, tmp); - tcg_temp_free_i64(tmp); - break; - case 0x0a: /* SPKA D2(B2) [S] */ - /* Set PSW Key from Address */ - check_privileged(env, s, ilc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp2 = tcg_temp_new_i64(); - tcg_gen_andi_i64(tmp2, psw_mask, ~PSW_MASK_KEY); - tcg_gen_shli_i64(tmp, tmp, PSW_SHIFT_KEY - 4); - tcg_gen_or_i64(psw_mask, tmp2, tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp); - break; - case 0x0d: /* PTLB [S] */ - /* Purge TLB */ - check_privileged(env, s, ilc); - gen_helper_ptlb(cpu_env); - break; - case 0x10: /* SPX D2(B2) [S] */ - /* Set Prefix Register */ - check_privileged(env, s, ilc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - potential_page_fault(s); - gen_helper_spx(cpu_env, tmp); - tcg_temp_free_i64(tmp); - break; - case 0x11: /* STPX D2(B2) [S] */ - /* Store Prefix */ - check_privileged(env, s, ilc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp2 = tcg_temp_new_i64(); - tcg_gen_ld_i64(tmp2, cpu_env, offsetof(CPUS390XState, psa)); - tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s)); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x12: /* STAP D2(B2) [S] */ - /* Store CPU Address */ - check_privileged(env, s, ilc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_temp_new_i32(); - tcg_gen_ld_i32(tmp32_1, cpu_env, offsetof(CPUS390XState, cpu_num)); - tcg_gen_extu_i32_i64(tmp2, tmp32_1); - tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s)); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - break; - case 0x21: /* IPTE R1,R2 [RRE] */ - /* Invalidate PTE */ - check_privileged(env, s, ilc); - r1 = (insn >> 4) & 0xf; - r2 = insn & 0xf; - tmp = load_reg(r1); - tmp2 = load_reg(r2); - gen_helper_ipte(cpu_env, tmp, tmp2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x29: /* ISKE R1,R2 [RRE] */ - /* Insert Storage Key Extended */ - check_privileged(env, s, ilc); - r1 = (insn >> 4) & 0xf; - r2 = insn & 0xf; - tmp = load_reg(r2); - tmp2 = tcg_temp_new_i64(); - gen_helper_iske(tmp2, cpu_env, tmp); - store_reg(r1, tmp2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x2a: /* RRBE R1,R2 [RRE] */ - /* Set Storage Key Extended */ - check_privileged(env, s, ilc); - r1 = (insn >> 4) & 0xf; - r2 = insn & 0xf; - tmp32_1 = load_reg32(r1); - tmp = load_reg(r2); - gen_helper_rrbe(cc_op, cpu_env, tmp32_1, tmp); - set_cc_static(s); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp); - break; - case 0x2b: /* SSKE R1,R2 [RRE] */ - /* Set Storage Key Extended */ - check_privileged(env, s, ilc); - r1 = (insn >> 4) & 0xf; - r2 = insn & 0xf; - tmp32_1 = load_reg32(r1); - tmp = load_reg(r2); - gen_helper_sske(cpu_env, tmp32_1, tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp); - break; - case 0x34: /* STCH ? */ - /* Store Subchannel */ - check_privileged(env, s, ilc); - gen_op_movi_cc(s, 3); - break; - case 0x46: /* STURA R1,R2 [RRE] */ - /* Store Using Real Address */ - check_privileged(env, s, ilc); - r1 = (insn >> 4) & 0xf; - r2 = insn & 0xf; - tmp32_1 = load_reg32(r1); - tmp = load_reg(r2); - potential_page_fault(s); - gen_helper_stura(cpu_env, tmp, tmp32_1); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp); - break; - case 0x50: /* CSP R1,R2 [RRE] */ - /* Compare And Swap And Purge */ - check_privileged(env, s, ilc); - r1 = (insn >> 4) & 0xf; - r2 = insn & 0xf; - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r2); - gen_helper_csp(cc_op, cpu_env, tmp32_1, tmp32_2); - set_cc_static(s); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x5f: /* CHSC ? */ - /* Channel Subsystem Call */ - check_privileged(env, s, ilc); - gen_op_movi_cc(s, 3); - break; - case 0x78: /* STCKE D2(B2) [S] */ - /* Store Clock Extended */ - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - potential_page_fault(s); - gen_helper_stcke(cc_op, cpu_env, tmp); - set_cc_static(s); - tcg_temp_free_i64(tmp); - break; - case 0x79: /* SACF D2(B2) [S] */ - /* Store Clock Extended */ - check_privileged(env, s, ilc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - potential_page_fault(s); - gen_helper_sacf(cpu_env, tmp); - tcg_temp_free_i64(tmp); - /* addressing mode has changed, so end the block */ - s->pc += ilc * 2; - update_psw_addr(s); - s->is_jmp = DISAS_EXCP; - break; - case 0x7d: /* STSI D2,(B2) [S] */ - check_privileged(env, s, ilc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp32_1 = load_reg32(0); - tmp32_2 = load_reg32(1); - potential_page_fault(s); - gen_helper_stsi(cc_op, cpu_env, tmp, tmp32_1, tmp32_2); - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x9d: /* LFPC D2(B2) [S] */ - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_1, tmp2); - tcg_gen_st_i32(tmp32_1, cpu_env, offsetof(CPUS390XState, fpc)); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - break; - case 0xb1: /* STFL D2(B2) [S] */ - /* Store Facility List (CPU features) at 200 */ - check_privileged(env, s, ilc); - tmp2 = tcg_const_i64(0xc0000000); - tmp = tcg_const_i64(200); - tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s)); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp); - break; - case 0xb2: /* LPSWE D2(B2) [S] */ - /* Load PSW Extended */ - check_privileged(env, s, ilc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp2 = tcg_temp_new_i64(); - tmp3 = tcg_temp_new_i64(); - tcg_gen_qemu_ld64(tmp2, tmp, get_mem_index(s)); - tcg_gen_addi_i64(tmp, tmp, 8); - tcg_gen_qemu_ld64(tmp3, tmp, get_mem_index(s)); - gen_helper_load_psw(cpu_env, tmp2, tmp3); - /* we need to keep cc_op intact */ - s->is_jmp = DISAS_JUMP; - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x20: /* SERVC R1,R2 [RRE] */ - /* SCLP Service call (PV hypercall) */ - check_privileged(env, s, ilc); - potential_page_fault(s); - tmp32_1 = load_reg32(r2); - tmp = load_reg(r1); - gen_helper_servc(cc_op, cpu_env, tmp32_1, tmp); - set_cc_static(s); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp); - break; -#endif - default: - LOG_DISAS("illegal b2 operation 0x%x\n", op); - gen_illegal_opcode(env, s, ilc); - break; - } -} - -static void disas_b3(CPUS390XState *env, DisasContext *s, int op, int m3, - int r1, int r2) -{ - TCGv_i64 tmp; - TCGv_i32 tmp32_1, tmp32_2, tmp32_3; - LOG_DISAS("disas_b3: op 0x%x m3 0x%x r1 %d r2 %d\n", op, m3, r1, r2); -#define FP_HELPER(i) \ - tmp32_1 = tcg_const_i32(r1); \ - tmp32_2 = tcg_const_i32(r2); \ - gen_helper_ ## i(cpu_env, tmp32_1, tmp32_2); \ - tcg_temp_free_i32(tmp32_1); \ - tcg_temp_free_i32(tmp32_2); - -#define FP_HELPER_CC(i) \ - tmp32_1 = tcg_const_i32(r1); \ - tmp32_2 = tcg_const_i32(r2); \ - gen_helper_ ## i(cc_op, cpu_env, tmp32_1, tmp32_2); \ - set_cc_static(s); \ - tcg_temp_free_i32(tmp32_1); \ - tcg_temp_free_i32(tmp32_2); - - switch (op) { - case 0x0: /* LPEBR R1,R2 [RRE] */ - FP_HELPER_CC(lpebr); - break; - case 0x2: /* LTEBR R1,R2 [RRE] */ - FP_HELPER_CC(ltebr); - break; - case 0x3: /* LCEBR R1,R2 [RRE] */ - FP_HELPER_CC(lcebr); - break; - case 0x4: /* LDEBR R1,R2 [RRE] */ - FP_HELPER(ldebr); - break; - case 0x5: /* LXDBR R1,R2 [RRE] */ - FP_HELPER(lxdbr); - break; - case 0x9: /* CEBR R1,R2 [RRE] */ - FP_HELPER_CC(cebr); - break; - case 0xa: /* AEBR R1,R2 [RRE] */ - FP_HELPER_CC(aebr); - break; - case 0xb: /* SEBR R1,R2 [RRE] */ - FP_HELPER_CC(sebr); - break; - case 0xd: /* DEBR R1,R2 [RRE] */ - FP_HELPER(debr); - break; - case 0x10: /* LPDBR R1,R2 [RRE] */ - FP_HELPER_CC(lpdbr); - break; - case 0x12: /* LTDBR R1,R2 [RRE] */ - FP_HELPER_CC(ltdbr); - break; - case 0x13: /* LCDBR R1,R2 [RRE] */ - FP_HELPER_CC(lcdbr); - break; - case 0x15: /* SQBDR R1,R2 [RRE] */ - FP_HELPER(sqdbr); - break; - case 0x17: /* MEEBR R1,R2 [RRE] */ - FP_HELPER(meebr); - break; - case 0x19: /* CDBR R1,R2 [RRE] */ - FP_HELPER_CC(cdbr); - break; - case 0x1a: /* ADBR R1,R2 [RRE] */ - FP_HELPER_CC(adbr); - break; - case 0x1b: /* SDBR R1,R2 [RRE] */ - FP_HELPER_CC(sdbr); - break; - case 0x1c: /* MDBR R1,R2 [RRE] */ - FP_HELPER(mdbr); - break; - case 0x1d: /* DDBR R1,R2 [RRE] */ - FP_HELPER(ddbr); - break; - case 0xe: /* MAEBR R1,R3,R2 [RRF] */ - case 0x1e: /* MADBR R1,R3,R2 [RRF] */ - case 0x1f: /* MSDBR R1,R3,R2 [RRF] */ - /* for RRF insns, m3 is R1, r1 is R3, and r2 is R2 */ - tmp32_1 = tcg_const_i32(m3); - tmp32_2 = tcg_const_i32(r2); - tmp32_3 = tcg_const_i32(r1); - switch (op) { - case 0xe: - gen_helper_maebr(cpu_env, tmp32_1, tmp32_3, tmp32_2); - break; - case 0x1e: - gen_helper_madbr(cpu_env, tmp32_1, tmp32_3, tmp32_2); - break; - case 0x1f: - gen_helper_msdbr(cpu_env, tmp32_1, tmp32_3, tmp32_2); - break; - default: - tcg_abort(); - } - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0x40: /* LPXBR R1,R2 [RRE] */ - FP_HELPER_CC(lpxbr); - break; - case 0x42: /* LTXBR R1,R2 [RRE] */ - FP_HELPER_CC(ltxbr); - break; - case 0x43: /* LCXBR R1,R2 [RRE] */ - FP_HELPER_CC(lcxbr); - break; - case 0x44: /* LEDBR R1,R2 [RRE] */ - FP_HELPER(ledbr); - break; - case 0x45: /* LDXBR R1,R2 [RRE] */ - FP_HELPER(ldxbr); - break; - case 0x46: /* LEXBR R1,R2 [RRE] */ - FP_HELPER(lexbr); - break; - case 0x49: /* CXBR R1,R2 [RRE] */ - FP_HELPER_CC(cxbr); - break; - case 0x4a: /* AXBR R1,R2 [RRE] */ - FP_HELPER_CC(axbr); - break; - case 0x4b: /* SXBR R1,R2 [RRE] */ - FP_HELPER_CC(sxbr); - break; - case 0x4c: /* MXBR R1,R2 [RRE] */ - FP_HELPER(mxbr); - break; - case 0x4d: /* DXBR R1,R2 [RRE] */ - FP_HELPER(dxbr); - break; - case 0x65: /* LXR R1,R2 [RRE] */ - tmp = load_freg(r2); - store_freg(r1, tmp); - tcg_temp_free_i64(tmp); - tmp = load_freg(r2 + 2); - store_freg(r1 + 2, tmp); - tcg_temp_free_i64(tmp); - break; - case 0x74: /* LZER R1 [RRE] */ - tmp32_1 = tcg_const_i32(r1); - gen_helper_lzer(cpu_env, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x75: /* LZDR R1 [RRE] */ - tmp32_1 = tcg_const_i32(r1); - gen_helper_lzdr(cpu_env, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x76: /* LZXR R1 [RRE] */ - tmp32_1 = tcg_const_i32(r1); - gen_helper_lzxr(cpu_env, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x84: /* SFPC R1 [RRE] */ - tmp32_1 = load_reg32(r1); - tcg_gen_st_i32(tmp32_1, cpu_env, offsetof(CPUS390XState, fpc)); - tcg_temp_free_i32(tmp32_1); - break; - case 0x8c: /* EFPC R1 [RRE] */ - tmp32_1 = tcg_temp_new_i32(); - tcg_gen_ld_i32(tmp32_1, cpu_env, offsetof(CPUS390XState, fpc)); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x94: /* CEFBR R1,R2 [RRE] */ - case 0x95: /* CDFBR R1,R2 [RRE] */ - case 0x96: /* CXFBR R1,R2 [RRE] */ - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = load_reg32(r2); - switch (op) { - case 0x94: - gen_helper_cefbr(cpu_env, tmp32_1, tmp32_2); - break; - case 0x95: - gen_helper_cdfbr(cpu_env, tmp32_1, tmp32_2); - break; - case 0x96: - gen_helper_cxfbr(cpu_env, tmp32_1, tmp32_2); - break; - default: - tcg_abort(); - } - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x98: /* CFEBR R1,R2 [RRE] */ - case 0x99: /* CFDBR R1,R2 [RRE] */ - case 0x9a: /* CFXBR R1,R2 [RRE] */ - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r2); - tmp32_3 = tcg_const_i32(m3); - switch (op) { - case 0x98: - gen_helper_cfebr(cc_op, cpu_env, tmp32_1, tmp32_2, tmp32_3); - break; - case 0x99: - gen_helper_cfdbr(cc_op, cpu_env, tmp32_1, tmp32_2, tmp32_3); - break; - case 0x9a: - gen_helper_cfxbr(cc_op, cpu_env, tmp32_1, tmp32_2, tmp32_3); - break; - default: - tcg_abort(); - } - set_cc_static(s); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0xa4: /* CEGBR R1,R2 [RRE] */ - case 0xa5: /* CDGBR R1,R2 [RRE] */ - tmp32_1 = tcg_const_i32(r1); - tmp = load_reg(r2); - switch (op) { - case 0xa4: - gen_helper_cegbr(cpu_env, tmp32_1, tmp); - break; - case 0xa5: - gen_helper_cdgbr(cpu_env, tmp32_1, tmp); - break; - default: - tcg_abort(); - } - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp); - break; - case 0xa6: /* CXGBR R1,R2 [RRE] */ - tmp32_1 = tcg_const_i32(r1); - tmp = load_reg(r2); - gen_helper_cxgbr(cpu_env, tmp32_1, tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp); - break; - case 0xa8: /* CGEBR R1,R2 [RRE] */ - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r2); - tmp32_3 = tcg_const_i32(m3); - gen_helper_cgebr(cc_op, cpu_env, tmp32_1, tmp32_2, tmp32_3); - set_cc_static(s); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0xa9: /* CGDBR R1,R2 [RRE] */ - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r2); - tmp32_3 = tcg_const_i32(m3); - gen_helper_cgdbr(cc_op, cpu_env, tmp32_1, tmp32_2, tmp32_3); - set_cc_static(s); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0xaa: /* CGXBR R1,R2 [RRE] */ - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r2); - tmp32_3 = tcg_const_i32(m3); - gen_helper_cgxbr(cc_op, cpu_env, tmp32_1, tmp32_2, tmp32_3); - set_cc_static(s); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - default: - LOG_DISAS("illegal b3 operation 0x%x\n", op); - gen_illegal_opcode(env, s, 2); - break; - } - -#undef FP_HELPER_CC -#undef FP_HELPER -} - -static void disas_b9(CPUS390XState *env, DisasContext *s, int op, int r1, - int r2) -{ - TCGv_i64 tmp, tmp2, tmp3; - TCGv_i32 tmp32_1, tmp32_2, tmp32_3; - - LOG_DISAS("disas_b9: op 0x%x r1 %d r2 %d\n", op, r1, r2); - switch (op) { - case 0x0: /* LPGR R1,R2 [RRE] */ - case 0x1: /* LNGR R1,R2 [RRE] */ - case 0x2: /* LTGR R1,R2 [RRE] */ - case 0x3: /* LCGR R1,R2 [RRE] */ - case 0x10: /* LPGFR R1,R2 [RRE] */ - case 0x11: /* LNFGR R1,R2 [RRE] */ - case 0x12: /* LTGFR R1,R2 [RRE] */ - case 0x13: /* LCGFR R1,R2 [RRE] */ - if (op & 0x10) { - tmp = load_reg32_i64(r2); - } else { - tmp = load_reg(r2); - } - switch (op & 0xf) { - case 0x0: /* LP?GR */ - set_cc_abs64(s, tmp); - gen_helper_abs_i64(tmp, tmp); - store_reg(r1, tmp); - break; - case 0x1: /* LN?GR */ - set_cc_nabs64(s, tmp); - gen_helper_nabs_i64(tmp, tmp); - store_reg(r1, tmp); - break; - case 0x2: /* LT?GR */ - if (r1 != r2) { - store_reg(r1, tmp); - } - set_cc_s64(s, tmp); - break; - case 0x3: /* LC?GR */ - tcg_gen_neg_i64(regs[r1], tmp); - set_cc_comp64(s, regs[r1]); - break; - } - tcg_temp_free_i64(tmp); - break; - case 0x4: /* LGR R1,R2 [RRE] */ - store_reg(r1, regs[r2]); - break; - case 0x6: /* LGBR R1,R2 [RRE] */ - tmp2 = load_reg(r2); - tcg_gen_ext8s_i64(tmp2, tmp2); - store_reg(r1, tmp2); - tcg_temp_free_i64(tmp2); - break; - case 0x8: /* AGR R1,R2 [RRE] */ - case 0xa: /* ALGR R1,R2 [RRE] */ - tmp = load_reg(r1); - tmp2 = load_reg(r2); - tmp3 = tcg_temp_new_i64(); - tcg_gen_add_i64(tmp3, tmp, tmp2); - store_reg(r1, tmp3); - switch (op) { - case 0x8: - set_cc_add64(s, tmp, tmp2, tmp3); - break; - case 0xa: - set_cc_addu64(s, tmp, tmp2, tmp3); - break; - default: - tcg_abort(); - } - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x9: /* SGR R1,R2 [RRE] */ - case 0xb: /* SLGR R1,R2 [RRE] */ - case 0x1b: /* SLGFR R1,R2 [RRE] */ - case 0x19: /* SGFR R1,R2 [RRE] */ - tmp = load_reg(r1); - switch (op) { - case 0x1b: - tmp32_1 = load_reg32(r2); - tmp2 = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(tmp2, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x19: - tmp32_1 = load_reg32(r2); - tmp2 = tcg_temp_new_i64(); - tcg_gen_ext_i32_i64(tmp2, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - default: - tmp2 = load_reg(r2); - break; - } - tmp3 = tcg_temp_new_i64(); - tcg_gen_sub_i64(tmp3, tmp, tmp2); - store_reg(r1, tmp3); - switch (op) { - case 0x9: - case 0x19: - set_cc_sub64(s, tmp, tmp2, tmp3); - break; - case 0xb: - case 0x1b: - set_cc_subu64(s, tmp, tmp2, tmp3); - break; - default: - tcg_abort(); - } - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0xc: /* MSGR R1,R2 [RRE] */ - case 0x1c: /* MSGFR R1,R2 [RRE] */ - tmp = load_reg(r1); - tmp2 = load_reg(r2); - if (op == 0x1c) { - tcg_gen_ext32s_i64(tmp2, tmp2); - } - tcg_gen_mul_i64(tmp, tmp, tmp2); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0xd: /* DSGR R1,R2 [RRE] */ - case 0x1d: /* DSGFR R1,R2 [RRE] */ - tmp = load_reg(r1 + 1); - if (op == 0xd) { - tmp2 = load_reg(r2); - } else { - tmp32_1 = load_reg32(r2); - tmp2 = tcg_temp_new_i64(); - tcg_gen_ext_i32_i64(tmp2, tmp32_1); - tcg_temp_free_i32(tmp32_1); - } - tmp3 = tcg_temp_new_i64(); - tcg_gen_div_i64(tmp3, tmp, tmp2); - store_reg(r1 + 1, tmp3); - tcg_gen_rem_i64(tmp3, tmp, tmp2); - store_reg(r1, tmp3); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x14: /* LGFR R1,R2 [RRE] */ - tmp32_1 = load_reg32(r2); - tmp = tcg_temp_new_i64(); - tcg_gen_ext_i32_i64(tmp, tmp32_1); - store_reg(r1, tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp); - break; - case 0x16: /* LLGFR R1,R2 [RRE] */ - tmp32_1 = load_reg32(r2); - tmp = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(tmp, tmp32_1); - store_reg(r1, tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp); - break; - case 0x17: /* LLGTR R1,R2 [RRE] */ - tmp32_1 = load_reg32(r2); - tmp = tcg_temp_new_i64(); - tcg_gen_andi_i32(tmp32_1, tmp32_1, 0x7fffffffUL); - tcg_gen_extu_i32_i64(tmp, tmp32_1); - store_reg(r1, tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp); - break; - case 0x18: /* AGFR R1,R2 [RRE] */ - case 0x1a: /* ALGFR R1,R2 [RRE] */ - tmp32_1 = load_reg32(r2); - tmp2 = tcg_temp_new_i64(); - if (op == 0x18) { - tcg_gen_ext_i32_i64(tmp2, tmp32_1); - } else { - tcg_gen_extu_i32_i64(tmp2, tmp32_1); - } - tcg_temp_free_i32(tmp32_1); - tmp = load_reg(r1); - tmp3 = tcg_temp_new_i64(); - tcg_gen_add_i64(tmp3, tmp, tmp2); - store_reg(r1, tmp3); - if (op == 0x18) { - set_cc_add64(s, tmp, tmp2, tmp3); - } else { - set_cc_addu64(s, tmp, tmp2, tmp3); - } - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x0f: /* LRVGR R1,R2 [RRE] */ - tcg_gen_bswap64_i64(regs[r1], regs[r2]); - break; - case 0x1f: /* LRVR R1,R2 [RRE] */ - tmp32_1 = load_reg32(r2); - tcg_gen_bswap32_i32(tmp32_1, tmp32_1); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x20: /* CGR R1,R2 [RRE] */ - case 0x30: /* CGFR R1,R2 [RRE] */ - tmp2 = load_reg(r2); - if (op == 0x30) { - tcg_gen_ext32s_i64(tmp2, tmp2); - } - tmp = load_reg(r1); - cmp_s64(s, tmp, tmp2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x21: /* CLGR R1,R2 [RRE] */ - case 0x31: /* CLGFR R1,R2 [RRE] */ - tmp2 = load_reg(r2); - if (op == 0x31) { - tcg_gen_ext32u_i64(tmp2, tmp2); - } - tmp = load_reg(r1); - cmp_u64(s, tmp, tmp2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x26: /* LBR R1,R2 [RRE] */ - tmp32_1 = load_reg32(r2); - tcg_gen_ext8s_i32(tmp32_1, tmp32_1); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x27: /* LHR R1,R2 [RRE] */ - tmp32_1 = load_reg32(r2); - tcg_gen_ext16s_i32(tmp32_1, tmp32_1); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x80: /* NGR R1,R2 [RRE] */ - case 0x81: /* OGR R1,R2 [RRE] */ - case 0x82: /* XGR R1,R2 [RRE] */ - tmp = load_reg(r1); - tmp2 = load_reg(r2); - switch (op) { - case 0x80: - tcg_gen_and_i64(tmp, tmp, tmp2); - break; - case 0x81: - tcg_gen_or_i64(tmp, tmp, tmp2); - break; - case 0x82: - tcg_gen_xor_i64(tmp, tmp, tmp2); - break; - default: - tcg_abort(); - } - store_reg(r1, tmp); - set_cc_nz_u64(s, tmp); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x83: /* FLOGR R1,R2 [RRE] */ - tmp = load_reg(r2); - tmp32_1 = tcg_const_i32(r1); - gen_helper_flogr(cc_op, cpu_env, tmp32_1, tmp); - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - break; - case 0x84: /* LLGCR R1,R2 [RRE] */ - tmp = load_reg(r2); - tcg_gen_andi_i64(tmp, tmp, 0xff); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - break; - case 0x85: /* LLGHR R1,R2 [RRE] */ - tmp = load_reg(r2); - tcg_gen_andi_i64(tmp, tmp, 0xffff); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - break; - case 0x87: /* DLGR R1,R2 [RRE] */ - tmp32_1 = tcg_const_i32(r1); - tmp = load_reg(r2); - gen_helper_dlg(cpu_env, tmp32_1, tmp); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - break; - case 0x88: /* ALCGR R1,R2 [RRE] */ - tmp = load_reg(r1); - tmp2 = load_reg(r2); - tmp3 = tcg_temp_new_i64(); - gen_op_calc_cc(s); - tcg_gen_extu_i32_i64(tmp3, cc_op); - tcg_gen_shri_i64(tmp3, tmp3, 1); - tcg_gen_andi_i64(tmp3, tmp3, 1); - tcg_gen_add_i64(tmp3, tmp2, tmp3); - tcg_gen_add_i64(tmp3, tmp, tmp3); - store_reg(r1, tmp3); - set_cc_addu64(s, tmp, tmp2, tmp3); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x89: /* SLBGR R1,R2 [RRE] */ - tmp = load_reg(r1); - tmp2 = load_reg(r2); - tmp32_1 = tcg_const_i32(r1); - gen_op_calc_cc(s); - gen_helper_slbg(cc_op, cpu_env, cc_op, tmp32_1, tmp, tmp2); - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - break; - case 0x94: /* LLCR R1,R2 [RRE] */ - tmp32_1 = load_reg32(r2); - tcg_gen_andi_i32(tmp32_1, tmp32_1, 0xff); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x95: /* LLHR R1,R2 [RRE] */ - tmp32_1 = load_reg32(r2); - tcg_gen_andi_i32(tmp32_1, tmp32_1, 0xffff); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x96: /* MLR R1,R2 [RRE] */ - /* reg(r1, r1+1) = reg(r1+1) * reg(r2) */ - tmp2 = load_reg(r2); - tmp3 = load_reg((r1 + 1) & 15); - tcg_gen_ext32u_i64(tmp2, tmp2); - tcg_gen_ext32u_i64(tmp3, tmp3); - tcg_gen_mul_i64(tmp2, tmp2, tmp3); - store_reg32_i64((r1 + 1) & 15, tmp2); - tcg_gen_shri_i64(tmp2, tmp2, 32); - store_reg32_i64(r1, tmp2); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x97: /* DLR R1,R2 [RRE] */ - /* reg(r1) = reg(r1, r1+1) % reg(r2) */ - /* reg(r1+1) = reg(r1, r1+1) / reg(r2) */ - tmp = load_reg(r1); - tmp2 = load_reg(r2); - tmp3 = load_reg((r1 + 1) & 15); - tcg_gen_ext32u_i64(tmp2, tmp2); - tcg_gen_ext32u_i64(tmp3, tmp3); - tcg_gen_shli_i64(tmp, tmp, 32); - tcg_gen_or_i64(tmp, tmp, tmp3); - - tcg_gen_rem_i64(tmp3, tmp, tmp2); - tcg_gen_div_i64(tmp, tmp, tmp2); - store_reg32_i64((r1 + 1) & 15, tmp); - store_reg32_i64(r1, tmp3); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x98: /* ALCR R1,R2 [RRE] */ - tmp32_1 = load_reg32(r1); - tmp32_2 = load_reg32(r2); - tmp32_3 = tcg_temp_new_i32(); - /* XXX possible optimization point */ - gen_op_calc_cc(s); - gen_helper_addc_u32(tmp32_3, cc_op, tmp32_1, tmp32_2); - set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3); - store_reg32(r1, tmp32_3); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0x99: /* SLBR R1,R2 [RRE] */ - tmp32_1 = load_reg32(r2); - tmp32_2 = tcg_const_i32(r1); - gen_op_calc_cc(s); - gen_helper_slb(cc_op, cpu_env, cc_op, tmp32_2, tmp32_1); - set_cc_static(s); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - default: - LOG_DISAS("illegal b9 operation 0x%x\n", op); - gen_illegal_opcode(env, s, 2); - break; - } -} - -static void disas_c0(CPUS390XState *env, DisasContext *s, int op, int r1, int i2) -{ - TCGv_i64 tmp; - TCGv_i32 tmp32_1, tmp32_2; - uint64_t target = s->pc + i2 * 2LL; - int l1; - - LOG_DISAS("disas_c0: op 0x%x r1 %d i2 %d\n", op, r1, i2); - - switch (op) { - case 0: /* larl r1, i2 */ - tmp = tcg_const_i64(target); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - break; - case 0x1: /* LGFI R1,I2 [RIL] */ - tmp = tcg_const_i64((int64_t)i2); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - break; - case 0x4: /* BRCL M1,I2 [RIL] */ - /* m1 & (1 << (3 - cc)) */ - tmp32_1 = tcg_const_i32(3); - tmp32_2 = tcg_const_i32(1); - gen_op_calc_cc(s); - tcg_gen_sub_i32(tmp32_1, tmp32_1, cc_op); - tcg_gen_shl_i32(tmp32_2, tmp32_2, tmp32_1); - tcg_temp_free_i32(tmp32_1); - tmp32_1 = tcg_const_i32(r1); /* m1 == r1 */ - tcg_gen_and_i32(tmp32_1, tmp32_1, tmp32_2); - l1 = gen_new_label(); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp32_1, 0, l1); - gen_goto_tb(s, 0, target); - gen_set_label(l1); - gen_goto_tb(s, 1, s->pc + 6); - s->is_jmp = DISAS_TB_JUMP; - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x5: /* brasl r1, i2 */ - tmp = tcg_const_i64(pc_to_link_info(s, s->pc + 6)); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - gen_goto_tb(s, 0, target); - s->is_jmp = DISAS_TB_JUMP; - break; - case 0x7: /* XILF R1,I2 [RIL] */ - case 0xb: /* NILF R1,I2 [RIL] */ - case 0xd: /* OILF R1,I2 [RIL] */ - tmp32_1 = load_reg32(r1); - switch (op) { - case 0x7: - tcg_gen_xori_i32(tmp32_1, tmp32_1, (uint32_t)i2); - break; - case 0xb: - tcg_gen_andi_i32(tmp32_1, tmp32_1, (uint32_t)i2); - break; - case 0xd: - tcg_gen_ori_i32(tmp32_1, tmp32_1, (uint32_t)i2); - break; - default: - tcg_abort(); - } - store_reg32(r1, tmp32_1); - set_cc_nz_u32(s, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x9: /* IILF R1,I2 [RIL] */ - tmp32_1 = tcg_const_i32((uint32_t)i2); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0xa: /* NIHF R1,I2 [RIL] */ - tmp = load_reg(r1); - tmp32_1 = tcg_temp_new_i32(); - tcg_gen_andi_i64(tmp, tmp, (((uint64_t)((uint32_t)i2)) << 32) - | 0xffffffffULL); - store_reg(r1, tmp); - tcg_gen_shri_i64(tmp, tmp, 32); - tcg_gen_trunc_i64_i32(tmp32_1, tmp); - set_cc_nz_u32(s, tmp32_1); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - break; - case 0xe: /* LLIHF R1,I2 [RIL] */ - tmp = tcg_const_i64(((uint64_t)(uint32_t)i2) << 32); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - break; - case 0xf: /* LLILF R1,I2 [RIL] */ - tmp = tcg_const_i64((uint32_t)i2); - store_reg(r1, tmp); - tcg_temp_free_i64(tmp); - break; - default: - LOG_DISAS("illegal c0 operation 0x%x\n", op); - gen_illegal_opcode(env, s, 3); - break; - } -} - -static void disas_c2(CPUS390XState *env, DisasContext *s, int op, int r1, - int i2) -{ - TCGv_i64 tmp, tmp2, tmp3; - TCGv_i32 tmp32_1, tmp32_2, tmp32_3; - - switch (op) { - case 0x4: /* SLGFI R1,I2 [RIL] */ - case 0xa: /* ALGFI R1,I2 [RIL] */ - tmp = load_reg(r1); - tmp2 = tcg_const_i64((uint64_t)(uint32_t)i2); - tmp3 = tcg_temp_new_i64(); - switch (op) { - case 0x4: - tcg_gen_sub_i64(tmp3, tmp, tmp2); - set_cc_subu64(s, tmp, tmp2, tmp3); - break; - case 0xa: - tcg_gen_add_i64(tmp3, tmp, tmp2); - set_cc_addu64(s, tmp, tmp2, tmp3); - break; - default: - tcg_abort(); - } - store_reg(r1, tmp3); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x5: /* SLFI R1,I2 [RIL] */ - case 0xb: /* ALFI R1,I2 [RIL] */ - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_const_i32(i2); - tmp32_3 = tcg_temp_new_i32(); - switch (op) { - case 0x5: - tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2); - set_cc_subu32(s, tmp32_1, tmp32_2, tmp32_3); - break; - case 0xb: - tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2); - set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3); - break; - default: - tcg_abort(); - } - store_reg32(r1, tmp32_3); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0xc: /* CGFI R1,I2 [RIL] */ - tmp = load_reg(r1); - cmp_s64c(s, tmp, (int64_t)i2); - tcg_temp_free_i64(tmp); - break; - case 0xe: /* CLGFI R1,I2 [RIL] */ - tmp = load_reg(r1); - cmp_u64c(s, tmp, (uint64_t)(uint32_t)i2); - tcg_temp_free_i64(tmp); - break; - case 0xd: /* CFI R1,I2 [RIL] */ - tmp32_1 = load_reg32(r1); - cmp_s32c(s, tmp32_1, i2); - tcg_temp_free_i32(tmp32_1); - break; - case 0xf: /* CLFI R1,I2 [RIL] */ - tmp32_1 = load_reg32(r1); - cmp_u32c(s, tmp32_1, i2); - tcg_temp_free_i32(tmp32_1); - break; - default: - LOG_DISAS("illegal c2 operation 0x%x\n", op); - gen_illegal_opcode(env, s, 3); - break; - } -} - -static void gen_and_or_xor_i32(int opc, TCGv_i32 tmp, TCGv_i32 tmp2) -{ - switch (opc & 0xf) { - case 0x4: - tcg_gen_and_i32(tmp, tmp, tmp2); - break; + case 0xc: case 0x6: - tcg_gen_or_i32(tmp, tmp, tmp2); + case 0x3: + /* Effectively a 16-bit load. */ + tcg_gen_qemu_ld16u(tmp, o->in2, get_mem_index(s)); + len = 16; + goto one_insert; + + case 0x8: + case 0x4: + case 0x2: + case 0x1: + /* Effectively an 8-bit load. */ + tcg_gen_qemu_ld8u(tmp, o->in2, get_mem_index(s)); + len = 8; + goto one_insert; + + one_insert: + pos = base + ctz32(m3) * 8; + tcg_gen_deposit_i64(o->out, o->out, tmp, pos, len); + ccm = ((1ull << len) - 1) << pos; break; - case 0x7: - tcg_gen_xor_i32(tmp, tmp, tmp2); + + default: + /* This is going to be a sequence of loads and inserts. */ + pos = base + 32 - 8; + ccm = 0; + while (m3) { + if (m3 & 0x8) { + tcg_gen_qemu_ld8u(tmp, o->in2, get_mem_index(s)); + tcg_gen_addi_i64(o->in2, o->in2, 1); + tcg_gen_deposit_i64(o->out, o->out, tmp, pos, 8); + ccm |= 0xff << pos; + } + m3 = (m3 << 1) & 0xf; + pos -= 8; + } break; + } + + tcg_gen_movi_i64(tmp, ccm); + gen_op_update2_cc_i64(s, CC_OP_ICM, tmp, o->out); + tcg_temp_free_i64(tmp); + return NO_EXIT; +} + +static ExitStatus op_insi(DisasContext *s, DisasOps *o) +{ + int shift = s->insn->data & 0xff; + int size = s->insn->data >> 8; + tcg_gen_deposit_i64(o->out, o->in1, o->in2, shift, size); + return NO_EXIT; +} + +static ExitStatus op_ipm(DisasContext *s, DisasOps *o) +{ + TCGv_i64 t1; + + gen_op_calc_cc(s); + tcg_gen_andi_i64(o->out, o->out, ~0xff000000ull); + + t1 = tcg_temp_new_i64(); + tcg_gen_shli_i64(t1, psw_mask, 20); + tcg_gen_shri_i64(t1, t1, 36); + tcg_gen_or_i64(o->out, o->out, t1); + + tcg_gen_extu_i32_i64(t1, cc_op); + tcg_gen_shli_i64(t1, t1, 28); + tcg_gen_or_i64(o->out, o->out, t1); + tcg_temp_free_i64(t1); + return NO_EXIT; +} + +#ifndef CONFIG_USER_ONLY +static ExitStatus op_ipte(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + gen_helper_ipte(cpu_env, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_iske(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + gen_helper_iske(o->out, cpu_env, o->in2); + return NO_EXIT; +} +#endif + +static ExitStatus op_ldeb(DisasContext *s, DisasOps *o) +{ + gen_helper_ldeb(o->out, cpu_env, o->in2); + return NO_EXIT; +} + +static ExitStatus op_ledb(DisasContext *s, DisasOps *o) +{ + gen_helper_ledb(o->out, cpu_env, o->in2); + return NO_EXIT; +} + +static ExitStatus op_ldxb(DisasContext *s, DisasOps *o) +{ + gen_helper_ldxb(o->out, cpu_env, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_lexb(DisasContext *s, DisasOps *o) +{ + gen_helper_lexb(o->out, cpu_env, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_lxdb(DisasContext *s, DisasOps *o) +{ + gen_helper_lxdb(o->out, cpu_env, o->in2); + return_low128(o->out2); + return NO_EXIT; +} + +static ExitStatus op_lxeb(DisasContext *s, DisasOps *o) +{ + gen_helper_lxeb(o->out, cpu_env, o->in2); + return_low128(o->out2); + return NO_EXIT; +} + +static ExitStatus op_llgt(DisasContext *s, DisasOps *o) +{ + tcg_gen_andi_i64(o->out, o->in2, 0x7fffffff); + return NO_EXIT; +} + +static ExitStatus op_ld8s(DisasContext *s, DisasOps *o) +{ + tcg_gen_qemu_ld8s(o->out, o->in2, get_mem_index(s)); + return NO_EXIT; +} + +static ExitStatus op_ld8u(DisasContext *s, DisasOps *o) +{ + tcg_gen_qemu_ld8u(o->out, o->in2, get_mem_index(s)); + return NO_EXIT; +} + +static ExitStatus op_ld16s(DisasContext *s, DisasOps *o) +{ + tcg_gen_qemu_ld16s(o->out, o->in2, get_mem_index(s)); + return NO_EXIT; +} + +static ExitStatus op_ld16u(DisasContext *s, DisasOps *o) +{ + tcg_gen_qemu_ld16u(o->out, o->in2, get_mem_index(s)); + return NO_EXIT; +} + +static ExitStatus op_ld32s(DisasContext *s, DisasOps *o) +{ + tcg_gen_qemu_ld32s(o->out, o->in2, get_mem_index(s)); + return NO_EXIT; +} + +static ExitStatus op_ld32u(DisasContext *s, DisasOps *o) +{ + tcg_gen_qemu_ld32u(o->out, o->in2, get_mem_index(s)); + return NO_EXIT; +} + +static ExitStatus op_ld64(DisasContext *s, DisasOps *o) +{ + tcg_gen_qemu_ld64(o->out, o->in2, get_mem_index(s)); + return NO_EXIT; +} + +static ExitStatus op_loc(DisasContext *s, DisasOps *o) +{ + DisasCompare c; + + disas_jcc(s, &c, get_field(s->fields, m3)); + + if (c.is_64) { + tcg_gen_movcond_i64(c.cond, o->out, c.u.s64.a, c.u.s64.b, + o->in2, o->in1); + free_compare(&c); + } else { + TCGv_i32 t32 = tcg_temp_new_i32(); + TCGv_i64 t, z; + + tcg_gen_setcond_i32(c.cond, t32, c.u.s32.a, c.u.s32.b); + free_compare(&c); + + t = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(t, t32); + tcg_temp_free_i32(t32); + + z = tcg_const_i64(0); + tcg_gen_movcond_i64(TCG_COND_NE, o->out, t, z, o->in2, o->in1); + tcg_temp_free_i64(t); + tcg_temp_free_i64(z); + } + + return NO_EXIT; +} + +#ifndef CONFIG_USER_ONLY +static ExitStatus op_lctl(DisasContext *s, DisasOps *o) +{ + TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + check_privileged(s); + potential_page_fault(s); + gen_helper_lctl(cpu_env, r1, o->in2, r3); + tcg_temp_free_i32(r1); + tcg_temp_free_i32(r3); + return NO_EXIT; +} + +static ExitStatus op_lctlg(DisasContext *s, DisasOps *o) +{ + TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + check_privileged(s); + potential_page_fault(s); + gen_helper_lctlg(cpu_env, r1, o->in2, r3); + tcg_temp_free_i32(r1); + tcg_temp_free_i32(r3); + return NO_EXIT; +} +static ExitStatus op_lra(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + potential_page_fault(s); + gen_helper_lra(o->out, cpu_env, o->in2); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_lpsw(DisasContext *s, DisasOps *o) +{ + TCGv_i64 t1, t2; + + check_privileged(s); + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + tcg_gen_addi_i64(o->in2, o->in2, 4); + tcg_gen_qemu_ld32u(t2, o->in2, get_mem_index(s)); + /* Convert the 32-bit PSW_MASK into the 64-bit PSW_MASK. */ + tcg_gen_shli_i64(t1, t1, 32); + gen_helper_load_psw(cpu_env, t1, t2); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + return EXIT_NORETURN; +} + +static ExitStatus op_lpswe(DisasContext *s, DisasOps *o) +{ + TCGv_i64 t1, t2; + + check_privileged(s); + + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + tcg_gen_qemu_ld64(t1, o->in2, get_mem_index(s)); + tcg_gen_addi_i64(o->in2, o->in2, 8); + tcg_gen_qemu_ld64(t2, o->in2, get_mem_index(s)); + gen_helper_load_psw(cpu_env, t1, t2); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + return EXIT_NORETURN; +} +#endif + +static ExitStatus op_lam(DisasContext *s, DisasOps *o) +{ + TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + potential_page_fault(s); + gen_helper_lam(cpu_env, r1, o->in2, r3); + tcg_temp_free_i32(r1); + tcg_temp_free_i32(r3); + return NO_EXIT; +} + +static ExitStatus op_lm32(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, r1); + int r3 = get_field(s->fields, r3); + TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t4 = tcg_const_i64(4); + + while (1) { + tcg_gen_qemu_ld32u(t, o->in2, get_mem_index(s)); + store_reg32_i64(r1, t); + if (r1 == r3) { + break; + } + tcg_gen_add_i64(o->in2, o->in2, t4); + r1 = (r1 + 1) & 15; + } + + tcg_temp_free_i64(t); + tcg_temp_free_i64(t4); + return NO_EXIT; +} + +static ExitStatus op_lmh(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, r1); + int r3 = get_field(s->fields, r3); + TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t4 = tcg_const_i64(4); + + while (1) { + tcg_gen_qemu_ld32u(t, o->in2, get_mem_index(s)); + store_reg32h_i64(r1, t); + if (r1 == r3) { + break; + } + tcg_gen_add_i64(o->in2, o->in2, t4); + r1 = (r1 + 1) & 15; + } + + tcg_temp_free_i64(t); + tcg_temp_free_i64(t4); + return NO_EXIT; +} + +static ExitStatus op_lm64(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, r1); + int r3 = get_field(s->fields, r3); + TCGv_i64 t8 = tcg_const_i64(8); + + while (1) { + tcg_gen_qemu_ld64(regs[r1], o->in2, get_mem_index(s)); + if (r1 == r3) { + break; + } + tcg_gen_add_i64(o->in2, o->in2, t8); + r1 = (r1 + 1) & 15; + } + + tcg_temp_free_i64(t8); + return NO_EXIT; +} + +static ExitStatus op_mov2(DisasContext *s, DisasOps *o) +{ + o->out = o->in2; + o->g_out = o->g_in2; + TCGV_UNUSED_I64(o->in2); + o->g_in2 = false; + return NO_EXIT; +} + +static ExitStatus op_movx(DisasContext *s, DisasOps *o) +{ + o->out = o->in1; + o->out2 = o->in2; + o->g_out = o->g_in1; + o->g_out2 = o->g_in2; + TCGV_UNUSED_I64(o->in1); + TCGV_UNUSED_I64(o->in2); + o->g_in1 = o->g_in2 = false; + return NO_EXIT; +} + +static ExitStatus op_mvc(DisasContext *s, DisasOps *o) +{ + TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + potential_page_fault(s); + gen_helper_mvc(cpu_env, l, o->addr1, o->in2); + tcg_temp_free_i32(l); + return NO_EXIT; +} + +static ExitStatus op_mvcl(DisasContext *s, DisasOps *o) +{ + TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); + TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); + potential_page_fault(s); + gen_helper_mvcl(cc_op, cpu_env, r1, r2); + tcg_temp_free_i32(r1); + tcg_temp_free_i32(r2); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_mvcle(DisasContext *s, DisasOps *o) +{ + TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + potential_page_fault(s); + gen_helper_mvcle(cc_op, cpu_env, r1, o->in2, r3); + tcg_temp_free_i32(r1); + tcg_temp_free_i32(r3); + set_cc_static(s); + return NO_EXIT; +} + +#ifndef CONFIG_USER_ONLY +static ExitStatus op_mvcp(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, l1); + check_privileged(s); + potential_page_fault(s); + gen_helper_mvcp(cc_op, cpu_env, regs[r1], o->addr1, o->in2); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_mvcs(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, l1); + check_privileged(s); + potential_page_fault(s); + gen_helper_mvcs(cc_op, cpu_env, regs[r1], o->addr1, o->in2); + set_cc_static(s); + return NO_EXIT; +} +#endif + +static ExitStatus op_mvpg(DisasContext *s, DisasOps *o) +{ + potential_page_fault(s); + gen_helper_mvpg(cpu_env, regs[0], o->in1, o->in2); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_mvst(DisasContext *s, DisasOps *o) +{ + potential_page_fault(s); + gen_helper_mvst(o->in1, cpu_env, regs[0], o->in1, o->in2); + set_cc_static(s); + return_low128(o->in2); + return NO_EXIT; +} + +static ExitStatus op_mul(DisasContext *s, DisasOps *o) +{ + tcg_gen_mul_i64(o->out, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_mul128(DisasContext *s, DisasOps *o) +{ + tcg_gen_mulu2_i64(o->out2, o->out, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_meeb(DisasContext *s, DisasOps *o) +{ + gen_helper_meeb(o->out, cpu_env, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_mdeb(DisasContext *s, DisasOps *o) +{ + gen_helper_mdeb(o->out, cpu_env, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_mdb(DisasContext *s, DisasOps *o) +{ + gen_helper_mdb(o->out, cpu_env, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_mxb(DisasContext *s, DisasOps *o) +{ + gen_helper_mxb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); + return_low128(o->out2); + return NO_EXIT; +} + +static ExitStatus op_mxdb(DisasContext *s, DisasOps *o) +{ + gen_helper_mxdb(o->out, cpu_env, o->out, o->out2, o->in2); + return_low128(o->out2); + return NO_EXIT; +} + +static ExitStatus op_maeb(DisasContext *s, DisasOps *o) +{ + TCGv_i64 r3 = load_freg32_i64(get_field(s->fields, r3)); + gen_helper_maeb(o->out, cpu_env, o->in1, o->in2, r3); + tcg_temp_free_i64(r3); + return NO_EXIT; +} + +static ExitStatus op_madb(DisasContext *s, DisasOps *o) +{ + int r3 = get_field(s->fields, r3); + gen_helper_madb(o->out, cpu_env, o->in1, o->in2, fregs[r3]); + return NO_EXIT; +} + +static ExitStatus op_mseb(DisasContext *s, DisasOps *o) +{ + TCGv_i64 r3 = load_freg32_i64(get_field(s->fields, r3)); + gen_helper_mseb(o->out, cpu_env, o->in1, o->in2, r3); + tcg_temp_free_i64(r3); + return NO_EXIT; +} + +static ExitStatus op_msdb(DisasContext *s, DisasOps *o) +{ + int r3 = get_field(s->fields, r3); + gen_helper_msdb(o->out, cpu_env, o->in1, o->in2, fregs[r3]); + return NO_EXIT; +} + +static ExitStatus op_nabs(DisasContext *s, DisasOps *o) +{ + gen_helper_nabs_i64(o->out, o->in2); + return NO_EXIT; +} + +static ExitStatus op_nabsf32(DisasContext *s, DisasOps *o) +{ + tcg_gen_ori_i64(o->out, o->in2, 0x80000000ull); + return NO_EXIT; +} + +static ExitStatus op_nabsf64(DisasContext *s, DisasOps *o) +{ + tcg_gen_ori_i64(o->out, o->in2, 0x8000000000000000ull); + return NO_EXIT; +} + +static ExitStatus op_nabsf128(DisasContext *s, DisasOps *o) +{ + tcg_gen_ori_i64(o->out, o->in1, 0x8000000000000000ull); + tcg_gen_mov_i64(o->out2, o->in2); + return NO_EXIT; +} + +static ExitStatus op_nc(DisasContext *s, DisasOps *o) +{ + TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + potential_page_fault(s); + gen_helper_nc(cc_op, cpu_env, l, o->addr1, o->in2); + tcg_temp_free_i32(l); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_neg(DisasContext *s, DisasOps *o) +{ + tcg_gen_neg_i64(o->out, o->in2); + return NO_EXIT; +} + +static ExitStatus op_negf32(DisasContext *s, DisasOps *o) +{ + tcg_gen_xori_i64(o->out, o->in2, 0x80000000ull); + return NO_EXIT; +} + +static ExitStatus op_negf64(DisasContext *s, DisasOps *o) +{ + tcg_gen_xori_i64(o->out, o->in2, 0x8000000000000000ull); + return NO_EXIT; +} + +static ExitStatus op_negf128(DisasContext *s, DisasOps *o) +{ + tcg_gen_xori_i64(o->out, o->in1, 0x8000000000000000ull); + tcg_gen_mov_i64(o->out2, o->in2); + return NO_EXIT; +} + +static ExitStatus op_oc(DisasContext *s, DisasOps *o) +{ + TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + potential_page_fault(s); + gen_helper_oc(cc_op, cpu_env, l, o->addr1, o->in2); + tcg_temp_free_i32(l); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_or(DisasContext *s, DisasOps *o) +{ + tcg_gen_or_i64(o->out, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_ori(DisasContext *s, DisasOps *o) +{ + int shift = s->insn->data & 0xff; + int size = s->insn->data >> 8; + uint64_t mask = ((1ull << size) - 1) << shift; + + assert(!o->g_in2); + tcg_gen_shli_i64(o->in2, o->in2, shift); + tcg_gen_or_i64(o->out, o->in1, o->in2); + + /* Produce the CC from only the bits manipulated. */ + tcg_gen_andi_i64(cc_dst, o->out, mask); + set_cc_nz_u64(s, cc_dst); + return NO_EXIT; +} + +static ExitStatus op_popcnt(DisasContext *s, DisasOps *o) +{ + gen_helper_popcnt(o->out, o->in2); + return NO_EXIT; +} + +#ifndef CONFIG_USER_ONLY +static ExitStatus op_ptlb(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + gen_helper_ptlb(cpu_env); + return NO_EXIT; +} +#endif + +static ExitStatus op_risbg(DisasContext *s, DisasOps *o) +{ + int i3 = get_field(s->fields, i3); + int i4 = get_field(s->fields, i4); + int i5 = get_field(s->fields, i5); + int do_zero = i4 & 0x80; + uint64_t mask, imask, pmask; + int pos, len, rot; + + /* Adjust the arguments for the specific insn. */ + switch (s->fields->op2) { + case 0x55: /* risbg */ + i3 &= 63; + i4 &= 63; + pmask = ~0; + break; + case 0x5d: /* risbhg */ + i3 &= 31; + i4 &= 31; + pmask = 0xffffffff00000000ull; + break; + case 0x51: /* risblg */ + i3 &= 31; + i4 &= 31; + pmask = 0x00000000ffffffffull; + break; + default: + abort(); + } + + /* MASK is the set of bits to be inserted from R2. + Take care for I3/I4 wraparound. */ + mask = pmask >> i3; + if (i3 <= i4) { + mask ^= pmask >> i4 >> 1; + } else { + mask |= ~(pmask >> i4 >> 1); + } + mask &= pmask; + + /* IMASK is the set of bits to be kept from R1. In the case of the high/low + insns, we need to keep the other half of the register. */ + imask = ~mask | ~pmask; + if (do_zero) { + if (s->fields->op2 == 0x55) { + imask = 0; + } else { + imask = ~pmask; + } + } + + /* In some cases we can implement this with deposit, which can be more + efficient on some hosts. */ + if (~mask == imask && i3 <= i4) { + if (s->fields->op2 == 0x5d) { + i3 += 32, i4 += 32; + } + /* Note that we rotate the bits to be inserted to the lsb, not to + the position as described in the PoO. */ + len = i4 - i3 + 1; + pos = 63 - i4; + rot = (i5 - pos) & 63; + } else { + pos = len = -1; + rot = i5 & 63; + } + + /* Rotate the input as necessary. */ + tcg_gen_rotli_i64(o->in2, o->in2, rot); + + /* Insert the selected bits into the output. */ + if (pos >= 0) { + tcg_gen_deposit_i64(o->out, o->out, o->in2, pos, len); + } else if (imask == 0) { + tcg_gen_andi_i64(o->out, o->in2, mask); + } else { + tcg_gen_andi_i64(o->in2, o->in2, mask); + tcg_gen_andi_i64(o->out, o->out, imask); + tcg_gen_or_i64(o->out, o->out, o->in2); + } + return NO_EXIT; +} + +static ExitStatus op_rosbg(DisasContext *s, DisasOps *o) +{ + int i3 = get_field(s->fields, i3); + int i4 = get_field(s->fields, i4); + int i5 = get_field(s->fields, i5); + uint64_t mask; + + /* If this is a test-only form, arrange to discard the result. */ + if (i3 & 0x80) { + o->out = tcg_temp_new_i64(); + o->g_out = false; + } + + i3 &= 63; + i4 &= 63; + i5 &= 63; + + /* MASK is the set of bits to be operated on from R2. + Take care for I3/I4 wraparound. */ + mask = ~0ull >> i3; + if (i3 <= i4) { + mask ^= ~0ull >> i4 >> 1; + } else { + mask |= ~(~0ull >> i4 >> 1); + } + + /* Rotate the input as necessary. */ + tcg_gen_rotli_i64(o->in2, o->in2, i5); + + /* Operate. */ + switch (s->fields->op2) { + case 0x55: /* AND */ + tcg_gen_ori_i64(o->in2, o->in2, ~mask); + tcg_gen_and_i64(o->out, o->out, o->in2); + break; + case 0x56: /* OR */ + tcg_gen_andi_i64(o->in2, o->in2, mask); + tcg_gen_or_i64(o->out, o->out, o->in2); + break; + case 0x57: /* XOR */ + tcg_gen_andi_i64(o->in2, o->in2, mask); + tcg_gen_xor_i64(o->out, o->out, o->in2); + break; + default: + abort(); + } + + /* Set the CC. */ + tcg_gen_andi_i64(cc_dst, o->out, mask); + set_cc_nz_u64(s, cc_dst); + return NO_EXIT; +} + +static ExitStatus op_rev16(DisasContext *s, DisasOps *o) +{ + tcg_gen_bswap16_i64(o->out, o->in2); + return NO_EXIT; +} + +static ExitStatus op_rev32(DisasContext *s, DisasOps *o) +{ + tcg_gen_bswap32_i64(o->out, o->in2); + return NO_EXIT; +} + +static ExitStatus op_rev64(DisasContext *s, DisasOps *o) +{ + tcg_gen_bswap64_i64(o->out, o->in2); + return NO_EXIT; +} + +static ExitStatus op_rll32(DisasContext *s, DisasOps *o) +{ + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 to = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(t1, o->in1); + tcg_gen_trunc_i64_i32(t2, o->in2); + tcg_gen_rotl_i32(to, t1, t2); + tcg_gen_extu_i32_i64(o->out, to); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(to); + return NO_EXIT; +} + +static ExitStatus op_rll64(DisasContext *s, DisasOps *o) +{ + tcg_gen_rotl_i64(o->out, o->in1, o->in2); + return NO_EXIT; +} + +#ifndef CONFIG_USER_ONLY +static ExitStatus op_rrbe(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + gen_helper_rrbe(cc_op, cpu_env, o->in2); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_sacf(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + gen_helper_sacf(cpu_env, o->in2); + /* Addressing mode has changed, so end the block. */ + return EXIT_PC_STALE; +} +#endif + +static ExitStatus op_sar(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, r1); + tcg_gen_st32_i64(o->in2, cpu_env, offsetof(CPUS390XState, aregs[r1])); + return NO_EXIT; +} + +static ExitStatus op_seb(DisasContext *s, DisasOps *o) +{ + gen_helper_seb(o->out, cpu_env, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_sdb(DisasContext *s, DisasOps *o) +{ + gen_helper_sdb(o->out, cpu_env, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_sxb(DisasContext *s, DisasOps *o) +{ + gen_helper_sxb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); + return_low128(o->out2); + return NO_EXIT; +} + +static ExitStatus op_sqeb(DisasContext *s, DisasOps *o) +{ + gen_helper_sqeb(o->out, cpu_env, o->in2); + return NO_EXIT; +} + +static ExitStatus op_sqdb(DisasContext *s, DisasOps *o) +{ + gen_helper_sqdb(o->out, cpu_env, o->in2); + return NO_EXIT; +} + +static ExitStatus op_sqxb(DisasContext *s, DisasOps *o) +{ + gen_helper_sqxb(o->out, cpu_env, o->in1, o->in2); + return_low128(o->out2); + return NO_EXIT; +} + +#ifndef CONFIG_USER_ONLY +static ExitStatus op_servc(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + potential_page_fault(s); + gen_helper_servc(cc_op, cpu_env, o->in2, o->in1); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_sigp(DisasContext *s, DisasOps *o) +{ + TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); + check_privileged(s); + potential_page_fault(s); + gen_helper_sigp(cc_op, cpu_env, o->in2, r1, o->in1); + tcg_temp_free_i32(r1); + return NO_EXIT; +} +#endif + +static ExitStatus op_soc(DisasContext *s, DisasOps *o) +{ + DisasCompare c; + TCGv_i64 a; + int lab, r1; + + disas_jcc(s, &c, get_field(s->fields, m3)); + + lab = gen_new_label(); + if (c.is_64) { + tcg_gen_brcond_i64(c.cond, c.u.s64.a, c.u.s64.b, lab); + } else { + tcg_gen_brcond_i32(c.cond, c.u.s32.a, c.u.s32.b, lab); + } + free_compare(&c); + + r1 = get_field(s->fields, r1); + a = get_address(s, 0, get_field(s->fields, b2), get_field(s->fields, d2)); + if (s->insn->data) { + tcg_gen_qemu_st64(regs[r1], a, get_mem_index(s)); + } else { + tcg_gen_qemu_st32(regs[r1], a, get_mem_index(s)); + } + tcg_temp_free_i64(a); + + gen_set_label(lab); + return NO_EXIT; +} + +static ExitStatus op_sla(DisasContext *s, DisasOps *o) +{ + uint64_t sign = 1ull << s->insn->data; + enum cc_op cco = s->insn->data == 31 ? CC_OP_SLA_32 : CC_OP_SLA_64; + gen_op_update2_cc_i64(s, cco, o->in1, o->in2); + tcg_gen_shl_i64(o->out, o->in1, o->in2); + /* The arithmetic left shift is curious in that it does not affect + the sign bit. Copy that over from the source unchanged. */ + tcg_gen_andi_i64(o->out, o->out, ~sign); + tcg_gen_andi_i64(o->in1, o->in1, sign); + tcg_gen_or_i64(o->out, o->out, o->in1); + return NO_EXIT; +} + +static ExitStatus op_sll(DisasContext *s, DisasOps *o) +{ + tcg_gen_shl_i64(o->out, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_sra(DisasContext *s, DisasOps *o) +{ + tcg_gen_sar_i64(o->out, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_srl(DisasContext *s, DisasOps *o) +{ + tcg_gen_shr_i64(o->out, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_sfpc(DisasContext *s, DisasOps *o) +{ + gen_helper_sfpc(cpu_env, o->in2); + return NO_EXIT; +} + +static ExitStatus op_sfas(DisasContext *s, DisasOps *o) +{ + gen_helper_sfas(cpu_env, o->in2); + return NO_EXIT; +} + +static ExitStatus op_srnm(DisasContext *s, DisasOps *o) +{ + int b2 = get_field(s->fields, b2); + int d2 = get_field(s->fields, d2); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + int mask, pos, len; + + switch (s->fields->op2) { + case 0x99: /* SRNM */ + pos = 0, len = 2; + break; + case 0xb8: /* SRNMB */ + pos = 0, len = 3; + break; + case 0xb9: /* SRNMT */ + pos = 4, len = 3; default: tcg_abort(); } + mask = (1 << len) - 1; + + /* Insert the value into the appropriate field of the FPC. */ + if (b2 == 0) { + tcg_gen_movi_i64(t1, d2 & mask); + } else { + tcg_gen_addi_i64(t1, regs[b2], d2); + tcg_gen_andi_i64(t1, t1, mask); + } + tcg_gen_ld32u_i64(t2, cpu_env, offsetof(CPUS390XState, fpc)); + tcg_gen_deposit_i64(t2, t2, t1, pos, len); + tcg_temp_free_i64(t1); + + /* Then install the new FPC to set the rounding mode in fpu_status. */ + gen_helper_sfpc(cpu_env, t2); + tcg_temp_free_i64(t2); + return NO_EXIT; } -static void disas_s390_insn(CPUS390XState *env, DisasContext *s) +#ifndef CONFIG_USER_ONLY +static ExitStatus op_spka(DisasContext *s, DisasOps *o) { - TCGv_i64 tmp, tmp2, tmp3, tmp4; - TCGv_i32 tmp32_1, tmp32_2, tmp32_3, tmp32_4; - unsigned char opc; - uint64_t insn; - int op, r1, r2, r3, d1, d2, x2, b1, b2, i, i2, r1b; - TCGv_i32 vl; - int ilc; - int l1; + check_privileged(s); + tcg_gen_shri_i64(o->in2, o->in2, 4); + tcg_gen_deposit_i64(psw_mask, psw_mask, o->in2, PSW_SHIFT_KEY - 4, 4); + return NO_EXIT; +} - opc = cpu_ldub_code(env, s->pc); - LOG_DISAS("opc 0x%x\n", opc); +static ExitStatus op_sske(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + gen_helper_sske(cpu_env, o->in1, o->in2); + return NO_EXIT; +} - ilc = get_ilc(opc); +static ExitStatus op_ssm(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + tcg_gen_deposit_i64(psw_mask, psw_mask, o->in2, 56, 8); + return NO_EXIT; +} +static ExitStatus op_stap(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + /* ??? Surely cpu address != cpu number. In any case the previous + version of this stored more than the required half-word, so it + is unlikely this has ever been tested. */ + tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, cpu_num)); + return NO_EXIT; +} + +static ExitStatus op_stck(DisasContext *s, DisasOps *o) +{ + gen_helper_stck(o->out, cpu_env); + /* ??? We don't implement clock states. */ + gen_op_movi_cc(s, 0); + return NO_EXIT; +} + +static ExitStatus op_stcke(DisasContext *s, DisasOps *o) +{ + TCGv_i64 c1 = tcg_temp_new_i64(); + TCGv_i64 c2 = tcg_temp_new_i64(); + gen_helper_stck(c1, cpu_env); + /* Shift the 64-bit value into its place as a zero-extended + 104-bit value. Note that "bit positions 64-103 are always + non-zero so that they compare differently to STCK"; we set + the least significant bit to 1. */ + tcg_gen_shli_i64(c2, c1, 56); + tcg_gen_shri_i64(c1, c1, 8); + tcg_gen_ori_i64(c2, c2, 0x10000); + tcg_gen_qemu_st64(c1, o->in2, get_mem_index(s)); + tcg_gen_addi_i64(o->in2, o->in2, 8); + tcg_gen_qemu_st64(c2, o->in2, get_mem_index(s)); + tcg_temp_free_i64(c1); + tcg_temp_free_i64(c2); + /* ??? We don't implement clock states. */ + gen_op_movi_cc(s, 0); + return NO_EXIT; +} + +static ExitStatus op_sckc(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + gen_helper_sckc(cpu_env, o->in2); + return NO_EXIT; +} + +static ExitStatus op_stckc(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + gen_helper_stckc(o->out, cpu_env); + return NO_EXIT; +} + +static ExitStatus op_stctg(DisasContext *s, DisasOps *o) +{ + TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + check_privileged(s); + potential_page_fault(s); + gen_helper_stctg(cpu_env, r1, o->in2, r3); + tcg_temp_free_i32(r1); + tcg_temp_free_i32(r3); + return NO_EXIT; +} + +static ExitStatus op_stctl(DisasContext *s, DisasOps *o) +{ + TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + check_privileged(s); + potential_page_fault(s); + gen_helper_stctl(cpu_env, r1, o->in2, r3); + tcg_temp_free_i32(r1); + tcg_temp_free_i32(r3); + return NO_EXIT; +} + +static ExitStatus op_stidp(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, cpu_num)); + return NO_EXIT; +} + +static ExitStatus op_spt(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + gen_helper_spt(cpu_env, o->in2); + return NO_EXIT; +} + +static ExitStatus op_stfl(DisasContext *s, DisasOps *o) +{ + TCGv_i64 f, a; + /* We really ought to have more complete indication of facilities + that we implement. Address this when STFLE is implemented. */ + check_privileged(s); + f = tcg_const_i64(0xc0000000); + a = tcg_const_i64(200); + tcg_gen_qemu_st32(f, a, get_mem_index(s)); + tcg_temp_free_i64(f); + tcg_temp_free_i64(a); + return NO_EXIT; +} + +static ExitStatus op_stpt(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + gen_helper_stpt(o->out, cpu_env); + return NO_EXIT; +} + +static ExitStatus op_stsi(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + potential_page_fault(s); + gen_helper_stsi(cc_op, cpu_env, o->in2, regs[0], regs[1]); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_spx(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + gen_helper_spx(cpu_env, o->in2); + return NO_EXIT; +} + +static ExitStatus op_subchannel(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + /* Not operational. */ + gen_op_movi_cc(s, 3); + return NO_EXIT; +} + +static ExitStatus op_stpx(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + tcg_gen_ld_i64(o->out, cpu_env, offsetof(CPUS390XState, psa)); + tcg_gen_andi_i64(o->out, o->out, 0x7fffe000); + return NO_EXIT; +} + +static ExitStatus op_stnosm(DisasContext *s, DisasOps *o) +{ + uint64_t i2 = get_field(s->fields, i2); + TCGv_i64 t; + + check_privileged(s); + + /* It is important to do what the instruction name says: STORE THEN. + If we let the output hook perform the store then if we fault and + restart, we'll have the wrong SYSTEM MASK in place. */ + t = tcg_temp_new_i64(); + tcg_gen_shri_i64(t, psw_mask, 56); + tcg_gen_qemu_st8(t, o->addr1, get_mem_index(s)); + tcg_temp_free_i64(t); + + if (s->fields->op == 0xac) { + tcg_gen_andi_i64(psw_mask, psw_mask, + (i2 << 56) | 0x00ffffffffffffffull); + } else { + tcg_gen_ori_i64(psw_mask, psw_mask, i2 << 56); + } + return NO_EXIT; +} + +static ExitStatus op_stura(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + potential_page_fault(s); + gen_helper_stura(cpu_env, o->in2, o->in1); + return NO_EXIT; +} +#endif + +static ExitStatus op_st8(DisasContext *s, DisasOps *o) +{ + tcg_gen_qemu_st8(o->in1, o->in2, get_mem_index(s)); + return NO_EXIT; +} + +static ExitStatus op_st16(DisasContext *s, DisasOps *o) +{ + tcg_gen_qemu_st16(o->in1, o->in2, get_mem_index(s)); + return NO_EXIT; +} + +static ExitStatus op_st32(DisasContext *s, DisasOps *o) +{ + tcg_gen_qemu_st32(o->in1, o->in2, get_mem_index(s)); + return NO_EXIT; +} + +static ExitStatus op_st64(DisasContext *s, DisasOps *o) +{ + tcg_gen_qemu_st64(o->in1, o->in2, get_mem_index(s)); + return NO_EXIT; +} + +static ExitStatus op_stam(DisasContext *s, DisasOps *o) +{ + TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); + TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); + potential_page_fault(s); + gen_helper_stam(cpu_env, r1, o->in2, r3); + tcg_temp_free_i32(r1); + tcg_temp_free_i32(r3); + return NO_EXIT; +} + +static ExitStatus op_stcm(DisasContext *s, DisasOps *o) +{ + int m3 = get_field(s->fields, m3); + int pos, base = s->insn->data; + TCGv_i64 tmp = tcg_temp_new_i64(); + + pos = base + ctz32(m3) * 8; + switch (m3) { + case 0xf: + /* Effectively a 32-bit store. */ + tcg_gen_shri_i64(tmp, o->in1, pos); + tcg_gen_qemu_st32(tmp, o->in2, get_mem_index(s)); + break; + + case 0xc: + case 0x6: + case 0x3: + /* Effectively a 16-bit store. */ + tcg_gen_shri_i64(tmp, o->in1, pos); + tcg_gen_qemu_st16(tmp, o->in2, get_mem_index(s)); + break; + + case 0x8: + case 0x4: + case 0x2: + case 0x1: + /* Effectively an 8-bit store. */ + tcg_gen_shri_i64(tmp, o->in1, pos); + tcg_gen_qemu_st8(tmp, o->in2, get_mem_index(s)); + break; + + default: + /* This is going to be a sequence of shifts and stores. */ + pos = base + 32 - 8; + while (m3) { + if (m3 & 0x8) { + tcg_gen_shri_i64(tmp, o->in1, pos); + tcg_gen_qemu_st8(tmp, o->in2, get_mem_index(s)); + tcg_gen_addi_i64(o->in2, o->in2, 1); + } + m3 = (m3 << 1) & 0xf; + pos -= 8; + } + break; + } + tcg_temp_free_i64(tmp); + return NO_EXIT; +} + +static ExitStatus op_stm(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, r1); + int r3 = get_field(s->fields, r3); + int size = s->insn->data; + TCGv_i64 tsize = tcg_const_i64(size); + + while (1) { + if (size == 8) { + tcg_gen_qemu_st64(regs[r1], o->in2, get_mem_index(s)); + } else { + tcg_gen_qemu_st32(regs[r1], o->in2, get_mem_index(s)); + } + if (r1 == r3) { + break; + } + tcg_gen_add_i64(o->in2, o->in2, tsize); + r1 = (r1 + 1) & 15; + } + + tcg_temp_free_i64(tsize); + return NO_EXIT; +} + +static ExitStatus op_stmh(DisasContext *s, DisasOps *o) +{ + int r1 = get_field(s->fields, r1); + int r3 = get_field(s->fields, r3); + TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 t4 = tcg_const_i64(4); + TCGv_i64 t32 = tcg_const_i64(32); + + while (1) { + tcg_gen_shl_i64(t, regs[r1], t32); + tcg_gen_qemu_st32(t, o->in2, get_mem_index(s)); + if (r1 == r3) { + break; + } + tcg_gen_add_i64(o->in2, o->in2, t4); + r1 = (r1 + 1) & 15; + } + + tcg_temp_free_i64(t); + tcg_temp_free_i64(t4); + tcg_temp_free_i64(t32); + return NO_EXIT; +} + +static ExitStatus op_srst(DisasContext *s, DisasOps *o) +{ + potential_page_fault(s); + gen_helper_srst(o->in1, cpu_env, regs[0], o->in1, o->in2); + set_cc_static(s); + return_low128(o->in2); + return NO_EXIT; +} + +static ExitStatus op_sub(DisasContext *s, DisasOps *o) +{ + tcg_gen_sub_i64(o->out, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_subb(DisasContext *s, DisasOps *o) +{ + DisasCompare cmp; + TCGv_i64 borrow; + + tcg_gen_sub_i64(o->out, o->in1, o->in2); + + /* The !borrow flag is the msb of CC. Since we want the inverse of + that, we ask for a comparison of CC=0 | CC=1 -> mask of 8 | 4. */ + disas_jcc(s, &cmp, 8 | 4); + borrow = tcg_temp_new_i64(); + if (cmp.is_64) { + tcg_gen_setcond_i64(cmp.cond, borrow, cmp.u.s64.a, cmp.u.s64.b); + } else { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_setcond_i32(cmp.cond, t, cmp.u.s32.a, cmp.u.s32.b); + tcg_gen_extu_i32_i64(borrow, t); + tcg_temp_free_i32(t); + } + free_compare(&cmp); + + tcg_gen_sub_i64(o->out, o->out, borrow); + tcg_temp_free_i64(borrow); + return NO_EXIT; +} + +static ExitStatus op_svc(DisasContext *s, DisasOps *o) +{ + TCGv_i32 t; + + update_psw_addr(s); + update_cc_op(s); + + t = tcg_const_i32(get_field(s->fields, i1) & 0xff); + tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, int_svc_code)); + tcg_temp_free_i32(t); + + t = tcg_const_i32(s->next_pc - s->pc); + tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, int_svc_ilen)); + tcg_temp_free_i32(t); + + gen_exception(EXCP_SVC); + return EXIT_NORETURN; +} + +static ExitStatus op_tceb(DisasContext *s, DisasOps *o) +{ + gen_helper_tceb(cc_op, o->in1, o->in2); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_tcdb(DisasContext *s, DisasOps *o) +{ + gen_helper_tcdb(cc_op, o->in1, o->in2); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_tcxb(DisasContext *s, DisasOps *o) +{ + gen_helper_tcxb(cc_op, o->out, o->out2, o->in2); + set_cc_static(s); + return NO_EXIT; +} + +#ifndef CONFIG_USER_ONLY +static ExitStatus op_tprot(DisasContext *s, DisasOps *o) +{ + potential_page_fault(s); + gen_helper_tprot(cc_op, o->addr1, o->in2); + set_cc_static(s); + return NO_EXIT; +} +#endif + +static ExitStatus op_tr(DisasContext *s, DisasOps *o) +{ + TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + potential_page_fault(s); + gen_helper_tr(cpu_env, l, o->addr1, o->in2); + tcg_temp_free_i32(l); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_unpk(DisasContext *s, DisasOps *o) +{ + TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); + potential_page_fault(s); + gen_helper_unpk(cpu_env, l, o->addr1, o->in2); + tcg_temp_free_i32(l); + return NO_EXIT; +} + +static ExitStatus op_xc(DisasContext *s, DisasOps *o) +{ + int d1 = get_field(s->fields, d1); + int d2 = get_field(s->fields, d2); + int b1 = get_field(s->fields, b1); + int b2 = get_field(s->fields, b2); + int l = get_field(s->fields, l1); + TCGv_i32 t32; + + o->addr1 = get_address(s, 0, b1, d1); + + /* If the addresses are identical, this is a store/memset of zero. */ + if (b1 == b2 && d1 == d2 && (l + 1) <= 32) { + o->in2 = tcg_const_i64(0); + + l++; + while (l >= 8) { + tcg_gen_qemu_st64(o->in2, o->addr1, get_mem_index(s)); + l -= 8; + if (l > 0) { + tcg_gen_addi_i64(o->addr1, o->addr1, 8); + } + } + if (l >= 4) { + tcg_gen_qemu_st32(o->in2, o->addr1, get_mem_index(s)); + l -= 4; + if (l > 0) { + tcg_gen_addi_i64(o->addr1, o->addr1, 4); + } + } + if (l >= 2) { + tcg_gen_qemu_st16(o->in2, o->addr1, get_mem_index(s)); + l -= 2; + if (l > 0) { + tcg_gen_addi_i64(o->addr1, o->addr1, 2); + } + } + if (l) { + tcg_gen_qemu_st8(o->in2, o->addr1, get_mem_index(s)); + } + gen_op_movi_cc(s, 0); + return NO_EXIT; + } + + /* But in general we'll defer to a helper. */ + o->in2 = get_address(s, 0, b2, d2); + t32 = tcg_const_i32(l); + potential_page_fault(s); + gen_helper_xc(cc_op, cpu_env, t32, o->addr1, o->in2); + tcg_temp_free_i32(t32); + set_cc_static(s); + return NO_EXIT; +} + +static ExitStatus op_xor(DisasContext *s, DisasOps *o) +{ + tcg_gen_xor_i64(o->out, o->in1, o->in2); + return NO_EXIT; +} + +static ExitStatus op_xori(DisasContext *s, DisasOps *o) +{ + int shift = s->insn->data & 0xff; + int size = s->insn->data >> 8; + uint64_t mask = ((1ull << size) - 1) << shift; + + assert(!o->g_in2); + tcg_gen_shli_i64(o->in2, o->in2, shift); + tcg_gen_xor_i64(o->out, o->in1, o->in2); + + /* Produce the CC from only the bits manipulated. */ + tcg_gen_andi_i64(cc_dst, o->out, mask); + set_cc_nz_u64(s, cc_dst); + return NO_EXIT; +} + +static ExitStatus op_zero(DisasContext *s, DisasOps *o) +{ + o->out = tcg_const_i64(0); + return NO_EXIT; +} + +static ExitStatus op_zero2(DisasContext *s, DisasOps *o) +{ + o->out = tcg_const_i64(0); + o->out2 = o->out; + o->g_out2 = true; + return NO_EXIT; +} + +/* ====================================================================== */ +/* The "Cc OUTput" generators. Given the generated output (and in some cases + the original inputs), update the various cc data structures in order to + be able to compute the new condition code. */ + +static void cout_abs32(DisasContext *s, DisasOps *o) +{ + gen_op_update1_cc_i64(s, CC_OP_ABS_32, o->out); +} + +static void cout_abs64(DisasContext *s, DisasOps *o) +{ + gen_op_update1_cc_i64(s, CC_OP_ABS_64, o->out); +} + +static void cout_adds32(DisasContext *s, DisasOps *o) +{ + gen_op_update3_cc_i64(s, CC_OP_ADD_32, o->in1, o->in2, o->out); +} + +static void cout_adds64(DisasContext *s, DisasOps *o) +{ + gen_op_update3_cc_i64(s, CC_OP_ADD_64, o->in1, o->in2, o->out); +} + +static void cout_addu32(DisasContext *s, DisasOps *o) +{ + gen_op_update3_cc_i64(s, CC_OP_ADDU_32, o->in1, o->in2, o->out); +} + +static void cout_addu64(DisasContext *s, DisasOps *o) +{ + gen_op_update3_cc_i64(s, CC_OP_ADDU_64, o->in1, o->in2, o->out); +} + +static void cout_addc32(DisasContext *s, DisasOps *o) +{ + gen_op_update3_cc_i64(s, CC_OP_ADDC_32, o->in1, o->in2, o->out); +} + +static void cout_addc64(DisasContext *s, DisasOps *o) +{ + gen_op_update3_cc_i64(s, CC_OP_ADDC_64, o->in1, o->in2, o->out); +} + +static void cout_cmps32(DisasContext *s, DisasOps *o) +{ + gen_op_update2_cc_i64(s, CC_OP_LTGT_32, o->in1, o->in2); +} + +static void cout_cmps64(DisasContext *s, DisasOps *o) +{ + gen_op_update2_cc_i64(s, CC_OP_LTGT_64, o->in1, o->in2); +} + +static void cout_cmpu32(DisasContext *s, DisasOps *o) +{ + gen_op_update2_cc_i64(s, CC_OP_LTUGTU_32, o->in1, o->in2); +} + +static void cout_cmpu64(DisasContext *s, DisasOps *o) +{ + gen_op_update2_cc_i64(s, CC_OP_LTUGTU_64, o->in1, o->in2); +} + +static void cout_f32(DisasContext *s, DisasOps *o) +{ + gen_op_update1_cc_i64(s, CC_OP_NZ_F32, o->out); +} + +static void cout_f64(DisasContext *s, DisasOps *o) +{ + gen_op_update1_cc_i64(s, CC_OP_NZ_F64, o->out); +} + +static void cout_f128(DisasContext *s, DisasOps *o) +{ + gen_op_update2_cc_i64(s, CC_OP_NZ_F128, o->out, o->out2); +} + +static void cout_nabs32(DisasContext *s, DisasOps *o) +{ + gen_op_update1_cc_i64(s, CC_OP_NABS_32, o->out); +} + +static void cout_nabs64(DisasContext *s, DisasOps *o) +{ + gen_op_update1_cc_i64(s, CC_OP_NABS_64, o->out); +} + +static void cout_neg32(DisasContext *s, DisasOps *o) +{ + gen_op_update1_cc_i64(s, CC_OP_COMP_32, o->out); +} + +static void cout_neg64(DisasContext *s, DisasOps *o) +{ + gen_op_update1_cc_i64(s, CC_OP_COMP_64, o->out); +} + +static void cout_nz32(DisasContext *s, DisasOps *o) +{ + tcg_gen_ext32u_i64(cc_dst, o->out); + gen_op_update1_cc_i64(s, CC_OP_NZ, cc_dst); +} + +static void cout_nz64(DisasContext *s, DisasOps *o) +{ + gen_op_update1_cc_i64(s, CC_OP_NZ, o->out); +} + +static void cout_s32(DisasContext *s, DisasOps *o) +{ + gen_op_update1_cc_i64(s, CC_OP_LTGT0_32, o->out); +} + +static void cout_s64(DisasContext *s, DisasOps *o) +{ + gen_op_update1_cc_i64(s, CC_OP_LTGT0_64, o->out); +} + +static void cout_subs32(DisasContext *s, DisasOps *o) +{ + gen_op_update3_cc_i64(s, CC_OP_SUB_32, o->in1, o->in2, o->out); +} + +static void cout_subs64(DisasContext *s, DisasOps *o) +{ + gen_op_update3_cc_i64(s, CC_OP_SUB_64, o->in1, o->in2, o->out); +} + +static void cout_subu32(DisasContext *s, DisasOps *o) +{ + gen_op_update3_cc_i64(s, CC_OP_SUBU_32, o->in1, o->in2, o->out); +} + +static void cout_subu64(DisasContext *s, DisasOps *o) +{ + gen_op_update3_cc_i64(s, CC_OP_SUBU_64, o->in1, o->in2, o->out); +} + +static void cout_subb32(DisasContext *s, DisasOps *o) +{ + gen_op_update3_cc_i64(s, CC_OP_SUBB_32, o->in1, o->in2, o->out); +} + +static void cout_subb64(DisasContext *s, DisasOps *o) +{ + gen_op_update3_cc_i64(s, CC_OP_SUBB_64, o->in1, o->in2, o->out); +} + +static void cout_tm32(DisasContext *s, DisasOps *o) +{ + gen_op_update2_cc_i64(s, CC_OP_TM_32, o->in1, o->in2); +} + +static void cout_tm64(DisasContext *s, DisasOps *o) +{ + gen_op_update2_cc_i64(s, CC_OP_TM_64, o->in1, o->in2); +} + +/* ====================================================================== */ +/* The "PREPeration" generators. These initialize the DisasOps.OUT fields + with the TCG register to which we will write. Used in combination with + the "wout" generators, in some cases we need a new temporary, and in + some cases we can write to a TCG global. */ + +static void prep_new(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->out = tcg_temp_new_i64(); +} +#define SPEC_prep_new 0 + +static void prep_new_P(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->out = tcg_temp_new_i64(); + o->out2 = tcg_temp_new_i64(); +} +#define SPEC_prep_new_P 0 + +static void prep_r1(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->out = regs[get_field(f, r1)]; + o->g_out = true; +} +#define SPEC_prep_r1 0 + +static void prep_r1_P(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r1 = get_field(f, r1); + o->out = regs[r1]; + o->out2 = regs[r1 + 1]; + o->g_out = o->g_out2 = true; +} +#define SPEC_prep_r1_P SPEC_r1_even + +static void prep_f1(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->out = fregs[get_field(f, r1)]; + o->g_out = true; +} +#define SPEC_prep_f1 0 + +static void prep_x1(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r1 = get_field(f, r1); + o->out = fregs[r1]; + o->out2 = fregs[r1 + 2]; + o->g_out = o->g_out2 = true; +} +#define SPEC_prep_x1 SPEC_r1_f128 + +/* ====================================================================== */ +/* The "Write OUTput" generators. These generally perform some non-trivial + copy of data to TCG globals, or to main memory. The trivial cases are + generally handled by having a "prep" generator install the TCG global + as the destination of the operation. */ + +static void wout_r1(DisasContext *s, DisasFields *f, DisasOps *o) +{ + store_reg(get_field(f, r1), o->out); +} +#define SPEC_wout_r1 0 + +static void wout_r1_8(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r1 = get_field(f, r1); + tcg_gen_deposit_i64(regs[r1], regs[r1], o->out, 0, 8); +} +#define SPEC_wout_r1_8 0 + +static void wout_r1_16(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r1 = get_field(f, r1); + tcg_gen_deposit_i64(regs[r1], regs[r1], o->out, 0, 16); +} +#define SPEC_wout_r1_16 0 + +static void wout_r1_32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + store_reg32_i64(get_field(f, r1), o->out); +} +#define SPEC_wout_r1_32 0 + +static void wout_r1_P32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r1 = get_field(f, r1); + store_reg32_i64(r1, o->out); + store_reg32_i64(r1 + 1, o->out2); +} +#define SPEC_wout_r1_P32 SPEC_r1_even + +static void wout_r1_D32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r1 = get_field(f, r1); + store_reg32_i64(r1 + 1, o->out); + tcg_gen_shri_i64(o->out, o->out, 32); + store_reg32_i64(r1, o->out); +} +#define SPEC_wout_r1_D32 SPEC_r1_even + +static void wout_e1(DisasContext *s, DisasFields *f, DisasOps *o) +{ + store_freg32_i64(get_field(f, r1), o->out); +} +#define SPEC_wout_e1 0 + +static void wout_f1(DisasContext *s, DisasFields *f, DisasOps *o) +{ + store_freg(get_field(f, r1), o->out); +} +#define SPEC_wout_f1 0 + +static void wout_x1(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int f1 = get_field(s->fields, r1); + store_freg(f1, o->out); + store_freg(f1 + 2, o->out2); +} +#define SPEC_wout_x1 SPEC_r1_f128 + +static void wout_cond_r1r2_32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + if (get_field(f, r1) != get_field(f, r2)) { + store_reg32_i64(get_field(f, r1), o->out); + } +} +#define SPEC_wout_cond_r1r2_32 0 + +static void wout_cond_e1e2(DisasContext *s, DisasFields *f, DisasOps *o) +{ + if (get_field(f, r1) != get_field(f, r2)) { + store_freg32_i64(get_field(f, r1), o->out); + } +} +#define SPEC_wout_cond_e1e2 0 + +static void wout_m1_8(DisasContext *s, DisasFields *f, DisasOps *o) +{ + tcg_gen_qemu_st8(o->out, o->addr1, get_mem_index(s)); +} +#define SPEC_wout_m1_8 0 + +static void wout_m1_16(DisasContext *s, DisasFields *f, DisasOps *o) +{ + tcg_gen_qemu_st16(o->out, o->addr1, get_mem_index(s)); +} +#define SPEC_wout_m1_16 0 + +static void wout_m1_32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + tcg_gen_qemu_st32(o->out, o->addr1, get_mem_index(s)); +} +#define SPEC_wout_m1_32 0 + +static void wout_m1_64(DisasContext *s, DisasFields *f, DisasOps *o) +{ + tcg_gen_qemu_st64(o->out, o->addr1, get_mem_index(s)); +} +#define SPEC_wout_m1_64 0 + +static void wout_m2_32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + tcg_gen_qemu_st32(o->out, o->in2, get_mem_index(s)); +} +#define SPEC_wout_m2_32 0 + +/* ====================================================================== */ +/* The "INput 1" generators. These load the first operand to an insn. */ + +static void in1_r1(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = load_reg(get_field(f, r1)); +} +#define SPEC_in1_r1 0 + +static void in1_r1_o(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = regs[get_field(f, r1)]; + o->g_in1 = true; +} +#define SPEC_in1_r1_o 0 + +static void in1_r1_32s(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(o->in1, regs[get_field(f, r1)]); +} +#define SPEC_in1_r1_32s 0 + +static void in1_r1_32u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(o->in1, regs[get_field(f, r1)]); +} +#define SPEC_in1_r1_32u 0 + +static void in1_r1_sr32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = tcg_temp_new_i64(); + tcg_gen_shri_i64(o->in1, regs[get_field(f, r1)], 32); +} +#define SPEC_in1_r1_sr32 0 + +static void in1_r1p1(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = load_reg(get_field(f, r1) + 1); +} +#define SPEC_in1_r1p1 SPEC_r1_even + +static void in1_r1p1_32s(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(o->in1, regs[get_field(f, r1) + 1]); +} +#define SPEC_in1_r1p1_32s SPEC_r1_even + +static void in1_r1p1_32u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(o->in1, regs[get_field(f, r1) + 1]); +} +#define SPEC_in1_r1p1_32u SPEC_r1_even + +static void in1_r1_D32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r1 = get_field(f, r1); + o->in1 = tcg_temp_new_i64(); + tcg_gen_concat32_i64(o->in1, regs[r1 + 1], regs[r1]); +} +#define SPEC_in1_r1_D32 SPEC_r1_even + +static void in1_r2(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = load_reg(get_field(f, r2)); +} +#define SPEC_in1_r2 0 + +static void in1_r3(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = load_reg(get_field(f, r3)); +} +#define SPEC_in1_r3 0 + +static void in1_r3_o(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = regs[get_field(f, r3)]; + o->g_in1 = true; +} +#define SPEC_in1_r3_o 0 + +static void in1_r3_32s(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(o->in1, regs[get_field(f, r3)]); +} +#define SPEC_in1_r3_32s 0 + +static void in1_r3_32u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(o->in1, regs[get_field(f, r3)]); +} +#define SPEC_in1_r3_32u 0 + +static void in1_r3_D32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r3 = get_field(f, r3); + o->in1 = tcg_temp_new_i64(); + tcg_gen_concat32_i64(o->in1, regs[r3 + 1], regs[r3]); +} +#define SPEC_in1_r3_D32 SPEC_r3_even + +static void in1_e1(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = load_freg32_i64(get_field(f, r1)); +} +#define SPEC_in1_e1 0 + +static void in1_f1_o(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = fregs[get_field(f, r1)]; + o->g_in1 = true; +} +#define SPEC_in1_f1_o 0 + +static void in1_x1_o(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r1 = get_field(f, r1); + o->out = fregs[r1]; + o->out2 = fregs[r1 + 2]; + o->g_out = o->g_out2 = true; +} +#define SPEC_in1_x1_o SPEC_r1_f128 + +static void in1_f3_o(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in1 = fregs[get_field(f, r3)]; + o->g_in1 = true; +} +#define SPEC_in1_f3_o 0 + +static void in1_la1(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->addr1 = get_address(s, 0, get_field(f, b1), get_field(f, d1)); +} +#define SPEC_in1_la1 0 + +static void in1_la2(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int x2 = have_field(f, x2) ? get_field(f, x2) : 0; + o->addr1 = get_address(s, x2, get_field(f, b2), get_field(f, d2)); +} +#define SPEC_in1_la2 0 + +static void in1_m1_8u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in1_la1(s, f, o); + o->in1 = tcg_temp_new_i64(); + tcg_gen_qemu_ld8u(o->in1, o->addr1, get_mem_index(s)); +} +#define SPEC_in1_m1_8u 0 + +static void in1_m1_16s(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in1_la1(s, f, o); + o->in1 = tcg_temp_new_i64(); + tcg_gen_qemu_ld16s(o->in1, o->addr1, get_mem_index(s)); +} +#define SPEC_in1_m1_16s 0 + +static void in1_m1_16u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in1_la1(s, f, o); + o->in1 = tcg_temp_new_i64(); + tcg_gen_qemu_ld16u(o->in1, o->addr1, get_mem_index(s)); +} +#define SPEC_in1_m1_16u 0 + +static void in1_m1_32s(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in1_la1(s, f, o); + o->in1 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32s(o->in1, o->addr1, get_mem_index(s)); +} +#define SPEC_in1_m1_32s 0 + +static void in1_m1_32u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in1_la1(s, f, o); + o->in1 = tcg_temp_new_i64(); + tcg_gen_qemu_ld32u(o->in1, o->addr1, get_mem_index(s)); +} +#define SPEC_in1_m1_32u 0 + +static void in1_m1_64(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in1_la1(s, f, o); + o->in1 = tcg_temp_new_i64(); + tcg_gen_qemu_ld64(o->in1, o->addr1, get_mem_index(s)); +} +#define SPEC_in1_m1_64 0 + +/* ====================================================================== */ +/* The "INput 2" generators. These load the second operand to an insn. */ + +static void in2_r1_o(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = regs[get_field(f, r1)]; + o->g_in2 = true; +} +#define SPEC_in2_r1_o 0 + +static void in2_r1_16u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_temp_new_i64(); + tcg_gen_ext16u_i64(o->in2, regs[get_field(f, r1)]); +} +#define SPEC_in2_r1_16u 0 + +static void in2_r1_32u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(o->in2, regs[get_field(f, r1)]); +} +#define SPEC_in2_r1_32u 0 + +static void in2_r1_D32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r1 = get_field(f, r1); + o->in2 = tcg_temp_new_i64(); + tcg_gen_concat32_i64(o->in2, regs[r1 + 1], regs[r1]); +} +#define SPEC_in2_r1_D32 SPEC_r1_even + +static void in2_r2(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = load_reg(get_field(f, r2)); +} +#define SPEC_in2_r2 0 + +static void in2_r2_o(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = regs[get_field(f, r2)]; + o->g_in2 = true; +} +#define SPEC_in2_r2_o 0 + +static void in2_r2_nz(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r2 = get_field(f, r2); + if (r2 != 0) { + o->in2 = load_reg(r2); + } +} +#define SPEC_in2_r2_nz 0 + +static void in2_r2_8s(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_temp_new_i64(); + tcg_gen_ext8s_i64(o->in2, regs[get_field(f, r2)]); +} +#define SPEC_in2_r2_8s 0 + +static void in2_r2_8u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_temp_new_i64(); + tcg_gen_ext8u_i64(o->in2, regs[get_field(f, r2)]); +} +#define SPEC_in2_r2_8u 0 + +static void in2_r2_16s(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_temp_new_i64(); + tcg_gen_ext16s_i64(o->in2, regs[get_field(f, r2)]); +} +#define SPEC_in2_r2_16s 0 + +static void in2_r2_16u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_temp_new_i64(); + tcg_gen_ext16u_i64(o->in2, regs[get_field(f, r2)]); +} +#define SPEC_in2_r2_16u 0 + +static void in2_r3(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = load_reg(get_field(f, r3)); +} +#define SPEC_in2_r3 0 + +static void in2_r2_32s(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(o->in2, regs[get_field(f, r2)]); +} +#define SPEC_in2_r2_32s 0 + +static void in2_r2_32u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(o->in2, regs[get_field(f, r2)]); +} +#define SPEC_in2_r2_32u 0 + +static void in2_e2(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = load_freg32_i64(get_field(f, r2)); +} +#define SPEC_in2_e2 0 + +static void in2_f2_o(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = fregs[get_field(f, r2)]; + o->g_in2 = true; +} +#define SPEC_in2_f2_o 0 + +static void in2_x2_o(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int r2 = get_field(f, r2); + o->in1 = fregs[r2]; + o->in2 = fregs[r2 + 2]; + o->g_in1 = o->g_in2 = true; +} +#define SPEC_in2_x2_o SPEC_r2_f128 + +static void in2_ra2(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = get_address(s, 0, get_field(f, r2), 0); +} +#define SPEC_in2_ra2 0 + +static void in2_a2(DisasContext *s, DisasFields *f, DisasOps *o) +{ + int x2 = have_field(f, x2) ? get_field(f, x2) : 0; + o->in2 = get_address(s, x2, get_field(f, b2), get_field(f, d2)); +} +#define SPEC_in2_a2 0 + +static void in2_ri2(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_const_i64(s->pc + (int64_t)get_field(f, i2) * 2); +} +#define SPEC_in2_ri2 0 + +static void in2_sh32(DisasContext *s, DisasFields *f, DisasOps *o) +{ + help_l2_shift(s, f, o, 31); +} +#define SPEC_in2_sh32 0 + +static void in2_sh64(DisasContext *s, DisasFields *f, DisasOps *o) +{ + help_l2_shift(s, f, o, 63); +} +#define SPEC_in2_sh64 0 + +static void in2_m2_8u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_a2(s, f, o); + tcg_gen_qemu_ld8u(o->in2, o->in2, get_mem_index(s)); +} +#define SPEC_in2_m2_8u 0 + +static void in2_m2_16s(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_a2(s, f, o); + tcg_gen_qemu_ld16s(o->in2, o->in2, get_mem_index(s)); +} +#define SPEC_in2_m2_16s 0 + +static void in2_m2_16u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_a2(s, f, o); + tcg_gen_qemu_ld16u(o->in2, o->in2, get_mem_index(s)); +} +#define SPEC_in2_m2_16u 0 + +static void in2_m2_32s(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_a2(s, f, o); + tcg_gen_qemu_ld32s(o->in2, o->in2, get_mem_index(s)); +} +#define SPEC_in2_m2_32s 0 + +static void in2_m2_32u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_a2(s, f, o); + tcg_gen_qemu_ld32u(o->in2, o->in2, get_mem_index(s)); +} +#define SPEC_in2_m2_32u 0 + +static void in2_m2_64(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_a2(s, f, o); + tcg_gen_qemu_ld64(o->in2, o->in2, get_mem_index(s)); +} +#define SPEC_in2_m2_64 0 + +static void in2_mri2_16u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_ri2(s, f, o); + tcg_gen_qemu_ld16u(o->in2, o->in2, get_mem_index(s)); +} +#define SPEC_in2_mri2_16u 0 + +static void in2_mri2_32s(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_ri2(s, f, o); + tcg_gen_qemu_ld32s(o->in2, o->in2, get_mem_index(s)); +} +#define SPEC_in2_mri2_32s 0 + +static void in2_mri2_32u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_ri2(s, f, o); + tcg_gen_qemu_ld32u(o->in2, o->in2, get_mem_index(s)); +} +#define SPEC_in2_mri2_32u 0 + +static void in2_mri2_64(DisasContext *s, DisasFields *f, DisasOps *o) +{ + in2_ri2(s, f, o); + tcg_gen_qemu_ld64(o->in2, o->in2, get_mem_index(s)); +} +#define SPEC_in2_mri2_64 0 + +static void in2_i2(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_const_i64(get_field(f, i2)); +} +#define SPEC_in2_i2 0 + +static void in2_i2_8u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_const_i64((uint8_t)get_field(f, i2)); +} +#define SPEC_in2_i2_8u 0 + +static void in2_i2_16u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_const_i64((uint16_t)get_field(f, i2)); +} +#define SPEC_in2_i2_16u 0 + +static void in2_i2_32u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_const_i64((uint32_t)get_field(f, i2)); +} +#define SPEC_in2_i2_32u 0 + +static void in2_i2_16u_shl(DisasContext *s, DisasFields *f, DisasOps *o) +{ + uint64_t i2 = (uint16_t)get_field(f, i2); + o->in2 = tcg_const_i64(i2 << s->insn->data); +} +#define SPEC_in2_i2_16u_shl 0 + +static void in2_i2_32u_shl(DisasContext *s, DisasFields *f, DisasOps *o) +{ + uint64_t i2 = (uint32_t)get_field(f, i2); + o->in2 = tcg_const_i64(i2 << s->insn->data); +} +#define SPEC_in2_i2_32u_shl 0 + +/* ====================================================================== */ + +/* Find opc within the table of insns. This is formulated as a switch + statement so that (1) we get compile-time notice of cut-paste errors + for duplicated opcodes, and (2) the compiler generates the binary + search tree, rather than us having to post-process the table. */ + +#define C(OPC, NM, FT, FC, I1, I2, P, W, OP, CC) \ + D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, 0) + +#define D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D) insn_ ## NM, + +enum DisasInsnEnum { +#include "insn-data.def" +}; + +#undef D +#define D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D) { \ + .opc = OPC, \ + .fmt = FMT_##FT, \ + .fac = FAC_##FC, \ + .spec = SPEC_in1_##I1 | SPEC_in2_##I2 | SPEC_prep_##P | SPEC_wout_##W, \ + .name = #NM, \ + .help_in1 = in1_##I1, \ + .help_in2 = in2_##I2, \ + .help_prep = prep_##P, \ + .help_wout = wout_##W, \ + .help_cout = cout_##CC, \ + .help_op = op_##OP, \ + .data = D \ + }, + +/* Allow 0 to be used for NULL in the table below. */ +#define in1_0 NULL +#define in2_0 NULL +#define prep_0 NULL +#define wout_0 NULL +#define cout_0 NULL +#define op_0 NULL + +#define SPEC_in1_0 0 +#define SPEC_in2_0 0 +#define SPEC_prep_0 0 +#define SPEC_wout_0 0 + +static const DisasInsn insn_info[] = { +#include "insn-data.def" +}; + +#undef D +#define D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D) \ + case OPC: return &insn_info[insn_ ## NM]; + +static const DisasInsn *lookup_opc(uint16_t opc) +{ switch (opc) { -#ifndef CONFIG_USER_ONLY - case 0x01: /* SAM */ - insn = ld_code2(env, s->pc); - /* set addressing mode, but we only do 64bit anyways */ - break; -#endif - case 0x6: /* BCTR R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp32_1 = load_reg32(r1); - tcg_gen_subi_i32(tmp32_1, tmp32_1, 1); - store_reg32(r1, tmp32_1); +#include "insn-data.def" + default: + return NULL; + } +} - if (r2) { - gen_update_cc_op(s); - l1 = gen_new_label(); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp32_1, 0, l1); +#undef D +#undef C - /* not taking the branch, jump to after the instruction */ - gen_goto_tb(s, 0, s->pc + 2); - gen_set_label(l1); +/* Extract a field from the insn. The INSN should be left-aligned in + the uint64_t so that we can more easily utilize the big-bit-endian + definitions we extract from the Principals of Operation. */ - /* take the branch, move R2 into psw.addr */ - tmp32_1 = load_reg32(r2); - tmp = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(tmp, tmp32_1); - tcg_gen_mov_i64(psw_addr, tmp); - s->is_jmp = DISAS_JUMP; - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp); - } - break; - case 0x7: /* BCR M1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - if (r2) { - tmp = load_reg(r2); - gen_bcr(s, r1, tmp, s->pc); - tcg_temp_free_i64(tmp); - s->is_jmp = DISAS_TB_JUMP; - } else { - /* XXX: "serialization and checkpoint-synchronization function"? */ - } - break; - case 0xa: /* SVC I [RR] */ - insn = ld_code2(env, s->pc); - debug_insn(insn); - i = insn & 0xff; - update_psw_addr(s); - gen_op_calc_cc(s); - tmp32_1 = tcg_const_i32(i); - tmp32_2 = tcg_const_i32(ilc * 2); - tmp32_3 = tcg_const_i32(EXCP_SVC); - tcg_gen_st_i32(tmp32_1, cpu_env, offsetof(CPUS390XState, int_svc_code)); - tcg_gen_st_i32(tmp32_2, cpu_env, offsetof(CPUS390XState, int_svc_ilc)); - gen_helper_exception(cpu_env, tmp32_3); - s->is_jmp = DISAS_EXCP; - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0xd: /* BASR R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp = tcg_const_i64(pc_to_link_info(s, s->pc + 2)); - store_reg(r1, tmp); - if (r2) { - tmp2 = load_reg(r2); - tcg_gen_mov_i64(psw_addr, tmp2); - tcg_temp_free_i64(tmp2); - s->is_jmp = DISAS_JUMP; - } - tcg_temp_free_i64(tmp); - break; - case 0xe: /* MVCL R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r2); - potential_page_fault(s); - gen_helper_mvcl(cc_op, cpu_env, tmp32_1, tmp32_2); - set_cc_static(s); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x10: /* LPR R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp32_1 = load_reg32(r2); - set_cc_abs32(s, tmp32_1); - gen_helper_abs_i32(tmp32_1, tmp32_1); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x11: /* LNR R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp32_1 = load_reg32(r2); - set_cc_nabs32(s, tmp32_1); - gen_helper_nabs_i32(tmp32_1, tmp32_1); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x12: /* LTR R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp32_1 = load_reg32(r2); - if (r1 != r2) { - store_reg32(r1, tmp32_1); - } - set_cc_s32(s, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x13: /* LCR R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp32_1 = load_reg32(r2); - tcg_gen_neg_i32(tmp32_1, tmp32_1); - store_reg32(r1, tmp32_1); - set_cc_comp32(s, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x14: /* NR R1,R2 [RR] */ - case 0x16: /* OR R1,R2 [RR] */ - case 0x17: /* XR R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp32_2 = load_reg32(r2); - tmp32_1 = load_reg32(r1); - gen_and_or_xor_i32(opc, tmp32_1, tmp32_2); - store_reg32(r1, tmp32_1); - set_cc_nz_u32(s, tmp32_1); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x18: /* LR R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp32_1 = load_reg32(r2); - store_reg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x15: /* CLR R1,R2 [RR] */ - case 0x19: /* CR R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp32_1 = load_reg32(r1); - tmp32_2 = load_reg32(r2); - if (opc == 0x15) { - cmp_u32(s, tmp32_1, tmp32_2); - } else { - cmp_s32(s, tmp32_1, tmp32_2); - } - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x1a: /* AR R1,R2 [RR] */ - case 0x1e: /* ALR R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp32_1 = load_reg32(r1); - tmp32_2 = load_reg32(r2); - tmp32_3 = tcg_temp_new_i32(); - tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2); - store_reg32(r1, tmp32_3); - if (opc == 0x1a) { - set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3); - } else { - set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3); - } - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0x1b: /* SR R1,R2 [RR] */ - case 0x1f: /* SLR R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp32_1 = load_reg32(r1); - tmp32_2 = load_reg32(r2); - tmp32_3 = tcg_temp_new_i32(); - tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2); - store_reg32(r1, tmp32_3); - if (opc == 0x1b) { - set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3); - } else { - set_cc_subu32(s, tmp32_1, tmp32_2, tmp32_3); - } - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0x1c: /* MR R1,R2 [RR] */ - /* reg(r1, r1+1) = reg(r1+1) * reg(r2) */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp2 = load_reg(r2); - tmp3 = load_reg((r1 + 1) & 15); - tcg_gen_ext32s_i64(tmp2, tmp2); - tcg_gen_ext32s_i64(tmp3, tmp3); - tcg_gen_mul_i64(tmp2, tmp2, tmp3); - store_reg32_i64((r1 + 1) & 15, tmp2); - tcg_gen_shri_i64(tmp2, tmp2, 32); - store_reg32_i64(r1, tmp2); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x1d: /* DR R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp32_1 = load_reg32(r1); - tmp32_2 = load_reg32(r1 + 1); - tmp32_3 = load_reg32(r2); +static void extract_field(DisasFields *o, const DisasField *f, uint64_t insn) +{ + uint32_t r, m; - tmp = tcg_temp_new_i64(); /* dividend */ - tmp2 = tcg_temp_new_i64(); /* divisor */ - tmp3 = tcg_temp_new_i64(); + if (f->size == 0) { + return; + } - /* dividend is r(r1 << 32) | r(r1 + 1) */ - tcg_gen_extu_i32_i64(tmp, tmp32_1); - tcg_gen_extu_i32_i64(tmp2, tmp32_2); - tcg_gen_shli_i64(tmp, tmp, 32); - tcg_gen_or_i64(tmp, tmp, tmp2); + /* Zero extract the field from the insn. */ + r = (insn << f->beg) >> (64 - f->size); - /* divisor is r(r2) */ - tcg_gen_ext_i32_i64(tmp2, tmp32_3); - - tcg_gen_div_i64(tmp3, tmp, tmp2); - tcg_gen_rem_i64(tmp, tmp, tmp2); - - tcg_gen_trunc_i64_i32(tmp32_1, tmp); - tcg_gen_trunc_i64_i32(tmp32_2, tmp3); - - store_reg32(r1, tmp32_1); /* remainder */ - store_reg32(r1 + 1, tmp32_2); /* quotient */ - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); + /* Sign-extend, or un-swap the field as necessary. */ + switch (f->type) { + case 0: /* unsigned */ break; - case 0x28: /* LDR R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp = load_freg(r2); - store_freg(r1, tmp); - tcg_temp_free_i64(tmp); + case 1: /* signed */ + assert(f->size <= 32); + m = 1u << (f->size - 1); + r = (r ^ m) - m; break; - case 0x38: /* LER R1,R2 [RR] */ - insn = ld_code2(env, s->pc); - decode_rr(s, insn, &r1, &r2); - tmp32_1 = load_freg32(r2); - store_freg32(r1, tmp32_1); - tcg_temp_free_i32(tmp32_1); - break; - case 0x40: /* STH R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = load_reg(r1); - tcg_gen_qemu_st16(tmp2, tmp, get_mem_index(s)); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x41: /* la */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - store_reg(r1, tmp); /* FIXME: 31/24-bit addressing */ - tcg_temp_free_i64(tmp); - break; - case 0x42: /* STC R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = load_reg(r1); - tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s)); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x43: /* IC R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s)); - store_reg8(r1, tmp2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x44: /* EX R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = load_reg(r1); - tmp3 = tcg_const_i64(s->pc + 4); - update_psw_addr(s); - gen_op_calc_cc(s); - gen_helper_ex(cc_op, cpu_env, cc_op, tmp2, tmp, tmp3); - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x46: /* BCT R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tcg_temp_free_i64(tmp); - - tmp32_1 = load_reg32(r1); - tcg_gen_subi_i32(tmp32_1, tmp32_1, 1); - store_reg32(r1, tmp32_1); - - gen_update_cc_op(s); - l1 = gen_new_label(); - tcg_gen_brcondi_i32(TCG_COND_NE, tmp32_1, 0, l1); - - /* not taking the branch, jump to after the instruction */ - gen_goto_tb(s, 0, s->pc + 4); - gen_set_label(l1); - - /* take the branch, move R2 into psw.addr */ - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tcg_gen_mov_i64(psw_addr, tmp); - s->is_jmp = DISAS_JUMP; - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp); - break; - case 0x47: /* BC M1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - gen_bcr(s, r1, tmp, s->pc + 4); - tcg_temp_free_i64(tmp); - s->is_jmp = DISAS_TB_JUMP; - break; - case 0x48: /* LH R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld16s(tmp2, tmp, get_mem_index(s)); - store_reg32_i64(r1, tmp2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x49: /* CH R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_temp_new_i32(); - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld16s(tmp2, tmp, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_2, tmp2); - cmp_s32(s, tmp32_1, tmp32_2); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x4a: /* AH R1,D2(X2,B2) [RX] */ - case 0x4b: /* SH R1,D2(X2,B2) [RX] */ - case 0x4c: /* MH R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_temp_new_i64(); - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_temp_new_i32(); - tmp32_3 = tcg_temp_new_i32(); - - tcg_gen_qemu_ld16s(tmp2, tmp, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_2, tmp2); - switch (opc) { - case 0x4a: - tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2); - set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3); - break; - case 0x4b: - tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2); - set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3); - break; - case 0x4c: - tcg_gen_mul_i32(tmp32_3, tmp32_1, tmp32_2); - break; - default: - tcg_abort(); - } - store_reg32(r1, tmp32_3); - - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x4d: /* BAS R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_const_i64(pc_to_link_info(s, s->pc + 4)); - store_reg(r1, tmp2); - tcg_gen_mov_i64(psw_addr, tmp); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - s->is_jmp = DISAS_JUMP; - break; - case 0x4e: /* CVD R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tmp32_1, regs[r1]); - gen_helper_cvd(tmp2, tmp32_1); - tcg_gen_qemu_st64(tmp2, tmp, get_mem_index(s)); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - break; - case 0x50: /* st r1, d2(x2, b2) */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = load_reg(r1); - tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s)); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x55: /* CL R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_temp_new_i32(); - tmp32_2 = load_reg32(r1); - tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_1, tmp2); - cmp_u32(s, tmp32_2, tmp32_1); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x54: /* N R1,D2(X2,B2) [RX] */ - case 0x56: /* O R1,D2(X2,B2) [RX] */ - case 0x57: /* X R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_temp_new_i64(); - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_2, tmp2); - gen_and_or_xor_i32(opc, tmp32_1, tmp32_2); - store_reg32(r1, tmp32_1); - set_cc_nz_u32(s, tmp32_1); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x58: /* l r1, d2(x2, b2) */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_1, tmp2); - store_reg32(r1, tmp32_1); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - break; - case 0x59: /* C R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_temp_new_i32(); - tmp32_2 = load_reg32(r1); - tcg_gen_qemu_ld32s(tmp2, tmp, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_1, tmp2); - cmp_s32(s, tmp32_2, tmp32_1); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x5a: /* A R1,D2(X2,B2) [RX] */ - case 0x5b: /* S R1,D2(X2,B2) [RX] */ - case 0x5e: /* AL R1,D2(X2,B2) [RX] */ - case 0x5f: /* SL R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_temp_new_i32(); - tmp32_3 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32s(tmp, tmp, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_2, tmp); - switch (opc) { - case 0x5a: - case 0x5e: - tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2); - break; - case 0x5b: - case 0x5f: - tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2); - break; - default: - tcg_abort(); - } - store_reg32(r1, tmp32_3); - switch (opc) { - case 0x5a: - set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3); - break; - case 0x5e: - set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3); - break; - case 0x5b: - set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3); - break; - case 0x5f: - set_cc_subu32(s, tmp32_1, tmp32_2, tmp32_3); - break; - default: - tcg_abort(); - } - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - break; - case 0x5c: /* M R1,D2(X2,B2) [RX] */ - /* reg(r1, r1+1) = reg(r1+1) * *(s32*)addr */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32s(tmp2, tmp, get_mem_index(s)); - tmp3 = load_reg((r1 + 1) & 15); - tcg_gen_ext32s_i64(tmp2, tmp2); - tcg_gen_ext32s_i64(tmp3, tmp3); - tcg_gen_mul_i64(tmp2, tmp2, tmp3); - store_reg32_i64((r1 + 1) & 15, tmp2); - tcg_gen_shri_i64(tmp2, tmp2, 32); - store_reg32_i64(r1, tmp2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x5d: /* D R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp3 = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp32_1 = load_reg32(r1); - tmp32_2 = load_reg32(r1 + 1); - - tmp = tcg_temp_new_i64(); - tmp2 = tcg_temp_new_i64(); - - /* dividend is r(r1 << 32) | r(r1 + 1) */ - tcg_gen_extu_i32_i64(tmp, tmp32_1); - tcg_gen_extu_i32_i64(tmp2, tmp32_2); - tcg_gen_shli_i64(tmp, tmp, 32); - tcg_gen_or_i64(tmp, tmp, tmp2); - - /* divisor is in memory */ - tcg_gen_qemu_ld32s(tmp2, tmp3, get_mem_index(s)); - - /* XXX divisor == 0 -> FixP divide exception */ - - tcg_gen_div_i64(tmp3, tmp, tmp2); - tcg_gen_rem_i64(tmp, tmp, tmp2); - - tcg_gen_trunc_i64_i32(tmp32_1, tmp); - tcg_gen_trunc_i64_i32(tmp32_2, tmp3); - - store_reg32(r1, tmp32_1); /* remainder */ - store_reg32(r1 + 1, tmp32_2); /* quotient */ - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x60: /* STD R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = load_freg(r1); - tcg_gen_qemu_st64(tmp2, tmp, get_mem_index(s)); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x68: /* LD R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld64(tmp2, tmp, get_mem_index(s)); - store_freg(r1, tmp2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x70: /* STE R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_temp_new_i64(); - tmp32_1 = load_freg32(r1); - tcg_gen_extu_i32_i64(tmp2, tmp32_1); - tcg_gen_qemu_st32(tmp2, tmp, get_mem_index(s)); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - break; - case 0x71: /* MS R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_temp_new_i64(); - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32s(tmp2, tmp, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_2, tmp2); - tcg_gen_mul_i32(tmp32_1, tmp32_1, tmp32_2); - store_reg32(r1, tmp32_1); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x78: /* LE R1,D2(X2,B2) [RX] */ - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp2 = tcg_temp_new_i64(); - tmp32_1 = tcg_temp_new_i32(); - tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_1, tmp2); - store_freg32(r1, tmp32_1); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - break; -#ifndef CONFIG_USER_ONLY - case 0x80: /* SSM D2(B2) [S] */ - /* Set System Mask */ - check_privileged(env, s, ilc); - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp2 = tcg_temp_new_i64(); - tmp3 = tcg_temp_new_i64(); - tcg_gen_andi_i64(tmp3, psw_mask, ~0xff00000000000000ULL); - tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s)); - tcg_gen_shli_i64(tmp2, tmp2, 56); - tcg_gen_or_i64(psw_mask, tmp3, tmp2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; - case 0x82: /* LPSW D2(B2) [S] */ - /* Load PSW */ - check_privileged(env, s, ilc); - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp2 = tcg_temp_new_i64(); - tmp3 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); - tcg_gen_addi_i64(tmp, tmp, 4); - tcg_gen_qemu_ld32u(tmp3, tmp, get_mem_index(s)); - gen_helper_load_psw(cpu_env, tmp2, tmp3); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - /* we need to keep cc_op intact */ - s->is_jmp = DISAS_JUMP; - break; - case 0x83: /* DIAG R1,R3,D2 [RS] */ - /* Diagnose call (KVM hypercall) */ - check_privileged(env, s, ilc); - potential_page_fault(s); - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp32_1 = tcg_const_i32(insn & 0xfff); - tmp2 = load_reg(2); - tmp3 = load_reg(1); - gen_helper_diag(tmp2, cpu_env, tmp32_1, tmp2, tmp3); - store_reg(2, tmp2); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; -#endif - case 0x88: /* SRL R1,D2(B2) [RS] */ - case 0x89: /* SLL R1,D2(B2) [RS] */ - case 0x8a: /* SRA R1,D2(B2) [RS] */ - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_temp_new_i32(); - tcg_gen_trunc_i64_i32(tmp32_2, tmp); - tcg_gen_andi_i32(tmp32_2, tmp32_2, 0x3f); - switch (opc) { - case 0x88: - tcg_gen_shr_i32(tmp32_1, tmp32_1, tmp32_2); - break; - case 0x89: - tcg_gen_shl_i32(tmp32_1, tmp32_1, tmp32_2); - break; - case 0x8a: - tcg_gen_sar_i32(tmp32_1, tmp32_1, tmp32_2); - set_cc_s32(s, tmp32_1); - break; - default: - tcg_abort(); - } - store_reg32(r1, tmp32_1); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x8c: /* SRDL R1,D2(B2) [RS] */ - case 0x8d: /* SLDL R1,D2(B2) [RS] */ - case 0x8e: /* SRDA R1,D2(B2) [RS] */ - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); /* shift */ - tmp2 = tcg_temp_new_i64(); - tmp32_1 = load_reg32(r1); - tmp32_2 = load_reg32(r1 + 1); - tcg_gen_concat_i32_i64(tmp2, tmp32_2, tmp32_1); /* operand */ - switch (opc) { - case 0x8c: - tcg_gen_shr_i64(tmp2, tmp2, tmp); - break; - case 0x8d: - tcg_gen_shl_i64(tmp2, tmp2, tmp); - break; - case 0x8e: - tcg_gen_sar_i64(tmp2, tmp2, tmp); - set_cc_s64(s, tmp2); - break; - } - tcg_gen_shri_i64(tmp, tmp2, 32); - tcg_gen_trunc_i64_i32(tmp32_1, tmp); - store_reg32(r1, tmp32_1); - tcg_gen_trunc_i64_i32(tmp32_2, tmp2); - store_reg32(r1 + 1, tmp32_2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x98: /* LM R1,R3,D2(B2) [RS] */ - case 0x90: /* STM R1,R3,D2(B2) [RS] */ - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - - tmp = get_address(s, 0, b2, d2); - tmp2 = tcg_temp_new_i64(); - tmp3 = tcg_const_i64(4); - tmp4 = tcg_const_i64(0xffffffff00000000ULL); - for (i = r1;; i = (i + 1) % 16) { - if (opc == 0x98) { - tcg_gen_qemu_ld32u(tmp2, tmp, get_mem_index(s)); - tcg_gen_and_i64(regs[i], regs[i], tmp4); - tcg_gen_or_i64(regs[i], regs[i], tmp2); - } else { - tcg_gen_qemu_st32(regs[i], tmp, get_mem_index(s)); - } - if (i == r3) { - break; - } - tcg_gen_add_i64(tmp, tmp, tmp3); - } - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - tcg_temp_free_i64(tmp4); - break; - case 0x91: /* TM D1(B1),I2 [SI] */ - insn = ld_code4(env, s->pc); - tmp = decode_si(s, insn, &i2, &b1, &d1); - tmp2 = tcg_const_i64(i2); - tcg_gen_qemu_ld8u(tmp, tmp, get_mem_index(s)); - cmp_64(s, tmp, tmp2, CC_OP_TM_32); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x92: /* MVI D1(B1),I2 [SI] */ - insn = ld_code4(env, s->pc); - tmp = decode_si(s, insn, &i2, &b1, &d1); - tmp2 = tcg_const_i64(i2); - tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s)); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x94: /* NI D1(B1),I2 [SI] */ - case 0x96: /* OI D1(B1),I2 [SI] */ - case 0x97: /* XI D1(B1),I2 [SI] */ - insn = ld_code4(env, s->pc); - tmp = decode_si(s, insn, &i2, &b1, &d1); - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s)); - switch (opc) { - case 0x94: - tcg_gen_andi_i64(tmp2, tmp2, i2); - break; - case 0x96: - tcg_gen_ori_i64(tmp2, tmp2, i2); - break; - case 0x97: - tcg_gen_xori_i64(tmp2, tmp2, i2); - break; - default: - tcg_abort(); - } - tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s)); - set_cc_nz_u64(s, tmp2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x95: /* CLI D1(B1),I2 [SI] */ - insn = ld_code4(env, s->pc); - tmp = decode_si(s, insn, &i2, &b1, &d1); - tmp2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s)); - cmp_u64c(s, tmp2, i2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0x9a: /* LAM R1,R3,D2(B2) [RS] */ - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - gen_helper_lam(cpu_env, tmp32_1, tmp, tmp32_2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0x9b: /* STAM R1,R3,D2(B2) [RS] */ - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - gen_helper_stam(cpu_env, tmp32_1, tmp, tmp32_2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0xa5: - insn = ld_code4(env, s->pc); - r1 = (insn >> 20) & 0xf; - op = (insn >> 16) & 0xf; - i2 = insn & 0xffff; - disas_a5(env, s, op, r1, i2); - break; - case 0xa7: - insn = ld_code4(env, s->pc); - r1 = (insn >> 20) & 0xf; - op = (insn >> 16) & 0xf; - i2 = (short)insn; - disas_a7(env, s, op, r1, i2); - break; - case 0xa8: /* MVCLE R1,R3,D2(B2) [RS] */ - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - gen_helper_mvcle(cc_op, cpu_env, tmp32_1, tmp, tmp32_2); - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0xa9: /* CLCLE R1,R3,D2(B2) [RS] */ - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - gen_helper_clcle(cc_op, cpu_env, tmp32_1, tmp, tmp32_2); - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; -#ifndef CONFIG_USER_ONLY - case 0xac: /* STNSM D1(B1),I2 [SI] */ - case 0xad: /* STOSM D1(B1),I2 [SI] */ - check_privileged(env, s, ilc); - insn = ld_code4(env, s->pc); - tmp = decode_si(s, insn, &i2, &b1, &d1); - tmp2 = tcg_temp_new_i64(); - tcg_gen_shri_i64(tmp2, psw_mask, 56); - tcg_gen_qemu_st8(tmp2, tmp, get_mem_index(s)); - if (opc == 0xac) { - tcg_gen_andi_i64(psw_mask, psw_mask, - ((uint64_t)i2 << 56) | 0x00ffffffffffffffULL); - } else { - tcg_gen_ori_i64(psw_mask, psw_mask, (uint64_t)i2 << 56); - } - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - case 0xae: /* SIGP R1,R3,D2(B2) [RS] */ - check_privileged(env, s, ilc); - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp2 = load_reg(r3); - tmp32_1 = tcg_const_i32(r1); - potential_page_fault(s); - gen_helper_sigp(cc_op, cpu_env, tmp, tmp32_1, tmp2); - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - break; - case 0xb1: /* LRA R1,D2(X2, B2) [RX] */ - check_privileged(env, s, ilc); - insn = ld_code4(env, s->pc); - tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2); - tmp32_1 = tcg_const_i32(r1); - potential_page_fault(s); - gen_helper_lra(cc_op, cpu_env, tmp, tmp32_1); - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - break; -#endif - case 0xb2: - insn = ld_code4(env, s->pc); - op = (insn >> 16) & 0xff; - switch (op) { - case 0x9c: /* STFPC D2(B2) [S] */ - d2 = insn & 0xfff; - b2 = (insn >> 12) & 0xf; - tmp32_1 = tcg_temp_new_i32(); - tmp = tcg_temp_new_i64(); - tmp2 = get_address(s, 0, b2, d2); - tcg_gen_ld_i32(tmp32_1, cpu_env, offsetof(CPUS390XState, fpc)); - tcg_gen_extu_i32_i64(tmp, tmp32_1); - tcg_gen_qemu_st32(tmp, tmp2, get_mem_index(s)); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; - default: - disas_b2(env, s, op, insn); - break; - } - break; - case 0xb3: - insn = ld_code4(env, s->pc); - op = (insn >> 16) & 0xff; - r3 = (insn >> 12) & 0xf; /* aka m3 */ - r1 = (insn >> 4) & 0xf; - r2 = insn & 0xf; - disas_b3(env, s, op, r3, r1, r2); - break; -#ifndef CONFIG_USER_ONLY - case 0xb6: /* STCTL R1,R3,D2(B2) [RS] */ - /* Store Control */ - check_privileged(env, s, ilc); - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - gen_helper_stctl(cpu_env, tmp32_1, tmp, tmp32_2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0xb7: /* LCTL R1,R3,D2(B2) [RS] */ - /* Load Control */ - check_privileged(env, s, ilc); - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - gen_helper_lctl(cpu_env, tmp32_1, tmp, tmp32_2); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; -#endif - case 0xb9: - insn = ld_code4(env, s->pc); - r1 = (insn >> 4) & 0xf; - r2 = insn & 0xf; - op = (insn >> 16) & 0xff; - disas_b9(env, s, op, r1, r2); - break; - case 0xba: /* CS R1,R3,D2(B2) [RS] */ - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_const_i32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - gen_helper_cs(cc_op, cpu_env, tmp32_1, tmp, tmp32_2); - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0xbd: /* CLM R1,M3,D2(B2) [RS] */ - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - gen_helper_clm(cc_op, cpu_env, tmp32_1, tmp32_2, tmp); - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0xbe: /* STCM R1,M3,D2(B2) [RS] */ - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - tmp = get_address(s, 0, b2, d2); - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_const_i32(r3); - potential_page_fault(s); - gen_helper_stcm(cpu_env, tmp32_1, tmp32_2, tmp); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - break; - case 0xbf: /* ICM R1,M3,D2(B2) [RS] */ - insn = ld_code4(env, s->pc); - decode_rs(s, insn, &r1, &r3, &b2, &d2); - if (r3 == 15) { - /* effectively a 32-bit load */ - tmp = get_address(s, 0, b2, d2); - tmp32_1 = tcg_temp_new_i32(); - tmp32_2 = tcg_const_i32(r3); - tcg_gen_qemu_ld32u(tmp, tmp, get_mem_index(s)); - store_reg32_i64(r1, tmp); - tcg_gen_trunc_i64_i32(tmp32_1, tmp); - set_cc_icm(s, tmp32_2, tmp32_1); - tcg_temp_free_i64(tmp); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - } else if (r3) { - uint32_t mask = 0x00ffffffUL; - uint32_t shift = 24; - int m3 = r3; - tmp = get_address(s, 0, b2, d2); - tmp2 = tcg_temp_new_i64(); - tmp32_1 = load_reg32(r1); - tmp32_2 = tcg_temp_new_i32(); - tmp32_3 = tcg_const_i32(r3); - tmp32_4 = tcg_const_i32(0); - while (m3) { - if (m3 & 8) { - tcg_gen_qemu_ld8u(tmp2, tmp, get_mem_index(s)); - tcg_gen_trunc_i64_i32(tmp32_2, tmp2); - if (shift) { - tcg_gen_shli_i32(tmp32_2, tmp32_2, shift); - } - tcg_gen_andi_i32(tmp32_1, tmp32_1, mask); - tcg_gen_or_i32(tmp32_1, tmp32_1, tmp32_2); - tcg_gen_or_i32(tmp32_4, tmp32_4, tmp32_2); - tcg_gen_addi_i64(tmp, tmp, 1); - } - m3 = (m3 << 1) & 0xf; - mask = (mask >> 8) | 0xff000000UL; - shift -= 8; - } - store_reg32(r1, tmp32_1); - set_cc_icm(s, tmp32_3, tmp32_4); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i32(tmp32_1); - tcg_temp_free_i32(tmp32_2); - tcg_temp_free_i32(tmp32_3); - tcg_temp_free_i32(tmp32_4); - } else { - /* i.e. env->cc = 0 */ - gen_op_movi_cc(s, 0); - } - break; - case 0xc0: - case 0xc2: - insn = ld_code6(env, s->pc); - r1 = (insn >> 36) & 0xf; - op = (insn >> 32) & 0xf; - i2 = (int)insn; - switch (opc) { - case 0xc0: - disas_c0(env, s, op, r1, i2); - break; - case 0xc2: - disas_c2(env, s, op, r1, i2); - break; - default: - tcg_abort(); - } - break; - case 0xd2: /* MVC D1(L,B1),D2(B2) [SS] */ - case 0xd4: /* NC D1(L,B1),D2(B2) [SS] */ - case 0xd5: /* CLC D1(L,B1),D2(B2) [SS] */ - case 0xd6: /* OC D1(L,B1),D2(B2) [SS] */ - case 0xd7: /* XC D1(L,B1),D2(B2) [SS] */ - case 0xdc: /* TR D1(L,B1),D2(B2) [SS] */ - case 0xf3: /* UNPK D1(L1,B1),D2(L2,B2) [SS] */ - insn = ld_code6(env, s->pc); - vl = tcg_const_i32((insn >> 32) & 0xff); - b1 = (insn >> 28) & 0xf; - b2 = (insn >> 12) & 0xf; - d1 = (insn >> 16) & 0xfff; - d2 = insn & 0xfff; - tmp = get_address(s, 0, b1, d1); - tmp2 = get_address(s, 0, b2, d2); - switch (opc) { - case 0xd2: - gen_op_mvc(s, (insn >> 32) & 0xff, tmp, tmp2); - break; - case 0xd4: - potential_page_fault(s); - gen_helper_nc(cc_op, cpu_env, vl, tmp, tmp2); - set_cc_static(s); - break; - case 0xd5: - gen_op_clc(s, (insn >> 32) & 0xff, tmp, tmp2); - break; - case 0xd6: - potential_page_fault(s); - gen_helper_oc(cc_op, cpu_env, vl, tmp, tmp2); - set_cc_static(s); - break; - case 0xd7: - potential_page_fault(s); - gen_helper_xc(cc_op, cpu_env, vl, tmp, tmp2); - set_cc_static(s); - break; - case 0xdc: - potential_page_fault(s); - gen_helper_tr(cpu_env, vl, tmp, tmp2); - set_cc_static(s); - break; - case 0xf3: - potential_page_fault(s); - gen_helper_unpk(cpu_env, vl, tmp, tmp2); - break; - default: - tcg_abort(); - } - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - break; -#ifndef CONFIG_USER_ONLY - case 0xda: /* MVCP D1(R1,B1),D2(B2),R3 [SS] */ - case 0xdb: /* MVCS D1(R1,B1),D2(B2),R3 [SS] */ - check_privileged(env, s, ilc); - potential_page_fault(s); - insn = ld_code6(env, s->pc); - r1 = (insn >> 36) & 0xf; - r3 = (insn >> 32) & 0xf; - b1 = (insn >> 28) & 0xf; - d1 = (insn >> 16) & 0xfff; - b2 = (insn >> 12) & 0xf; - d2 = insn & 0xfff; - tmp = load_reg(r1); - /* XXX key in r3 */ - tmp2 = get_address(s, 0, b1, d1); - tmp3 = get_address(s, 0, b2, d2); - if (opc == 0xda) { - gen_helper_mvcp(cc_op, cpu_env, tmp, tmp2, tmp3); - } else { - gen_helper_mvcs(cc_op, cpu_env, tmp, tmp2, tmp3); - } - set_cc_static(s); - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp3); - break; -#endif - case 0xe3: - insn = ld_code6(env, s->pc); - debug_insn(insn); - op = insn & 0xff; - r1 = (insn >> 36) & 0xf; - x2 = (insn >> 32) & 0xf; - b2 = (insn >> 28) & 0xf; - d2 = ((int)((((insn >> 16) & 0xfff) - | ((insn << 4) & 0xff000)) << 12)) >> 12; - disas_e3(env, s, op, r1, x2, b2, d2 ); - break; -#ifndef CONFIG_USER_ONLY - case 0xe5: - /* Test Protection */ - check_privileged(env, s, ilc); - insn = ld_code6(env, s->pc); - debug_insn(insn); - disas_e5(env, s, insn); - break; -#endif - case 0xeb: - insn = ld_code6(env, s->pc); - debug_insn(insn); - op = insn & 0xff; - r1 = (insn >> 36) & 0xf; - r3 = (insn >> 32) & 0xf; - b2 = (insn >> 28) & 0xf; - d2 = ((int)((((insn >> 16) & 0xfff) - | ((insn << 4) & 0xff000)) << 12)) >> 12; - disas_eb(env, s, op, r1, r3, b2, d2); - break; - case 0xed: - insn = ld_code6(env, s->pc); - debug_insn(insn); - op = insn & 0xff; - r1 = (insn >> 36) & 0xf; - x2 = (insn >> 32) & 0xf; - b2 = (insn >> 28) & 0xf; - d2 = (short)((insn >> 16) & 0xfff); - r1b = (insn >> 12) & 0xf; - disas_ed(env, s, op, r1, x2, b2, d2, r1b); + case 2: /* dl+dh split, signed 20 bit. */ + r = ((int8_t)r << 12) | (r >> 8); break; default: - qemu_log_mask(LOG_UNIMP, "unimplemented opcode 0x%x\n", opc); - gen_illegal_opcode(env, s, ilc); + abort(); + } + + /* Validate that the "compressed" encoding we selected above is valid. + I.e. we havn't make two different original fields overlap. */ + assert(((o->presentC >> f->indexC) & 1) == 0); + o->presentC |= 1 << f->indexC; + o->presentO |= 1 << f->indexO; + + o->c[f->indexC] = r; +} + +/* Lookup the insn at the current PC, extracting the operands into O and + returning the info struct for the insn. Returns NULL for invalid insn. */ + +static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s, + DisasFields *f) +{ + uint64_t insn, pc = s->pc; + int op, op2, ilen; + const DisasInsn *info; + + insn = ld_code2(env, pc); + op = (insn >> 8) & 0xff; + ilen = get_ilen(op); + s->next_pc = s->pc + ilen; + + switch (ilen) { + case 2: + insn = insn << 48; + break; + case 4: + insn = ld_code4(env, pc) << 32; + break; + case 6: + insn = (insn << 48) | (ld_code4(env, pc + 2) << 16); + break; + default: + abort(); + } + + /* We can't actually determine the insn format until we've looked up + the full insn opcode. Which we can't do without locating the + secondary opcode. Assume by default that OP2 is at bit 40; for + those smaller insns that don't actually have a secondary opcode + this will correctly result in OP2 = 0. */ + switch (op) { + case 0x01: /* E */ + case 0x80: /* S */ + case 0x82: /* S */ + case 0x93: /* S */ + case 0xb2: /* S, RRF, RRE */ + case 0xb3: /* RRE, RRD, RRF */ + case 0xb9: /* RRE, RRF */ + case 0xe5: /* SSE, SIL */ + op2 = (insn << 8) >> 56; + break; + case 0xa5: /* RI */ + case 0xa7: /* RI */ + case 0xc0: /* RIL */ + case 0xc2: /* RIL */ + case 0xc4: /* RIL */ + case 0xc6: /* RIL */ + case 0xc8: /* SSF */ + case 0xcc: /* RIL */ + op2 = (insn << 12) >> 60; + break; + case 0xd0 ... 0xdf: /* SS */ + case 0xe1: /* SS */ + case 0xe2: /* SS */ + case 0xe8: /* SS */ + case 0xe9: /* SS */ + case 0xea: /* SS */ + case 0xee ... 0xf3: /* SS */ + case 0xf8 ... 0xfd: /* SS */ + op2 = 0; + break; + default: + op2 = (insn << 40) >> 56; break; } - /* Instruction length is encoded in the opcode */ - s->pc += (ilc * 2); + memset(f, 0, sizeof(*f)); + f->op = op; + f->op2 = op2; + + /* Lookup the instruction. */ + info = lookup_opc(op << 8 | op2); + + /* If we found it, extract the operands. */ + if (info != NULL) { + DisasFormat fmt = info->fmt; + int i; + + for (i = 0; i < NUM_C_FIELD; ++i) { + extract_field(f, &format_info[fmt].op[i], insn); + } + } + return info; +} + +static ExitStatus translate_one(CPUS390XState *env, DisasContext *s) +{ + const DisasInsn *insn; + ExitStatus ret = NO_EXIT; + DisasFields f; + DisasOps o; + + /* Search for the insn in the table. */ + insn = extract_insn(env, s, &f); + + /* Not found means unimplemented/illegal opcode. */ + if (insn == NULL) { + qemu_log_mask(LOG_UNIMP, "unimplemented opcode 0x%02x%02x\n", + f.op, f.op2); + gen_illegal_opcode(s); + return EXIT_NORETURN; + } + + /* Check for insn specification exceptions. */ + if (insn->spec) { + int spec = insn->spec, excp = 0, r; + + if (spec & SPEC_r1_even) { + r = get_field(&f, r1); + if (r & 1) { + excp = PGM_SPECIFICATION; + } + } + if (spec & SPEC_r2_even) { + r = get_field(&f, r2); + if (r & 1) { + excp = PGM_SPECIFICATION; + } + } + if (spec & SPEC_r3_even) { + r = get_field(&f, r3); + if (r & 1) { + excp = PGM_SPECIFICATION; + } + } + if (spec & SPEC_r1_f128) { + r = get_field(&f, r1); + if (r > 13) { + excp = PGM_SPECIFICATION; + } + } + if (spec & SPEC_r2_f128) { + r = get_field(&f, r2); + if (r > 13) { + excp = PGM_SPECIFICATION; + } + } + if (excp) { + gen_program_exception(s, excp); + return EXIT_NORETURN; + } + } + + /* Set up the strutures we use to communicate with the helpers. */ + s->insn = insn; + s->fields = &f; + o.g_out = o.g_out2 = o.g_in1 = o.g_in2 = false; + TCGV_UNUSED_I64(o.out); + TCGV_UNUSED_I64(o.out2); + TCGV_UNUSED_I64(o.in1); + TCGV_UNUSED_I64(o.in2); + TCGV_UNUSED_I64(o.addr1); + + /* Implement the instruction. */ + if (insn->help_in1) { + insn->help_in1(s, &f, &o); + } + if (insn->help_in2) { + insn->help_in2(s, &f, &o); + } + if (insn->help_prep) { + insn->help_prep(s, &f, &o); + } + if (insn->help_op) { + ret = insn->help_op(s, &o); + } + if (insn->help_wout) { + insn->help_wout(s, &f, &o); + } + if (insn->help_cout) { + insn->help_cout(s, &o); + } + + /* Free any temporaries created by the helpers. */ + if (!TCGV_IS_UNUSED_I64(o.out) && !o.g_out) { + tcg_temp_free_i64(o.out); + } + if (!TCGV_IS_UNUSED_I64(o.out2) && !o.g_out2) { + tcg_temp_free_i64(o.out2); + } + if (!TCGV_IS_UNUSED_I64(o.in1) && !o.g_in1) { + tcg_temp_free_i64(o.in1); + } + if (!TCGV_IS_UNUSED_I64(o.in2) && !o.g_in2) { + tcg_temp_free_i64(o.in2); + } + if (!TCGV_IS_UNUSED_I64(o.addr1)) { + tcg_temp_free_i64(o.addr1); + } + + /* Advance to the next instruction. */ + s->pc = s->next_pc; + return ret; } static inline void gen_intermediate_code_internal(CPUS390XState *env, @@ -5121,6 +4744,8 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env, int j, lj = -1; int num_insns, max_insns; CPUBreakpoint *bp; + ExitStatus status; + bool do_debug; pc_start = tb->pc; @@ -5129,10 +4754,10 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env, pc_start &= 0x7fffffff; } - dc.pc = pc_start; - dc.is_jmp = DISAS_NEXT; dc.tb = tb; + dc.pc = pc_start; dc.cc_op = CC_OP_DYNAMIC; + do_debug = dc.singlestep_enabled = env->singlestep_enabled; gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; @@ -5144,17 +4769,9 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env, max_insns = CF_COUNT_MASK; } - gen_icount_start(); + gen_tb_start(); do { - if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { - QTAILQ_FOREACH(bp, &env->breakpoints, entry) { - if (bp->pc == dc.pc) { - gen_debug(&dc); - break; - } - } - } if (search_pc) { j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; if (lj < j) { @@ -5168,7 +4785,7 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env, tcg_ctx.gen_opc_instr_start[lj] = 1; tcg_ctx.gen_opc_icount[lj] = num_insns; } - if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + if (++num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { gen_io_start(); } @@ -5176,37 +4793,59 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env, tcg_gen_debug_insn_start(dc.pc); } - disas_s390_insn(env, &dc); - - num_insns++; - if (env->singlestep_enabled) { - gen_debug(&dc); + status = NO_EXIT; + if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { + QTAILQ_FOREACH(bp, &env->breakpoints, entry) { + if (bp->pc == dc.pc) { + status = EXIT_PC_STALE; + do_debug = true; + break; + } + } + } + if (status == NO_EXIT) { + status = translate_one(env, &dc); } - } while (!dc.is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end - && dc.pc < next_page_start - && num_insns < max_insns && !env->singlestep_enabled - && !singlestep); - if (!dc.is_jmp) { - update_psw_addr(&dc); - } - - if (singlestep && dc.cc_op != CC_OP_DYNAMIC) { - gen_op_calc_cc(&dc); - } else { - /* next TB starts off with CC_OP_DYNAMIC, so make sure the cc op type - is in env */ - gen_op_set_cc_op(&dc); - } + /* If we reach a page boundary, are single stepping, + or exhaust instruction count, stop generation. */ + if (status == NO_EXIT + && (dc.pc >= next_page_start + || tcg_ctx.gen_opc_ptr >= gen_opc_end + || num_insns >= max_insns + || singlestep + || env->singlestep_enabled)) { + status = EXIT_PC_STALE; + } + } while (status == NO_EXIT); if (tb->cflags & CF_LAST_IO) { gen_io_end(); } - /* Generate the return instruction */ - if (dc.is_jmp != DISAS_TB_JUMP) { - tcg_gen_exit_tb(0); + + switch (status) { + case EXIT_GOTO_TB: + case EXIT_NORETURN: + break; + case EXIT_PC_STALE: + update_psw_addr(&dc); + /* FALLTHRU */ + case EXIT_PC_UPDATED: + /* Next TB starts off with CC_OP_DYNAMIC, so make sure the + cc op type is in env */ + update_cc_op(&dc); + /* Exit the TB, either by raising a debug exception or by return. */ + if (do_debug) { + gen_exception(EXCP_DEBUG); + } else { + tcg_gen_exit_tb(0); + } + break; + default: + abort(); } - gen_icount_end(tb, num_insns); + + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; if (search_pc) { j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; @@ -5218,6 +4857,7 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env, tb->size = dc.pc - pc_start; tb->icount = num_insns; } + #if defined(S390X_DEBUG_DISAS) if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { qemu_log("IN: %s\n", lookup_symbol(pc_start)); diff --git a/target-sh4/Makefile.objs b/target-sh4/Makefile.objs index ca20f21443..cb448a840f 100644 --- a/target-sh4/Makefile.objs +++ b/target-sh4/Makefile.objs @@ -1,2 +1 @@ obj-y += translate.o op_helper.o helper.o cpu.o -obj-$(CONFIG_SOFTMMU) += machine.o diff --git a/target-sh4/cpu-qom.h b/target-sh4/cpu-qom.h index 09573c9c34..f8c80d30b4 100644 --- a/target-sh4/cpu-qom.h +++ b/target-sh4/cpu-qom.h @@ -24,6 +24,10 @@ #define TYPE_SUPERH_CPU "superh-cpu" +#define TYPE_SH7750R_CPU "sh7750r-" TYPE_SUPERH_CPU +#define TYPE_SH7751R_CPU "sh7751r-" TYPE_SUPERH_CPU +#define TYPE_SH7785_CPU "sh7785-" TYPE_SUPERH_CPU + #define SUPERH_CPU_CLASS(klass) \ OBJECT_CLASS_CHECK(SuperHCPUClass, (klass), TYPE_SUPERH_CPU) #define SUPERH_CPU(obj) \ @@ -33,7 +37,12 @@ /** * SuperHCPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. + * @name: The name. + * @pvr: Processor Version Register + * @prr: Processor Revision Register + * @cvr: Cache Version Register * * A SuperH CPU model. */ @@ -42,7 +51,13 @@ typedef struct SuperHCPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); + + const char *name; + uint32_t pvr; + uint32_t prr; + uint32_t cvr; } SuperHCPUClass; /** @@ -66,5 +81,8 @@ static inline SuperHCPU *sh_env_get_cpu(CPUSH4State *env) #define ENV_GET_CPU(e) CPU(sh_env_get_cpu(e)) +#define ENV_OFFSET offsetof(SuperHCPU, env) + +void superh_cpu_do_interrupt(CPUState *cpu); #endif diff --git a/target-sh4/cpu.c b/target-sh4/cpu.c index a1a177fa88..898aecde4f 100644 --- a/target-sh4/cpu.c +++ b/target-sh4/cpu.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "qemu-common.h" +#include "migration/vmstate.h" /* CPUClass::reset() */ @@ -31,7 +32,7 @@ static void superh_cpu_reset(CPUState *s) CPUSH4State *env = &cpu->env; if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); + qemu_log("CPU Reset (CPU %d)\n", s->cpu_index); log_cpu_state(env, 0); } @@ -53,23 +54,227 @@ static void superh_cpu_reset(CPUState *s) set_default_nan_mode(1, &env->fp_status); } -static void superh_cpu_initfn(Object *obj) +typedef struct SuperHCPUListState { + fprintf_function cpu_fprintf; + FILE *file; +} SuperHCPUListState; + +/* Sort alphabetically by type name. */ +static gint superh_cpu_list_compare(gconstpointer a, gconstpointer b) +{ + ObjectClass *class_a = (ObjectClass *)a; + ObjectClass *class_b = (ObjectClass *)b; + const char *name_a, *name_b; + + name_a = object_class_get_name(class_a); + name_b = object_class_get_name(class_b); + return strcmp(name_a, name_b); +} + +static void superh_cpu_list_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); + SuperHCPUListState *s = user_data; + + (*s->cpu_fprintf)(s->file, "%s\n", + scc->name); +} + +void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ + SuperHCPUListState s = { + .cpu_fprintf = cpu_fprintf, + .file = f, + }; + GSList *list; + + list = object_class_get_list(TYPE_SUPERH_CPU, false); + list = g_slist_sort(list, superh_cpu_list_compare); + g_slist_foreach(list, superh_cpu_list_entry, &s); + g_slist_free(list); +} + +static gint superh_cpu_name_compare(gconstpointer a, gconstpointer b) +{ + const SuperHCPUClass *scc = SUPERH_CPU_CLASS(a); + const char *name = b; + + return strcasecmp(scc->name, name); +} + +static ObjectClass *superh_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + GSList *list, *item; + + if (cpu_model == NULL) { + return NULL; + } + if (strcasecmp(cpu_model, "any") == 0) { + return object_class_by_name(TYPE_SH7750R_CPU); + } + + oc = object_class_by_name(cpu_model); + if (oc != NULL && object_class_dynamic_cast(oc, TYPE_SUPERH_CPU) != NULL + && !object_class_is_abstract(oc)) { + return oc; + } + + oc = NULL; + list = object_class_get_list(TYPE_SUPERH_CPU, false); + item = g_slist_find_custom(list, cpu_model, superh_cpu_name_compare); + if (item != NULL) { + oc = item->data; + } + g_slist_free(list); + return oc; +} + +SuperHCPU *cpu_sh4_init(const char *cpu_model) +{ + SuperHCPU *cpu; + CPUSH4State *env; + ObjectClass *oc; + + oc = superh_cpu_class_by_name(cpu_model); + if (oc == NULL) { + return NULL; + } + cpu = SUPERH_CPU(object_new(object_class_get_name(oc))); + env = &cpu->env; + env->cpu_model_str = cpu_model; + + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + + return cpu; +} + +static void sh7750r_cpu_initfn(Object *obj) { SuperHCPU *cpu = SUPERH_CPU(obj); CPUSH4State *env = &cpu->env; + env->id = SH_CPU_SH7750R; + env->features = SH_FEATURE_BCR3_AND_BCR4; +} + +static void sh7750r_class_init(ObjectClass *oc, void *data) +{ + SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); + + scc->name = "SH7750R"; + scc->pvr = 0x00050000; + scc->prr = 0x00000100; + scc->cvr = 0x00110000; +} + +static const TypeInfo sh7750r_type_info = { + .name = TYPE_SH7750R_CPU, + .parent = TYPE_SUPERH_CPU, + .class_init = sh7750r_class_init, + .instance_init = sh7750r_cpu_initfn, +}; + +static void sh7751r_cpu_initfn(Object *obj) +{ + SuperHCPU *cpu = SUPERH_CPU(obj); + CPUSH4State *env = &cpu->env; + + env->id = SH_CPU_SH7751R; + env->features = SH_FEATURE_BCR3_AND_BCR4; +} + +static void sh7751r_class_init(ObjectClass *oc, void *data) +{ + SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); + + scc->name = "SH7751R"; + scc->pvr = 0x04050005; + scc->prr = 0x00000113; + scc->cvr = 0x00110000; /* Neutered caches, should be 0x20480000 */ +} + +static const TypeInfo sh7751r_type_info = { + .name = TYPE_SH7751R_CPU, + .parent = TYPE_SUPERH_CPU, + .class_init = sh7751r_class_init, + .instance_init = sh7751r_cpu_initfn, +}; + +static void sh7785_cpu_initfn(Object *obj) +{ + SuperHCPU *cpu = SUPERH_CPU(obj); + CPUSH4State *env = &cpu->env; + + env->id = SH_CPU_SH7785; + env->features = SH_FEATURE_SH4A; +} + +static void sh7785_class_init(ObjectClass *oc, void *data) +{ + SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); + + scc->name = "SH7785"; + scc->pvr = 0x10300700; + scc->prr = 0x00000200; + scc->cvr = 0x71440211; +} + +static const TypeInfo sh7785_type_info = { + .name = TYPE_SH7785_CPU, + .parent = TYPE_SUPERH_CPU, + .class_init = sh7785_class_init, + .instance_init = sh7785_cpu_initfn, +}; + +static void superh_cpu_realizefn(DeviceState *dev, Error **errp) +{ + SuperHCPU *cpu = SUPERH_CPU(dev); + SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(dev); + + cpu_reset(CPU(cpu)); + qemu_init_vcpu(&cpu->env); + + scc->parent_realize(dev, errp); +} + +static void superh_cpu_initfn(Object *obj) +{ + CPUState *cs = CPU(obj); + SuperHCPU *cpu = SUPERH_CPU(obj); + CPUSH4State *env = &cpu->env; + + cs->env_ptr = env; cpu_exec_init(env); env->movcal_backup_tail = &(env->movcal_backup); + + if (tcg_enabled()) { + sh4_translate_init(); + } } +static const VMStateDescription vmstate_sh_cpu = { + .name = "cpu", + .unmigratable = 1, +}; + static void superh_cpu_class_init(ObjectClass *oc, void *data) { + DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); + scc->parent_realize = dc->realize; + dc->realize = superh_cpu_realizefn; + scc->parent_reset = cc->reset; cc->reset = superh_cpu_reset; + + cc->class_by_name = superh_cpu_class_by_name; + cc->do_interrupt = superh_cpu_do_interrupt; + dc->vmsd = &vmstate_sh_cpu; } static const TypeInfo superh_cpu_type_info = { @@ -77,7 +282,7 @@ static const TypeInfo superh_cpu_type_info = { .parent = TYPE_CPU, .instance_size = sizeof(SuperHCPU), .instance_init = superh_cpu_initfn, - .abstract = false, + .abstract = true, .class_size = sizeof(SuperHCPUClass), .class_init = superh_cpu_class_init, }; @@ -85,6 +290,9 @@ static const TypeInfo superh_cpu_type_info = { static void superh_cpu_register_types(void) { type_register_static(&superh_cpu_type_info); + type_register_static(&sh7750r_type_info); + type_register_static(&sh7751r_type_info); + type_register_static(&sh7785_type_info); } type_init(superh_cpu_register_types) diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h index 34e9b0acf7..fd7da92548 100644 --- a/target-sh4/cpu.h +++ b/target-sh4/cpu.h @@ -179,9 +179,6 @@ typedef struct CPUSH4State { CPU_COMMON int id; /* CPU model */ - uint32_t pvr; /* Processor Version Register */ - uint32_t prr; /* Processor Revision Register */ - uint32_t cvr; /* Cache Version Register */ void *intc_handle; int in_sleep; /* SR_BL ignored during sleep */ @@ -191,6 +188,7 @@ typedef struct CPUSH4State { #include "cpu-qom.h" +void sh4_translate_init(void); SuperHCPU *cpu_sh4_init(const char *cpu_model); int cpu_sh4_exec(CPUSH4State * s); int cpu_sh4_signal_handler(int host_signum, void *pinfo, @@ -198,7 +196,6 @@ int cpu_sh4_signal_handler(int host_signum, void *pinfo, int cpu_sh4_handle_mmu_fault(CPUSH4State * env, target_ulong address, int rw, int mmu_idx); #define cpu_handle_mmu_fault cpu_sh4_handle_mmu_fault -void do_interrupt(CPUSH4State * env); void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf); #if !defined(CONFIG_USER_ONLY) @@ -371,9 +368,7 @@ static inline void cpu_get_tb_cpu_state(CPUSH4State *env, target_ulong *pc, static inline bool cpu_has_work(CPUState *cpu) { - CPUSH4State *env = &SUPERH_CPU(cpu)->env; - - return env->interrupt_request & CPU_INTERRUPT_HARD; + return cpu->interrupt_request & CPU_INTERRUPT_HARD; } #include "exec/exec-all.h" diff --git a/target-sh4/helper.c b/target-sh4/helper.c index ddebc78964..0a9cb3ac98 100644 --- a/target-sh4/helper.c +++ b/target-sh4/helper.c @@ -31,9 +31,12 @@ #if defined(CONFIG_USER_ONLY) -void do_interrupt (CPUSH4State *env) +void superh_cpu_do_interrupt(CPUState *cs) { - env->exception_index = -1; + SuperHCPU *cpu = SUPERH_CPU(cs); + CPUSH4State *env = &cpu->env; + + env->exception_index = -1; } int cpu_sh4_handle_mmu_fault(CPUSH4State * env, target_ulong address, int rw, @@ -78,9 +81,11 @@ int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) #define MMU_DADDR_ERROR_READ (-12) #define MMU_DADDR_ERROR_WRITE (-13) -void do_interrupt(CPUSH4State * env) +void superh_cpu_do_interrupt(CPUState *cs) { - int do_irq = env->interrupt_request & CPU_INTERRUPT_HARD; + SuperHCPU *cpu = SUPERH_CPU(cs); + CPUSH4State *env = &cpu->env; + int do_irq = cs->interrupt_request & CPU_INTERRUPT_HARD; int do_exp, irq_vector = env->exception_index; /* prioritize exceptions over interrupts */ diff --git a/target-sh4/machine.c b/target-sh4/machine.c deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/target-sh4/op_helper.c b/target-sh4/op_helper.c index 09e3d23aff..e955e810b5 100644 --- a/target-sh4/op_helper.c +++ b/target-sh4/op_helper.c @@ -102,7 +102,9 @@ void helper_debug(CPUSH4State *env) void helper_sleep(CPUSH4State *env) { - env->halted = 1; + CPUState *cs = CPU(sh_env_get_cpu(env)); + + cs->halted = 1; env->in_sleep = 1; raise_exception(env, EXCP_HLT, 0); } diff --git a/target-sh4/translate.c b/target-sh4/translate.c index 260aaab559..14fdb8fc2d 100644 --- a/target-sh4/translate.c +++ b/target-sh4/translate.c @@ -71,7 +71,7 @@ static uint32_t gen_opc_hflags[OPC_BUF_SIZE]; #include "exec/gen-icount.h" -static void sh4_translate_init(void) +void sh4_translate_init(void) { int i; static int done_init = 0; @@ -175,90 +175,6 @@ void cpu_dump_state(CPUSH4State * env, FILE * f, } } -typedef struct { - const char *name; - int id; - uint32_t pvr; - uint32_t prr; - uint32_t cvr; - uint32_t features; -} sh4_def_t; - -static sh4_def_t sh4_defs[] = { - { - .name = "SH7750R", - .id = SH_CPU_SH7750R, - .pvr = 0x00050000, - .prr = 0x00000100, - .cvr = 0x00110000, - .features = SH_FEATURE_BCR3_AND_BCR4, - }, { - .name = "SH7751R", - .id = SH_CPU_SH7751R, - .pvr = 0x04050005, - .prr = 0x00000113, - .cvr = 0x00110000, /* Neutered caches, should be 0x20480000 */ - .features = SH_FEATURE_BCR3_AND_BCR4, - }, { - .name = "SH7785", - .id = SH_CPU_SH7785, - .pvr = 0x10300700, - .prr = 0x00000200, - .cvr = 0x71440211, - .features = SH_FEATURE_SH4A, - }, -}; - -static const sh4_def_t *cpu_sh4_find_by_name(const char *name) -{ - int i; - - if (strcasecmp(name, "any") == 0) - return &sh4_defs[0]; - - for (i = 0; i < ARRAY_SIZE(sh4_defs); i++) - if (strcasecmp(name, sh4_defs[i].name) == 0) - return &sh4_defs[i]; - - return NULL; -} - -void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(sh4_defs); i++) - (*cpu_fprintf)(f, "%s\n", sh4_defs[i].name); -} - -static void cpu_register(CPUSH4State *env, const sh4_def_t *def) -{ - env->pvr = def->pvr; - env->prr = def->prr; - env->cvr = def->cvr; - env->id = def->id; -} - -SuperHCPU *cpu_sh4_init(const char *cpu_model) -{ - SuperHCPU *cpu; - CPUSH4State *env; - const sh4_def_t *def; - - def = cpu_sh4_find_by_name(cpu_model); - if (!def) - return NULL; - cpu = SUPERH_CPU(object_new(TYPE_SUPERH_CPU)); - env = &cpu->env; - env->features = def->features; - sh4_translate_init(); - env->cpu_model_str = cpu_model; - cpu_reset(CPU(cpu)); - cpu_register(env, def); - qemu_init_vcpu(env); - return cpu; -} - static void gen_goto_tb(DisasContext * ctx, int n, target_ulong dest) { TranslationBlock *tb; @@ -833,36 +749,10 @@ static void _decode_opc(DisasContext * ctx) gen_helper_div1(REG(B11_8), cpu_env, REG(B7_4), REG(B11_8)); return; case 0x300d: /* dmuls.l Rm,Rn */ - { - TCGv_i64 tmp1 = tcg_temp_new_i64(); - TCGv_i64 tmp2 = tcg_temp_new_i64(); - - tcg_gen_ext_i32_i64(tmp1, REG(B7_4)); - tcg_gen_ext_i32_i64(tmp2, REG(B11_8)); - tcg_gen_mul_i64(tmp1, tmp1, tmp2); - tcg_gen_trunc_i64_i32(cpu_macl, tmp1); - tcg_gen_shri_i64(tmp1, tmp1, 32); - tcg_gen_trunc_i64_i32(cpu_mach, tmp1); - - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp1); - } + tcg_gen_muls2_i32(cpu_macl, cpu_mach, REG(B7_4), REG(B11_8)); return; case 0x3005: /* dmulu.l Rm,Rn */ - { - TCGv_i64 tmp1 = tcg_temp_new_i64(); - TCGv_i64 tmp2 = tcg_temp_new_i64(); - - tcg_gen_extu_i32_i64(tmp1, REG(B7_4)); - tcg_gen_extu_i32_i64(tmp2, REG(B11_8)); - tcg_gen_mul_i64(tmp1, tmp1, tmp2); - tcg_gen_trunc_i64_i32(cpu_macl, tmp1); - tcg_gen_shri_i64(tmp1, tmp1, 32); - tcg_gen_trunc_i64_i32(cpu_mach, tmp1); - - tcg_temp_free_i64(tmp2); - tcg_temp_free_i64(tmp1); - } + tcg_gen_mulu2_i32(cpu_macl, cpu_mach, REG(B7_4), REG(B11_8)); return; case 0x600e: /* exts.b Rm,Rn */ tcg_gen_ext8s_i32(REG(B11_8), REG(B7_4)); @@ -1985,7 +1875,7 @@ gen_intermediate_code_internal(CPUSH4State * env, TranslationBlock * tb, max_insns = tb->cflags & CF_COUNT_MASK; if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_icount_start(); + gen_tb_start(); while (ctx.bstate == BS_NONE && tcg_ctx.gen_opc_ptr < gen_opc_end) { if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { QTAILQ_FOREACH(bp, &env->breakpoints, entry) { @@ -2055,7 +1945,7 @@ gen_intermediate_code_internal(CPUSH4State * env, TranslationBlock * tb, } } - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; if (search_pc) { i = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; diff --git a/target-sparc/cpu-qom.h b/target-sparc/cpu-qom.h index 2a738ae360..d4fe89ebfd 100644 --- a/target-sparc/cpu-qom.h +++ b/target-sparc/cpu-qom.h @@ -38,6 +38,7 @@ /** * SPARCCPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. * * A SPARC CPU model. @@ -47,6 +48,7 @@ typedef struct SPARCCPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); } SPARCCPUClass; @@ -71,5 +73,8 @@ static inline SPARCCPU *sparc_env_get_cpu(CPUSPARCState *env) #define ENV_GET_CPU(e) CPU(sparc_env_get_cpu(e)) +#define ENV_OFFSET offsetof(SPARCCPU, env) + +void sparc_cpu_do_interrupt(CPUState *cpu); #endif diff --git a/target-sparc/cpu.c b/target-sparc/cpu.c index 882d30642a..ab1adfd7ea 100644 --- a/target-sparc/cpu.c +++ b/target-sparc/cpu.c @@ -31,7 +31,7 @@ static void sparc_cpu_reset(CPUState *s) CPUSPARCState *env = &cpu->env; if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); + qemu_log("CPU Reset (CPU %d)\n", s->cpu_index); log_cpu_state(env, 0); } @@ -114,15 +114,12 @@ SPARCCPU *cpu_sparc_init(const char *cpu_model) cpu = SPARC_CPU(object_new(TYPE_SPARC_CPU)); env = &cpu->env; - if (tcg_enabled()) { - gen_intermediate_code_init(env); - } - if (cpu_sparc_register(env, cpu_model) < 0) { - object_delete(OBJECT(cpu)); + object_unref(OBJECT(cpu)); return NULL; } - qemu_init_vcpu(env); + + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); return cpu; } @@ -583,13 +580,13 @@ static const sparc_def_t sparc_defs[] = { .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ .mmu_version = 0xf3000000, .mmu_bm = 0x00000000, - .mmu_ctpr_mask = 0x007ffff0, - .mmu_cxr_mask = 0x0000003f, + .mmu_ctpr_mask = 0xfffffffc, + .mmu_cxr_mask = 0x000000ff, .mmu_sfsr_mask = 0xffffffff, .mmu_trcr_mask = 0xffffffff, .nwindows = 8, .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN | - CPU_FEATURE_ASR17 | CPU_FEATURE_CACHE_CTRL, + CPU_FEATURE_ASR17 | CPU_FEATURE_CACHE_CTRL | CPU_FEATURE_POWERDOWN, }, #endif }; @@ -851,12 +848,28 @@ void cpu_dump_state(CPUSPARCState *env, FILE *f, fprintf_function cpu_fprintf, cpu_fprintf(f, "\n"); } +static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) +{ + SPARCCPU *cpu = SPARC_CPU(dev); + SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(dev); + + qemu_init_vcpu(&cpu->env); + + scc->parent_realize(dev, errp); +} + static void sparc_cpu_initfn(Object *obj) { + CPUState *cs = CPU(obj); SPARCCPU *cpu = SPARC_CPU(obj); CPUSPARCState *env = &cpu->env; + cs->env_ptr = env; cpu_exec_init(env); + + if (tcg_enabled()) { + gen_intermediate_code_init(env); + } } static void sparc_cpu_uninitfn(Object *obj) @@ -871,9 +884,15 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) { SPARCCPUClass *scc = SPARC_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + scc->parent_realize = dc->realize; + dc->realize = sparc_cpu_realizefn; scc->parent_reset = cc->reset; cc->reset = sparc_cpu_reset; + + cc->do_interrupt = sparc_cpu_do_interrupt; } static const TypeInfo sparc_cpu_type_info = { diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index 7389b03514..6fa77789cd 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -270,6 +270,7 @@ typedef struct sparc_def_t { #define CPU_FEATURE_TA0_SHUTDOWN (1 << 14) /* Shutdown on "ta 0x0" */ #define CPU_FEATURE_ASR17 (1 << 15) #define CPU_FEATURE_CACHE_CTRL (1 << 16) +#define CPU_FEATURE_POWERDOWN (1 << 17) #ifndef TARGET_SPARC64 #define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \ @@ -552,7 +553,6 @@ int cpu_cwp_dec(CPUSPARCState *env1, int cwp); void cpu_set_cwp(CPUSPARCState *env1, int new_cwp); /* int_helper.c */ -void do_interrupt(CPUSPARCState *env); void leon3_irq_manager(CPUSPARCState *env, void *irq_manager, int intno); /* sun4m.c, sun4u.c */ @@ -761,9 +761,10 @@ static inline bool tb_am_enabled(int tb_flags) static inline bool cpu_has_work(CPUState *cpu) { - CPUSPARCState *env1 = &SPARC_CPU(cpu)->env; + SPARCCPU *sparc_cpu = SPARC_CPU(cpu); + CPUSPARCState *env1 = &sparc_cpu->env; - return (env1->interrupt_request & CPU_INTERRUPT_HARD) && + return (cpu->interrupt_request & CPU_INTERRUPT_HARD) && cpu_interrupts_enabled(env1); } diff --git a/target-sparc/helper.c b/target-sparc/helper.c index 91ecfc7aa8..57c20af478 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -225,3 +225,16 @@ target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1, cpu_restore_state(env, GETPC()); helper_raise_exception(env, TT_TOVF); } + +#ifndef TARGET_SPARC64 +void helper_power_down(CPUSPARCState *env) +{ + CPUState *cs = CPU(sparc_env_get_cpu(env)); + + cs->halted = 1; + env->exception_index = EXCP_HLT; + env->pc = env->npc; + env->npc = env->pc + 4; + cpu_loop_exit(env); +} +#endif diff --git a/target-sparc/helper.h b/target-sparc/helper.h index cfcdab1ea4..15f73283fa 100644 --- a/target-sparc/helper.h +++ b/target-sparc/helper.h @@ -4,6 +4,7 @@ DEF_HELPER_1(rett, void, env) DEF_HELPER_2(wrpsr, void, env, tl) DEF_HELPER_1(rdpsr, tl, env) +DEF_HELPER_1(power_down, void, env) #else DEF_HELPER_2(wrpil, void, env, tl) DEF_HELPER_2(wrpstate, void, env, tl) diff --git a/target-sparc/int32_helper.c b/target-sparc/int32_helper.c index c35f522e0f..722146065a 100644 --- a/target-sparc/int32_helper.c +++ b/target-sparc/int32_helper.c @@ -58,8 +58,10 @@ static const char * const excp_names[0x80] = { }; #endif -void do_interrupt(CPUSPARCState *env) +void sparc_cpu_do_interrupt(CPUState *cs) { + SPARCCPU *cpu = SPARC_CPU(cs); + CPUSPARCState *env = &cpu->env; int cwp, intno = env->exception_index; /* Compute PSR before exposing state. */ diff --git a/target-sparc/int64_helper.c b/target-sparc/int64_helper.c index df37aa1d14..f411884c6e 100644 --- a/target-sparc/int64_helper.c +++ b/target-sparc/int64_helper.c @@ -59,8 +59,10 @@ static const char * const excp_names[0x80] = { }; #endif -void do_interrupt(CPUSPARCState *env) +void sparc_cpu_do_interrupt(CPUState *cs) { + SPARCCPU *cpu = SPARC_CPU(cs); + CPUSPARCState *env = &cpu->env; int intno = env->exception_index; trap_state *tsptr; diff --git a/target-sparc/ldst_helper.c b/target-sparc/ldst_helper.c index cf1bddf2db..6d767fb45a 100644 --- a/target-sparc/ldst_helper.c +++ b/target-sparc/ldst_helper.c @@ -514,6 +514,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, int asi, int size, #endif break; case 3: /* MMU probe */ + case 0x18: /* LEON3 MMU probe */ { int mmulev; @@ -528,6 +529,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, int asi, int size, } break; case 4: /* read MMU regs */ + case 0x19: /* LEON3 read MMU regs */ { int reg = (addr >> 8) & 0x1f; @@ -603,6 +605,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, int asi, int size, case 0xf: /* D-cache data */ break; case 0x20: /* MMU passthrough */ + case 0x1c: /* LEON MMU passthrough */ switch (size) { case 1: ret = ldub_phys(addr); @@ -844,6 +847,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, int asi, #endif break; case 3: /* MMU flush */ + case 0x18: /* LEON3 MMU flush */ { int mmulev; @@ -868,6 +872,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, int asi, } break; case 4: /* write MMU regs */ + case 0x19: /* LEON3 write MMU regs */ { int reg = (addr >> 8) & 0x1f; uint32_t oldreg; @@ -996,6 +1001,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, int asi, } break; case 0x20: /* MMU passthrough */ + case 0x1c: /* LEON MMU passthrough */ { switch (size) { case 1: @@ -1850,7 +1856,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, DPRINTF_MMU("LSU change: 0x%" PRIx64 " -> 0x%" PRIx64 "\n", oldreg, env->lsu); #ifdef DEBUG_MMU - dump_mmu(stdout, fprintf, env1); + dump_mmu(stdout, fprintf, env); #endif tlb_flush(env, 1); } diff --git a/target-sparc/translate.c b/target-sparc/translate.c index ca75e1aa48..eb6e800977 100644 --- a/target-sparc/translate.c +++ b/target-sparc/translate.c @@ -448,19 +448,16 @@ static void gen_op_addx_int(DisasContext *dc, TCGv dst, TCGv src1, case CC_OP_ADD: case CC_OP_TADD: case CC_OP_TADDTV: -#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32 - { - /* For 32-bit hosts, we can re-use the host's hardware carry - generation by using an ADD2 opcode. We discard the low - part of the output. Ideally we'd combine this operation - with the add that generated the carry in the first place. */ - TCGv dst_low = tcg_temp_new(); - tcg_gen_op6_i32(INDEX_op_add2_i32, dst_low, dst, - cpu_cc_src, src1, cpu_cc_src2, src2); - tcg_temp_free(dst_low); + if (TARGET_LONG_BITS == 32) { + /* We can re-use the host's hardware carry generation by using + an ADD2 opcode. We discard the low part of the output. + Ideally we'd combine this operation with the add that + generated the carry in the first place. */ + carry = tcg_temp_new(); + tcg_gen_add2_tl(carry, dst, cpu_cc_src, src1, cpu_cc_src2, src2); + tcg_temp_free(carry); goto add_done; } -#endif carry_32 = gen_add32_carry32(); break; @@ -492,9 +489,7 @@ static void gen_op_addx_int(DisasContext *dc, TCGv dst, TCGv src1, tcg_temp_free(carry); #endif -#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32 add_done: -#endif if (update_cc) { tcg_gen_mov_tl(cpu_cc_src, src1); tcg_gen_mov_tl(cpu_cc_src2, src2); @@ -554,19 +549,16 @@ static void gen_op_subx_int(DisasContext *dc, TCGv dst, TCGv src1, case CC_OP_SUB: case CC_OP_TSUB: case CC_OP_TSUBTV: -#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32 - { - /* For 32-bit hosts, we can re-use the host's hardware carry - generation by using a SUB2 opcode. We discard the low - part of the output. Ideally we'd combine this operation - with the add that generated the carry in the first place. */ - TCGv dst_low = tcg_temp_new(); - tcg_gen_op6_i32(INDEX_op_sub2_i32, dst_low, dst, - cpu_cc_src, src1, cpu_cc_src2, src2); - tcg_temp_free(dst_low); + if (TARGET_LONG_BITS == 32) { + /* We can re-use the host's hardware carry generation by using + a SUB2 opcode. We discard the low part of the output. + Ideally we'd combine this operation with the add that + generated the carry in the first place. */ + carry = tcg_temp_new(); + tcg_gen_sub2_tl(carry, dst, cpu_cc_src, src1, cpu_cc_src2, src2); + tcg_temp_free(carry); goto sub_done; } -#endif carry_32 = gen_sub32_carry32(); break; @@ -592,9 +584,7 @@ static void gen_op_subx_int(DisasContext *dc, TCGv dst, TCGv src1, tcg_temp_free(carry); #endif -#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32 sub_done: -#endif if (update_cc) { tcg_gen_mov_tl(cpu_cc_src, src1); tcg_gen_mov_tl(cpu_cc_src2, src2); @@ -652,39 +642,30 @@ static inline void gen_op_mulscc(TCGv dst, TCGv src1, TCGv src2) static inline void gen_op_multiply(TCGv dst, TCGv src1, TCGv src2, int sign_ext) { - TCGv_i32 r_src1, r_src2; - TCGv_i64 r_temp, r_temp2; - - r_src1 = tcg_temp_new_i32(); - r_src2 = tcg_temp_new_i32(); - - tcg_gen_trunc_tl_i32(r_src1, src1); - tcg_gen_trunc_tl_i32(r_src2, src2); - - r_temp = tcg_temp_new_i64(); - r_temp2 = tcg_temp_new_i64(); +#if TARGET_LONG_BITS == 32 + if (sign_ext) { + tcg_gen_muls2_tl(dst, cpu_y, src1, src2); + } else { + tcg_gen_mulu2_tl(dst, cpu_y, src1, src2); + } +#else + TCGv t0 = tcg_temp_new_i64(); + TCGv t1 = tcg_temp_new_i64(); if (sign_ext) { - tcg_gen_ext_i32_i64(r_temp, r_src2); - tcg_gen_ext_i32_i64(r_temp2, r_src1); + tcg_gen_ext32s_i64(t0, src1); + tcg_gen_ext32s_i64(t1, src2); } else { - tcg_gen_extu_i32_i64(r_temp, r_src2); - tcg_gen_extu_i32_i64(r_temp2, r_src1); + tcg_gen_ext32u_i64(t0, src1); + tcg_gen_ext32u_i64(t1, src2); } - tcg_gen_mul_i64(r_temp2, r_temp, r_temp2); + tcg_gen_mul_i64(dst, t0, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); - tcg_gen_shri_i64(r_temp, r_temp2, 32); - tcg_gen_trunc_i64_tl(cpu_y, r_temp); - tcg_temp_free_i64(r_temp); - tcg_gen_andi_tl(cpu_y, cpu_y, 0xffffffff); - - tcg_gen_trunc_i64_tl(dst, r_temp2); - - tcg_temp_free_i64(r_temp2); - - tcg_temp_free_i32(r_src1); - tcg_temp_free_i32(r_src2); + tcg_gen_shri_i64(cpu_y, dst, 32); +#endif } static inline void gen_op_umul(TCGv dst, TCGv src1, TCGv src2) @@ -3642,6 +3623,11 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) in the SPARCv8 manual, nop on the microSPARC II */ + if ((rd == 0x13) && (dc->def->features & + CPU_FEATURE_POWERDOWN)) { + /* LEON3 power-down */ + gen_helper_power_down(cpu_env); + } break; #else case 0x2: /* V9 wrccr */ @@ -5263,7 +5249,7 @@ static inline void gen_intermediate_code_internal(TranslationBlock * tb, max_insns = tb->cflags & CF_COUNT_MASK; if (max_insns == 0) max_insns = CF_COUNT_MASK; - gen_icount_start(); + gen_tb_start(); do { if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { QTAILQ_FOREACH(bp, &env->breakpoints, entry) { @@ -5333,7 +5319,7 @@ static inline void gen_intermediate_code_internal(TranslationBlock * tb, tcg_gen_exit_tb(0); } } - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; if (spc) { j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; diff --git a/target-unicore32/Makefile.objs b/target-unicore32/Makefile.objs index 8e143da937..6b41b1e9ef 100644 --- a/target-unicore32/Makefile.objs +++ b/target-unicore32/Makefile.objs @@ -1,4 +1,4 @@ obj-y += translate.o op_helper.o helper.o cpu.o obj-y += ucf64_helper.o -obj-$(CONFIG_SOFTMMU) += machine.o softmmu.o +obj-$(CONFIG_SOFTMMU) += softmmu.o diff --git a/target-unicore32/cpu-qom.h b/target-unicore32/cpu-qom.h index fe40b2d6a8..ba4dee4b9f 100644 --- a/target-unicore32/cpu-qom.h +++ b/target-unicore32/cpu-qom.h @@ -25,6 +25,7 @@ /** * UniCore32CPUClass: + * @parent_realize: The parent class' realize handler. * * A UniCore32 CPU model. */ @@ -32,6 +33,8 @@ typedef struct UniCore32CPUClass { /*< private >*/ CPUClass parent_class; /*< public >*/ + + DeviceRealize parent_realize; } UniCore32CPUClass; /** @@ -55,5 +58,8 @@ static inline UniCore32CPU *uc32_env_get_cpu(CPUUniCore32State *env) #define ENV_GET_CPU(e) CPU(uc32_env_get_cpu(e)) +#define ENV_OFFSET offsetof(UniCore32CPU, env) + +void uc32_cpu_do_interrupt(CPUState *cpu); #endif diff --git a/target-unicore32/cpu.c b/target-unicore32/cpu.c index 884c101010..66a1a74646 100644 --- a/target-unicore32/cpu.c +++ b/target-unicore32/cpu.c @@ -14,6 +14,7 @@ #include "cpu.h" #include "qemu-common.h" +#include "migration/vmstate.h" static inline void set_feature(CPUUniCore32State *env, int feature) { @@ -22,6 +23,25 @@ static inline void set_feature(CPUUniCore32State *env, int feature) /* CPU models */ +static ObjectClass *uc32_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + + if (cpu_model == NULL) { + return NULL; + } + + typename = g_strdup_printf("%s-" TYPE_UNICORE32_CPU, cpu_model); + oc = object_class_by_name(typename); + g_free(typename); + if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_UNICORE32_CPU) || + object_class_is_abstract(oc))) { + oc = NULL; + } + return oc; +} + typedef struct UniCore32CPUInfo { const char *name; void (*instance_init)(Object *obj); @@ -61,13 +81,25 @@ static const UniCore32CPUInfo uc32_cpus[] = { { .name = "any", .instance_init = uc32_any_cpu_initfn }, }; +static void uc32_cpu_realizefn(DeviceState *dev, Error **errp) +{ + UniCore32CPU *cpu = UNICORE32_CPU(dev); + UniCore32CPUClass *ucc = UNICORE32_CPU_GET_CLASS(dev); + + qemu_init_vcpu(&cpu->env); + + ucc->parent_realize(dev, errp); +} + static void uc32_cpu_initfn(Object *obj) { + CPUState *cs = CPU(obj); UniCore32CPU *cpu = UNICORE32_CPU(obj); CPUUniCore32State *env = &cpu->env; + static bool inited; + cs->env_ptr = env; cpu_exec_init(env); - env->cpu_model_str = object_get_typename(obj); #ifdef CONFIG_USER_ONLY env->uncached_asr = ASR_MODE_USER; @@ -78,17 +110,42 @@ static void uc32_cpu_initfn(Object *obj) #endif tlb_flush(env, 1); + + if (tcg_enabled() && !inited) { + inited = true; + uc32_translate_init(); + } +} + +static const VMStateDescription vmstate_uc32_cpu = { + .name = "cpu", + .unmigratable = 1, +}; + +static void uc32_cpu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + UniCore32CPUClass *ucc = UNICORE32_CPU_CLASS(oc); + + ucc->parent_realize = dc->realize; + dc->realize = uc32_cpu_realizefn; + + cc->class_by_name = uc32_cpu_class_by_name; + cc->do_interrupt = uc32_cpu_do_interrupt; + dc->vmsd = &vmstate_uc32_cpu; } static void uc32_register_cpu_type(const UniCore32CPUInfo *info) { TypeInfo type_info = { - .name = info->name, .parent = TYPE_UNICORE32_CPU, .instance_init = info->instance_init, }; - type_register_static(&type_info); + type_info.name = g_strdup_printf("%s-" TYPE_UNICORE32_CPU, info->name); + type_register(&type_info); + g_free((void *)type_info.name); } static const TypeInfo uc32_cpu_type_info = { @@ -98,6 +155,7 @@ static const TypeInfo uc32_cpu_type_info = { .instance_init = uc32_cpu_initfn, .abstract = true, .class_size = sizeof(UniCore32CPUClass), + .class_init = uc32_cpu_class_init, }; static void uc32_cpu_register_types(void) diff --git a/target-unicore32/cpu.h b/target-unicore32/cpu.h index 509ce7c69d..5b2b9d1cc7 100644 --- a/target-unicore32/cpu.h +++ b/target-unicore32/cpu.h @@ -133,8 +133,6 @@ int uc32_cpu_signal_handler(int host_signum, void *pinfo, void *puc); int uc32_cpu_handle_mmu_fault(CPUUniCore32State *env, target_ulong address, int rw, int mmu_idx); -#define CPU_SAVE_VERSION 2 - /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel #define MMU_MODE1_SUFFIX _user @@ -178,14 +176,11 @@ static inline void cpu_get_tb_cpu_state(CPUUniCore32State *env, target_ulong *pc } void uc32_translate_init(void); -void do_interrupt(CPUUniCore32State *); void switch_mode(CPUUniCore32State *, int); static inline bool cpu_has_work(CPUState *cpu) { - CPUUniCore32State *env = &UNICORE32_CPU(cpu)->env; - - return env->interrupt_request & + return cpu->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXITTB); } diff --git a/target-unicore32/helper.c b/target-unicore32/helper.c index ff4f628404..61eb2c374a 100644 --- a/target-unicore32/helper.c +++ b/target-unicore32/helper.c @@ -13,7 +13,9 @@ #include "exec/gdbstub.h" #include "helper.h" #include "qemu/host-utils.h" +#ifndef CONFIG_USER_ONLY #include "ui/console.h" +#endif #undef DEBUG_UC32 @@ -27,20 +29,18 @@ CPUUniCore32State *uc32_cpu_init(const char *cpu_model) { UniCore32CPU *cpu; CPUUniCore32State *env; - static int inited = 1; + ObjectClass *oc; - if (object_class_by_name(cpu_model) == NULL) { + oc = cpu_class_by_name(TYPE_UNICORE32_CPU, cpu_model); + if (oc == NULL) { return NULL; } - cpu = UNICORE32_CPU(object_new(cpu_model)); + cpu = UNICORE32_CPU(object_new(object_class_get_name(oc))); env = &cpu->env; + env->cpu_model_str = cpu_model; - if (inited) { - inited = 0; - uc32_translate_init(); - } + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); - qemu_init_vcpu(env); return env; } @@ -242,8 +242,11 @@ void switch_mode(CPUUniCore32State *env, int mode) } } -void do_interrupt(CPUUniCore32State *env) +void uc32_cpu_do_interrupt(CPUState *cs) { + UniCore32CPU *cpu = UNICORE32_CPU(cs); + CPUUniCore32State *env = &cpu->env; + cpu_abort(env, "NO interrupt in user mode\n"); } diff --git a/target-unicore32/machine.c b/target-unicore32/machine.c deleted file mode 100644 index 60b2ec1771..0000000000 --- a/target-unicore32/machine.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Generic machine functions for UniCore32 ISA - * - * Copyright (C) 2010-2012 Guan Xuetao - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation, or any later version. - * See the COPYING file in the top-level directory. - */ -#include "hw/hw.h" - -void cpu_save(QEMUFile *f, void *opaque) -{ - hw_error("%s not supported yet.\n", __func__); -} - -int cpu_load(QEMUFile *f, void *opaque, int version_id) -{ - hw_error("%s not supported yet.\n", __func__); - - return 0; -} diff --git a/target-unicore32/softmmu.c b/target-unicore32/softmmu.c index fc27100f27..eadaeb11ab 100644 --- a/target-unicore32/softmmu.c +++ b/target-unicore32/softmmu.c @@ -72,8 +72,10 @@ void switch_mode(CPUUniCore32State *env, int mode) } /* Handle a CPU exception. */ -void do_interrupt(CPUUniCore32State *env) +void uc32_cpu_do_interrupt(CPUState *cs) { + UniCore32CPU *cpu = UNICORE32_CPU(cs); + CPUUniCore32State *env = &cpu->env; uint32_t addr; int new_mode; @@ -112,7 +114,7 @@ void do_interrupt(CPUUniCore32State *env) /* The PC already points to the proper instruction. */ env->regs[30] = env->regs[31]; env->regs[31] = addr; - env->interrupt_request |= CPU_INTERRUPT_EXITTB; + cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } static int get_phys_addr_ucv2(CPUUniCore32State *env, uint32_t address, diff --git a/target-unicore32/translate.c b/target-unicore32/translate.c index f4498bcb14..151e35e6bb 100644 --- a/target-unicore32/translate.c +++ b/target-unicore32/translate.c @@ -267,37 +267,6 @@ static void gen_exception(int excp) dead_tmp(tmp); } -/* FIXME: Most targets have native widening multiplication. - It would be good to use that instead of a full wide multiply. */ -/* 32x32->64 multiply. Marks inputs as dead. */ -static TCGv_i64 gen_mulu_i64_i32(TCGv a, TCGv b) -{ - TCGv_i64 tmp1 = tcg_temp_new_i64(); - TCGv_i64 tmp2 = tcg_temp_new_i64(); - - tcg_gen_extu_i32_i64(tmp1, a); - dead_tmp(a); - tcg_gen_extu_i32_i64(tmp2, b); - dead_tmp(b); - tcg_gen_mul_i64(tmp1, tmp1, tmp2); - tcg_temp_free_i64(tmp2); - return tmp1; -} - -static TCGv_i64 gen_muls_i64_i32(TCGv a, TCGv b) -{ - TCGv_i64 tmp1 = tcg_temp_new_i64(); - TCGv_i64 tmp2 = tcg_temp_new_i64(); - - tcg_gen_ext_i32_i64(tmp1, a); - dead_tmp(a); - tcg_gen_ext_i32_i64(tmp2, b); - dead_tmp(b); - tcg_gen_mul_i64(tmp1, tmp1, tmp2); - tcg_temp_free_i64(tmp2); - return tmp1; -} - #define gen_set_CF(var) tcg_gen_st_i32(var, cpu_env, offsetof(CPUUniCore32State, CF)) /* Set CF to the top bit of var. */ @@ -1219,38 +1188,6 @@ static void disas_coproc_insn(CPUUniCore32State *env, DisasContext *s, } } - -/* Store a 64-bit value to a register pair. Clobbers val. */ -static void gen_storeq_reg(DisasContext *s, int rlow, int rhigh, TCGv_i64 val) -{ - TCGv tmp; - tmp = new_tmp(); - tcg_gen_trunc_i64_i32(tmp, val); - store_reg(s, rlow, tmp); - tmp = new_tmp(); - tcg_gen_shri_i64(val, val, 32); - tcg_gen_trunc_i64_i32(tmp, val); - store_reg(s, rhigh, tmp); -} - -/* load and add a 64-bit value from a register pair. */ -static void gen_addq(DisasContext *s, TCGv_i64 val, int rlow, int rhigh) -{ - TCGv_i64 tmp; - TCGv tmpl; - TCGv tmph; - - /* Load 64-bit value rd:rn. */ - tmpl = load_reg(s, rlow); - tmph = load_reg(s, rhigh); - tmp = tcg_temp_new_i64(); - tcg_gen_concat_i32_i64(tmp, tmpl, tmph); - dead_tmp(tmpl); - dead_tmp(tmph); - tcg_gen_add_i64(val, val, tmp); - tcg_temp_free_i64(tmp); -} - /* data processing instructions */ static void do_datap(CPUUniCore32State *env, DisasContext *s, uint32_t insn) { @@ -1445,24 +1382,26 @@ static void do_datap(CPUUniCore32State *env, DisasContext *s, uint32_t insn) /* multiply */ static void do_mult(CPUUniCore32State *env, DisasContext *s, uint32_t insn) { - TCGv tmp; - TCGv tmp2; - TCGv_i64 tmp64; + TCGv tmp, tmp2, tmp3, tmp4; if (UCOP_SET(27)) { /* 64 bit mul */ tmp = load_reg(s, UCOP_REG_M); tmp2 = load_reg(s, UCOP_REG_N); if (UCOP_SET(26)) { - tmp64 = gen_muls_i64_i32(tmp, tmp2); + tcg_gen_muls2_i32(tmp, tmp2, tmp, tmp2); } else { - tmp64 = gen_mulu_i64_i32(tmp, tmp2); + tcg_gen_mulu2_i32(tmp, tmp2, tmp, tmp2); } if (UCOP_SET(25)) { /* mult accumulate */ - gen_addq(s, tmp64, UCOP_REG_LO, UCOP_REG_HI); + tmp3 = load_reg(s, UCOP_REG_LO); + tmp4 = load_reg(s, UCOP_REG_HI); + tcg_gen_add2_i32(tmp, tmp2, tmp, tmp2, tmp3, tmp4); + dead_tmp(tmp3); + dead_tmp(tmp4); } - gen_storeq_reg(s, UCOP_REG_LO, UCOP_REG_HI, tmp64); - tcg_temp_free_i64(tmp64); + store_reg(s, UCOP_REG_LO, tmp); + store_reg(s, UCOP_REG_HI, tmp2); } else { /* 32 bit mul */ tmp = load_reg(s, UCOP_REG_M); @@ -1982,7 +1921,7 @@ static inline void gen_intermediate_code_internal(CPUUniCore32State *env, } #endif - gen_icount_start(); + gen_tb_start(); do { if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { QTAILQ_FOREACH(bp, &env->breakpoints, entry) { @@ -2102,7 +2041,7 @@ static inline void gen_intermediate_code_internal(CPUUniCore32State *env, } done_generating: - gen_icount_end(tb, num_insns); + gen_tb_end(tb, num_insns); *tcg_ctx.gen_opc_ptr = INDEX_op_end; #ifdef DEBUG_DISAS diff --git a/target-xtensa/Makefile.objs b/target-xtensa/Makefile.objs index b30e5a8466..644b7f99bb 100644 --- a/target-xtensa/Makefile.objs +++ b/target-xtensa/Makefile.objs @@ -3,4 +3,3 @@ obj-y += core-dc232b.o obj-y += core-dc233c.o obj-y += core-fsf.o obj-y += translate.o op_helper.o helper.o cpu.o -obj-$(CONFIG_SOFTMMU) += machine.o diff --git a/target-xtensa/cpu-qom.h b/target-xtensa/cpu-qom.h index e344a9aa79..af0ce2823c 100644 --- a/target-xtensa/cpu-qom.h +++ b/target-xtensa/cpu-qom.h @@ -43,6 +43,7 @@ /** * XtensaCPUClass: + * @parent_realize: The parent class' realize handler. * @parent_reset: The parent class' reset handler. * * An Xtensa CPU model. @@ -52,6 +53,7 @@ typedef struct XtensaCPUClass { CPUClass parent_class; /*< public >*/ + DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); } XtensaCPUClass; @@ -76,5 +78,8 @@ static inline XtensaCPU *xtensa_env_get_cpu(const CPUXtensaState *env) #define ENV_GET_CPU(e) CPU(xtensa_env_get_cpu(e)) +#define ENV_OFFSET offsetof(XtensaCPU, env) + +void xtensa_cpu_do_interrupt(CPUState *cpu); #endif diff --git a/target-xtensa/cpu.c b/target-xtensa/cpu.c index 035b07c1c5..6e93dd8d24 100644 --- a/target-xtensa/cpu.c +++ b/target-xtensa/cpu.c @@ -30,6 +30,7 @@ #include "cpu.h" #include "qemu-common.h" +#include "migration/vmstate.h" /* CPUClass::reset() */ @@ -56,21 +57,52 @@ static void xtensa_cpu_reset(CPUState *s) reset_mmu(env); } +static void xtensa_cpu_realizefn(DeviceState *dev, Error **errp) +{ + XtensaCPU *cpu = XTENSA_CPU(dev); + XtensaCPUClass *xcc = XTENSA_CPU_GET_CLASS(dev); + + qemu_init_vcpu(&cpu->env); + + xcc->parent_realize(dev, errp); +} + static void xtensa_cpu_initfn(Object *obj) { + CPUState *cs = CPU(obj); XtensaCPU *cpu = XTENSA_CPU(obj); CPUXtensaState *env = &cpu->env; + static bool tcg_inited; + cs->env_ptr = env; cpu_exec_init(env); + + if (tcg_enabled() && !tcg_inited) { + tcg_inited = true; + xtensa_translate_init(); + cpu_set_debug_excp_handler(xtensa_breakpoint_handler); + } } +static const VMStateDescription vmstate_xtensa_cpu = { + .name = "cpu", + .unmigratable = 1, +}; + static void xtensa_cpu_class_init(ObjectClass *oc, void *data) { + DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); XtensaCPUClass *xcc = XTENSA_CPU_CLASS(cc); + xcc->parent_realize = dc->realize; + dc->realize = xtensa_cpu_realizefn; + xcc->parent_reset = cc->reset; cc->reset = xtensa_cpu_reset; + + cc->do_interrupt = xtensa_cpu_do_interrupt; + dc->vmsd = &vmstate_xtensa_cpu; } static const TypeInfo xtensa_cpu_type_info = { diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index 5acf78c692..6c9fc35dcc 100644 --- a/target-xtensa/cpu.h +++ b/target-xtensa/cpu.h @@ -385,9 +385,9 @@ static inline CPUXtensaState *cpu_init(const char *cpu_model) } void xtensa_translate_init(void); +void xtensa_breakpoint_handler(CPUXtensaState *env); int cpu_xtensa_exec(CPUXtensaState *s); void xtensa_register_core(XtensaConfigList *node); -void do_interrupt(CPUXtensaState *s); void check_interrupts(CPUXtensaState *s); void xtensa_irq_init(CPUXtensaState *env); void *xtensa_get_extint(CPUXtensaState *env, unsigned extint); diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c index 94c03a1d3c..6f613c66a6 100644 --- a/target-xtensa/helper.c +++ b/target-xtensa/helper.c @@ -54,7 +54,7 @@ static uint32_t check_hw_breakpoints(CPUXtensaState *env) return 0; } -static void breakpoint_handler(CPUXtensaState *env) +void xtensa_breakpoint_handler(CPUXtensaState *env) { if (env->watchpoint_hit) { if (env->watchpoint_hit->flags & BP_CPU) { @@ -72,8 +72,6 @@ static void breakpoint_handler(CPUXtensaState *env) XtensaCPU *cpu_xtensa_init(const char *cpu_model) { - static int tcg_inited; - static int debug_handler_inited; XtensaCPU *cpu; CPUXtensaState *env; const XtensaConfig *config = NULL; @@ -93,18 +91,10 @@ XtensaCPU *cpu_xtensa_init(const char *cpu_model) env = &cpu->env; env->config = config; - if (!tcg_inited) { - tcg_inited = 1; - xtensa_translate_init(); - } - - if (!debug_handler_inited && tcg_enabled()) { - debug_handler_inited = 1; - cpu_set_debug_excp_handler(breakpoint_handler); - } - xtensa_irq_init(env); - qemu_init_vcpu(env); + + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + return cpu; } @@ -188,8 +178,11 @@ static void handle_interrupt(CPUXtensaState *env) } } -void do_interrupt(CPUXtensaState *env) +void xtensa_cpu_do_interrupt(CPUState *cs) { + XtensaCPU *cpu = XTENSA_CPU(cs); + CPUXtensaState *env = &cpu->env; + if (env->exception_index == EXC_IRQ) { qemu_log_mask(CPU_LOG_INT, "%s(EXC_IRQ) level = %d, cintlevel = %d, " diff --git a/target-xtensa/machine.c b/target-xtensa/machine.c deleted file mode 100644 index ddeffb2da4..0000000000 --- a/target-xtensa/machine.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the Open Source and Linux Lab nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "hw/hw.h" -#include "hw/boards.h" - -void cpu_save(QEMUFile *f, void *opaque) -{ -} - -int cpu_load(QEMUFile *f, void *opaque, int version_id) -{ - return 0; -} diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c index 3813a72626..1037101f2e 100644 --- a/target-xtensa/op_helper.c +++ b/target-xtensa/op_helper.c @@ -373,6 +373,8 @@ void HELPER(dump_state)(CPUXtensaState *env) void HELPER(waiti)(CPUXtensaState *env, uint32_t pc, uint32_t intlevel) { + CPUState *cpu; + env->pc = pc; env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | (intlevel << PS_INTLEVEL_SHIFT); @@ -382,8 +384,9 @@ void HELPER(waiti)(CPUXtensaState *env, uint32_t pc, uint32_t intlevel) return; } + cpu = CPU(xtensa_env_get_cpu(env)); env->halt_clock = qemu_get_clock_ns(vm_clock); - env->halted = 1; + cpu->halted = 1; if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) { xtensa_rearm_ccompare_timer(env); } diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index 7029ac4814..06d68dbaeb 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -1652,24 +1652,16 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) case 11: /*MULSHi*/ HAS_OPTION(XTENSA_OPTION_32_BIT_IMUL_HIGH); { - TCGv_i64 r = tcg_temp_new_i64(); - TCGv_i64 s = tcg_temp_new_i64(); - TCGv_i64 t = tcg_temp_new_i64(); + TCGv lo = tcg_temp_new(); if (OP2 == 10) { - tcg_gen_extu_i32_i64(s, cpu_R[RRR_S]); - tcg_gen_extu_i32_i64(t, cpu_R[RRR_T]); + tcg_gen_mulu2_i32(lo, cpu_R[RRR_R], + cpu_R[RRR_S], cpu_R[RRR_T]); } else { - tcg_gen_ext_i32_i64(s, cpu_R[RRR_S]); - tcg_gen_ext_i32_i64(t, cpu_R[RRR_T]); + tcg_gen_muls2_i32(lo, cpu_R[RRR_R], + cpu_R[RRR_S], cpu_R[RRR_T]); } - tcg_gen_mul_i64(r, s, t); - tcg_gen_shri_i64(r, r, 32); - tcg_gen_trunc_i64_i32(cpu_R[RRR_R], r); - - tcg_temp_free_i64(r); - tcg_temp_free_i64(s); - tcg_temp_free_i64(t); + tcg_temp_free(lo); } break; @@ -2495,27 +2487,24 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) tcg_gen_sari_i32(cpu_SR[ACCHI], cpu_SR[ACCLO], 31); } } else { - TCGv_i32 res = tcg_temp_new_i32(); - TCGv_i64 res64 = tcg_temp_new_i64(); - TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv_i32 lo = tcg_temp_new_i32(); + TCGv_i32 hi = tcg_temp_new_i32(); - tcg_gen_mul_i32(res, m1, m2); - tcg_gen_ext_i32_i64(res64, res); - tcg_gen_concat_i32_i64(tmp, - cpu_SR[ACCLO], cpu_SR[ACCHI]); + tcg_gen_mul_i32(lo, m1, m2); + tcg_gen_sari_i32(hi, lo, 31); if (op == MAC16_MULA) { - tcg_gen_add_i64(tmp, tmp, res64); + tcg_gen_add2_i32(cpu_SR[ACCLO], cpu_SR[ACCHI], + cpu_SR[ACCLO], cpu_SR[ACCHI], + lo, hi); } else { - tcg_gen_sub_i64(tmp, tmp, res64); + tcg_gen_sub2_i32(cpu_SR[ACCLO], cpu_SR[ACCHI], + cpu_SR[ACCLO], cpu_SR[ACCHI], + lo, hi); } - tcg_gen_trunc_i64_i32(cpu_SR[ACCLO], tmp); - tcg_gen_shri_i64(tmp, tmp, 32); - tcg_gen_trunc_i64_i32(cpu_SR[ACCHI], tmp); tcg_gen_ext8s_i32(cpu_SR[ACCHI], cpu_SR[ACCHI]); - tcg_temp_free(res); - tcg_temp_free_i64(res64); - tcg_temp_free_i64(tmp); + tcg_temp_free_i32(lo); + tcg_temp_free_i32(hi); } tcg_temp_free(m1); tcg_temp_free(m2); @@ -2924,7 +2913,7 @@ static void gen_intermediate_code_internal( dc.next_icount = tcg_temp_local_new_i32(); } - gen_icount_start(); + gen_tb_start(); if (env->singlestep_enabled && env->exception_taken) { env->exception_taken = 0; @@ -3002,7 +2991,7 @@ static void gen_intermediate_code_internal( if (dc.is_jmp == DISAS_NEXT) { gen_jumpi(&dc, dc.pc, 0); } - gen_icount_end(tb, insn_count); + gen_tb_end(tb, insn_count); *tcg_ctx.gen_opc_ptr = INDEX_op_end; if (search_pc) { diff --git a/tcg-runtime.c b/tcg-runtime.c index abfc36498f..4b66e51ce7 100644 --- a/tcg-runtime.c +++ b/tcg-runtime.c @@ -22,7 +22,7 @@ * THE SOFTWARE. */ #include - +#include "qemu/host-utils.h" #include "tcg/tcg-runtime.h" /* 32-bit helpers */ @@ -83,3 +83,17 @@ uint64_t tcg_helper_remu_i64(uint64_t arg1, uint64_t arg2) { return arg1 % arg2; } + +uint64_t tcg_helper_muluh_i64(uint64_t arg1, uint64_t arg2) +{ + uint64_t l, h; + mulu64(&l, &h, arg1, arg2); + return h; +} + +int64_t tcg_helper_mulsh_i64(int64_t arg1, int64_t arg2) +{ + uint64_t l, h; + muls64(&l, &h, arg1, arg2); + return h; +} diff --git a/tcg/README b/tcg/README index ec1ac79375..934e7afc96 100644 --- a/tcg/README +++ b/tcg/README @@ -361,6 +361,24 @@ Write 8, 16, 32 or 64 bits to host memory. All this opcodes assume that the pointed host memory doesn't correspond to a global. In the latter case the behaviour is unpredictable. +********* Multiword arithmetic support + +* add2_i32/i64 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high +* sub2_i32/i64 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high + +Similar to add/sub, except that the double-word inputs T1 and T2 are +formed from two single-word arguments, and the double-word output T0 +is returned in two single-word outputs. + +* mulu2_i32/i64 t0_low, t0_high, t1, t2 + +Similar to mul, except two unsigned inputs T1 and T2 yielding the full +double-word product T0. The later is returned in two single-word outputs. + +* muls2_i32/i64 t0_low, t0_high, t1, t2 + +Similar to mulu2, except the two inputs T1 and T2 are signed. + ********* 64-bit target on 32-bit host support The following opcodes are internal to TCG. Thus they are to be implemented by @@ -372,18 +390,6 @@ They are emitted as needed by inline functions within "tcg-op.h". Similar to brcond, except that the 64-bit values T0 and T1 are formed from two 32-bit arguments. -* add2_i32 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high -* sub2_i32 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high - -Similar to add/sub, except that the 64-bit inputs T1 and T2 are -formed from two 32-bit arguments, and the 64-bit output T0 -is returned in two 32-bit outputs. - -* mulu2_i32 t0_low, t0_high, t1, t2 - -Similar to mul, except two 32-bit (unsigned) inputs T1 and T2 yielding -the full 64-bit product T0. The later is returned in two 32-bit outputs. - * setcond2_i32 dest, t1_low, t1_high, t2_low, t2_high, cond Similar to setcond, except that the 64-bit values T1 and T2 are diff --git a/tcg/arm/tcg-target.c b/tcg/arm/tcg-target.c index c3ac85e054..94c6ca43aa 100644 --- a/tcg/arm/tcg-target.c +++ b/tcg/arm/tcg-target.c @@ -1145,7 +1145,7 @@ static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) TCG_REG_R0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); /* We assume that the offset is contained within 20 bits. */ tlb_offset = offsetof(CPUArchState, tlb_table[mem_index][0].addr_read); - assert(tlb_offset & ~0xfffff == 0); + assert((tlb_offset & ~0xfffff) == 0); if (tlb_offset > 0xfff) { tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_REG_R0, 0xa00 | (tlb_offset >> 12)); @@ -1354,7 +1354,7 @@ static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) TCG_AREG0, TCG_REG_R0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); /* We assume that the offset is contained within 20 bits. */ tlb_offset = offsetof(CPUArchState, tlb_table[mem_index][0].addr_write); - assert(tlb_offset & ~0xfffff == 0); + assert((tlb_offset & ~0xfffff) == 0); if (tlb_offset > 0xfff) { tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_REG_R0, 0xa00 | (tlb_offset >> 12)); @@ -1647,6 +1647,9 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_mulu2_i32: tcg_out_umull32(s, COND_AL, args[0], args[1], args[2], args[3]); break; + case INDEX_op_muls2_i32: + tcg_out_smull32(s, COND_AL, args[0], args[1], args[2], args[3]); + break; /* XXX: Perhaps args[2] & 0x1f is wrong */ case INDEX_op_shl_i32: c = const_args[2] ? @@ -1798,6 +1801,7 @@ static const TCGTargetOpDef arm_op_defs[] = { { INDEX_op_sub_i32, { "r", "r", "rI" } }, { INDEX_op_mul_i32, { "r", "r", "r" } }, { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } }, + { INDEX_op_muls2_i32, { "r", "r", "r", "r" } }, { INDEX_op_and_i32, { "r", "r", "rI" } }, { INDEX_op_andc_i32, { "r", "r", "rI" } }, { INDEX_op_or_i32, { "r", "r", "rI" } }, diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 7083f3a700..b6eed1f3f4 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -75,6 +75,7 @@ typedef enum { #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_deposit_i32 0 #define TCG_TARGET_HAS_movcond_i32 1 +#define TCG_TARGET_HAS_muls2_i32 1 enum { TCG_AREG0 = TCG_REG_R6, diff --git a/tcg/hppa/tcg-target.c b/tcg/hppa/tcg-target.c index 5b27cf6f12..656e73621a 100644 --- a/tcg/hppa/tcg-target.c +++ b/tcg/hppa/tcg-target.c @@ -824,7 +824,7 @@ static void tcg_out_brcond2(TCGContext *s, int cond, TCGArg al, TCGArg ah, tcg_out_brcond(s, TCG_COND_EQ, ah, bh, bhconst, label_index); break; case TCG_COND_NE: - tcg_out_brcond(s, TCG_COND_NE, al, bl, bhconst, label_index); + tcg_out_brcond(s, TCG_COND_NE, al, bl, blconst, label_index); tcg_out_brcond(s, TCG_COND_NE, ah, bh, bhconst, label_index); break; default: diff --git a/tcg/hppa/tcg-target.h b/tcg/hppa/tcg-target.h index e2754fe970..ebd53d9e36 100644 --- a/tcg/hppa/tcg-target.h +++ b/tcg/hppa/tcg-target.h @@ -98,6 +98,7 @@ typedef enum { #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_movcond_i32 1 +#define TCG_TARGET_HAS_muls2_i32 0 /* optional instructions automatically implemented */ #define TCG_TARGET_HAS_neg_i32 0 /* sub rd, 0, rs */ diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c index ae8274652a..9eec06c8a4 100644 --- a/tcg/i386/tcg-target.c +++ b/tcg/i386/tcg-target.c @@ -97,6 +97,18 @@ static const int tcg_target_call_oarg_regs[] = { # define TCG_REG_L1 TCG_REG_EDX #endif +/* For 32-bit, we are going to attempt to determine at runtime whether cmov + is available. However, the host compiler must supply , as we're + not going to go so far as our own inline assembly. */ +#if TCG_TARGET_REG_BITS == 64 +# define have_cmov 1 +#elif defined(CONFIG_CPUID_H) +#include +static bool have_cmov; +#else +# define have_cmov 0 +#endif + static uint8_t *tb_ret_addr; static void patch_reloc(uint8_t *code_ptr, int type, @@ -943,7 +955,14 @@ static void tcg_out_movcond32(TCGContext *s, TCGCond cond, TCGArg dest, TCGArg v1) { tcg_out_cmp(s, c1, c2, const_c2, 0); - tcg_out_modrm(s, OPC_CMOVCC | tcg_cond_to_jcc[cond], dest, v1); + if (have_cmov) { + tcg_out_modrm(s, OPC_CMOVCC | tcg_cond_to_jcc[cond], dest, v1); + } else { + int over = gen_new_label(); + tcg_out_jxx(s, tcg_cond_to_jcc[tcg_invert_cond(cond)], over, 1); + tcg_out_mov(s, TCG_TYPE_I32, dest, v1); + tcg_out_label(s, over, s->code_ptr); + } } #if TCG_TARGET_REG_BITS == 64 @@ -1903,6 +1922,37 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_qemu_st(s, args, 3); break; + OP_32_64(mulu2): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_MUL, args[3]); + break; + OP_32_64(muls2): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IMUL, args[3]); + break; + OP_32_64(add2): + if (const_args[4]) { + tgen_arithi(s, ARITH_ADD + rexw, args[0], args[4], 1); + } else { + tgen_arithr(s, ARITH_ADD + rexw, args[0], args[4]); + } + if (const_args[5]) { + tgen_arithi(s, ARITH_ADC + rexw, args[1], args[5], 1); + } else { + tgen_arithr(s, ARITH_ADC + rexw, args[1], args[5]); + } + break; + OP_32_64(sub2): + if (const_args[4]) { + tgen_arithi(s, ARITH_SUB + rexw, args[0], args[4], 1); + } else { + tgen_arithr(s, ARITH_SUB + rexw, args[0], args[4]); + } + if (const_args[5]) { + tgen_arithi(s, ARITH_SBB + rexw, args[1], args[5], 1); + } else { + tgen_arithr(s, ARITH_SBB + rexw, args[1], args[5]); + } + break; + #if TCG_TARGET_REG_BITS == 32 case INDEX_op_brcond2_i32: tcg_out_brcond2(s, args, const_args, 0); @@ -1910,33 +1960,6 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_setcond2_i32: tcg_out_setcond2(s, args, const_args); break; - case INDEX_op_mulu2_i32: - tcg_out_modrm(s, OPC_GRP3_Ev, EXT3_MUL, args[3]); - break; - case INDEX_op_add2_i32: - if (const_args[4]) { - tgen_arithi(s, ARITH_ADD, args[0], args[4], 1); - } else { - tgen_arithr(s, ARITH_ADD, args[0], args[4]); - } - if (const_args[5]) { - tgen_arithi(s, ARITH_ADC, args[1], args[5], 1); - } else { - tgen_arithr(s, ARITH_ADC, args[1], args[5]); - } - break; - case INDEX_op_sub2_i32: - if (const_args[4]) { - tgen_arithi(s, ARITH_SUB, args[0], args[4], 1); - } else { - tgen_arithr(s, ARITH_SUB, args[0], args[4]); - } - if (const_args[5]) { - tgen_arithi(s, ARITH_SBB, args[1], args[5], 1); - } else { - tgen_arithr(s, ARITH_SBB, args[1], args[5]); - } - break; #else /* TCG_TARGET_REG_BITS == 64 */ case INDEX_op_movi_i64: tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]); @@ -2059,10 +2082,12 @@ static const TCGTargetOpDef x86_op_defs[] = { { INDEX_op_movcond_i32, { "r", "r", "ri", "r", "0" } }, #endif -#if TCG_TARGET_REG_BITS == 32 { INDEX_op_mulu2_i32, { "a", "d", "a", "r" } }, + { INDEX_op_muls2_i32, { "a", "d", "a", "r" } }, { INDEX_op_add2_i32, { "r", "r", "0", "1", "ri", "ri" } }, { INDEX_op_sub2_i32, { "r", "r", "0", "1", "ri", "ri" } }, + +#if TCG_TARGET_REG_BITS == 32 { INDEX_op_brcond2_i32, { "r", "r", "ri", "ri" } }, { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } }, #else @@ -2080,7 +2105,7 @@ static const TCGTargetOpDef x86_op_defs[] = { { INDEX_op_st32_i64, { "ri", "r" } }, { INDEX_op_st_i64, { "re", "r" } }, - { INDEX_op_add_i64, { "r", "0", "re" } }, + { INDEX_op_add_i64, { "r", "r", "re" } }, { INDEX_op_mul_i64, { "r", "0", "re" } }, { INDEX_op_div2_i64, { "a", "d", "0", "1", "r" } }, { INDEX_op_divu2_i64, { "a", "d", "0", "1", "r" } }, @@ -2113,6 +2138,11 @@ static const TCGTargetOpDef x86_op_defs[] = { { INDEX_op_deposit_i64, { "Q", "0", "Q" } }, { INDEX_op_movcond_i64, { "r", "r", "re", "r", "0" } }, + + { INDEX_op_mulu2_i64, { "a", "d", "a", "r" } }, + { INDEX_op_muls2_i64, { "a", "d", "a", "r" } }, + { INDEX_op_add2_i64, { "r", "r", "0", "1", "re", "re" } }, + { INDEX_op_sub2_i64, { "r", "r", "0", "1", "re", "re" } }, #endif #if TCG_TARGET_REG_BITS == 64 @@ -2243,6 +2273,16 @@ static void tcg_target_qemu_prologue(TCGContext *s) static void tcg_target_init(TCGContext *s) { + /* For 32-bit, 99% certainty that we're running on hardware that supports + cmov, but we still need to check. In case cmov is not available, we'll + use a small forward branch. */ +#ifndef have_cmov + { + unsigned a, b, c, d; + have_cmov = (__get_cpuid(1, &a, &b, &c, &d) && (d & bit_CMOV)); + } +#endif + #if !defined(CONFIG_USER_ONLY) /* fail safe */ if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry)) diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index 5352ac0254..e3f6bb965f 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -91,12 +91,11 @@ typedef enum { #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_deposit_i32 1 -#if defined(__x86_64__) || defined(__i686__) -/* Use cmov only if the compiler is already doing so. */ #define TCG_TARGET_HAS_movcond_i32 1 -#else -#define TCG_TARGET_HAS_movcond_i32 0 -#endif +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 1 +#define TCG_TARGET_HAS_muls2_i32 1 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_div2_i64 1 @@ -119,6 +118,10 @@ typedef enum { #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_deposit_i64 1 #define TCG_TARGET_HAS_movcond_i64 1 +#define TCG_TARGET_HAS_add2_i64 1 +#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_mulu2_i64 1 +#define TCG_TARGET_HAS_muls2_i64 1 #endif #define TCG_TARGET_deposit_i32_valid(ofs, len) \ diff --git a/tcg/ia64/tcg-target.h b/tcg/ia64/tcg-target.h index 7f3401ecdd..e3d72ea52f 100644 --- a/tcg/ia64/tcg-target.h +++ b/tcg/ia64/tcg-target.h @@ -136,6 +136,14 @@ typedef enum { #define TCG_TARGET_HAS_movcond_i64 1 #define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_deposit_i64 1 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i32 0 +#define TCG_TARGET_HAS_sub2_i64 0 +#define TCG_TARGET_HAS_mulu2_i32 0 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muls2_i32 0 +#define TCG_TARGET_HAS_muls2_i64 0 #define TCG_TARGET_deposit_i32_valid(ofs, len) ((len) <= 16) #define TCG_TARGET_deposit_i64_valid(ofs, len) ((len) <= 16) diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index 78af664cca..0384bd384f 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -87,6 +87,7 @@ typedef enum { #define TCG_TARGET_HAS_orc_i32 0 #define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 +#define TCG_TARGET_HAS_muls2_i32 0 /* optional instructions only implemented on MIPS4, MIPS32 and Loongson 2 */ #if (defined(__mips_isa_rev) && (__mips_isa_rev >= 1)) || \ diff --git a/tcg/optimize.c b/tcg/optimize.c index 9109b813e0..bc6e5c16a9 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -46,6 +46,7 @@ struct tcg_temp_info { uint16_t prev_copy; uint16_t next_copy; tcg_target_ulong val; + tcg_target_ulong mask; }; static struct tcg_temp_info temps[TCG_MAX_TEMPS]; @@ -63,6 +64,17 @@ static void reset_temp(TCGArg temp) } } temps[temp].state = TCG_TEMP_UNDEF; + temps[temp].mask = -1; +} + +/* Reset all temporaries, given that there are NB_TEMPS of them. */ +static void reset_all_temps(int nb_temps) +{ + int i; + for (i = 0; i < nb_temps; i++) { + temps[i].state = TCG_TEMP_UNDEF; + temps[i].mask = -1; + } } static int op_bits(TCGOpcode op) @@ -139,33 +151,35 @@ static bool temps_are_copies(TCGArg arg1, TCGArg arg2) static void tcg_opt_gen_mov(TCGContext *s, TCGArg *gen_args, TCGArg dst, TCGArg src) { - reset_temp(dst); - assert(temps[src].state != TCG_TEMP_CONST); + reset_temp(dst); + temps[dst].mask = temps[src].mask; + assert(temps[src].state != TCG_TEMP_CONST); - if (s->temps[src].type == s->temps[dst].type) { - if (temps[src].state != TCG_TEMP_COPY) { - temps[src].state = TCG_TEMP_COPY; - temps[src].next_copy = src; - temps[src].prev_copy = src; - } - temps[dst].state = TCG_TEMP_COPY; - temps[dst].next_copy = temps[src].next_copy; - temps[dst].prev_copy = src; - temps[temps[dst].next_copy].prev_copy = dst; - temps[src].next_copy = dst; + if (s->temps[src].type == s->temps[dst].type) { + if (temps[src].state != TCG_TEMP_COPY) { + temps[src].state = TCG_TEMP_COPY; + temps[src].next_copy = src; + temps[src].prev_copy = src; } + temps[dst].state = TCG_TEMP_COPY; + temps[dst].next_copy = temps[src].next_copy; + temps[dst].prev_copy = src; + temps[temps[dst].next_copy].prev_copy = dst; + temps[src].next_copy = dst; + } - gen_args[0] = dst; - gen_args[1] = src; + gen_args[0] = dst; + gen_args[1] = src; } static void tcg_opt_gen_movi(TCGArg *gen_args, TCGArg dst, TCGArg val) { - reset_temp(dst); - temps[dst].state = TCG_TEMP_CONST; - temps[dst].val = val; - gen_args[0] = dst; - gen_args[1] = val; + reset_temp(dst); + temps[dst].state = TCG_TEMP_CONST; + temps[dst].val = val; + temps[dst].mask = val; + gen_args[0] = dst; + gen_args[1] = val; } static TCGOpcode op_to_mov(TCGOpcode op) @@ -470,6 +484,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, TCGArg *args, TCGOpDef *tcg_op_defs) { int i, nb_ops, op_index, nb_temps, nb_globals, nb_call_args; + tcg_target_ulong mask, affected; TCGOpcode op; const TCGOpDef *def; TCGArg *gen_args; @@ -482,7 +497,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, nb_temps = s->nb_temps; nb_globals = s->nb_globals; - memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info)); + reset_all_temps(nb_temps); nb_ops = tcg_opc_ptr - s->gen_opc_buf; gen_args = args; @@ -539,11 +554,12 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, args[5] = tcg_invert_cond(args[5]); } break; - case INDEX_op_add2_i32: + CASE_OP_32_64(add2): swap_commutative(args[0], &args[2], &args[4]); swap_commutative(args[1], &args[3], &args[5]); break; - case INDEX_op_mulu2_i32: + CASE_OP_32_64(mulu2): + CASE_OP_32_64(muls2): swap_commutative(args[0], &args[2], &args[3]); break; case INDEX_op_brcond2_i32: @@ -612,6 +628,113 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, break; } + /* Simplify using known-zero bits */ + mask = -1; + affected = -1; + switch (op) { + CASE_OP_32_64(ext8s): + if ((temps[args[1]].mask & 0x80) != 0) { + break; + } + CASE_OP_32_64(ext8u): + mask = 0xff; + goto and_const; + CASE_OP_32_64(ext16s): + if ((temps[args[1]].mask & 0x8000) != 0) { + break; + } + CASE_OP_32_64(ext16u): + mask = 0xffff; + goto and_const; + case INDEX_op_ext32s_i64: + if ((temps[args[1]].mask & 0x80000000) != 0) { + break; + } + case INDEX_op_ext32u_i64: + mask = 0xffffffffU; + goto and_const; + + CASE_OP_32_64(and): + mask = temps[args[2]].mask; + if (temps[args[2]].state == TCG_TEMP_CONST) { + and_const: + affected = temps[args[1]].mask & ~mask; + } + mask = temps[args[1]].mask & mask; + break; + + CASE_OP_32_64(sar): + if (temps[args[2]].state == TCG_TEMP_CONST) { + mask = ((tcg_target_long)temps[args[1]].mask + >> temps[args[2]].val); + } + break; + + CASE_OP_32_64(shr): + if (temps[args[2]].state == TCG_TEMP_CONST) { + mask = temps[args[1]].mask >> temps[args[2]].val; + } + break; + + CASE_OP_32_64(shl): + if (temps[args[2]].state == TCG_TEMP_CONST) { + mask = temps[args[1]].mask << temps[args[2]].val; + } + break; + + CASE_OP_32_64(neg): + /* Set to 1 all bits to the left of the rightmost. */ + mask = -(temps[args[1]].mask & -temps[args[1]].mask); + break; + + CASE_OP_32_64(deposit): + tmp = ((1ull << args[4]) - 1); + mask = ((temps[args[1]].mask & ~(tmp << args[3])) + | ((temps[args[2]].mask & tmp) << args[3])); + break; + + CASE_OP_32_64(or): + CASE_OP_32_64(xor): + mask = temps[args[1]].mask | temps[args[2]].mask; + break; + + CASE_OP_32_64(setcond): + mask = 1; + break; + + CASE_OP_32_64(movcond): + mask = temps[args[3]].mask | temps[args[4]].mask; + break; + + default: + break; + } + + if (mask == 0) { + assert(def->nb_oargs == 1); + s->gen_opc_buf[op_index] = op_to_movi(op); + tcg_opt_gen_movi(gen_args, args[0], 0); + args += def->nb_oargs + def->nb_iargs + def->nb_cargs; + gen_args += 2; + continue; + } + if (affected == 0) { + assert(def->nb_oargs == 1); + if (temps_are_copies(args[0], args[1])) { + s->gen_opc_buf[op_index] = INDEX_op_nop; + } else if (temps[args[1]].state != TCG_TEMP_CONST) { + s->gen_opc_buf[op_index] = op_to_mov(op); + tcg_opt_gen_mov(s, gen_args, args[0], args[1]); + gen_args += 2; + } else { + s->gen_opc_buf[op_index] = op_to_movi(op); + tcg_opt_gen_movi(gen_args, args[0], temps[args[1]].val); + gen_args += 2; + } + args += def->nb_iargs + 1; + continue; + } + /* Simplify expression for "op r, a, 0 => movi r, 0" cases */ switch (op) { CASE_OP_32_64(and): @@ -768,7 +891,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, tmp = do_constant_folding_cond(op, args[0], args[1], args[2]); if (tmp != 2) { if (tmp) { - memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info)); + reset_all_temps(nb_temps); s->gen_opc_buf[op_index] = INDEX_op_br; gen_args[0] = args[3]; gen_args += 1; @@ -861,7 +984,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, tmp = do_constant_folding_cond2(&args[0], &args[2], args[4]); if (tmp != 2) { if (tmp) { - memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info)); + reset_all_temps(nb_temps); s->gen_opc_buf[op_index] = INDEX_op_br; gen_args[0] = args[5]; gen_args += 1; @@ -875,7 +998,7 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, && temps[args[3]].val == 0) { /* Simplify LT/GE comparisons vs zero to a single compare vs the high word of the input. */ - memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info)); + reset_all_temps(nb_temps); s->gen_opc_buf[op_index] = INDEX_op_brcond_i32; gen_args[0] = args[1]; gen_args[1] = args[3]; @@ -938,9 +1061,10 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, /* Default case: we know nothing about operation (or were unable to compute the operation result) so no propagation is done. We trash everything if the operation is the end of a basic - block, otherwise we only trash the output args. */ + block, otherwise we only trash the output args. "mask" is + the non-zero bits mask for the first output arg. */ if (def->flags & TCG_OPF_BB_END) { - memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info)); + reset_all_temps(nb_temps); } else { for (i = 0; i < def->nb_oargs; i++) { reset_temp(args[i]); diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index ea26769581..17a6bb367a 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -94,11 +94,12 @@ typedef enum { #define TCG_TARGET_HAS_nor_i32 1 #define TCG_TARGET_HAS_deposit_i32 1 #define TCG_TARGET_HAS_movcond_i32 1 +#define TCG_TARGET_HAS_muls2_i32 0 #define TCG_AREG0 TCG_REG_R27 #define tcg_qemu_tb_exec(env, tb_ptr) \ ((long __attribute__ ((longcall)) \ - (*)(void *, void *))code_gen_prologue)(env, tb_ptr) + (*)(void *, void *))tcg_ctx.code_gen_prologue)(env, tb_ptr) #endif diff --git a/tcg/ppc64/tcg-target.h b/tcg/ppc64/tcg-target.h index 9b8e9a07b8..aa6a0f0306 100644 --- a/tcg/ppc64/tcg-target.h +++ b/tcg/ppc64/tcg-target.h @@ -85,6 +85,10 @@ typedef enum { #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_deposit_i32 0 #define TCG_TARGET_HAS_movcond_i32 0 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 +#define TCG_TARGET_HAS_mulu2_i32 0 +#define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rot_i64 0 @@ -106,6 +110,10 @@ typedef enum { #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_deposit_i64 0 #define TCG_TARGET_HAS_movcond_i64 0 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 #define TCG_AREG0 TCG_REG_R27 diff --git a/tcg/s390/tcg-target.h b/tcg/s390/tcg-target.h index c87b4138b5..40211e68f1 100644 --- a/tcg/s390/tcg-target.h +++ b/tcg/s390/tcg-target.h @@ -65,6 +65,10 @@ typedef enum TCGReg { #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_deposit_i32 0 #define TCG_TARGET_HAS_movcond_i32 0 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 +#define TCG_TARGET_HAS_mulu2_i32 0 +#define TCG_TARGET_HAS_muls2_i32 0 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_div2_i64 1 @@ -87,6 +91,10 @@ typedef enum TCGReg { #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_deposit_i64 0 #define TCG_TARGET_HAS_movcond_i64 0 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 #endif /* used for function call generation */ diff --git a/tcg/sparc/tcg-target.c b/tcg/sparc/tcg-target.c index 03db514a1d..025af9b379 100644 --- a/tcg/sparc/tcg-target.c +++ b/tcg/sparc/tcg-target.c @@ -776,6 +776,7 @@ static void tcg_out_setcond2_i32(TCGContext *s, TCGCond cond, TCGArg ret, break; } } +#endif static void tcg_out_addsub2(TCGContext *s, TCGArg rl, TCGArg rh, TCGArg al, TCGArg ah, TCGArg bl, int blconst, @@ -792,7 +793,6 @@ static void tcg_out_addsub2(TCGContext *s, TCGArg rl, TCGArg rh, tcg_out_arithc(s, rh, ah, bh, bhconst, oph); tcg_out_mov(s, TCG_TYPE_I32, rl, tmp); } -#endif /* Generate global QEMU prologue and epilogue code */ static void tcg_target_qemu_prologue(TCGContext *s) @@ -1327,6 +1327,8 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, args[3], const_args[3], args[4], const_args[4]); break; +#endif + case INDEX_op_add2_i32: tcg_out_addsub2(s, args[0], args[1], args[2], args[3], args[4], const_args[4], args[5], const_args[5], @@ -1342,7 +1344,6 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, ARITH_UMUL); tcg_out_rdy(s, args[1]); break; -#endif case INDEX_op_qemu_ld8u: tcg_out_qemu_ld(s, args, 0); @@ -1511,10 +1512,11 @@ static const TCGTargetOpDef sparc_op_defs[] = { #if TCG_TARGET_REG_BITS == 32 { INDEX_op_brcond2_i32, { "rZ", "rZ", "rJ", "rJ" } }, { INDEX_op_setcond2_i32, { "r", "rZ", "rZ", "rJ", "rJ" } }, +#endif + { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rJ", "rJ" } }, { INDEX_op_sub2_i32, { "r", "r", "rZ", "rZ", "rJ", "rJ" } }, { INDEX_op_mulu2_i32, { "r", "r", "rZ", "rJ" } }, -#endif #if TCG_TARGET_REG_BITS == 64 { INDEX_op_mov_i64, { "r", "r" } }, diff --git a/tcg/sparc/tcg-target.h b/tcg/sparc/tcg-target.h index 256f973c6d..b5217bef25 100644 --- a/tcg/sparc/tcg-target.h +++ b/tcg/sparc/tcg-target.h @@ -102,6 +102,10 @@ typedef enum { #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_deposit_i32 0 #define TCG_TARGET_HAS_movcond_i32 1 +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 1 +#define TCG_TARGET_HAS_muls2_i32 0 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_div_i64 1 @@ -124,6 +128,10 @@ typedef enum { #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_deposit_i64 0 #define TCG_TARGET_HAS_movcond_i64 1 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 #endif #define TCG_AREG0 TCG_REG_I0 diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h index 0b3cb0be3a..d70b2eba33 100644 --- a/tcg/tcg-op.h +++ b/tcg/tcg-op.h @@ -2246,6 +2246,26 @@ static inline void tcg_gen_concat32_i64(TCGv_i64 dest, TCGv_i64 low, tcg_gen_deposit_i64(dest, low, high, 32, 32); } +static inline void tcg_gen_extr_i64_i32(TCGv_i32 lo, TCGv_i32 hi, TCGv_i64 arg) +{ +#if TCG_TARGET_REG_BITS == 32 + tcg_gen_mov_i32(lo, TCGV_LOW(arg)); + tcg_gen_mov_i32(hi, TCGV_HIGH(arg)); +#else + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_trunc_i64_i32(lo, arg); + tcg_gen_shri_i64(t0, arg, 32); + tcg_gen_trunc_i64_i32(hi, t0); + tcg_temp_free_i64(t0); +#endif +} + +static inline void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg) +{ + tcg_gen_ext32u_i64(lo, arg); + tcg_gen_shri_i64(hi, arg, 32); +} + static inline void tcg_gen_movcond_i32(TCGCond cond, TCGv_i32 ret, TCGv_i32 c1, TCGv_i32 c2, TCGv_i32 v1, TCGv_i32 v2) @@ -2312,6 +2332,204 @@ static inline void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, #endif } +static inline void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, + TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh) +{ + if (TCG_TARGET_HAS_add2_i32) { + tcg_gen_op6_i32(INDEX_op_add2_i32, rl, rh, al, ah, bl, bh); + /* Allow the optimizer room to replace add2 with two moves. */ + tcg_gen_op0(INDEX_op_nop); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(t0, al, ah); + tcg_gen_concat_i32_i64(t1, bl, bh); + tcg_gen_add_i64(t0, t0, t1); + tcg_gen_extr_i64_i32(rl, rh, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +static inline void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, + TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh) +{ + if (TCG_TARGET_HAS_sub2_i32) { + tcg_gen_op6_i32(INDEX_op_sub2_i32, rl, rh, al, ah, bl, bh); + /* Allow the optimizer room to replace sub2 with two moves. */ + tcg_gen_op0(INDEX_op_nop); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(t0, al, ah); + tcg_gen_concat_i32_i64(t1, bl, bh); + tcg_gen_sub_i64(t0, t0, t1); + tcg_gen_extr_i64_i32(rl, rh, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +static inline void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, + TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_mulu2_i32) { + tcg_gen_op4_i32(INDEX_op_mulu2_i32, rl, rh, arg1, arg2); + /* Allow the optimizer room to replace mulu2 with two moves. */ + tcg_gen_op0(INDEX_op_nop); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(t0, arg1); + tcg_gen_extu_i32_i64(t1, arg2); + tcg_gen_mul_i64(t0, t0, t1); + tcg_gen_extr_i64_i32(rl, rh, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +static inline void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, + TCGv_i32 arg1, TCGv_i32 arg2) +{ + if (TCG_TARGET_HAS_muls2_i32) { + tcg_gen_op4_i32(INDEX_op_muls2_i32, rl, rh, arg1, arg2); + /* Allow the optimizer room to replace muls2 with two moves. */ + tcg_gen_op0(INDEX_op_nop); + } else if (TCG_TARGET_REG_BITS == 32 && TCG_TARGET_HAS_mulu2_i32) { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + tcg_gen_op4_i32(INDEX_op_mulu2_i32, t0, t1, arg1, arg2); + /* Allow the optimizer room to replace mulu2 with two moves. */ + tcg_gen_op0(INDEX_op_nop); + /* Adjust for negative inputs. */ + tcg_gen_sari_i32(t2, arg1, 31); + tcg_gen_sari_i32(t3, arg2, 31); + tcg_gen_and_i32(t2, t2, arg2); + tcg_gen_and_i32(t3, t3, arg1); + tcg_gen_sub_i32(rh, t1, t2); + tcg_gen_sub_i32(rh, rh, t3); + tcg_gen_mov_i32(rl, t0); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(t0, arg1); + tcg_gen_ext_i32_i64(t1, arg2); + tcg_gen_mul_i64(t0, t0, t1); + tcg_gen_extr_i64_i32(rl, rh, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +static inline void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, + TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh) +{ + if (TCG_TARGET_HAS_add2_i64) { + tcg_gen_op6_i64(INDEX_op_add2_i64, rl, rh, al, ah, bl, bh); + /* Allow the optimizer room to replace add2 with two moves. */ + tcg_gen_op0(INDEX_op_nop); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_add_i64(t0, al, bl); + tcg_gen_setcond_i64(TCG_COND_LTU, t1, t0, al); + tcg_gen_add_i64(rh, ah, bh); + tcg_gen_add_i64(rh, rh, t1); + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +static inline void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, + TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh) +{ + if (TCG_TARGET_HAS_sub2_i64) { + tcg_gen_op6_i64(INDEX_op_sub2_i64, rl, rh, al, ah, bl, bh); + /* Allow the optimizer room to replace sub2 with two moves. */ + tcg_gen_op0(INDEX_op_nop); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_sub_i64(t0, al, bl); + tcg_gen_setcond_i64(TCG_COND_LTU, t1, al, bl); + tcg_gen_sub_i64(rh, ah, bh); + tcg_gen_sub_i64(rh, rh, t1); + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} + +static inline void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, + TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_HAS_mulu2_i64) { + tcg_gen_op4_i64(INDEX_op_mulu2_i64, rl, rh, arg1, arg2); + /* Allow the optimizer room to replace mulu2 with two moves. */ + tcg_gen_op0(INDEX_op_nop); + } else if (TCG_TARGET_HAS_mulu2_i64) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + tcg_gen_op4_i64(INDEX_op_mulu2_i64, t0, t1, arg1, arg2); + /* Allow the optimizer room to replace mulu2 with two moves. */ + tcg_gen_op0(INDEX_op_nop); + /* Adjust for negative inputs. */ + tcg_gen_sari_i64(t2, arg1, 63); + tcg_gen_sari_i64(t3, arg2, 63); + tcg_gen_and_i64(t2, t2, arg2); + tcg_gen_and_i64(t3, t3, arg1); + tcg_gen_sub_i64(rh, t1, t2); + tcg_gen_sub_i64(rh, rh, t3); + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); + tcg_temp_free_i64(t3); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + int sizemask = 0; + /* Return value and both arguments are 64-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 1, 0); + sizemask |= tcg_gen_sizemask(1, 1, 0); + sizemask |= tcg_gen_sizemask(2, 1, 0); + tcg_gen_mul_i64(t0, arg1, arg2); + tcg_gen_helper64(tcg_helper_muluh_i64, sizemask, rh, arg1, arg2); + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); + } +} + +static inline void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, + TCGv_i64 arg1, TCGv_i64 arg2) +{ + if (TCG_TARGET_HAS_muls2_i64) { + tcg_gen_op4_i64(INDEX_op_muls2_i64, rl, rh, arg1, arg2); + /* Allow the optimizer room to replace muls2 with two moves. */ + tcg_gen_op0(INDEX_op_nop); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + tcg_gen_mul_i64(t0, arg1, arg2); + tcg_gen_helper64(tcg_helper_mulsh_i64, sizemask, rh, arg1, arg2); + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); + } +} + /***************************************/ /* QEMU specific operations. Their type depend on the QEMU CPU type. */ @@ -2329,6 +2547,7 @@ static inline void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, #define tcg_gen_qemu_ldst_op tcg_gen_op3i_i32 #define tcg_gen_qemu_ldst_op_i64 tcg_gen_qemu_ldst_op_i64_i32 #define TCGV_UNUSED(x) TCGV_UNUSED_I32(x) +#define TCGV_IS_UNUSED(x) TCGV_IS_UNUSED_I32(x) #define TCGV_EQUAL(a, b) TCGV_EQUAL_I32(a, b) #else #define TCGv TCGv_i64 @@ -2340,6 +2559,7 @@ static inline void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, #define tcg_gen_qemu_ldst_op tcg_gen_op3i_i64 #define tcg_gen_qemu_ldst_op_i64 tcg_gen_qemu_ldst_op_i64_i64 #define TCGV_UNUSED(x) TCGV_UNUSED_I64(x) +#define TCGV_IS_UNUSED(x) TCGV_IS_UNUSED_I64(x) #define TCGV_EQUAL(a, b) TCGV_EQUAL_I64(a, b) #endif @@ -2623,6 +2843,7 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) #define tcg_gen_bswap32_tl tcg_gen_bswap32_i64 #define tcg_gen_bswap64_tl tcg_gen_bswap64_i64 #define tcg_gen_concat_tl_i64 tcg_gen_concat32_i64 +#define tcg_gen_extr_i64_tl tcg_gen_extr32_i64 #define tcg_gen_andc_tl tcg_gen_andc_i64 #define tcg_gen_eqv_tl tcg_gen_eqv_i64 #define tcg_gen_nand_tl tcg_gen_nand_i64 @@ -2636,6 +2857,10 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) #define tcg_const_tl tcg_const_i64 #define tcg_const_local_tl tcg_const_local_i64 #define tcg_gen_movcond_tl tcg_gen_movcond_i64 +#define tcg_gen_add2_tl tcg_gen_add2_i64 +#define tcg_gen_sub2_tl tcg_gen_sub2_i64 +#define tcg_gen_mulu2_tl tcg_gen_mulu2_i64 +#define tcg_gen_muls2_tl tcg_gen_muls2_i64 #else #define tcg_gen_movi_tl tcg_gen_movi_i32 #define tcg_gen_mov_tl tcg_gen_mov_i32 @@ -2695,6 +2920,7 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) #define tcg_gen_bswap16_tl tcg_gen_bswap16_i32 #define tcg_gen_bswap32_tl tcg_gen_bswap32_i32 #define tcg_gen_concat_tl_i64 tcg_gen_concat_i32_i64 +#define tcg_gen_extr_tl_i64 tcg_gen_extr_i32_i64 #define tcg_gen_andc_tl tcg_gen_andc_i32 #define tcg_gen_eqv_tl tcg_gen_eqv_i32 #define tcg_gen_nand_tl tcg_gen_nand_i32 @@ -2708,6 +2934,10 @@ static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index) #define tcg_const_tl tcg_const_i32 #define tcg_const_local_tl tcg_const_local_i32 #define tcg_gen_movcond_tl tcg_gen_movcond_i32 +#define tcg_gen_add2_tl tcg_gen_add2_i32 +#define tcg_gen_sub2_tl tcg_gen_sub2_i32 +#define tcg_gen_mulu2_tl tcg_gen_mulu2_i32 +#define tcg_gen_muls2_tl tcg_gen_muls2_i32 #endif #if TCG_TARGET_REG_BITS == 32 diff --git a/tcg/tcg-opc.h b/tcg/tcg-opc.h index 9651063414..4246e9c1fa 100644 --- a/tcg/tcg-opc.h +++ b/tcg/tcg-opc.h @@ -83,10 +83,11 @@ DEF(deposit_i32, 1, 2, 2, IMPL(TCG_TARGET_HAS_deposit_i32)) DEF(brcond_i32, 0, 2, 2, TCG_OPF_BB_END) -DEF(add2_i32, 2, 4, 0, IMPL(TCG_TARGET_REG_BITS == 32)) -DEF(sub2_i32, 2, 4, 0, IMPL(TCG_TARGET_REG_BITS == 32)) +DEF(add2_i32, 2, 4, 0, IMPL(TCG_TARGET_HAS_add2_i32)) +DEF(sub2_i32, 2, 4, 0, IMPL(TCG_TARGET_HAS_sub2_i32)) +DEF(mulu2_i32, 2, 2, 0, IMPL(TCG_TARGET_HAS_mulu2_i32)) +DEF(muls2_i32, 2, 2, 0, IMPL(TCG_TARGET_HAS_muls2_i32)) DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | IMPL(TCG_TARGET_REG_BITS == 32)) -DEF(mulu2_i32, 2, 2, 0, IMPL(TCG_TARGET_REG_BITS == 32)) DEF(setcond2_i32, 1, 4, 1, IMPL(TCG_TARGET_REG_BITS == 32)) DEF(ext8s_i32, 1, 1, 0, IMPL(TCG_TARGET_HAS_ext8s_i32)) @@ -158,6 +159,11 @@ DEF(eqv_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_eqv_i64)) DEF(nand_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_nand_i64)) DEF(nor_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_nor_i64)) +DEF(add2_i64, 2, 4, 0, IMPL64 | IMPL(TCG_TARGET_HAS_add2_i64)) +DEF(sub2_i64, 2, 4, 0, IMPL64 | IMPL(TCG_TARGET_HAS_sub2_i64)) +DEF(mulu2_i64, 2, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_mulu2_i64)) +DEF(muls2_i64, 2, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_muls2_i64)) + /* QEMU specific */ #if TARGET_LONG_BITS > TCG_TARGET_REG_BITS DEF(debug_insn_start, 0, 0, 2, 0) diff --git a/tcg/tcg-runtime.h b/tcg/tcg-runtime.h index 5615b133e0..a1ebef9f9c 100644 --- a/tcg/tcg-runtime.h +++ b/tcg/tcg-runtime.h @@ -12,7 +12,9 @@ int64_t tcg_helper_shr_i64(int64_t arg1, int64_t arg2); int64_t tcg_helper_sar_i64(int64_t arg1, int64_t arg2); int64_t tcg_helper_div_i64(int64_t arg1, int64_t arg2); int64_t tcg_helper_rem_i64(int64_t arg1, int64_t arg2); +int64_t tcg_helper_mulsh_i64(int64_t arg1, int64_t arg2); uint64_t tcg_helper_divu_i64(uint64_t arg1, uint64_t arg2); uint64_t tcg_helper_remu_i64(uint64_t arg1, uint64_t arg2); +uint64_t tcg_helper_muluh_i64(uint64_t arg1, uint64_t arg2); #endif diff --git a/tcg/tcg.c b/tcg/tcg.c index ede51a3960..1d8265e72e 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -263,7 +263,7 @@ void tcg_context_init(TCGContext *s) void tcg_prologue_init(TCGContext *s) { /* init global prologue and epilogue */ - s->code_buf = code_gen_prologue; + s->code_buf = s->code_gen_prologue; s->code_ptr = s->code_buf; tcg_target_qemu_prologue(s); flush_icache_range((tcg_target_ulong)s->code_buf, @@ -800,7 +800,6 @@ static char *tcg_get_arg_str_idx(TCGContext *s, char *buf, int buf_size, assert(idx >= 0 && idx < s->nb_temps); ts = &s->temps[idx]; - assert(ts); if (idx < s->nb_globals) { pstrcpy(buf, buf_size, ts->name); } else { @@ -1218,7 +1217,7 @@ static inline void tcg_la_bb_end(TCGContext *s, uint8_t *dead_temps, static void tcg_liveness_analysis(TCGContext *s) { int i, op_index, nb_args, nb_iargs, nb_oargs, arg, nb_ops; - TCGOpcode op; + TCGOpcode op, op_new; TCGArg *args; const TCGOpDef *def; uint8_t *dead_temps, *mem_temps; @@ -1325,7 +1324,17 @@ static void tcg_liveness_analysis(TCGContext *s) break; case INDEX_op_add2_i32: + op_new = INDEX_op_add_i32; + goto do_addsub2; case INDEX_op_sub2_i32: + op_new = INDEX_op_sub_i32; + goto do_addsub2; + case INDEX_op_add2_i64: + op_new = INDEX_op_add_i64; + goto do_addsub2; + case INDEX_op_sub2_i64: + op_new = INDEX_op_sub_i64; + do_addsub2: args -= 6; nb_iargs = 4; nb_oargs = 2; @@ -1338,12 +1347,7 @@ static void tcg_liveness_analysis(TCGContext *s) goto do_remove; } /* Create the single operation plus nop. */ - if (op == INDEX_op_add2_i32) { - op = INDEX_op_add_i32; - } else { - op = INDEX_op_sub_i32; - } - s->gen_opc_buf[op_index] = op; + s->gen_opc_buf[op_index] = op = op_new; args[1] = args[2]; args[2] = args[4]; assert(s->gen_opc_buf[op_index + 1] == INDEX_op_nop); @@ -1355,6 +1359,13 @@ static void tcg_liveness_analysis(TCGContext *s) goto do_not_remove; case INDEX_op_mulu2_i32: + case INDEX_op_muls2_i32: + op_new = INDEX_op_mul_i32; + goto do_mul2; + case INDEX_op_mulu2_i64: + case INDEX_op_muls2_i64: + op_new = INDEX_op_mul_i64; + do_mul2: args -= 4; nb_iargs = 2; nb_oargs = 2; @@ -1363,7 +1374,7 @@ static void tcg_liveness_analysis(TCGContext *s) if (dead_temps[args[0]] && !mem_temps[args[0]]) { goto do_remove; } - s->gen_opc_buf[op_index] = op = INDEX_op_mul_i32; + s->gen_opc_buf[op_index] = op = op_new; args[1] = args[2]; args[2] = args[3]; assert(s->gen_opc_buf[op_index + 1] == INDEX_op_nop); diff --git a/tcg/tcg.h b/tcg/tcg.h index b2e2a25653..df375cf31e 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -57,8 +57,8 @@ typedef uint64_t TCGRegSet; #error unsupported #endif -/* Turn some undef macros into false macros. */ #if TCG_TARGET_REG_BITS == 32 +/* Turn some undef macros into false macros. */ #define TCG_TARGET_HAS_div_i64 0 #define TCG_TARGET_HAS_div2_i64 0 #define TCG_TARGET_HAS_rot_i64 0 @@ -80,6 +80,14 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_deposit_i64 0 #define TCG_TARGET_HAS_movcond_i64 0 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 +/* Turn some undef macros into true macros. */ +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 1 #endif #ifndef TCG_TARGET_deposit_i32_valid @@ -270,6 +278,9 @@ typedef int TCGv_i64; #define TCGV_UNUSED_I32(x) x = MAKE_TCGV_I32(-1) #define TCGV_UNUSED_I64(x) x = MAKE_TCGV_I64(-1) +#define TCGV_IS_UNUSED_I32(x) (GET_TCGV_I32(x) == -1) +#define TCGV_IS_UNUSED_I64(x) (GET_TCGV_I64(x) == -1) + /* call flags */ /* Helper does not read globals (either directly or through an exception). It implies TCG_CALL_NO_WRITE_GLOBALS. */ @@ -459,6 +470,17 @@ struct TCGContext { uint16_t gen_opc_icount[OPC_BUF_SIZE]; uint8_t gen_opc_instr_start[OPC_BUF_SIZE]; + /* Code generation */ + int code_gen_max_blocks; + uint8_t *code_gen_prologue; + uint8_t *code_gen_buffer; + size_t code_gen_buffer_size; + /* threshold to flush the translated code buffer */ + size_t code_gen_buffer_max_size; + uint8_t *code_gen_ptr; + + TBContext tb_ctx; + #if defined(CONFIG_QEMU_LDST_OPTIMIZATION) && defined(CONFIG_SOFTMMU) /* labels info for qemu_ld/st IRs The labels help to generate TLB miss case codes at the end of TB */ @@ -655,12 +677,58 @@ TCGv_i64 tcg_const_i64(int64_t val); TCGv_i32 tcg_const_local_i32(int32_t val); TCGv_i64 tcg_const_local_i64(int64_t val); -extern uint8_t *code_gen_prologue; +/** + * tcg_qemu_tb_exec: + * @env: CPUArchState * for the CPU + * @tb_ptr: address of generated code for the TB to execute + * + * Start executing code from a given translation block. + * Where translation blocks have been linked, execution + * may proceed from the given TB into successive ones. + * Control eventually returns only when some action is needed + * from the top-level loop: either control must pass to a TB + * which has not yet been directly linked, or an asynchronous + * event such as an interrupt needs handling. + * + * The return value is a pointer to the next TB to execute + * (if known; otherwise zero). This pointer is assumed to be + * 4-aligned, and the bottom two bits are used to return further + * information: + * 0, 1: the link between this TB and the next is via the specified + * TB index (0 or 1). That is, we left the TB via (the equivalent + * of) "goto_tb ". The main loop uses this to determine + * how to link the TB just executed to the next. + * 2: we are using instruction counting code generation, and we + * did not start executing this TB because the instruction counter + * would hit zero midway through it. In this case the next-TB pointer + * returned is the TB we were about to execute, and the caller must + * arrange to execute the remaining count of instructions. + * 3: we stopped because the CPU's exit_request flag was set + * (usually meaning that there is an interrupt that needs to be + * handled). The next-TB pointer returned is the TB we were + * about to execute when we noticed the pending exit request. + * + * If the bottom two bits indicate an exit-via-index then the CPU + * state is correctly synchronised and ready for execution of the next + * TB (and in particular the guest PC is the address to execute next). + * Otherwise, we gave up on execution of this TB before it started, and + * the caller must fix up the CPU state by calling cpu_pc_from_tb() + * with the next-TB pointer we return. + * + * Note that TCG targets may use a different definition of tcg_qemu_tb_exec + * to this default (which just calls the prologue.code emitted by + * tcg_target_qemu_prologue()). + */ +#define TB_EXIT_MASK 3 +#define TB_EXIT_IDX0 0 +#define TB_EXIT_IDX1 1 +#define TB_EXIT_ICOUNT_EXPIRED 2 +#define TB_EXIT_REQUESTED 3 -/* TCG targets may use a different definition of tcg_qemu_tb_exec. */ #if !defined(tcg_qemu_tb_exec) # define tcg_qemu_tb_exec(env, tb_ptr) \ - ((tcg_target_ulong (*)(void *, void *))code_gen_prologue)(env, tb_ptr) + ((tcg_target_ulong (*)(void *, void *))tcg_ctx.code_gen_prologue)(env, \ + tb_ptr) #endif void tcg_register_jit(void *buf, size_t buf_size); diff --git a/tcg/tci/README b/tcg/tci/README index 6ac1ac99d6..dc57f076b5 100644 --- a/tcg/tci/README +++ b/tcg/tci/README @@ -52,7 +52,7 @@ The only difference from running QEMU with TCI to running without TCI should be speed. Especially during development of TCI, it was very useful to compare runs with and without TCI. Create /tmp/qemu.log by - qemu-system-i386 -d in_asm,op_opt,cpu -singlestep + qemu-system-i386 -d in_asm,op_opt,cpu -D /tmp/qemu.log -singlestep once with interpreter and once without interpreter and compare the resulting qemu.log files. This is also useful to see the effects of additional diff --git a/tcg/tci/tcg-target.c b/tcg/tci/tcg-target.c index 1707169ea8..2d561b32e1 100644 --- a/tcg/tci/tcg-target.c +++ b/tcg/tci/tcg-target.c @@ -888,7 +888,7 @@ static void tcg_target_init(TCGContext *s) #if defined(CONFIG_DEBUG_TCG_INTERPRETER) const char *envval = getenv("DEBUG_TCG"); if (envval) { - cpu_set_log(strtol(envval, NULL, 0)); + qemu_set_log(strtol(envval, NULL, 0)); } #endif diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index a832f5cf52..1f17576f54 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -76,6 +76,7 @@ #define TCG_TARGET_HAS_orc_i32 0 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_movcond_i32 0 +#define TCG_TARGET_HAS_muls2_i32 0 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_bswap16_i64 1 @@ -100,6 +101,14 @@ #define TCG_TARGET_HAS_orc_i64 0 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_movcond_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 + +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 +#define TCG_TARGET_HAS_mulu2_i32 0 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 +#define TCG_TARGET_HAS_mulu2_i64 0 #endif /* TCG_TARGET_REG_BITS == 64 */ /* Number of registers available. diff --git a/tests/.gitignore b/tests/.gitignore index f9041f3d32..fb05c2ae87 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -4,10 +4,18 @@ check-qint check-qjson check-qlist check-qstring +test-aio +test-cutils +test-hbitmap +test-iov +test-mul64 test-qapi-types.[ch] test-qapi-visit.[ch] test-qmp-commands.h test-qmp-commands test-qmp-input-strict test-qmp-marshal.c +test-thread-pool +test-x86-cpuid +test-xbzrle *-test diff --git a/tests/Makefile b/tests/Makefile index b60f0fb8f0..567e36e777 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,33 +1,80 @@ export SRC_PATH check-unit-y = tests/check-qdict$(EXESUF) +gcov-files-check-qdict-y = qobject/qdict.c check-unit-y += tests/check-qfloat$(EXESUF) +gcov-files-check-qfloat-y = qobject/qfloat.c check-unit-y += tests/check-qint$(EXESUF) +gcov-files-check-qint-y = qobject/qint.c check-unit-y += tests/check-qstring$(EXESUF) +gcov-files-check-qstring-y = qobject/qstring.c check-unit-y += tests/check-qlist$(EXESUF) +gcov-files-check-qlist-y = qobject/qlist.c check-unit-y += tests/check-qjson$(EXESUF) +gcov-files-check-qjson-y = qobject/qjson.c check-unit-y += tests/test-qmp-output-visitor$(EXESUF) +gcov-files-test-qmp-output-visitor-y = qapi/qmp-output-visitor.c check-unit-y += tests/test-qmp-input-visitor$(EXESUF) +gcov-files-test-qmp-input-visitor-y = qapi/qmp-input-visitor.c check-unit-y += tests/test-qmp-input-strict$(EXESUF) check-unit-y += tests/test-qmp-commands$(EXESUF) +gcov-files-test-qmp-commands-y = qapi/qmp-dispatch.c check-unit-y += tests/test-string-input-visitor$(EXESUF) +gcov-files-test-string-input-visitor-y = qapi/string-input-visitor.c check-unit-y += tests/test-string-output-visitor$(EXESUF) +gcov-files-test-string-output-visitor-y = qapi/string-output-visitor.c check-unit-y += tests/test-coroutine$(EXESUF) +ifeq ($(CONFIG_WIN32),y) +gcov-files-test-coroutine-y = coroutine-win32.c +else +ifeq ($(CONFIG_UCONTEXT_COROUTINE),y) +gcov-files-test-coroutine-y = coroutine-ucontext.c +else +ifeq ($(CONFIG_SIGALTSTACK_COROUTINE),y) +gcov-files-test-coroutine-y = coroutine-sigaltstack.c +else +gcov-files-test-coroutine-y = coroutine-gthread.c +endif +endif +endif check-unit-y += tests/test-visitor-serialization$(EXESUF) check-unit-y += tests/test-iov$(EXESUF) +gcov-files-test-iov-y = util/iov.c check-unit-y += tests/test-aio$(EXESUF) +gcov-files-test-aio-$(CONFIG_WIN32) = aio-win32.c +gcov-files-test-aio-$(CONFIG_POSIX) = aio-posix.c check-unit-y += tests/test-thread-pool$(EXESUF) +gcov-files-test-thread-pool-y = thread-pool.c +gcov-files-test-hbitmap-y = util/hbitmap.c +check-unit-y += tests/test-hbitmap$(EXESUF) +check-unit-y += tests/test-x86-cpuid$(EXESUF) +# all code tested by test-x86-cpuid is inside topology.h +gcov-files-test-x86-cpuid-y = +check-unit-y += tests/test-xbzrle$(EXESUF) +gcov-files-test-xbzrle-y = xbzrle.c +check-unit-y += tests/test-cutils$(EXESUF) +gcov-files-test-cutils-y += util/cutils.c +check-unit-y += tests/test-mul64$(EXESUF) +gcov-files-test-mul64-y = util/host-utils.c check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh # All QTests for now are POSIX-only, but the dependencies are # really in libqtest, not in the testcases themselves. check-qtest-i386-y = tests/fdc-test$(EXESUF) +gcov-files-i386-y = hw/fdc.c check-qtest-i386-y += tests/hd-geo-test$(EXESUF) +gcov-files-i386-y += hw/hd-geometry.c check-qtest-i386-y += tests/rtc-test$(EXESUF) check-qtest-x86_64-y = $(check-qtest-i386-y) -check-qtest-sparc-y = tests/m48t59-test$(EXESUF) -check-qtest-sparc64-y = tests/m48t59-test$(EXESUF) +gcov-files-i386-y += i386-softmmu/hw/mc146818rtc.c +gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y)) +#check-qtest-sparc-y = tests/m48t59-test$(EXESUF) +#check-qtest-sparc64-y = tests/m48t59-test$(EXESUF) +gcov-files-sparc-y += hw/m48t59.c +gcov-files-sparc64-y += hw/m48t59.c +check-qtest-arm-y = tests/tmp105-test$(EXESUF) +gcov-files-arm-y += hw/tmp105.c GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h @@ -36,24 +83,29 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/test-coroutine.o tests/test-string-output-visitor.o \ tests/test-string-input-visitor.o tests/test-qmp-output-visitor.o \ tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \ - tests/test-qmp-commands.o tests/test-visitor-serialization.o + tests/test-qmp-commands.o tests/test-visitor-serialization.o \ + tests/test-x86-cpuid.o tests/test-mul64.o -test-qapi-obj-y = $(qobject-obj-y) $(qapi-obj-y) qemu-tool.o -test-qapi-obj-y += tests/test-qapi-visit.o tests/test-qapi-types.o -test-qapi-obj-y += module.o +test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o $(test-obj-y): QEMU_INCLUDES += -Itests -tests/check-qint$(EXESUF): tests/check-qint.o qint.o -tests/check-qstring$(EXESUF): tests/check-qstring.o qstring.o -tests/check-qdict$(EXESUF): tests/check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o -tests/check-qlist$(EXESUF): tests/check-qlist.o qlist.o qint.o -tests/check-qfloat$(EXESUF): tests/check-qfloat.o qfloat.o -tests/check-qjson$(EXESUF): tests/check-qjson.o $(qobject-obj-y) qemu-tool.o -tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(coroutine-obj-y) $(tools-obj-y) $(block-obj-y) iov.o libqemustub.a -tests/test-aio$(EXESUF): tests/test-aio.o $(coroutine-obj-y) $(tools-obj-y) $(block-obj-y) libqemustub.a -tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(coroutine-obj-y) $(tools-obj-y) $(block-obj-y) libqemustub.a -tests/test-iov$(EXESUF): tests/test-iov.o iov.o +tests/test-x86-cpuid.o: QEMU_INCLUDES += -I$(SRC_PATH)/target-i386 + +tests/check-qint$(EXESUF): tests/check-qint.o libqemuutil.a +tests/check-qstring$(EXESUF): tests/check-qstring.o libqemuutil.a +tests/check-qdict$(EXESUF): tests/check-qdict.o libqemuutil.a +tests/check-qlist$(EXESUF): tests/check-qlist.o libqemuutil.a +tests/check-qfloat$(EXESUF): tests/check-qfloat.o libqemuutil.a +tests/check-qjson$(EXESUF): tests/check-qjson.o libqemuutil.a libqemustub.a +tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil.a libqemustub.a +tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a +tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemuutil.a libqemustub.a +tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a +tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a +tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o +tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o xbzrle.o page_cache.o libqemuutil.a +tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o tests/test-qapi-types.c tests/test-qapi-types.h :\ $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py @@ -66,18 +118,21 @@ $(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o tests -p "test-" < $<, " GEN $@") -tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) -tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) -tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) -tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) -tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) -tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) -tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) +tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a +tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a +tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a +tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a +tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a +tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) qapi-types.o qapi-visit.o libqemuutil.a libqemustub.a +tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a -tests/rtc-test$(EXESUF): tests/rtc-test.o $(trace-obj-y) -tests/m48t59-test$(EXESUF): tests/m48t59-test.o $(trace-obj-y) -tests/fdc-test$(EXESUF): tests/fdc-test.o tests/libqtest.o $(trace-obj-y) -tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o tests/libqtest.o $(trace-obj-y) +tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a + +tests/rtc-test$(EXESUF): tests/rtc-test.o +tests/m48t59-test$(EXESUF): tests/m48t59-test.o +tests/fdc-test$(EXESUF): tests/fdc-test.o +tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o +tests/tmp105-test$(EXESUF): tests/tmp105-test.o # QTest rules @@ -85,7 +140,8 @@ TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS))) QTEST_TARGETS=$(foreach TARGET,$(TARGETS), $(if $(check-qtest-$(TARGET)-y), $(TARGET),)) check-qtest-$(CONFIG_POSIX)=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y)) -qtest-obj-y = tests/libqtest.o $(oslib-obj-y) libqemustub.a +qtest-obj-y = tests/libqtest.o libqemuutil.a libqemustub.a +qtest-obj-y += tests/libi2c.o tests/libi2c-omap.o $(check-qtest-y): $(qtest-obj-y) .PHONY: check-help @@ -108,17 +164,28 @@ check-help: SPEED = quick GTESTER_OPTIONS = -k $(if $(V),--verbose,-q) +GCOV_OPTIONS = -n $(if $(V),-f,) # gtester tests, possibly with verbose output .PHONY: $(patsubst %, check-qtest-%, $(QTEST_TARGETS)) $(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: $(check-qtest-y) + $(if $(CONFIG_GCOV),@rm -f *.gcda */*.gcda */*/*.gcda */*/*/*.gcda,) $(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \ gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y),"GTESTER $@") + $(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y); do \ + echo Gcov report for $$f:;\ + $(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \ + done,) .PHONY: $(patsubst %, check-%, $(check-unit-y)) $(patsubst %, check-%, $(check-unit-y)): check-%: % + $(if $(CONFIG_GCOV),@rm -f *.gcda */*.gcda */*/*.gcda */*/*/*.gcda,) $(call quiet-command,gtester $(GTESTER_OPTIONS) -m=$(SPEED) $*,"GTESTER $*") + $(if $(CONFIG_GCOV),@for f in $(gcov-files-$(subst tests/,,$*)-y); do \ + echo Gcov report for $$f:;\ + $(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \ + done,) # gtester tests with XML output diff --git a/tests/check-qjson.c b/tests/check-qjson.c index 32ffb436df..ec85a0cf2d 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -1,8 +1,10 @@ /* * Copyright IBM, Corp. 2009 + * Copyright (c) 2013 Red Hat Inc. * * Authors: * Anthony Liguori + * Markus Armbruster , * * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. * See the COPYING.LIB file in the top-level directory. @@ -131,6 +133,667 @@ static void single_quote_string(void) } } +static void utf8_string(void) +{ + /* + * FIXME Current behavior for invalid UTF-8 sequences is + * incorrect. This test expects current, incorrect results. + * They're all marked "bug:" below, and are to be replaced by + * correct ones as the bugs get fixed. + * + * The JSON parser rejects some invalid sequences, but accepts + * others without correcting the problem. + * + * The JSON formatter replaces some invalid sequences by U+FFFF (a + * noncharacter), and goes wonky for others. + * + * For both directions, we should either reject all invalid + * sequences, or minimize overlong sequences and replace all other + * invalid sequences by a suitable replacement character. A + * common choice for replacement is U+FFFD. + * + * Problem: we can't easily deal with embedded U+0000. Parsing + * the JSON string "this \\u0000" is fun" yields "this \0 is fun", + * which gets misinterpreted as NUL-terminated "this ". We should + * consider using overlong encoding \xC0\x80 for U+0000 ("modified + * UTF-8"). + * + * Test cases are scraped from Markus Kuhn's UTF-8 decoder + * capability and stress test at + * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt + */ + static const struct { + const char *json_in; + const char *utf8_out; + const char *json_out; /* defaults to @json_in */ + const char *utf8_in; /* defaults to @utf8_out */ + } test_cases[] = { + /* + * Bug markers used here: + * - bug: not corrected + * JSON parser fails to correct invalid sequence(s) + * - bug: rejected + * JSON parser rejects invalid sequence(s) + * We may choose to define this as feature + * - bug: want "\"...\"" + * JSON formatter produces incorrect result, this is the + * correct one, assuming replacement character U+FFFF + * - bug: want "..." (no \") + * JSON parser produces incorrect result, this is the + * correct one, assuming replacement character U+FFFF + * We may choose to reject instead of replace + * Not marked explicitly, but trivial to find: + * - JSON formatter replacing invalid sequence by \\uFFFF is a + * bug if we want it to fail for invalid sequences. + */ + + /* 1 Some correct UTF-8 text */ + { + /* a bit of German */ + "\"Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt" + " jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.\"", + "Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt" + " jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.", + "\"Falsches \\u00DCben von Xylophonmusik qu\\u00E4lt" + " jeden gr\\u00F6\\u00DFeren Zwerg.\"", + }, + { + /* a bit of Greek */ + "\"\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5\"", + "\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5", + "\"\\u03BA\\u1F79\\u03C3\\u03BC\\u03B5\"", + }, + /* 2 Boundary condition test cases */ + /* 2.1 First possible sequence of a certain length */ + /* 2.1.1 1 byte U+0000 */ + { + "\"\\u0000\"", + "", /* bug: want overlong "\xC0\x80" */ + "\"\"", /* bug: want "\"\\u0000\"" */ + }, + /* 2.1.2 2 bytes U+0080 */ + { + "\"\xC2\x80\"", + "\xC2\x80", + "\"\\u0080\"", + }, + /* 2.1.3 3 bytes U+0800 */ + { + "\"\xE0\xA0\x80\"", + "\xE0\xA0\x80", + "\"\\u0800\"", + }, + /* 2.1.4 4 bytes U+10000 */ + { + "\"\xF0\x90\x80\x80\"", + "\xF0\x90\x80\x80", + "\"\\u0400\\uFFFF\"", /* bug: want "\"\\uD800\\uDC00\"" */ + }, + /* 2.1.5 5 bytes U+200000 */ + { + "\"\xF8\x88\x80\x80\x80\"", + NULL, /* bug: rejected */ + "\"\\u8200\\uFFFF\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + "\xF8\x88\x80\x80\x80", + }, + /* 2.1.6 6 bytes U+4000000 */ + { + "\"\xFC\x84\x80\x80\x80\x80\"", + NULL, /* bug: rejected */ + "\"\\uC100\\uFFFF\\uFFFF\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + "\xFC\x84\x80\x80\x80\x80", + }, + /* 2.2 Last possible sequence of a certain length */ + /* 2.2.1 1 byte U+007F */ + { + "\"\x7F\"", + "\x7F", + "\"\177\"", + }, + /* 2.2.2 2 bytes U+07FF */ + { + "\"\xDF\xBF\"", + "\xDF\xBF", + "\"\\u07FF\"", + }, + /* 2.2.3 3 bytes U+FFFF */ + { + "\"\xEF\xBF\xBF\"", + "\xEF\xBF\xBF", + "\"\\uFFFF\"", + }, + /* 2.2.4 4 bytes U+1FFFFF */ + { + "\"\xF7\xBF\xBF\xBF\"", + NULL, /* bug: rejected */ + "\"\\u7FFF\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + "\xF7\xBF\xBF\xBF", + }, + /* 2.2.5 5 bytes U+3FFFFFF */ + { + "\"\xFB\xBF\xBF\xBF\xBF\"", + NULL, /* bug: rejected */ + "\"\\uBFFF\\uFFFF\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + "\xFB\xBF\xBF\xBF\xBF", + }, + /* 2.2.6 6 bytes U+7FFFFFFF */ + { + "\"\xFD\xBF\xBF\xBF\xBF\xBF\"", + NULL, /* bug: rejected */ + "\"\\uDFFF\\uFFFF\\uFFFF\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + "\xFD\xBF\xBF\xBF\xBF\xBF", + }, + /* 2.3 Other boundary conditions */ + { + /* U+D7FF */ + "\"\xED\x9F\xBF\"", + "\xED\x9F\xBF", + "\"\\uD7FF\"", + }, + { + /* U+E000 */ + "\"\xEE\x80\x80\"", + "\xEE\x80\x80", + "\"\\uE000\"", + }, + { + /* U+FFFD */ + "\"\xEF\xBF\xBD\"", + "\xEF\xBF\xBD", + "\"\\uFFFD\"", + }, + { + /* U+10FFFF */ + "\"\xF4\x8F\xBF\xBF\"", + "\xF4\x8F\xBF\xBF", + "\"\\u43FF\\uFFFF\"", /* bug: want "\"\\uDBFF\\uDFFF\"" */ + }, + { + /* U+110000 */ + "\"\xF4\x90\x80\x80\"", + "\xF4\x90\x80\x80", + "\"\\u4400\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + }, + /* 3 Malformed sequences */ + /* 3.1 Unexpected continuation bytes */ + /* 3.1.1 First continuation byte */ + { + "\"\x80\"", + "\x80", /* bug: not corrected */ + "\"\\uFFFF\"", + }, + /* 3.1.2 Last continuation byte */ + { + "\"\xBF\"", + "\xBF", /* bug: not corrected */ + "\"\\uFFFF\"", + }, + /* 3.1.3 2 continuation bytes */ + { + "\"\x80\xBF\"", + "\x80\xBF", /* bug: not corrected */ + "\"\\uFFFF\\uFFFF\"", + }, + /* 3.1.4 3 continuation bytes */ + { + "\"\x80\xBF\x80\"", + "\x80\xBF\x80", /* bug: not corrected */ + "\"\\uFFFF\\uFFFF\\uFFFF\"", + }, + /* 3.1.5 4 continuation bytes */ + { + "\"\x80\xBF\x80\xBF\"", + "\x80\xBF\x80\xBF", /* bug: not corrected */ + "\"\\uFFFF\\uFFFF\\uFFFF\\uFFFF\"", + }, + /* 3.1.6 5 continuation bytes */ + { + "\"\x80\xBF\x80\xBF\x80\"", + "\x80\xBF\x80\xBF\x80", /* bug: not corrected */ + "\"\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\"", + }, + /* 3.1.7 6 continuation bytes */ + { + "\"\x80\xBF\x80\xBF\x80\xBF\"", + "\x80\xBF\x80\xBF\x80\xBF", /* bug: not corrected */ + "\"\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\"", + }, + /* 3.1.8 7 continuation bytes */ + { + "\"\x80\xBF\x80\xBF\x80\xBF\x80\"", + "\x80\xBF\x80\xBF\x80\xBF\x80", /* bug: not corrected */ + "\"\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\"", + }, + /* 3.1.9 Sequence of all 64 possible continuation bytes */ + { + "\"\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" + "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7" + "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" + "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7" + "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\"", + /* bug: not corrected */ + "\x80\x81\x82\x83\x84\x85\x86\x87" + "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" + "\x90\x91\x92\x93\x94\x95\x96\x97" + "\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" + "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7" + "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" + "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7" + "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF", + "\"\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF" + "\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF" + "\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF" + "\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF" + "\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF" + "\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF" + "\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF" + "\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\"" + }, + /* 3.2 Lonely start characters */ + /* 3.2.1 All 32 first bytes of 2-byte sequences, followed by space */ + { + "\"\xC0 \xC1 \xC2 \xC3 \xC4 \xC5 \xC6 \xC7 " + "\xC8 \xC9 \xCA \xCB \xCC \xCD \xCE \xCF " + "\xD0 \xD1 \xD2 \xD3 \xD4 \xD5 \xD6 \xD7 " + "\xD8 \xD9 \xDA \xDB \xDC \xDD \xDE \xDF \"", + NULL, /* bug: rejected */ + "\"\\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF " + "\\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF " + "\\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF " + "\\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \"", + "\xC0 \xC1 \xC2 \xC3 \xC4 \xC5 \xC6 \xC7 " + "\xC8 \xC9 \xCA \xCB \xCC \xCD \xCE \xCF " + "\xD0 \xD1 \xD2 \xD3 \xD4 \xD5 \xD6 \xD7 " + "\xD8 \xD9 \xDA \xDB \xDC \xDD \xDE \xDF ", + }, + /* 3.2.2 All 16 first bytes of 3-byte sequences, followed by space */ + { + "\"\xE0 \xE1 \xE2 \xE3 \xE4 \xE5 \xE6 \xE7 " + "\xE8 \xE9 \xEA \xEB \xEC \xED \xEE \xEF \"", + /* bug: not corrected */ + "\xE0 \xE1 \xE2 \xE3 \xE4 \xE5 \xE6 \xE7 " + "\xE8 \xE9 \xEA \xEB \xEC \xED \xEE \xEF ", + "\"\\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF " + "\\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \"", + }, + /* 3.2.3 All 8 first bytes of 4-byte sequences, followed by space */ + { + "\"\xF0 \xF1 \xF2 \xF3 \xF4 \xF5 \xF6 \xF7 \"", + NULL, /* bug: rejected */ + "\"\\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \\uFFFF \"", + "\xF0 \xF1 \xF2 \xF3 \xF4 \xF5 \xF6 \xF7 ", + }, + /* 3.2.4 All 4 first bytes of 5-byte sequences, followed by space */ + { + "\"\xF8 \xF9 \xFA \xFB \"", + NULL, /* bug: rejected */ + "\"\\uFFFF \\uFFFF \\uFFFF \\uFFFF \"", + "\xF8 \xF9 \xFA \xFB ", + }, + /* 3.2.5 All 2 first bytes of 6-byte sequences, followed by space */ + { + "\"\xFC \xFD \"", + NULL, /* bug: rejected */ + "\"\\uFFFF \\uFFFF \"", + "\xFC \xFD ", + }, + /* 3.3 Sequences with last continuation byte missing */ + /* 3.3.1 2-byte sequence with last byte missing (U+0000) */ + { + "\"\xC0\"", + NULL, /* bug: rejected */ + "\"\\uFFFF\"", + "\xC0", + }, + /* 3.3.2 3-byte sequence with last byte missing (U+0000) */ + { + "\"\xE0\x80\"", + "\xE0\x80", /* bug: not corrected */ + "\"\\uFFFF\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + }, + /* 3.3.3 4-byte sequence with last byte missing (U+0000) */ + { + "\"\xF0\x80\x80\"", + "\xF0\x80\x80", /* bug: not corrected */ + "\"\\u0000\"", /* bug: want "\"\\uFFFF\"" */ + }, + /* 3.3.4 5-byte sequence with last byte missing (U+0000) */ + { + /* invalid */ + "\"\xF8\x80\x80\x80\"", /* bug: not corrected */ + NULL, /* bug: rejected */ + "\"\\u8000\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + "\xF8\x80\x80\x80", + }, + /* 3.3.5 6-byte sequence with last byte missing (U+0000) */ + { + "\"\xFC\x80\x80\x80\x80\"", + NULL, /* bug: rejected */ + "\"\\uC000\\uFFFF\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + "\xFC\x80\x80\x80\x80", + }, + /* 3.3.6 2-byte sequence with last byte missing (U+07FF) */ + { + "\"\xDF\"", + "\xDF", /* bug: not corrected */ + "\"\\uFFFF\"", + }, + /* 3.3.7 3-byte sequence with last byte missing (U+FFFF) */ + { + "\"\xEF\xBF\"", + "\xEF\xBF", /* bug: not corrected */ + "\"\\uFFFF\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + }, + /* 3.3.8 4-byte sequence with last byte missing (U+1FFFFF) */ + { + "\"\xF7\xBF\xBF\"", + NULL, /* bug: rejected */ + "\"\\u7FFF\"", /* bug: want "\"\\uFFFF\"" */ + "\xF7\xBF\xBF", + }, + /* 3.3.9 5-byte sequence with last byte missing (U+3FFFFFF) */ + { + "\"\xFB\xBF\xBF\xBF\"", + NULL, /* bug: rejected */ + "\"\\uBFFF\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + "\xFB\xBF\xBF\xBF", + }, + /* 3.3.10 6-byte sequence with last byte missing (U+7FFFFFFF) */ + { + "\"\xFD\xBF\xBF\xBF\xBF\"", + NULL, /* bug: rejected */ + "\"\\uDFFF\\uFFFF\\uFFFF\"", /* bug: want "\"\\uFFFF\"", */ + "\xFD\xBF\xBF\xBF\xBF", + }, + /* 3.4 Concatenation of incomplete sequences */ + { + "\"\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80" + "\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF\xBF\xBF\xFD\xBF\xBF\xBF\xBF\"", + NULL, /* bug: rejected */ + /* bug: want "\"\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF" + "\\uFFFF\\uFFFF\\uFFFF\\uFFFF\\uFFFF\"" */ + "\"\\u0020\\uFFFF\\u0000\\u8000\\uFFFF\\uC000\\uFFFF\\uFFFF" + "\\u07EF\\uFFFF\\u7FFF\\uBFFF\\uFFFF\\uDFFF\\uFFFF\\uFFFF\"", + "\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80" + "\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF\xBF\xBF\xFD\xBF\xBF\xBF\xBF", + }, + /* 3.5 Impossible bytes */ + { + "\"\xFE\"", + NULL, /* bug: rejected */ + "\"\\uFFFF\"", + "\xFE", + }, + { + "\"\xFF\"", + NULL, /* bug: rejected */ + "\"\\uFFFF\"", + "\xFF", + }, + { + "\"\xFE\xFE\xFF\xFF\"", + NULL, /* bug: rejected */ + /* bug: want "\"\\uFFFF\\uFFFF\\uFFFF\\uFFFF\"" */ + "\"\\uEFBF\\uFFFF\"", + "\xFE\xFE\xFF\xFF", + }, + /* 4 Overlong sequences */ + /* 4.1 Overlong '/' */ + { + "\"\xC0\xAF\"", + NULL, /* bug: rejected */ + "\"\\u002F\"", /* bug: want "\"/\"" */ + "\xC0\xAF", + }, + { + "\"\xE0\x80\xAF\"", + "\xE0\x80\xAF", /* bug: not corrected */ + "\"\\u002F\"", /* bug: want "\"/\"" */ + }, + { + "\"\xF0\x80\x80\xAF\"", + "\xF0\x80\x80\xAF", /* bug: not corrected */ + "\"\\u0000\\uFFFF\"" /* bug: want "\"/\"" */ + }, + { + "\"\xF8\x80\x80\x80\xAF\"", + NULL, /* bug: rejected */ + "\"\\u8000\\uFFFF\\uFFFF\"", /* bug: want "\"/\"" */ + "\xF8\x80\x80\x80\xAF", + }, + { + "\"\xFC\x80\x80\x80\x80\xAF\"", + NULL, /* bug: rejected */ + "\"\\uC000\\uFFFF\\uFFFF\\uFFFF\"", /* bug: want "\"/\"" */ + "\xFC\x80\x80\x80\x80\xAF", + }, + /* 4.2 Maximum overlong sequences */ + { + /* \U+007F */ + "\"\xC1\xBF\"", + NULL, /* bug: rejected */ + "\"\\u007F\"", /* bug: want "\"\177\"" */ + "\xC1\xBF", + }, + { + /* \U+07FF */ + "\"\xE0\x9F\xBF\"", + "\xE0\x9F\xBF", /* bug: not corrected */ + "\"\\u07FF\"", + }, + { + /* \U+FFFF */ + "\"\xF0\x8F\xBF\xBF\"", + "\xF0\x8F\xBF\xBF", /* bug: not corrected */ + "\"\\u03FF\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + }, + { + /* \U+1FFFFF */ + "\"\xF8\x87\xBF\xBF\xBF\"", + NULL, /* bug: rejected */ + "\"\\u81FF\\uFFFF\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + "\xF8\x87\xBF\xBF\xBF", + }, + { + /* \U+3FFFFFF */ + "\"\xFC\x83\xBF\xBF\xBF\xBF\"", + NULL, /* bug: rejected */ + "\"\\uC0FF\\uFFFF\\uFFFF\\uFFFF\"", /* bug: want "\"\\uFFFF\"" */ + "\xFC\x83\xBF\xBF\xBF\xBF", + }, + /* 4.3 Overlong representation of the NUL character */ + { + /* \U+0000 */ + "\"\xC0\x80\"", + NULL, /* bug: rejected */ + "\"\\u0000\"", + "\xC0\x80", + }, + { + /* \U+0000 */ + "\"\xE0\x80\x80\"", + "\xE0\x80\x80", /* bug: not corrected */ + "\"\\u0000\"", + }, + { + /* \U+0000 */ + "\"\xF0\x80\x80\x80\"", + "\xF0\x80\x80\x80", /* bug: not corrected */ + "\"\\u0000\\uFFFF\"", /* bug: want "\"\\u0000\"" */ + }, + { + /* \U+0000 */ + "\"\xF8\x80\x80\x80\x80\"", + NULL, /* bug: rejected */ + "\"\\u8000\\uFFFF\\uFFFF\"", /* bug: want "\"\\u0000\"" */ + "\xF8\x80\x80\x80\x80", + }, + { + /* \U+0000 */ + "\"\xFC\x80\x80\x80\x80\x80\"", + NULL, /* bug: rejected */ + "\"\\uC000\\uFFFF\\uFFFF\\uFFFF\"", /* bug: want "\"\\u0000\"" */ + "\xFC\x80\x80\x80\x80\x80", + }, + /* 5 Illegal code positions */ + /* 5.1 Single UTF-16 surrogates */ + { + /* \U+D800 */ + "\"\xED\xA0\x80\"", + "\xED\xA0\x80", /* bug: not corrected */ + "\"\\uD800\"", /* bug: want "\"\\uFFFF\"" */ + }, + { + /* \U+DB7F */ + "\"\xED\xAD\xBF\"", + "\xED\xAD\xBF", /* bug: not corrected */ + "\"\\uDB7F\"", /* bug: want "\"\\uFFFF\"" */ + }, + { + /* \U+DB80 */ + "\"\xED\xAE\x80\"", + "\xED\xAE\x80", /* bug: not corrected */ + "\"\\uDB80\"", /* bug: want "\"\\uFFFF\"" */ + }, + { + /* \U+DBFF */ + "\"\xED\xAF\xBF\"", + "\xED\xAF\xBF", /* bug: not corrected */ + "\"\\uDBFF\"", /* bug: want "\"\\uFFFF\"" */ + }, + { + /* \U+DC00 */ + "\"\xED\xB0\x80\"", + "\xED\xB0\x80", /* bug: not corrected */ + "\"\\uDC00\"", /* bug: want "\"\\uFFFF\"" */ + }, + { + /* \U+DF80 */ + "\"\xED\xBE\x80\"", + "\xED\xBE\x80", /* bug: not corrected */ + "\"\\uDF80\"", /* bug: want "\"\\uFFFF\"" */ + }, + { + /* \U+DFFF */ + "\"\xED\xBF\xBF\"", + "\xED\xBF\xBF", /* bug: not corrected */ + "\"\\uDFFF\"", /* bug: want "\"\\uFFFF\"" */ + }, + /* 5.2 Paired UTF-16 surrogates */ + { + /* \U+D800\U+DC00 */ + "\"\xED\xA0\x80\xED\xB0\x80\"", + "\xED\xA0\x80\xED\xB0\x80", /* bug: not corrected */ + "\"\\uD800\\uDC00\"", /* bug: want "\"\\uFFFF\\uFFFF\"" */ + }, + { + /* \U+D800\U+DFFF */ + "\"\xED\xA0\x80\xED\xBF\xBF\"", + "\xED\xA0\x80\xED\xBF\xBF", /* bug: not corrected */ + "\"\\uD800\\uDFFF\"", /* bug: want "\"\\uFFFF\\uFFFF\"" */ + }, + { + /* \U+DB7F\U+DC00 */ + "\"\xED\xAD\xBF\xED\xB0\x80\"", + "\xED\xAD\xBF\xED\xB0\x80", /* bug: not corrected */ + "\"\\uDB7F\\uDC00\"", /* bug: want "\"\\uFFFF\\uFFFF\"" */ + }, + { + /* \U+DB7F\U+DFFF */ + "\"\xED\xAD\xBF\xED\xBF\xBF\"", + "\xED\xAD\xBF\xED\xBF\xBF", /* bug: not corrected */ + "\"\\uDB7F\\uDFFF\"", /* bug: want "\"\\uFFFF\\uFFFF\"" */ + }, + { + /* \U+DB80\U+DC00 */ + "\"\xED\xAE\x80\xED\xB0\x80\"", + "\xED\xAE\x80\xED\xB0\x80", /* bug: not corrected */ + "\"\\uDB80\\uDC00\"", /* bug: want "\"\\uFFFF\\uFFFF\"" */ + }, + { + /* \U+DB80\U+DFFF */ + "\"\xED\xAE\x80\xED\xBF\xBF\"", + "\xED\xAE\x80\xED\xBF\xBF", /* bug: not corrected */ + "\"\\uDB80\\uDFFF\"", /* bug: want "\"\\uFFFF\\uFFFF\"" */ + }, + { + /* \U+DBFF\U+DC00 */ + "\"\xED\xAF\xBF\xED\xB0\x80\"", + "\xED\xAF\xBF\xED\xB0\x80", /* bug: not corrected */ + "\"\\uDBFF\\uDC00\"", /* bug: want "\"\\uFFFF\\uFFFF\"" */ + }, + { + /* \U+DBFF\U+DFFF */ + "\"\xED\xAF\xBF\xED\xBF\xBF\"", + "\xED\xAF\xBF\xED\xBF\xBF", /* bug: not corrected */ + "\"\\uDBFF\\uDFFF\"", /* bug: want "\"\\uFFFF\\uFFFF\"" */ + }, + /* 5.3 Other illegal code positions */ + { + /* \U+FFFE */ + "\"\xEF\xBF\xBE\"", + "\xEF\xBF\xBE", /* bug: not corrected */ + "\"\\uFFFE\"", /* bug: not corrected */ + }, + { + /* \U+FFFF */ + "\"\xEF\xBF\xBF\"", + "\xEF\xBF\xBF", /* bug: not corrected */ + "\"\\uFFFF\"", /* bug: not corrected */ + }, + {} + }; + int i; + QObject *obj; + QString *str; + const char *json_in, *utf8_out, *utf8_in, *json_out; + + for (i = 0; test_cases[i].json_in; i++) { + json_in = test_cases[i].json_in; + utf8_out = test_cases[i].utf8_out; + utf8_in = test_cases[i].utf8_in ?: test_cases[i].utf8_out; + json_out = test_cases[i].json_out ?: test_cases[i].json_in; + + obj = qobject_from_json(json_in); + if (utf8_out) { + g_assert(obj); + g_assert(qobject_type(obj) == QTYPE_QSTRING); + str = qobject_to_qstring(obj); + g_assert_cmpstr(qstring_get_str(str), ==, utf8_out); + } else { + g_assert(!obj); + } + qobject_decref(obj); + + obj = QOBJECT(qstring_from_str(utf8_in)); + str = qobject_to_json(obj); + if (json_out) { + g_assert(str); + g_assert_cmpstr(qstring_get_str(str), ==, json_out); + } else { + g_assert(!str); + } + QDECREF(str); + qobject_decref(obj); + + /* + * Disabled, because json_out currently contains the crap + * qobject_to_json() produces. + * FIXME Enable once these bugs have been fixed. + */ + if (0 && json_out != json_in) { + obj = qobject_from_json(json_out); + g_assert(obj); + g_assert(qobject_type(obj) == QTYPE_QSTRING); + str = qobject_to_qstring(obj); + g_assert_cmpstr(qstring_get_str(str), ==, utf8_out); + } + } +} + static void vararg_string(void) { int i; @@ -748,6 +1411,7 @@ int main(int argc, char **argv) g_test_add_func("/literals/string/simple", simple_string); g_test_add_func("/literals/string/escaped", escaped_string); + g_test_add_func("/literals/string/utf8", utf8_string); g_test_add_func("/literals/string/single_quote", single_quote_string); g_test_add_func("/literals/string/vararg", vararg_string); diff --git a/tests/libi2c-omap.c b/tests/libi2c-omap.c new file mode 100644 index 0000000000..c52458cbd6 --- /dev/null +++ b/tests/libi2c-omap.c @@ -0,0 +1,173 @@ +/* + * QTest I2C driver + * + * Copyright (c) 2012 Andreas Färber + * + * 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 "libi2c.h" + +#include +#include + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "libqtest.h" + +enum OMAPI2CRegisters { + OMAP_I2C_REV = 0x00, + OMAP_I2C_STAT = 0x08, + OMAP_I2C_CNT = 0x18, + OMAP_I2C_DATA = 0x1c, + OMAP_I2C_CON = 0x24, + OMAP_I2C_SA = 0x2c, +}; + +enum OMAPI2CSTATBits { + OMAP_I2C_STAT_NACK = 1 << 1, + OMAP_I2C_STAT_ARDY = 1 << 2, + OMAP_I2C_STAT_RRDY = 1 << 3, + OMAP_I2C_STAT_XRDY = 1 << 4, + OMAP_I2C_STAT_ROVR = 1 << 11, + OMAP_I2C_STAT_SBD = 1 << 15, +}; + +enum OMAPI2CCONBits { + OMAP_I2C_CON_STT = 1 << 0, + OMAP_I2C_CON_STP = 1 << 1, + OMAP_I2C_CON_TRX = 1 << 9, + OMAP_I2C_CON_MST = 1 << 10, + OMAP_I2C_CON_BE = 1 << 14, + OMAP_I2C_CON_I2C_EN = 1 << 15, +}; + +typedef struct OMAPI2C { + I2CAdapter parent; + + uint64_t addr; +} OMAPI2C; + + +static void omap_i2c_set_slave_addr(OMAPI2C *s, uint8_t addr) +{ + uint16_t data = addr; + + writew(s->addr + OMAP_I2C_SA, data); + data = readw(s->addr + OMAP_I2C_SA); + g_assert_cmphex(data, ==, addr); +} + +static void omap_i2c_send(I2CAdapter *i2c, uint8_t addr, + const uint8_t *buf, uint16_t len) +{ + OMAPI2C *s = (OMAPI2C *)i2c; + uint16_t data; + + omap_i2c_set_slave_addr(s, addr); + + data = len; + writew(s->addr + OMAP_I2C_CNT, data); + + data = OMAP_I2C_CON_I2C_EN | + OMAP_I2C_CON_TRX | + OMAP_I2C_CON_MST | + OMAP_I2C_CON_STT | + OMAP_I2C_CON_STP; + writew(s->addr + OMAP_I2C_CON, data); + data = readw(s->addr + OMAP_I2C_CON); + g_assert((data & OMAP_I2C_CON_STP) != 0); + + data = readw(s->addr + OMAP_I2C_STAT); + g_assert((data & OMAP_I2C_STAT_NACK) == 0); + + while (len > 1) { + data = readw(s->addr + OMAP_I2C_STAT); + g_assert((data & OMAP_I2C_STAT_XRDY) != 0); + + data = buf[0] | ((uint16_t)buf[1] << 8); + writew(s->addr + OMAP_I2C_DATA, data); + buf = (uint8_t *)buf + 2; + len -= 2; + } + if (len == 1) { + data = readw(s->addr + OMAP_I2C_STAT); + g_assert((data & OMAP_I2C_STAT_XRDY) != 0); + + data = buf[0]; + writew(s->addr + OMAP_I2C_DATA, data); + } + + data = readw(s->addr + OMAP_I2C_CON); + g_assert((data & OMAP_I2C_CON_STP) == 0); +} + +static void omap_i2c_recv(I2CAdapter *i2c, uint8_t addr, + uint8_t *buf, uint16_t len) +{ + OMAPI2C *s = (OMAPI2C *)i2c; + uint16_t data, stat; + + omap_i2c_set_slave_addr(s, addr); + + data = len; + writew(s->addr + OMAP_I2C_CNT, data); + + data = OMAP_I2C_CON_I2C_EN | + OMAP_I2C_CON_MST | + OMAP_I2C_CON_STT | + OMAP_I2C_CON_STP; + writew(s->addr + OMAP_I2C_CON, data); + data = readw(s->addr + OMAP_I2C_CON); + g_assert((data & OMAP_I2C_CON_STP) == 0); + + data = readw(s->addr + OMAP_I2C_STAT); + g_assert((data & OMAP_I2C_STAT_NACK) == 0); + + data = readw(s->addr + OMAP_I2C_CNT); + g_assert_cmpuint(data, ==, len); + + while (len > 0) { + data = readw(s->addr + OMAP_I2C_STAT); + g_assert((data & OMAP_I2C_STAT_RRDY) != 0); + g_assert((data & OMAP_I2C_STAT_ROVR) == 0); + + data = readw(s->addr + OMAP_I2C_DATA); + + stat = readw(s->addr + OMAP_I2C_STAT); + + if (unlikely(len == 1)) { + g_assert((stat & OMAP_I2C_STAT_SBD) != 0); + + buf[0] = data & 0xff; + buf++; + len--; + } else { + buf[0] = data & 0xff; + buf[1] = data >> 8; + buf += 2; + len -= 2; + } + } + + data = readw(s->addr + OMAP_I2C_CON); + g_assert((data & OMAP_I2C_CON_STP) == 0); +} + +I2CAdapter *omap_i2c_create(uint64_t addr) +{ + OMAPI2C *s = g_malloc0(sizeof(*s)); + I2CAdapter *i2c = (I2CAdapter *)s; + uint16_t data; + + s->addr = addr; + + i2c->send = omap_i2c_send; + i2c->recv = omap_i2c_recv; + + /* verify the mmio address by looking for a known signature */ + data = readw(addr + OMAP_I2C_REV); + g_assert_cmphex(data, ==, 0x34); + + return i2c; +} diff --git a/tests/libi2c.c b/tests/libi2c.c new file mode 100644 index 0000000000..13ec85c0cb --- /dev/null +++ b/tests/libi2c.c @@ -0,0 +1,22 @@ +/* + * QTest I2C driver + * + * Copyright (c) 2012 Andreas Färber + * + * 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 "libi2c.h" +#include "libqtest.h" + +void i2c_send(I2CAdapter *i2c, uint8_t addr, + const uint8_t *buf, uint16_t len) +{ + i2c->send(i2c, addr, buf, len); +} + +void i2c_recv(I2CAdapter *i2c, uint8_t addr, + uint8_t *buf, uint16_t len) +{ + i2c->recv(i2c, addr, buf, len); +} diff --git a/tests/libi2c.h b/tests/libi2c.h new file mode 100644 index 0000000000..1ce9af4053 --- /dev/null +++ b/tests/libi2c.h @@ -0,0 +1,30 @@ +/* + * I2C libqos + * + * Copyright (c) 2012 Andreas Färber + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef LIBQOS_I2C_H +#define LIBQOS_I2C_H + +#include + +typedef struct I2CAdapter I2CAdapter; +struct I2CAdapter { + void (*send)(I2CAdapter *adapter, uint8_t addr, + const uint8_t *buf, uint16_t len); + void (*recv)(I2CAdapter *adapter, uint8_t addr, + uint8_t *buf, uint16_t len); +}; + +void i2c_send(I2CAdapter *i2c, uint8_t addr, + const uint8_t *buf, uint16_t len); +void i2c_recv(I2CAdapter *i2c, uint8_t addr, + uint8_t *buf, uint16_t len); + +/* libi2c-omap.c */ +I2CAdapter *omap_i2c_create(uint64_t addr); + +#endif diff --git a/tests/libqtest.c b/tests/libqtest.c index 913fa0535c..389596a055 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -3,10 +3,12 @@ * * Copyright IBM, Corp. 2012 * Copyright Red Hat, Inc. 2012 + * Copyright SUSE LINUX Products GmbH 2013 * * Authors: * Anthony Liguori * Paolo Bonzini + * Andreas Färber * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -39,7 +41,8 @@ struct QTestState int qmp_fd; bool irq_level[MAX_IRQ]; GString *rx; - gchar *pid_file; + gchar *pid_file; /* QEMU PID file */ + int child_pid; /* Child process created to execute QEMU */ char *socket_path, *qmp_socket_path; }; @@ -144,6 +147,7 @@ QTestState *qtest_init(const char *extra_args) s->rx = g_string_new(""); s->pid_file = pid_file; + s->child_pid = pid; for (i = 0; i < MAX_IRQ; i++) { s->irq_level[i] = false; } @@ -165,8 +169,9 @@ void qtest_quit(QTestState *s) pid_t pid = qtest_qemu_pid(s); if (pid != -1) { + /* kill QEMU, but wait for the child created by us to run system() */ kill(pid, SIGTERM); - waitpid(pid, &status, 0); + waitpid(s->child_pid, &status, 0); } unlink(s->pid_file); @@ -285,16 +290,13 @@ redo: return words; } -void qtest_qmp(QTestState *s, const char *fmt, ...) +void qtest_qmpv(QTestState *s, const char *fmt, va_list ap) { - va_list ap; bool has_reply = false; int nesting = 0; /* Send QMP request */ - va_start(ap, fmt); socket_sendf(s->qmp_fd, fmt, ap); - va_end(ap); /* Receive reply */ while (!has_reply || nesting > 0) { @@ -323,6 +325,15 @@ void qtest_qmp(QTestState *s, const char *fmt, ...) } } +void qtest_qmp(QTestState *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qtest_qmpv(s, fmt, ap); + va_end(ap); +} + const char *qtest_get_arch(void) { const char *qemu = getenv("QTEST_QEMU_BINARY"); @@ -428,6 +439,66 @@ uint32_t qtest_inl(QTestState *s, uint16_t addr) return qtest_in(s, "inl", addr); } +static void qtest_write(QTestState *s, const char *cmd, uint64_t addr, + uint64_t value) +{ + qtest_sendf(s, "%s 0x%" PRIx64 " 0x%" PRIx64 "\n", cmd, addr, value); + qtest_rsp(s, 0); +} + +void qtest_writeb(QTestState *s, uint64_t addr, uint8_t value) +{ + qtest_write(s, "writeb", addr, value); +} + +void qtest_writew(QTestState *s, uint64_t addr, uint16_t value) +{ + qtest_write(s, "writew", addr, value); +} + +void qtest_writel(QTestState *s, uint64_t addr, uint32_t value) +{ + qtest_write(s, "writel", addr, value); +} + +void qtest_writeq(QTestState *s, uint64_t addr, uint64_t value) +{ + qtest_write(s, "writeq", addr, value); +} + +static uint64_t qtest_read(QTestState *s, const char *cmd, uint64_t addr) +{ + gchar **args; + uint64_t value; + + qtest_sendf(s, "%s 0x%" PRIx64 "\n", cmd, addr); + args = qtest_rsp(s, 2); + value = strtoull(args[1], NULL, 0); + g_strfreev(args); + + return value; +} + +uint8_t qtest_readb(QTestState *s, uint64_t addr) +{ + return qtest_read(s, "readb", addr); +} + +uint16_t qtest_readw(QTestState *s, uint64_t addr) +{ + return qtest_read(s, "readw", addr); +} + +uint32_t qtest_readl(QTestState *s, uint64_t addr) +{ + return qtest_read(s, "readl", addr); +} + +uint64_t qtest_readq(QTestState *s, uint64_t addr) +{ + return qtest_read(s, "readq", addr); +} + static int hex2nib(char ch) { if (ch >= '0' && ch <= '9') { diff --git a/tests/libqtest.h b/tests/libqtest.h index c8ade856fc..437bda39f3 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -3,10 +3,12 @@ * * Copyright IBM, Corp. 2012 * Copyright Red Hat, Inc. 2012 + * Copyright SUSE LINUX Products GmbH 2013 * * Authors: * Anthony Liguori * Paolo Bonzini + * Andreas Färber * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -17,6 +19,7 @@ #include #include +#include #include typedef struct QTestState QTestState; @@ -26,12 +29,14 @@ extern QTestState *global_qtest; /** * qtest_init: * @extra_args: other arguments to pass to QEMU. + * + * Returns: #QTestState instance. */ QTestState *qtest_init(const char *extra_args); /** * qtest_quit: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * * Shut down the QEMU process associated to @s. */ @@ -39,25 +44,35 @@ void qtest_quit(QTestState *s); /** * qtest_qmp: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * @fmt...: QMP message to send to qemu * * Sends a QMP message to QEMU */ void qtest_qmp(QTestState *s, const char *fmt, ...); +/** + * qtest_qmpv: + * @s: #QTestState instance to operate on. + * @fmt: QMP message to send to QEMU + * @ap: QMP message arguments + * + * Sends a QMP message to QEMU. + */ +void qtest_qmpv(QTestState *s, const char *fmt, va_list ap); + /** * qtest_get_irq: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * @num: Interrupt to observe. * - * Return the level of the @num interrupt. + * Returns: The level of the @num interrupt. */ bool qtest_get_irq(QTestState *s, int num); /** * qtest_irq_intercept_in: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * @string: QOM path of a device. * * Associate qtest irqs with the GPIO-in pins of the device @@ -67,7 +82,7 @@ void qtest_irq_intercept_in(QTestState *s, const char *string); /** * qtest_irq_intercept_out: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * @string: QOM path of a device. * * Associate qtest irqs with the GPIO-out pins of the device @@ -77,7 +92,7 @@ void qtest_irq_intercept_out(QTestState *s, const char *string); /** * qtest_outb: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * @addr: I/O port to write to. * @value: Value being written. * @@ -87,7 +102,7 @@ void qtest_outb(QTestState *s, uint16_t addr, uint8_t value); /** * qtest_outw: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * @addr: I/O port to write to. * @value: Value being written. * @@ -97,7 +112,7 @@ void qtest_outw(QTestState *s, uint16_t addr, uint16_t value); /** * qtest_outl: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * @addr: I/O port to write to. * @value: Value being written. * @@ -107,9 +122,8 @@ void qtest_outl(QTestState *s, uint16_t addr, uint32_t value); /** * qtest_inb: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * @addr: I/O port to read from. - * @value: Value being written. * * Returns an 8-bit value from an I/O port. */ @@ -117,9 +131,8 @@ uint8_t qtest_inb(QTestState *s, uint16_t addr); /** * qtest_inw: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * @addr: I/O port to read from. - * @value: Value being written. * * Returns a 16-bit value from an I/O port. */ @@ -127,17 +140,100 @@ uint16_t qtest_inw(QTestState *s, uint16_t addr); /** * qtest_inl: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * @addr: I/O port to read from. - * @value: Value being written. * * Returns a 32-bit value from an I/O port. */ uint32_t qtest_inl(QTestState *s, uint16_t addr); +/** + * qtest_writeb: + * @s: #QTestState instance to operate on. + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes an 8-bit value to memory. + */ +void qtest_writeb(QTestState *s, uint64_t addr, uint8_t value); + +/** + * qtest_writew: + * @s: #QTestState instance to operate on. + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes a 16-bit value to memory. + */ +void qtest_writew(QTestState *s, uint64_t addr, uint16_t value); + +/** + * qtest_writel: + * @s: #QTestState instance to operate on. + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes a 32-bit value to memory. + */ +void qtest_writel(QTestState *s, uint64_t addr, uint32_t value); + +/** + * qtest_writeq: + * @s: #QTestState instance to operate on. + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes a 64-bit value to memory. + */ +void qtest_writeq(QTestState *s, uint64_t addr, uint64_t value); + +/** + * qtest_readb: + * @s: #QTestState instance to operate on. + * @addr: Guest address to read from. + * + * Reads an 8-bit value from memory. + * + * Returns: Value read. + */ +uint8_t qtest_readb(QTestState *s, uint64_t addr); + +/** + * qtest_readw: + * @s: #QTestState instance to operate on. + * @addr: Guest address to read from. + * + * Reads a 16-bit value from memory. + * + * Returns: Value read. + */ +uint16_t qtest_readw(QTestState *s, uint64_t addr); + +/** + * qtest_readl: + * @s: #QTestState instance to operate on. + * @addr: Guest address to read from. + * + * Reads a 32-bit value from memory. + * + * Returns: Value read. + */ +uint32_t qtest_readl(QTestState *s, uint64_t addr); + +/** + * qtest_readq: + * @s: #QTestState instance to operate on. + * @addr: Guest address to read from. + * + * Reads a 64-bit value from memory. + * + * Returns: Value read. + */ +uint64_t qtest_readq(QTestState *s, uint64_t addr); + /** * qtest_memread: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * @addr: Guest address to read from. * @data: Pointer to where memory contents will be stored. * @size: Number of bytes to read. @@ -148,7 +244,7 @@ void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size); /** * qtest_memwrite: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * @addr: Guest address to write to. * @data: Pointer to the bytes that will be written to guest memory. * @size: Number of bytes to write. @@ -159,10 +255,11 @@ void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size) /** * qtest_clock_step_next: - * @s: QTestState instance to operate on. + * @s: #QTestState instance to operate on. * - * Advance the vm_clock to the next deadline. Return the current - * value of the vm_clock in nanoseconds. + * Advance the vm_clock to the next deadline. + * + * Returns: The current value of the vm_clock in nanoseconds. */ int64_t qtest_clock_step_next(QTestState *s); @@ -171,8 +268,9 @@ int64_t qtest_clock_step_next(QTestState *s); * @s: QTestState instance to operate on. * @step: Number of nanoseconds to advance the clock by. * - * Advance the vm_clock by @step nanoseconds. Return the current - * value of the vm_clock in nanoseconds. + * Advance the vm_clock by @step nanoseconds. + * + * Returns: The current value of the vm_clock in nanoseconds. */ int64_t qtest_clock_step(QTestState *s, int64_t step); @@ -182,14 +280,15 @@ int64_t qtest_clock_step(QTestState *s, int64_t step); * @val: Nanoseconds value to advance the clock to. * * Advance the vm_clock to @val nanoseconds since the VM was launched. - * Return the current value of the vm_clock in nanoseconds. + * + * Returns: The current value of the vm_clock in nanoseconds. */ int64_t qtest_clock_set(QTestState *s, int64_t val); /** * qtest_get_arch: * - * Returns the architecture for the QEMU executable under test. + * Returns: The architecture for the QEMU executable under test. */ const char *qtest_get_arch(void); @@ -200,7 +299,7 @@ const char *qtest_get_arch(void); * * Add a GTester testcase with the given name and function. * The path is prefixed with the architecture under test, as - * returned by qtest_get_arch. + * returned by qtest_get_arch(). */ void qtest_add_func(const char *str, void (*fn)); @@ -208,12 +307,16 @@ void qtest_add_func(const char *str, void (*fn)); * qtest_start: * @args: other arguments to pass to QEMU * - * Start QEMU and assign the resulting QTestState to a global variable. - * The global variable is used by "shortcut" macros documented below. + * Start QEMU and assign the resulting #QTestState to a global variable. + * The global variable is used by "shortcut" functions documented below. + * + * Returns: #QTestState instance. */ -#define qtest_start(args) ( \ - global_qtest = qtest_init((args)) \ - ) +static inline QTestState *qtest_start(const char *args) +{ + global_qtest = qtest_init(args); + return global_qtest; +} /** * qmp: @@ -221,15 +324,25 @@ void qtest_add_func(const char *str, void (*fn)); * * Sends a QMP message to QEMU */ -#define qmp(fmt, ...) qtest_qmp(global_qtest, fmt, ## __VA_ARGS__) +static inline void qmp(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qtest_qmpv(global_qtest, fmt, ap); + va_end(ap); +} /** * get_irq: * @num: Interrupt to observe. * - * Return the level of the @num interrupt. + * Returns: The level of the @num interrupt. */ -#define get_irq(num) qtest_get_irq(global_qtest, num) +static inline bool get_irq(int num) +{ + return qtest_get_irq(global_qtest, num); +} /** * irq_intercept_in: @@ -238,7 +351,10 @@ void qtest_add_func(const char *str, void (*fn)); * Associate qtest irqs with the GPIO-in pins of the device * whose path is specified by @string. */ -#define irq_intercept_in(string) qtest_irq_intercept_in(global_qtest, string) +static inline void irq_intercept_in(const char *string) +{ + qtest_irq_intercept_in(global_qtest, string); +} /** * qtest_irq_intercept_out: @@ -247,7 +363,10 @@ void qtest_add_func(const char *str, void (*fn)); * Associate qtest irqs with the GPIO-out pins of the device * whose path is specified by @string. */ -#define irq_intercept_out(string) qtest_irq_intercept_out(global_qtest, string) +static inline void irq_intercept_out(const char *string) +{ + qtest_irq_intercept_out(global_qtest, string); +} /** * outb: @@ -256,7 +375,10 @@ void qtest_add_func(const char *str, void (*fn)); * * Write an 8-bit value to an I/O port. */ -#define outb(addr, val) qtest_outb(global_qtest, addr, val) +static inline void outb(uint16_t addr, uint8_t value) +{ + qtest_outb(global_qtest, addr, value); +} /** * outw: @@ -265,7 +387,10 @@ void qtest_add_func(const char *str, void (*fn)); * * Write a 16-bit value to an I/O port. */ -#define outw(addr, val) qtest_outw(global_qtest, addr, val) +static inline void outw(uint16_t addr, uint16_t value) +{ + qtest_outw(global_qtest, addr, value); +} /** * outl: @@ -274,34 +399,149 @@ void qtest_add_func(const char *str, void (*fn)); * * Write a 32-bit value to an I/O port. */ -#define outl(addr, val) qtest_outl(global_qtest, addr, val) +static inline void outl(uint16_t addr, uint32_t value) +{ + qtest_outl(global_qtest, addr, value); +} /** * inb: * @addr: I/O port to read from. - * @value: Value being written. * - * Returns an 8-bit value from an I/O port. + * Reads an 8-bit value from an I/O port. + * + * Returns: Value read. */ -#define inb(addr) qtest_inb(global_qtest, addr) +static inline uint8_t inb(uint16_t addr) +{ + return qtest_inb(global_qtest, addr); +} /** * inw: * @addr: I/O port to read from. - * @value: Value being written. * - * Returns a 16-bit value from an I/O port. + * Reads a 16-bit value from an I/O port. + * + * Returns: Value read. */ -#define inw(addr) qtest_inw(global_qtest, addr) +static inline uint16_t inw(uint16_t addr) +{ + return qtest_inw(global_qtest, addr); +} /** * inl: * @addr: I/O port to read from. + * + * Reads a 32-bit value from an I/O port. + * + * Returns: Value read. + */ +static inline uint32_t inl(uint16_t addr) +{ + return qtest_inl(global_qtest, addr); +} + +/** + * writeb: + * @addr: Guest address to write to. * @value: Value being written. * - * Returns a 32-bit value from an I/O port. + * Writes an 8-bit value to guest memory. */ -#define inl(addr) qtest_inl(global_qtest, addr) +static inline void writeb(uint64_t addr, uint8_t value) +{ + qtest_writeb(global_qtest, addr, value); +} + +/** + * writew: + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes a 16-bit value to guest memory. + */ +static inline void writew(uint64_t addr, uint16_t value) +{ + qtest_writew(global_qtest, addr, value); +} + +/** + * writel: + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes a 32-bit value to guest memory. + */ +static inline void writel(uint64_t addr, uint32_t value) +{ + qtest_writel(global_qtest, addr, value); +} + +/** + * writeq: + * @addr: Guest address to write to. + * @value: Value being written. + * + * Writes a 64-bit value to guest memory. + */ +static inline void writeq(uint64_t addr, uint64_t value) +{ + qtest_writeq(global_qtest, addr, value); +} + +/** + * readb: + * @addr: Guest address to read from. + * + * Reads an 8-bit value from guest memory. + * + * Returns: Value read. + */ +static inline uint8_t readb(uint64_t addr) +{ + return qtest_readb(global_qtest, addr); +} + +/** + * readw: + * @addr: Guest address to read from. + * + * Reads a 16-bit value from guest memory. + * + * Returns: Value read. + */ +static inline uint16_t readw(uint64_t addr) +{ + return qtest_readw(global_qtest, addr); +} + +/** + * readl: + * @addr: Guest address to read from. + * + * Reads a 32-bit value from guest memory. + * + * Returns: Value read. + */ +static inline uint32_t readl(uint64_t addr) +{ + return qtest_readl(global_qtest, addr); +} + +/** + * readq: + * @addr: Guest address to read from. + * + * Reads a 64-bit value from guest memory. + * + * Returns: Value read. + */ +static inline uint64_t readq(uint64_t addr) +{ + return qtest_readq(global_qtest, addr); +} /** * memread: @@ -311,7 +551,10 @@ void qtest_add_func(const char *str, void (*fn)); * * Read guest memory into a buffer. */ -#define memread(addr, data, size) qtest_memread(global_qtest, addr, data, size) +static inline void memread(uint64_t addr, void *data, size_t size) +{ + qtest_memread(global_qtest, addr, data, size); +} /** * memwrite: @@ -321,32 +564,47 @@ void qtest_add_func(const char *str, void (*fn)); * * Write a buffer to guest memory. */ -#define memwrite(addr, data, size) qtest_memwrite(global_qtest, addr, data, size) +static inline void memwrite(uint64_t addr, const void *data, size_t size) +{ + qtest_memwrite(global_qtest, addr, data, size); +} /** * clock_step_next: * - * Advance the vm_clock to the next deadline. Return the current - * value of the vm_clock in nanoseconds. + * Advance the vm_clock to the next deadline. + * + * Returns: The current value of the vm_clock in nanoseconds. */ -#define clock_step_next() qtest_clock_step_next(global_qtest) +static inline int64_t clock_step_next(void) +{ + return qtest_clock_step_next(global_qtest); +} /** * clock_step: * @step: Number of nanoseconds to advance the clock by. * - * Advance the vm_clock by @step nanoseconds. Return the current - * value of the vm_clock in nanoseconds. + * Advance the vm_clock by @step nanoseconds. + * + * Returns: The current value of the vm_clock in nanoseconds. */ -#define clock_step(step) qtest_clock_step(global_qtest, step) +static inline int64_t clock_step(int64_t step) +{ + return qtest_clock_step(global_qtest, step); +} /** * clock_set: * @val: Nanoseconds value to advance the clock to. * * Advance the vm_clock to @val nanoseconds since the VM was launched. - * Return the current value of the vm_clock in nanoseconds. + * + * Returns: The current value of the vm_clock in nanoseconds. */ -#define clock_set(val) qtest_clock_set(global_qtest, val) +static inline int64_t clock_set(int64_t val) +{ + return qtest_clock_set(global_qtest, val); +} #endif diff --git a/tests/m48t59-test.c b/tests/m48t59-test.c index 5179681ca5..4081a5fdb2 100644 --- a/tests/m48t59-test.c +++ b/tests/m48t59-test.c @@ -35,17 +35,14 @@ static bool use_mmio; static uint8_t cmos_read_mmio(uint8_t reg) { - uint8_t data; - - memread(base + (uint32_t)reg_base + (uint32_t)reg, &data, 1); - return data; + return readb(base + (uint32_t)reg_base + (uint32_t)reg); } static void cmos_write_mmio(uint8_t reg, uint8_t val) { uint8_t data = val; - memwrite(base + (uint32_t)reg_base + (uint32_t)reg, &data, 1); + writeb(base + (uint32_t)reg_base + (uint32_t)reg, data); } static uint8_t cmos_read_ioio(uint8_t reg) @@ -142,7 +139,9 @@ static void cmos_get_date_time(struct tm *date) date->tm_mday = mday; date->tm_mon = mon - 1; date->tm_year = base_year + year - 1900; +#ifndef __sun__ date->tm_gmtoff = 0; +#endif ts = mktime(date); } @@ -233,6 +232,11 @@ static void fuzz_registers(void) reg = (uint8_t)g_test_rand_int_range(0, 16); val = (uint8_t)g_test_rand_int_range(0, 256); + if (reg == 7) { + /* watchdog setup register, may trigger system reset, skip */ + continue; + } + cmos_write(reg, val); cmos_read(reg); } diff --git a/tests/qemu-iotests/007 b/tests/qemu-iotests/007 index 0139264c4f..c454f2c8ec 100755 --- a/tests/qemu-iotests/007 +++ b/tests/qemu-iotests/007 @@ -50,10 +50,9 @@ _make_test_img 1M for i in `seq 1 10`; do echo "savevm $i" - # XXX(hch): adding -nographic would be good, but hangs the test - $QEMU -hda $TEST_IMG -monitor stdio >/dev/null 2>&1 </dev/null 2>&1 <&1 | grep -v "refcount=1 reference=0" +_check_test_img 2>&1 | grep -v "refcount=1 reference=0" done done @@ -147,7 +147,7 @@ echo echo "Event: $event; errno: $errno; imm: $imm; once: $once; write $vmstate" $QEMU_IO -c "write $vmstate 0 64M" $BLKDBG_TEST_IMG | _filter_qemu_io -$QEMU_IMG check $TEST_IMG 2>&1 | grep -v "refcount=1 reference=0" +_check_test_img 2>&1 | grep -v "refcount=1 reference=0" done done @@ -186,7 +186,7 @@ echo echo "Event: $event; errno: $errno; imm: $imm; once: $once" $QEMU_IO -c "write -b 0 64k" $BLKDBG_TEST_IMG | _filter_qemu_io -$QEMU_IMG check $TEST_IMG 2>&1 | grep -v "refcount=1 reference=0" +_check_test_img 2>&1 | grep -v "refcount=1 reference=0" done done diff --git a/tests/qemu-iotests/036 b/tests/qemu-iotests/036 index 329533e9ea..4dbfc5724c 100755 --- a/tests/qemu-iotests/036 +++ b/tests/qemu-iotests/036 @@ -59,7 +59,8 @@ _make_test_img 64M echo echo === Repair image === echo -$QEMU_IMG check -r all $TEST_IMG +_check_test_img -r all + ./qcow2.py $TEST_IMG dump-header # success, all done diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index c5ae806ecb..ae3517575c 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -86,7 +86,7 @@ $QEMU_IO -r -c "read -P 0x5a 0 512" $TEST_IMG | _filter_qemu_io echo echo "== Repairing the image file must succeed ==" -$QEMU_IMG check -r all $TEST_IMG +_check_test_img -r all # The dirty bit must not be set ./qcow2.py $TEST_IMG dump-header | grep incompatible_features diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index c6eb851871..720eeff921 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -207,6 +207,37 @@ class TestSingleDrive(ImageMirroringTestCase): self.assertTrue(self.compare_images(test_img, target_img), 'target image does not match source after mirroring') + def test_small_buffer(self): + self.assert_no_active_mirrors() + + # A small buffer is rounded up automatically + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', + buf_size=4096, target=target_img) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait() + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/file', target_img) + self.vm.shutdown() + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + + def test_small_buffer2(self): + self.assert_no_active_mirrors() + + qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d' + % (TestSingleDrive.image_len, TestSingleDrive.image_len), target_img) + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', + buf_size=65536, mode='existing', target=target_img) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait() + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/file', target_img) + self.vm.shutdown() + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + def test_large_cluster(self): self.assert_no_active_mirrors() @@ -292,6 +323,75 @@ class TestMirrorNoBacking(ImageMirroringTestCase): self.assertTrue(self.compare_images(test_img, target_img), 'target image does not match source after mirroring') + def test_large_cluster(self): + self.assert_no_active_mirrors() + + # qemu-img create fails if the image is not there + qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d' + %(TestMirrorNoBacking.image_len), target_backing_img) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' + % (TestMirrorNoBacking.image_len, target_backing_img), target_img) + os.remove(target_backing_img) + + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', + mode='existing', target=target_img) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait() + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/file', target_img) + self.vm.shutdown() + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + +class TestMirrorResized(ImageMirroringTestCase): + backing_len = 1 * 1024 * 1024 # MB + image_len = 2 * 1024 * 1024 # MB + + def setUp(self): + self.create_image(backing_img, TestMirrorResized.backing_len) + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) + qemu_img('resize', test_img, '2M') + self.vm = iotests.VM().add_drive(test_img) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(test_img) + os.remove(backing_img) + try: + os.remove(target_img) + except OSError: + pass + + def test_complete_top(self): + self.assert_no_active_mirrors() + + result = self.vm.qmp('drive-mirror', device='drive0', sync='top', + target=target_img) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait() + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/file', target_img) + self.vm.shutdown() + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + + def test_complete_full(self): + self.assert_no_active_mirrors() + + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', + target=target_img) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait() + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/file', target_img) + self.vm.shutdown() + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + class TestReadErrors(ImageMirroringTestCase): image_len = 2 * 1024 * 1024 # MB @@ -330,6 +430,9 @@ new_state = "1" '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' % (self.blkdebug_file, backing_img), test_img) + # Write something for tests that use sync='top' + qemu_io('-c', 'write %d 512' % (self.MIRROR_GRANULARITY + 65536), + test_img) self.vm = iotests.VM().add_drive(test_img) self.vm.launch() @@ -383,6 +486,32 @@ new_state = "1" self.complete_and_wait() self.vm.shutdown() + def test_large_cluster(self): + self.assert_no_active_mirrors() + + # Test COW into the target image. The first half of the + # cluster at MIRROR_GRANULARITY has to be copied from + # backing_img, even though sync='top'. + qemu_img('create', '-f', iotests.imgfmt, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img) + result = self.vm.qmp('drive-mirror', device='drive0', sync='top', + on_source_error='ignore', + mode='existing', target=target_img) + self.assert_qmp(result, 'return', {}) + + event = self.vm.get_qmp_event(wait=True) + self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/operation', 'read') + result = self.vm.qmp('query-block-jobs') + self.assert_qmp(result, 'return[0]/paused', False) + self.complete_and_wait() + self.vm.shutdown() + + # Detach blkdebug to compare images successfully + qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img) + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + def test_stop_read(self): self.assert_no_active_mirrors() diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out index 71009c239f..42314e9c00 100644 --- a/tests/qemu-iotests/041.out +++ b/tests/qemu-iotests/041.out @@ -1,5 +1,5 @@ -.................. +........................ ---------------------------------------------------------------------- -Ran 18 tests +Ran 24 tests OK diff --git a/tests/qemu-iotests/044.out b/tests/qemu-iotests/044.out index 7a4007137d..5eed3f87a3 100644 --- a/tests/qemu-iotests/044.out +++ b/tests/qemu-iotests/044.out @@ -1,4 +1,6 @@ No errors were found on the image. +7292415/8391499= 86.90% allocated, 0.00% fragmented, 0.00% compressed clusters +Image end offset: 4296447488 . ---------------------------------------------------------------------- Ran 1 tests diff --git a/tests/qemu-iotests/047 b/tests/qemu-iotests/047 new file mode 100755 index 0000000000..0cf36b434f --- /dev/null +++ b/tests/qemu-iotests/047 @@ -0,0 +1,75 @@ +#!/bin/bash +# +# Regression test for commit b7ab0fea (which was a corruption fix, +# despite the commit message claiming otherwise) +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) 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 . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto generic +_supported_os Linux + +size=128M + +_make_test_img $size + +function qemu_io_cmds() +{ +cat < wrote 327680/327680 bytes at offset 0 +320 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 131072/131072 bytes at offset 327680 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 131072/131072 bytes at offset 1048576 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 131072/131072 bytes at offset 458752 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> discard 131072/131072 bytes at offset 327680 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> qemu-io> qemu-io> wrote 491520/491520 bytes at offset 0 +480 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> qemu-io> qemu-io> read 491520/491520 bytes at offset 0 +480 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> read 98304/98304 bytes at offset 491520 +96 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> read 131072/131072 bytes at offset 1048576 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> No errors were found on the image. +*** done diff --git a/tests/qemu-iotests/048 b/tests/qemu-iotests/048 new file mode 100755 index 0000000000..7cce049d2d --- /dev/null +++ b/tests/qemu-iotests/048 @@ -0,0 +1,78 @@ +#!/bin/bash +## +## qemu-img compare test +## +## +## Copyright (C) 2013 Red Hat, Inc. +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) 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 . +## +# +# creator +owner=mrezanin@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + echo "Cleanup" + _cleanup_test_img + rm ${TEST_IMG2} +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +_compare() +{ + $QEMU_IMG compare "$@" $TEST_IMG ${TEST_IMG2} + echo $? +} + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.pattern + +_supported_fmt raw qcow qcow2 qed +_supported_proto file +_supported_os Linux + +# Setup test basic parameters +TEST_IMG2=$TEST_IMG.2 +CLUSTER_SIZE=4096 +size=1024M + +_make_test_img $size +io_pattern write 524288 $CLUSTER_SIZE $CLUSTER_SIZE 4 45 + +# Compare identical images +cp $TEST_IMG ${TEST_IMG2} +_compare +_compare -q + +# Compare images with different size +$QEMU_IMG resize $TEST_IMG +512M +_compare +_compare -s + +# Compare images with different content +io_pattern write 1228800 $CLUSTER_SIZE 0 1 67 +_compare +io_pattern write 0 $CLUSTER_SIZE 0 1 123 +_compare + +# Cleanup +status=0 diff --git a/tests/qemu-iotests/048.out b/tests/qemu-iotests/048.out new file mode 100644 index 0000000000..68f65d5e19 --- /dev/null +++ b/tests/qemu-iotests/048.out @@ -0,0 +1,31 @@ +QA output created by 048 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +=== IO: pattern 45 +qemu-io> wrote 4096/4096 bytes at offset 524288 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 4096/4096 bytes at offset 528384 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 4096/4096 bytes at offset 532480 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> wrote 4096/4096 bytes at offset 536576 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> Images are identical. +0 +0 +Image resized. +Warning: Image size mismatch! +Images are identical. +0 +Strict mode: Image size mismatch! +1 +=== IO: pattern 67 +qemu-io> wrote 4096/4096 bytes at offset 1228800 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> Content mismatch at offset 1228800! +1 +=== IO: pattern 123 +qemu-io> wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> Content mismatch at offset 0! +1 +Cleanup diff --git a/tests/qemu-iotests/049 b/tests/qemu-iotests/049 new file mode 100755 index 0000000000..6c6017e2d2 --- /dev/null +++ b/tests/qemu-iotests/049 @@ -0,0 +1,123 @@ +#!/bin/bash +# +# Check qemu-img option parsing +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) 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 . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +function filter_test_dir() +{ + sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ + -e "s#$TEST_DIR#TEST_DIR#g" +} + +function test_qemu_img() +{ + echo qemu-img "$@" | filter_test_dir + $QEMU_IMG "$@" 2>&1 | filter_test_dir + echo +} + +echo "=== Check correct interpretation of suffixes for image size ===" +echo +sizes="1024 1024b 1k 1K 1M 1G 1T " +sizes+="1024.0 1024.0b 1.5k 1.5K 1.5M 1.5G 1.5T" + +echo "== 1. Traditional size parameter ==" +echo +for s in $sizes; do + test_qemu_img create -f $IMGFMT $TEST_IMG $s +done + +echo "== 2. Specifying size via -o ==" +echo +for s in $sizes; do + test_qemu_img create -f $IMGFMT -o size=$s $TEST_IMG +done + +echo "== 3. Invalid sizes ==" +echo +sizes="-1024 -1k 1kilobyte foobar" + +for s in $sizes; do + test_qemu_img create -f $IMGFMT $TEST_IMG -- $s + test_qemu_img create -f $IMGFMT -o size=$s $TEST_IMG +done + +echo "== Check correct interpretation of suffixes for cluster size ==" +echo +sizes="1024 1024b 1k 1K 1M " +sizes+="1024.0 1024.0b 0.5k 0.5K 0.5M" + +for s in $sizes; do + test_qemu_img create -f $IMGFMT -o cluster_size=$s $TEST_IMG 64M +done + +echo "== Check compat level option ==" +echo +test_qemu_img create -f $IMGFMT -o compat=0.10 $TEST_IMG 64M +test_qemu_img create -f $IMGFMT -o compat=1.1 $TEST_IMG 64M + +test_qemu_img create -f $IMGFMT -o compat=0.42 $TEST_IMG 64M +test_qemu_img create -f $IMGFMT -o compat=foobar $TEST_IMG 64M + +echo "== Check preallocation option ==" +echo +test_qemu_img create -f $IMGFMT -o preallocation=off $TEST_IMG 64M +test_qemu_img create -f $IMGFMT -o preallocation=metadata $TEST_IMG 64M +test_qemu_img create -f $IMGFMT -o preallocation=1234 $TEST_IMG 64M + +echo "== Check encryption option ==" +echo +test_qemu_img create -f $IMGFMT -o encryption=off $TEST_IMG 64M +test_qemu_img create -f $IMGFMT -o encryption=on $TEST_IMG 64M + +echo "== Check lazy_refcounts option (only with v3) ==" +echo +test_qemu_img create -f $IMGFMT -o compat=1.1,lazy_refcounts=off $TEST_IMG 64M +test_qemu_img create -f $IMGFMT -o compat=1.1,lazy_refcounts=on $TEST_IMG 64M + +test_qemu_img create -f $IMGFMT -o compat=0.10,lazy_refcounts=off $TEST_IMG 64M +test_qemu_img create -f $IMGFMT -o compat=0.10,lazy_refcounts=on $TEST_IMG 64M + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out new file mode 100644 index 0000000000..72db13f8d2 --- /dev/null +++ b/tests/qemu-iotests/049.out @@ -0,0 +1,212 @@ +QA output created by 049 +=== Check correct interpretation of suffixes for image size === + +== 1. Traditional size parameter == + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024b +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1k +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1K +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1T +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0b +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5k +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5K +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5T +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off + +== 2. Specifying size via -o == + +qemu-img create -f qcow2 -o size=1024 TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o size=1024b TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o size=1k TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o size=1K TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o size=1M TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o size=1G TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o size=1T TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o size=1024.0 TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o size=1024.0b TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o size=1.5k TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o size=1.5K TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o size=1.5M TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o size=1.5G TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o size=1.5T TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 encryption=off cluster_size=65536 lazy_refcounts=off + +== 3. Invalid sizes == + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1024 +qemu-img: Image size must be less than 8 EiB! + +qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2 +qemu-img: qcow2 doesn't support shrinking images yet +qemu-img: Formatting or formatting option not supported for file format 'qcow2' +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k +qemu-img: Image size must be less than 8 EiB! + +qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2 +qemu-img: qcow2 doesn't support shrinking images yet +qemu-img: Formatting or formatting option not supported for file format 'qcow2' +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte +qemu-img: Invalid image size specified! You may use k, M, G or T suffixes for +qemu-img: kilobytes, megabytes, gigabytes and terabytes. + +qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar +qemu-img: Invalid image size specified! You may use k, M, G or T suffixes for +qemu-img: kilobytes, megabytes, gigabytes and terabytes. + +qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2 +qemu-img: Parameter 'size' expects a size +qemu-img: Invalid options for file format 'qcow2'. + +== Check correct interpretation of suffixes for cluster size == + +qemu-img create -f qcow2 -o cluster_size=1024 TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off + +qemu-img create -f qcow2 -o cluster_size=1024b TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off + +qemu-img create -f qcow2 -o cluster_size=1k TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off + +qemu-img create -f qcow2 -o cluster_size=1K TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off + +qemu-img create -f qcow2 -o cluster_size=1M TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1048576 lazy_refcounts=off + +qemu-img create -f qcow2 -o cluster_size=1024.0 TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off + +qemu-img create -f qcow2 -o cluster_size=1024.0b TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=1024 lazy_refcounts=off + +qemu-img create -f qcow2 -o cluster_size=0.5k TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off + +qemu-img create -f qcow2 -o cluster_size=0.5K TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=512 lazy_refcounts=off + +qemu-img create -f qcow2 -o cluster_size=0.5M TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=524288 lazy_refcounts=off + +== Check compat level option == + +qemu-img create -f qcow2 -o compat=0.10 TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M +Invalid compatibility level: '0.42' +qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M +Invalid compatibility level: 'foobar' +qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off + +== Check preallocation option == + +qemu-img create -f qcow2 -o preallocation=off TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='off' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M +Invalid preallocation mode: '1234' +qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off + +== Check encryption option == + +qemu-img create -f qcow2 -o encryption=off TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o encryption=on TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on cluster_size=65536 lazy_refcounts=off + +== Check lazy_refcounts option (only with v3) == + +qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=off TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o compat=1.1,lazy_refcounts=on TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=on + +qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off + +qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M +Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) +qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on + +*** done diff --git a/tests/qemu-iotests/050 b/tests/qemu-iotests/050 new file mode 100755 index 0000000000..05793e2d4b --- /dev/null +++ b/tests/qemu-iotests/050 @@ -0,0 +1,75 @@ +#!/bin/bash +# +# Test qemu-img rebase with zero clusters +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) 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 . +# + +# creator +owner=pbonzini@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f $TEST_IMG.old + rm -f $TEST_IMG.new +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 qed +_supported_proto file +_supported_os Linux + +if test "$IMGFMT" = qcow2 && test $IMGOPTS = ""; then + IMGOPTS=compat=1.1 +fi + +echo +echo "== Creating images ==" + +size=10M +_make_test_img $size +$QEMU_IO -c "write -P 0x40 0 1048576" $TEST_IMG | _filter_qemu_io +mv $TEST_IMG $TEST_IMG.old + +_make_test_img $size +$QEMU_IO -c "write -P 0x5a 0 1048576" $TEST_IMG | _filter_qemu_io +mv $TEST_IMG $TEST_IMG.new + +_make_test_img -b $TEST_IMG.old $size +$QEMU_IO -c "write -z 0 1048576" $TEST_IMG | _filter_qemu_io + +echo +echo "== Rebasing the image ==" + +$QEMU_IMG rebase -b $TEST_IMG.new $TEST_IMG +$QEMU_IO -c "read -P 0x00 0 1048576" $TEST_IMG | _filter_qemu_io + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/050.out b/tests/qemu-iotests/050.out new file mode 100644 index 0000000000..3f5f7e1e7b --- /dev/null +++ b/tests/qemu-iotests/050.out @@ -0,0 +1,17 @@ +QA output created by 050 + +== Creating images == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 backing_file='TEST_DIR/t.IMGFMT.old' +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== Rebasing the image == +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index aef5f52b4f..e522d617aa 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -161,9 +161,10 @@ _cleanup_test_img() _check_test_img() { - $QEMU_IMG check -f $IMGFMT $TEST_IMG 2>&1 | \ - grep -v "fragmented$" | \ - sed -e 's/qemu-img\: This image format does not support checks/No errors were found on the image./' + $QEMU_IMG check "$@" -f $IMGFMT $TEST_IMG 2>&1 | \ + sed -e '/allocated.*fragmented.*compressed clusters/d' \ + -e 's/qemu-img: This image format does not support checks/No errors were found on the image./' \ + -e '/Image end offset: [0-9]\+/d' } _img_info() diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index a0307de06b..1d7e4f31a0 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -53,3 +53,7 @@ 044 rw auto 045 rw auto 046 rw auto aio +047 rw auto +048 img auto quick +049 rw auto +050 rw auto backing quick diff --git a/tests/rtc-test.c b/tests/rtc-test.c index 02edbf5727..9ab583b860 100644 --- a/tests/rtc-test.c +++ b/tests/rtc-test.c @@ -26,11 +26,6 @@ static int bcd2dec(int value) return (((value >> 4) & 0x0F) * 10) + (value & 0x0F); } -static int dec2bcd(int value) -{ - return ((value / 10) << 4) | (value % 10); -} - static uint8_t cmos_read(uint8_t reg) { outb(base + 0, reg); @@ -115,7 +110,9 @@ static void cmos_get_date_time(struct tm *date) date->tm_mday = mday; date->tm_mon = mon - 1; date->tm_year = base_year + year - 1900; +#ifndef __sun__ date->tm_gmtoff = 0; +#endif ts = mktime(date); } @@ -182,7 +179,7 @@ static int wiggle = 2; static void set_year_20xx(void) { /* Set BCD mode */ - cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_DM); + cmos_write(RTC_REG_B, REG_B_24H); cmos_write(RTC_REG_A, 0x76); cmos_write(RTC_YEAR, 0x11); cmos_write(RTC_CENTURY, 0x20); @@ -201,6 +198,10 @@ static void set_year_20xx(void) g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11); g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20); + if (sizeof(time_t) == 4) { + return; + } + /* Set a date in 2080 to ensure there is no year-2038 overflow. */ cmos_write(RTC_REG_A, 0x76); cmos_write(RTC_YEAR, 0x80); @@ -230,7 +231,7 @@ static void set_year_20xx(void) static void set_year_1980(void) { /* Set BCD mode */ - cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_DM); + cmos_write(RTC_REG_B, REG_B_24H); cmos_write(RTC_REG_A, 0x76); cmos_write(RTC_YEAR, 0x80); cmos_write(RTC_CENTURY, 0x19); @@ -253,32 +254,17 @@ static void set_year_1980(void) static void bcd_check_time(void) { /* Set BCD mode */ - cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_DM); + cmos_write(RTC_REG_B, REG_B_24H); check_time(wiggle); } static void dec_check_time(void) { /* Set DEC mode */ - cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_DM); + cmos_write(RTC_REG_B, REG_B_24H | REG_B_DM); check_time(wiggle); } -static void set_alarm_time(struct tm *tm) -{ - int sec; - - sec = tm->tm_sec; - - if ((cmos_read(RTC_REG_B) & REG_B_DM) == 0) { - sec = dec2bcd(sec); - } - - cmos_write(RTC_SECONDS_ALARM, sec); - cmos_write(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE); - cmos_write(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE); -} - static void alarm_time(void) { struct tm now; @@ -289,13 +275,15 @@ static void alarm_time(void) gmtime_r(&ts, &now); /* set DEC mode */ - cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_DM); + cmos_write(RTC_REG_B, REG_B_24H | REG_B_DM); g_assert(!get_irq(RTC_ISA_IRQ)); cmos_read(RTC_REG_C); now.tm_sec = (now.tm_sec + 2) % 60; - set_alarm_time(&now); + cmos_write(RTC_SECONDS_ALARM, now.tm_sec); + cmos_write(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE); + cmos_write(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE); cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_AIE); for (i = 0; i < 2 + wiggle; i++) { @@ -311,6 +299,197 @@ static void alarm_time(void) g_assert(cmos_read(RTC_REG_C) == 0); } +static void set_time(int mode, int h, int m, int s) +{ + /* set BCD 12 hour mode */ + cmos_write(RTC_REG_B, mode); + + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_HOURS, h); + cmos_write(RTC_MINUTES, m); + cmos_write(RTC_SECONDS, s); + cmos_write(RTC_REG_A, 0x26); +} + +#define assert_time(h, m, s) \ + do { \ + g_assert_cmpint(cmos_read(RTC_HOURS), ==, h); \ + g_assert_cmpint(cmos_read(RTC_MINUTES), ==, m); \ + g_assert_cmpint(cmos_read(RTC_SECONDS), ==, s); \ + } while(0) + +static void basic_12h_bcd(void) +{ + /* set BCD 12 hour mode */ + set_time(0, 0x81, 0x59, 0x00); + clock_step(1000000000LL); + assert_time(0x81, 0x59, 0x01); + clock_step(59000000000LL); + assert_time(0x82, 0x00, 0x00); + + /* test BCD wraparound */ + set_time(0, 0x09, 0x59, 0x59); + clock_step(60000000000LL); + assert_time(0x10, 0x00, 0x59); + + /* 12 AM -> 1 AM */ + set_time(0, 0x12, 0x59, 0x59); + clock_step(1000000000LL); + assert_time(0x01, 0x00, 0x00); + + /* 12 PM -> 1 PM */ + set_time(0, 0x92, 0x59, 0x59); + clock_step(1000000000LL); + assert_time(0x81, 0x00, 0x00); + + /* 11 AM -> 12 PM */ + set_time(0, 0x11, 0x59, 0x59); + clock_step(1000000000LL); + assert_time(0x92, 0x00, 0x00); + /* TODO: test day wraparound */ + + /* 11 PM -> 12 AM */ + set_time(0, 0x91, 0x59, 0x59); + clock_step(1000000000LL); + assert_time(0x12, 0x00, 0x00); + /* TODO: test day wraparound */ +} + +static void basic_12h_dec(void) +{ + /* set decimal 12 hour mode */ + set_time(REG_B_DM, 0x81, 59, 0); + clock_step(1000000000LL); + assert_time(0x81, 59, 1); + clock_step(59000000000LL); + assert_time(0x82, 0, 0); + + /* 12 PM -> 1 PM */ + set_time(REG_B_DM, 0x8c, 59, 59); + clock_step(1000000000LL); + assert_time(0x81, 0, 0); + + /* 12 AM -> 1 AM */ + set_time(REG_B_DM, 0x0c, 59, 59); + clock_step(1000000000LL); + assert_time(0x01, 0, 0); + + /* 11 AM -> 12 PM */ + set_time(REG_B_DM, 0x0b, 59, 59); + clock_step(1000000000LL); + assert_time(0x8c, 0, 0); + + /* 11 PM -> 12 AM */ + set_time(REG_B_DM, 0x8b, 59, 59); + clock_step(1000000000LL); + assert_time(0x0c, 0, 0); + /* TODO: test day wraparound */ +} + +static void basic_24h_bcd(void) +{ + /* set BCD 24 hour mode */ + set_time(REG_B_24H, 0x09, 0x59, 0x00); + clock_step(1000000000LL); + assert_time(0x09, 0x59, 0x01); + clock_step(59000000000LL); + assert_time(0x10, 0x00, 0x00); + + /* test BCD wraparound */ + set_time(REG_B_24H, 0x09, 0x59, 0x00); + clock_step(60000000000LL); + assert_time(0x10, 0x00, 0x00); + + /* TODO: test day wraparound */ + set_time(REG_B_24H, 0x23, 0x59, 0x00); + clock_step(60000000000LL); + assert_time(0x00, 0x00, 0x00); +} + +static void basic_24h_dec(void) +{ + /* set decimal 24 hour mode */ + set_time(REG_B_24H | REG_B_DM, 9, 59, 0); + clock_step(1000000000LL); + assert_time(9, 59, 1); + clock_step(59000000000LL); + assert_time(10, 0, 0); + + /* test BCD wraparound */ + set_time(REG_B_24H | REG_B_DM, 9, 59, 0); + clock_step(60000000000LL); + assert_time(10, 0, 0); + + /* TODO: test day wraparound */ + set_time(REG_B_24H | REG_B_DM, 23, 59, 0); + clock_step(60000000000LL); + assert_time(0, 0, 0); +} + +static void am_pm_alarm(void) +{ + cmos_write(RTC_MINUTES_ALARM, 0xC0); + cmos_write(RTC_SECONDS_ALARM, 0xC0); + + /* set BCD 12 hour mode */ + cmos_write(RTC_REG_B, 0); + + /* Set time and alarm hour. */ + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_HOURS_ALARM, 0x82); + cmos_write(RTC_HOURS, 0x81); + cmos_write(RTC_MINUTES, 0x59); + cmos_write(RTC_SECONDS, 0x00); + cmos_read(RTC_REG_C); + cmos_write(RTC_REG_A, 0x26); + + /* Check that alarm triggers when AM/PM is set. */ + clock_step(60000000000LL); + g_assert(cmos_read(RTC_HOURS) == 0x82); + g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0); + + /* + * Each of the following two tests takes over 60 seconds due to the time + * needed to report the PIT interrupts. Unfortunately, our PIT device + * model keeps counting even when GATE=0, so we cannot simply disable + * it in main(). + */ + if (g_test_quick()) { + return; + } + + /* set DEC 12 hour mode */ + cmos_write(RTC_REG_B, REG_B_DM); + + /* Set time and alarm hour. */ + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_HOURS_ALARM, 0x82); + cmos_write(RTC_HOURS, 3); + cmos_write(RTC_MINUTES, 0); + cmos_write(RTC_SECONDS, 0); + cmos_read(RTC_REG_C); + cmos_write(RTC_REG_A, 0x26); + + /* Check that alarm triggers. */ + clock_step(3600 * 11 * 1000000000LL); + g_assert(cmos_read(RTC_HOURS) == 0x82); + g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0); + + /* Same as above, with inverted HOURS and HOURS_ALARM. */ + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_HOURS_ALARM, 2); + cmos_write(RTC_HOURS, 3); + cmos_write(RTC_MINUTES, 0); + cmos_write(RTC_SECONDS, 0); + cmos_read(RTC_REG_C); + cmos_write(RTC_REG_A, 0x26); + + /* Check that alarm does not trigger if hours differ only by AM/PM. */ + clock_step(3600 * 11 * 1000000000LL); + g_assert(cmos_read(RTC_HOURS) == 0x82); + g_assert((cmos_read(RTC_REG_C) & REG_C_AF) == 0); +} + /* success if no crash or abort */ static void fuzz_registers(void) { @@ -330,7 +509,7 @@ static void fuzz_registers(void) static void register_b_set_flag(void) { /* Enable binary-coded decimal (BCD) mode and SET flag in Register B*/ - cmos_write(RTC_REG_B, (cmos_read(RTC_REG_B) & ~REG_B_DM) | REG_B_SET); + cmos_write(RTC_REG_B, REG_B_24H | REG_B_SET); cmos_write(RTC_REG_A, 0x76); cmos_write(RTC_YEAR, 0x11); @@ -376,13 +555,18 @@ int main(int argc, char **argv) s = qtest_start("-display none -rtc clock=vm"); qtest_irq_intercept_in(s, "ioapic"); - qtest_add_func("/rtc/bcd/check-time", bcd_check_time); - qtest_add_func("/rtc/dec/check-time", dec_check_time); - qtest_add_func("/rtc/alarm-time", alarm_time); + qtest_add_func("/rtc/check-time/bcd", bcd_check_time); + qtest_add_func("/rtc/check-time/dec", dec_check_time); + qtest_add_func("/rtc/alarm/interrupt", alarm_time); + qtest_add_func("/rtc/alarm/am-pm", am_pm_alarm); + qtest_add_func("/rtc/basic/dec-24h", basic_24h_dec); + qtest_add_func("/rtc/basic/bcd-24h", basic_24h_bcd); + qtest_add_func("/rtc/basic/dec-12h", basic_12h_dec); + qtest_add_func("/rtc/basic/bcd-12h", basic_12h_bcd); qtest_add_func("/rtc/set-year/20xx", set_year_20xx); qtest_add_func("/rtc/set-year/1980", set_year_1980); - qtest_add_func("/rtc/register_b_set_flag", register_b_set_flag); - qtest_add_func("/rtc/fuzz-registers", fuzz_registers); + qtest_add_func("/rtc/misc/register_b_set_flag", register_b_set_flag); + qtest_add_func("/rtc/misc/fuzz-registers", fuzz_registers); ret = g_test_run(); if (s) { diff --git a/tests/tcg/mips/mips32-dsp/extr_r_w.c b/tests/tcg/mips/mips32-dsp/extr_r_w.c index 0beeefd366..02e022427a 100644 --- a/tests/tcg/mips/mips32-dsp/extr_r_w.c +++ b/tests/tcg/mips/mips32-dsp/extr_r_w.c @@ -44,5 +44,28 @@ int main() assert(dsp == 0); assert(result == rt); + /* Clear dspcontrol */ + dsp = 0; + __asm + ("wrdsp %0\n\t" + : + : "r"(dsp) + ); + + ach = 0x3fffffff; + acl = 0x2bcdef01; + result = 0x7ffffffe; + __asm + ("mthi %2, $ac1\n\t" + "mtlo %3, $ac1\n\t" + "extr_r.w %0, $ac1, 0x1F\n\t" + "rddsp %1\n\t" + : "=r"(rt), "=r"(dsp) + : "r"(ach), "r"(acl) + ); + dsp = (dsp >> 23) & 0x01; + assert(dsp == 0); + assert(result == rt); + return 0; } diff --git a/tests/tcg/mips/mips32-dsp/extr_rs_w.c b/tests/tcg/mips/mips32-dsp/extr_rs_w.c index 24c748db20..c3a22ee704 100644 --- a/tests/tcg/mips/mips32-dsp/extr_rs_w.c +++ b/tests/tcg/mips/mips32-dsp/extr_rs_w.c @@ -44,5 +44,28 @@ int main() assert(dsp == 0); assert(result == rt); + /* Clear dspcontrol */ + dsp = 0; + __asm + ("wrdsp %0\n\t" + : + : "r"(dsp) + ); + + ach = 0x3fffffff; + acl = 0x2bcdef01; + result = 0x7ffffffe; + __asm + ("mthi %2, $ac1\n\t" + "mtlo %3, $ac1\n\t" + "extr_rs.w %0, $ac1, 0x1F\n\t" + "rddsp %1\n\t" + : "=r"(rt), "=r"(dsp) + : "r"(ach), "r"(acl) + ); + dsp = (dsp >> 23) & 0x01; + assert(dsp == 0); + assert(result == rt); + return 0; } diff --git a/tests/tcg/mips/mips32-dsp/extr_s_h.c b/tests/tcg/mips/mips32-dsp/extr_s_h.c index b2129134c8..9bc2a63cc2 100644 --- a/tests/tcg/mips/mips32-dsp/extr_s_h.c +++ b/tests/tcg/mips/mips32-dsp/extr_s_h.c @@ -59,5 +59,28 @@ int main() assert(dsp == 0); assert(result == rt); + /* Clear dsp */ + dsp = 0; + __asm + ("wrdsp %0\n\t" + : + : "r"(dsp) + ); + + ach = 0x123; + acl = 0x87654321; + result = 0x1238; + __asm + ("mthi %2, $ac1\n\t" + "mtlo %3, $ac1\n\t" + "extr_s.h %0, $ac1, 28\n\t" + "rddsp %1\n\t" + : "=r"(rt), "=r"(dsp) + : "r"(ach), "r"(acl) + ); + dsp = (dsp >> 23) & 0x01; + assert(dsp == 0); + assert(result == rt); + return 0; } diff --git a/tests/tcg/mips/mips32-dsp/extr_w.c b/tests/tcg/mips/mips32-dsp/extr_w.c index 02ab9ecaae..bd6b0b95c2 100644 --- a/tests/tcg/mips/mips32-dsp/extr_w.c +++ b/tests/tcg/mips/mips32-dsp/extr_w.c @@ -44,5 +44,28 @@ int main() assert(dsp == 0); assert(result == rt); + /* Clear dspcontrol */ + dsp = 0; + __asm + ("wrdsp %0\n\t" + : + : "r"(dsp) + ); + + ach = 0x3fffffff; + acl = 0x2bcdef01; + result = 0x7ffffffe; + __asm + ("mthi %2, $ac1\n\t" + "mtlo %3, $ac1\n\t" + "extr.w %0, $ac1, 0x1F\n\t" + "rddsp %1\n\t" + : "=r"(rt), "=r"(dsp) + : "r"(ach), "r"(acl) + ); + dsp = (dsp >> 23) & 0x01; + assert(dsp == 0); + assert(result == rt); + return 0; } diff --git a/tests/tcg/mips/mips32-dsp/extrv_r_w.c b/tests/tcg/mips/mips32-dsp/extrv_r_w.c index 005807b142..2403b3afe4 100644 --- a/tests/tcg/mips/mips32-dsp/extrv_r_w.c +++ b/tests/tcg/mips/mips32-dsp/extrv_r_w.c @@ -50,5 +50,30 @@ int main() assert(dsp == 0); assert(result == rt); + /* Clear dspcontrol */ + dsp = 0; + __asm + ("wrdsp %0\n\t" + : + : "r"(dsp) + ); + + rs = 31; + ach = 0x3fffffff; + acl = 0x2bcdef01; + result = 0x7ffffffe; + __asm + ("wrdsp %1, 0x01\n\t" + "mthi %3, $ac1\n\t" + "mtlo %4, $ac1\n\t" + "extrv_r.w %0, $ac1, %2\n\t" + "rddsp %1\n\t" + : "=r"(rt), "+r"(dsp) + : "r"(rs), "r"(ach), "r"(acl) + ); + dsp = (dsp >> 23) & 0x01; + assert(dsp == 0); + assert(result == rt); + return 0; } diff --git a/tests/tcg/mips/mips32-dsp/extrv_rs_w.c b/tests/tcg/mips/mips32-dsp/extrv_rs_w.c index c2d8513bb6..ccceeb9f4c 100644 --- a/tests/tcg/mips/mips32-dsp/extrv_rs_w.c +++ b/tests/tcg/mips/mips32-dsp/extrv_rs_w.c @@ -48,5 +48,30 @@ int main() assert(dsp == 0); assert(result == rt); + /* Clear dspcontrol */ + dsp = 0; + __asm + ("wrdsp %0\n\t" + : + : "r"(dsp) + ); + + rs = 0x1F; + ach = 0x3fffffff; + acl = 0x2bcdef01; + result = 0x7ffffffe; + __asm + ("wrdsp %1, 0x01\n\t" + "mthi %3, $ac1\n\t" + "mtlo %4, $ac1\n\t" + "extrv_rs.w %0, $ac1, %2\n\t" + "rddsp %1\n\t" + : "=r"(rt), "+r"(dsp) + : "r"(rs), "r"(ach), "r"(acl) + ); + dsp = (dsp >> 23) & 0x01; + assert(dsp == 0); + assert(result == rt); + return 0; } diff --git a/tests/tcg/mips/mips32-dsp/extrv_s_h.c b/tests/tcg/mips/mips32-dsp/extrv_s_h.c index 8c13b5eda5..feac3e2e33 100644 --- a/tests/tcg/mips/mips32-dsp/extrv_s_h.c +++ b/tests/tcg/mips/mips32-dsp/extrv_s_h.c @@ -67,5 +67,22 @@ int main() assert(dsp == 0); assert(result == rt); + rs = 0x1C; + ach = 0x123; + acl = 0x87654321; + result = 0x1238; + __asm + ("wrdsp %1, 0x01\n\t" + "mthi %3, $ac1\n\t" + "mtlo %4, $ac1\n\t" + "extrv_s.h %0, $ac1, %2\n\t" + "rddsp %1\n\t" + : "=r"(rt), "+r"(dsp) + : "r"(rs), "r"(ach), "r"(acl) + ); + dsp = (dsp >> 23) & 0x01; + assert(dsp == 0); + assert(result == rt); + return 0; } diff --git a/tests/tcg/mips/mips32-dsp/extrv_w.c b/tests/tcg/mips/mips32-dsp/extrv_w.c index 9cb493df39..9e8b238a04 100644 --- a/tests/tcg/mips/mips32-dsp/extrv_w.c +++ b/tests/tcg/mips/mips32-dsp/extrv_w.c @@ -50,5 +50,31 @@ int main() assert(dsp == 0); assert(result == rt); + /* Clear dspcontrol */ + dsp = 0; + __asm + ("wrdsp %0\n\t" + : + : "r"(dsp) + ); + + rs = 31; + ach = 0x3fffffff; + acl = 0x2bcdef01; + result = 0x7ffffffe; + __asm + ("wrdsp %1, 0x01\n\t" + "mthi %3, $ac1\n\t" + "mtlo %4, $ac1\n\t" + "extrv.w %0, $ac1, %2\n\t" + "rddsp %1\n\t" + : "=r"(rt), "+r"(dsp) + : "r"(rs), "r"(ach), "r"(acl) + ); + dsp = (dsp >> 23) & 0x01; + assert(dsp == 0); + assert(result == rt); + + return 0; } diff --git a/tests/tcg/mips/mips32-dsp/mthlip.c b/tests/tcg/mips/mips32-dsp/mthlip.c index 9549aae36a..85f94d8450 100644 --- a/tests/tcg/mips/mips32-dsp/mthlip.c +++ b/tests/tcg/mips/mips32-dsp/mthlip.c @@ -30,7 +30,7 @@ int main() assert(ach == resulth); assert(acl == resultl); - dsp = 0x3f; + dsp = 0x1f; ach = 0x05; acl = 0xB4CB; rs = 0x00FFBBAA; diff --git a/tests/tcg/mips/mips32-dsp/rddsp.c b/tests/tcg/mips/mips32-dsp/rddsp.c index e8948ec1d9..2f30285033 100644 --- a/tests/tcg/mips/mips32-dsp/rddsp.c +++ b/tests/tcg/mips/mips32-dsp/rddsp.c @@ -6,14 +6,13 @@ int main() int dsp_i, dsp_o; int ccond_i, outflag_i, efi_i, c_i, scount_i, pos_i; int ccond_o, outflag_o, efi_o, c_o, scount_o, pos_o; - int ccond_r, outflag_r, efi_r, c_r, scount_r, pos_r; - ccond_i = 0x000000BC;/* 4 */ - outflag_i = 0x0000001B;/* 3 */ - efi_i = 0x00000001;/* 5 */ - c_i = 0x00000001;/* 2 */ - scount_i = 0x0000000F;/* 1 */ - pos_i = 0x0000000C;/* 0 */ + ccond_i = 0x0000000C; /* 4 */ + outflag_i = 0x0000001B; /* 3 */ + efi_i = 0x00000001; /* 5 */ + c_i = 0x00000001; /* 2 */ + scount_i = 0x0000000F; /* 1 */ + pos_i = 0x0000000C; /* 0 */ dsp_i = (ccond_i << 24) | \ (outflag_i << 16) | \ @@ -22,13 +21,6 @@ int main() (scount_i << 7) | \ pos_i; - ccond_r = ccond_i; - outflag_r = outflag_i; - efi_r = efi_i; - c_r = c_i; - scount_r = scount_i; - pos_r = pos_i; - __asm ("wrdsp %1, 0x3F\n\t" "rddsp %0, 0x3F\n\t" @@ -43,12 +35,12 @@ int main() scount_o = (dsp_o >> 7) & 0x3F; pos_o = dsp_o & 0x1F; - assert(ccond_o == ccond_r); - assert(outflag_o == outflag_r); - assert(efi_o == efi_r); - assert(c_o == c_r); - assert(scount_o == scount_r); - assert(pos_o == pos_r); + assert(ccond_o == ccond_i); + assert(outflag_o == outflag_i); + assert(efi_o == efi_i); + assert(c_o == c_i); + assert(scount_o == scount_i); + assert(pos_o == pos_i); return 0; } diff --git a/tests/tcg/mips/mips32-dsp/subq_s_ph.c b/tests/tcg/mips/mips32-dsp/subq_s_ph.c index 8e36dadef9..64c89ebd51 100644 --- a/tests/tcg/mips/mips32-dsp/subq_s_ph.c +++ b/tests/tcg/mips/mips32-dsp/subq_s_ph.c @@ -12,7 +12,8 @@ int main() resultdsp = 0x01; __asm - ("subq_s.ph %0, %2, %3\n\t" + ("wrdsp $0\n\t" + "subq_s.ph %0, %2, %3\n\t" "rddsp %1\n\t" : "=r"(rd), "=r"(dsp) : "r"(rs), "r"(rt) @@ -27,7 +28,24 @@ int main() resultdsp = 0x01; __asm - ("subq_s.ph %0, %2, %3\n\t" + ("wrdsp $0\n\t" + "subq_s.ph %0, %2, %3\n\t" + "rddsp %1\n\t" + : "=r"(rd), "=r"(dsp) + : "r"(rs), "r"(rt) + ); + dsp = (dsp >> 20) & 0x01; + assert(dsp == resultdsp); + assert(rd == result); + + rs = 0x12340000; + rt = 0x87658000; + result = 0x7FFF7FFF; + resultdsp = 0x01; + + __asm + ("wrdsp $0\n\t" + "subq_s.ph %0, %2, %3\n\t" "rddsp %1\n\t" : "=r"(rd), "=r"(dsp) : "r"(rs), "r"(rt) diff --git a/tests/tcg/mips/mips32-dsp/subq_s_w.c b/tests/tcg/mips/mips32-dsp/subq_s_w.c index 09022e9c85..9d456a90f4 100644 --- a/tests/tcg/mips/mips32-dsp/subq_s_w.c +++ b/tests/tcg/mips/mips32-dsp/subq_s_w.c @@ -12,7 +12,8 @@ int main() resultdsp = 0x01; __asm - ("subq_s.w %0, %2, %3\n\t" + ("wrdsp $0\n\t" + "subq_s.w %0, %2, %3\n\t" "rddsp %1\n\t" : "=r"(rd), "=r"(dsp) : "r"(rs), "r"(rt) @@ -24,10 +25,11 @@ int main() rs = 0x66666; rt = 0x55555; result = 0x11111; - resultdsp = 0x01; + resultdsp = 0x0; __asm - ("subq_s.w %0, %2, %3\n\t" + ("wrdsp $0\n\t" + "subq_s.w %0, %2, %3\n\t" "rddsp %1\n\t" : "=r"(rd), "=r"(dsp) : "r"(rs), "r"(rt) @@ -36,23 +38,37 @@ int main() assert(dsp == resultdsp); assert(rd == result); - -#if 0 - rs = 0x35555555; - rt = 0xf5555555; - result = 0x80000000; + rs = 0x0; + rt = 0x80000000; + result = 0x7FFFFFFF; resultdsp = 0x01; __asm - ("subq_s.w %0, %2, %3\n\t" + ("wrdsp $0\n\t" + "subq_s.w %0, %2, %3\n\t" "rddsp %1\n\t" : "=r"(rd), "=r"(dsp) : "r"(rs), "r"(rt) ); - dsp = (dsp >> 20) & 0x01; assert(dsp == resultdsp); assert(rd == result); -#endif + + rs = 0x80000000; + rt = 0x80000000; + result = 0; + resultdsp = 0x00; + + __asm + ("wrdsp $0\n\t" + "subq_s.w %0, %2, %3\n\t" + "rddsp %1\n\t" + : "=r"(rd), "=r"(dsp) + : "r"(rs), "r"(rt) + ); + dsp = (dsp >> 20) & 0x01; + assert(dsp == resultdsp); + assert(rd == result); + return 0; } diff --git a/tests/tcg/mips/mips32-dsp/wrdsp.c b/tests/tcg/mips/mips32-dsp/wrdsp.c index e8948ec1d9..dc54943a99 100644 --- a/tests/tcg/mips/mips32-dsp/wrdsp.c +++ b/tests/tcg/mips/mips32-dsp/wrdsp.c @@ -6,14 +6,13 @@ int main() int dsp_i, dsp_o; int ccond_i, outflag_i, efi_i, c_i, scount_i, pos_i; int ccond_o, outflag_o, efi_o, c_o, scount_o, pos_o; - int ccond_r, outflag_r, efi_r, c_r, scount_r, pos_r; - ccond_i = 0x000000BC;/* 4 */ - outflag_i = 0x0000001B;/* 3 */ - efi_i = 0x00000001;/* 5 */ - c_i = 0x00000001;/* 2 */ - scount_i = 0x0000000F;/* 1 */ - pos_i = 0x0000000C;/* 0 */ + ccond_i = 0x000000BC; /* 4 */ + outflag_i = 0x0000001B; /* 3 */ + efi_i = 0x00000001; /* 5 */ + c_i = 0x00000001; /* 2 */ + scount_i = 0x0000000F; /* 1 */ + pos_i = 0x0000000C; /* 0 */ dsp_i = (ccond_i << 24) | \ (outflag_i << 16) | \ @@ -22,13 +21,6 @@ int main() (scount_i << 7) | \ pos_i; - ccond_r = ccond_i; - outflag_r = outflag_i; - efi_r = efi_i; - c_r = c_i; - scount_r = scount_i; - pos_r = pos_i; - __asm ("wrdsp %1, 0x3F\n\t" "rddsp %0, 0x3F\n\t" @@ -43,12 +35,12 @@ int main() scount_o = (dsp_o >> 7) & 0x3F; pos_o = dsp_o & 0x1F; - assert(ccond_o == ccond_r); - assert(outflag_o == outflag_r); - assert(efi_o == efi_r); - assert(c_o == c_r); - assert(scount_o == scount_r); - assert(pos_o == pos_r); + assert(ccond_o == (ccond_i & 0x0F)); + assert(outflag_o == outflag_i); + assert(efi_o == efi_i); + assert(c_o == c_i); + assert(scount_o == scount_i); + assert(pos_o == pos_i); return 0; } diff --git a/tests/tcg/mips/mips32-dspr2/dpa_w_ph.c b/tests/tcg/mips/mips32-dspr2/dpa_w_ph.c index 1cfbdb080f..fae49f10eb 100644 --- a/tests/tcg/mips/mips32-dspr2/dpa_w_ph.c +++ b/tests/tcg/mips/mips32-dspr2/dpa_w_ph.c @@ -26,8 +26,8 @@ int main() ach = 6, acl = 7; rs = 0xFFFF00FF; rt = 0xFFFF0002; - resulth = 0x05; - resultl = 0xfffe0206; + resulth = 0x06; + resultl = 0x206; __asm ("mthi %0, $ac1\n\t" "mtlo %1, $ac1\n\t" diff --git a/tests/tcg/mips/mips32-dspr2/dpax_w_ph.c b/tests/tcg/mips/mips32-dspr2/dpax_w_ph.c index f75699755c..514797cfd1 100644 --- a/tests/tcg/mips/mips32-dspr2/dpax_w_ph.c +++ b/tests/tcg/mips/mips32-dspr2/dpax_w_ph.c @@ -23,5 +23,22 @@ int main() assert(ach == resulth); assert(acl == resultl); + ach = 6, acl = 7; + rs = 0xFFFF00FF; + rt = 0xFFFF0002; + resulth = 0x05; + resultl = 0xFFFFFF06; + __asm + ("mthi %0, $ac1\n\t" + "mtlo %1, $ac1\n\t" + "dpax.w.ph $ac1, %2, %3\n\t" + "mfhi %0, $ac1\n\t" + "mflo %1, $ac1\n\t" + : "+r"(ach), "+r"(acl) + : "r"(rs), "r"(rt) + ); + assert(ach == resulth); + assert(acl == resultl); + return 0; } diff --git a/tests/tcg/mips/mips32-dspr2/dps_w_ph.c b/tests/tcg/mips/mips32-dspr2/dps_w_ph.c index 8303643d18..f51f9b9d13 100644 --- a/tests/tcg/mips/mips32-dspr2/dps_w_ph.c +++ b/tests/tcg/mips/mips32-dspr2/dps_w_ph.c @@ -23,5 +23,22 @@ int main() assert(ach == resulth); assert(acl == resultl); + ach = 6, acl = 7; + rs = 0xFFFF00FF; + rt = 0xFFFF0002; + resulth = 0x05; + resultl = 0xFFFFFE08; + __asm + ("mthi %0, $ac1\n\t" + "mtlo %1, $ac1\n\t" + "dps.w.ph $ac1, %2, %3\n\t" + "mfhi %0, $ac1\n\t" + "mflo %1, $ac1\n\t" + : "+r"(ach), "+r"(acl) + : "r"(rs), "r"(rt) + ); + assert(ach == resulth); + assert(acl == resultl); + return 0; } diff --git a/tests/tcg/mips/mips32-dspr2/dpsx_w_ph.c b/tests/tcg/mips/mips32-dspr2/dpsx_w_ph.c index 6db59a4ccd..bb49a4031d 100644 --- a/tests/tcg/mips/mips32-dspr2/dpsx_w_ph.c +++ b/tests/tcg/mips/mips32-dspr2/dpsx_w_ph.c @@ -9,8 +9,8 @@ int main() rs = 0xBC0123AD; rt = 0x01643721; - resulth = 0x04; - resultl = 0xD751F050; + resulth = 0x05; + resultl = 0xE72F050; __asm ("mthi %0, $ac1\n\t" "mtlo %1, $ac1\n\t" diff --git a/tests/tcg/mips/mips32-dspr2/mulq_rs_w.c b/tests/tcg/mips/mips32-dspr2/mulq_rs_w.c index 669405faf1..7ba633bc17 100644 --- a/tests/tcg/mips/mips32-dspr2/mulq_rs_w.c +++ b/tests/tcg/mips/mips32-dspr2/mulq_rs_w.c @@ -8,7 +8,7 @@ int main() rs = 0x80001234; rt = 0x80004321; - result = 0x80005555; + result = 0x7FFFAAAB; __asm ("mulq_rs.w %0, %1, %2\n\t" diff --git a/tests/tcg/mips/mips32-dspr2/mulq_s_ph.c b/tests/tcg/mips/mips32-dspr2/mulq_s_ph.c index d0f7674a38..00e015542e 100644 --- a/tests/tcg/mips/mips32-dspr2/mulq_s_ph.c +++ b/tests/tcg/mips/mips32-dspr2/mulq_s_ph.c @@ -6,6 +6,21 @@ int main() int rd, rs, rt, dsp; int result, resultdsp; + rs = 0x80000000; + rt = 0x0ffc0000; + result = 0xF0040000; + resultdsp = 0; + + __asm + ("mulq_s.ph %0, %2, %3\n\t" + "rddsp %1\n\t" + : "=r"(rd), "=r"(dsp) + : "r"(rs), "r"(rt) + ); + dsp = (dsp >> 21) & 0x01; + assert(rd == result); + assert(dsp == resultdsp); + rs = 0x80001234; rt = 0x80004321; result = 0x7FFF098B; diff --git a/tests/tcg/mips/mips32-dspr2/mulq_s_w.c b/tests/tcg/mips/mips32-dspr2/mulq_s_w.c index df148b7ffb..9c2be06cc0 100644 --- a/tests/tcg/mips/mips32-dspr2/mulq_s_w.c +++ b/tests/tcg/mips/mips32-dspr2/mulq_s_w.c @@ -8,7 +8,7 @@ int main() rs = 0x80001234; rt = 0x80004321; - result = 0x80005555; + result = 0x7FFFAAAB; __asm ("mulq_s.w %0, %1, %2\n\t" diff --git a/tests/tcg/test-i386.c b/tests/tcg/test-i386.c index 6dc730d882..b05572b734 100644 --- a/tests/tcg/test-i386.c +++ b/tests/tcg/test-i386.c @@ -209,7 +209,7 @@ static inline long i2l(long v) #define TEST_LEA16(STR)\ {\ asm(".code16 ; .byte 0x67 ; leal " STR ", %0 ; .code32"\ - : "=wq" (res)\ + : "=r" (res)\ : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));\ printf("lea %s = %08lx\n", STR, res);\ } @@ -925,7 +925,7 @@ void test_fbcd(double a) void test_fenv(void) { - struct QEMU_PACKED { + struct __attribute__((__packed__)) { uint16_t fpuc; uint16_t dummy1; uint16_t fpus; @@ -935,7 +935,7 @@ void test_fenv(void) uint32_t ignored[4]; long double fpregs[8]; } float_env32; - struct QEMU_PACKED { + struct __attribute__((__packed__)) { uint16_t fpuc; uint16_t fpus; uint16_t fptag; @@ -1280,7 +1280,7 @@ void test_segs(void) struct { uint32_t offset; uint16_t seg; - } QEMU_PACKED segoff; + } __attribute__((__packed__)) segoff; ldt.entry_number = 1; ldt.base_addr = (unsigned long)&seg_data1; @@ -1828,7 +1828,7 @@ void test_exceptions(void) printf("lock nop exception:\n"); if (setjmp(jmp_env) == 0) { /* now execute an invalid instruction */ - asm volatile(".byte 0xf0, 0x90"); /* lock nop */ + asm volatile(".byte 0xf0, 0x90"); } printf("INT exception:\n"); diff --git a/tests/test-aio.c b/tests/test-aio.c index e4ebef76b9..c1738706cd 100644 --- a/tests/test-aio.c +++ b/tests/test-aio.c @@ -315,13 +315,13 @@ static void test_wait_event_notifier_noflush(void) event_notifier_set(&data.e); g_assert(aio_poll(ctx, false)); g_assert_cmpint(data.n, ==, 1); - g_assert(!aio_poll(ctx, false)); + g_assert(aio_poll(ctx, false)); g_assert_cmpint(data.n, ==, 1); event_notifier_set(&data.e); g_assert(aio_poll(ctx, false)); g_assert_cmpint(data.n, ==, 2); - g_assert(!aio_poll(ctx, false)); + g_assert(aio_poll(ctx, false)); g_assert_cmpint(data.n, ==, 2); event_notifier_set(&dummy.e); diff --git a/tests/test-coroutine.c b/tests/test-coroutine.c index 4c6cc81fb9..39be046ec7 100644 --- a/tests/test-coroutine.c +++ b/tests/test-coroutine.c @@ -183,7 +183,7 @@ static void perf_nesting(void) double duration; maxcycles = 100000000; - maxnesting = 20000; + maxnesting = 1000; Coroutine *root; NestData nd = { .n_enter = 0, diff --git a/tests/test-cutils.c b/tests/test-cutils.c new file mode 100644 index 0000000000..2a4556d3aa --- /dev/null +++ b/tests/test-cutils.c @@ -0,0 +1,251 @@ +/* + * cutils.c unit-tests + * + * Copyright (C) 2013 Red Hat Inc. + * + * Authors: + * Eduardo Habkost + * + * 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 +#include +#include + +#include "qemu-common.h" + + +static void test_parse_uint_null(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + int r; + + r = parse_uint(NULL, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == NULL); +} + +static void test_parse_uint_empty(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = ""; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == str); +} + +static void test_parse_uint_whitespace(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = " \t "; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == str); +} + + +static void test_parse_uint_invalid(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = " \t xxx"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == str); +} + + +static void test_parse_uint_trailing(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "123xxx"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 123); + g_assert(endptr == str + 3); +} + +static void test_parse_uint_correct(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "123"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 123); + g_assert(endptr == str + strlen(str)); +} + +static void test_parse_uint_octal(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "0123"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 0123); + g_assert(endptr == str + strlen(str)); +} + +static void test_parse_uint_decimal(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "0123"; + int r; + + r = parse_uint(str, &i, &endptr, 10); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 123); + g_assert(endptr == str + strlen(str)); +} + + +static void test_parse_uint_llong_max(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + char *str = g_strdup_printf("%llu", (unsigned long long)LLONG_MAX + 1); + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, (unsigned long long)LLONG_MAX + 1); + g_assert(endptr == str + strlen(str)); + + g_free(str); +} + +static void test_parse_uint_overflow(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = "99999999999999999999999999999999999999"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpint(i, ==, ULLONG_MAX); + g_assert(endptr == str + strlen(str)); +} + +static void test_parse_uint_negative(void) +{ + unsigned long long i = 999; + char f = 'X'; + char *endptr = &f; + const char *str = " \t -321"; + int r; + + r = parse_uint(str, &i, &endptr, 0); + + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpint(i, ==, 0); + g_assert(endptr == str + strlen(str)); +} + + +static void test_parse_uint_full_trailing(void) +{ + unsigned long long i = 999; + const char *str = "123xxx"; + int r; + + r = parse_uint_full(str, &i, 0); + + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpint(i, ==, 0); +} + +static void test_parse_uint_full_correct(void) +{ + unsigned long long i = 999; + const char *str = "123"; + int r; + + r = parse_uint_full(str, &i, 0); + + g_assert_cmpint(r, ==, 0); + g_assert_cmpint(i, ==, 123); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/cutils/parse_uint/null", test_parse_uint_null); + g_test_add_func("/cutils/parse_uint/empty", test_parse_uint_empty); + g_test_add_func("/cutils/parse_uint/whitespace", + test_parse_uint_whitespace); + g_test_add_func("/cutils/parse_uint/invalid", test_parse_uint_invalid); + g_test_add_func("/cutils/parse_uint/trailing", test_parse_uint_trailing); + g_test_add_func("/cutils/parse_uint/correct", test_parse_uint_correct); + g_test_add_func("/cutils/parse_uint/octal", test_parse_uint_octal); + g_test_add_func("/cutils/parse_uint/decimal", test_parse_uint_decimal); + g_test_add_func("/cutils/parse_uint/llong_max", test_parse_uint_llong_max); + g_test_add_func("/cutils/parse_uint/overflow", test_parse_uint_overflow); + g_test_add_func("/cutils/parse_uint/negative", test_parse_uint_negative); + g_test_add_func("/cutils/parse_uint_full/trailing", + test_parse_uint_full_trailing); + g_test_add_func("/cutils/parse_uint_full/correct", + test_parse_uint_full_correct); + + return g_test_run(); +} diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c new file mode 100644 index 0000000000..8c902f2055 --- /dev/null +++ b/tests/test-hbitmap.c @@ -0,0 +1,401 @@ +/* + * Hierarchical bitmap unit-tests. + * + * Copyright (C) 2012 Red Hat Inc. + * + * Author: Paolo Bonzini + * + * 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 +#include +#include "qemu/hbitmap.h" + +#define LOG_BITS_PER_LONG (BITS_PER_LONG == 32 ? 5 : 6) + +#define L1 BITS_PER_LONG +#define L2 (BITS_PER_LONG * L1) +#define L3 (BITS_PER_LONG * L2) + +typedef struct TestHBitmapData { + HBitmap *hb; + unsigned long *bits; + size_t size; + int granularity; +} TestHBitmapData; + + +/* Check that the HBitmap and the shadow bitmap contain the same data, + * ignoring the same "first" bits. + */ +static void hbitmap_test_check(TestHBitmapData *data, + uint64_t first) +{ + uint64_t count = 0; + size_t pos; + int bit; + HBitmapIter hbi; + int64_t i, next; + + hbitmap_iter_init(&hbi, data->hb, first); + + i = first; + for (;;) { + next = hbitmap_iter_next(&hbi); + if (next < 0) { + next = data->size; + } + + while (i < next) { + pos = i >> LOG_BITS_PER_LONG; + bit = i & (BITS_PER_LONG - 1); + i++; + g_assert_cmpint(data->bits[pos] & (1UL << bit), ==, 0); + } + + if (next == data->size) { + break; + } + + pos = i >> LOG_BITS_PER_LONG; + bit = i & (BITS_PER_LONG - 1); + i++; + count++; + g_assert_cmpint(data->bits[pos] & (1UL << bit), !=, 0); + } + + if (first == 0) { + g_assert_cmpint(count << data->granularity, ==, hbitmap_count(data->hb)); + } +} + +/* This is provided instead of a test setup function so that the sizes + are kept in the test functions (and not in main()) */ +static void hbitmap_test_init(TestHBitmapData *data, + uint64_t size, int granularity) +{ + size_t n; + data->hb = hbitmap_alloc(size, granularity); + + n = (size + BITS_PER_LONG - 1) / BITS_PER_LONG; + if (n == 0) { + n = 1; + } + data->bits = g_new0(unsigned long, n); + data->size = size; + data->granularity = granularity; + if (size) { + hbitmap_test_check(data, 0); + } +} + +static void hbitmap_test_teardown(TestHBitmapData *data, + const void *unused) +{ + if (data->hb) { + hbitmap_free(data->hb); + data->hb = NULL; + } + if (data->bits) { + g_free(data->bits); + data->bits = NULL; + } +} + +/* Set a range in the HBitmap and in the shadow "simple" bitmap. + * The two bitmaps are then tested against each other. + */ +static void hbitmap_test_set(TestHBitmapData *data, + uint64_t first, uint64_t count) +{ + hbitmap_set(data->hb, first, count); + while (count-- != 0) { + size_t pos = first >> LOG_BITS_PER_LONG; + int bit = first & (BITS_PER_LONG - 1); + first++; + + data->bits[pos] |= 1UL << bit; + } + + if (data->granularity == 0) { + hbitmap_test_check(data, 0); + } +} + +/* Reset a range in the HBitmap and in the shadow "simple" bitmap. + */ +static void hbitmap_test_reset(TestHBitmapData *data, + uint64_t first, uint64_t count) +{ + hbitmap_reset(data->hb, first, count); + while (count-- != 0) { + size_t pos = first >> LOG_BITS_PER_LONG; + int bit = first & (BITS_PER_LONG - 1); + first++; + + data->bits[pos] &= ~(1UL << bit); + } + + if (data->granularity == 0) { + hbitmap_test_check(data, 0); + } +} + +static void hbitmap_test_check_get(TestHBitmapData *data) +{ + uint64_t count = 0; + uint64_t i; + + for (i = 0; i < data->size; i++) { + size_t pos = i >> LOG_BITS_PER_LONG; + int bit = i & (BITS_PER_LONG - 1); + unsigned long val = data->bits[pos] & (1UL << bit); + count += hbitmap_get(data->hb, i); + g_assert_cmpint(hbitmap_get(data->hb, i), ==, val != 0); + } + g_assert_cmpint(count, ==, hbitmap_count(data->hb)); +} + +static void test_hbitmap_zero(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, 0, 0); +} + +static void test_hbitmap_unaligned(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3 + 23, 0); + hbitmap_test_set(data, 0, 1); + hbitmap_test_set(data, L3 + 22, 1); +} + +static void test_hbitmap_iter_empty(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L1, 0); +} + +static void test_hbitmap_iter_partial(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3, 0); + hbitmap_test_set(data, 0, L3); + hbitmap_test_check(data, 1); + hbitmap_test_check(data, L1 - 1); + hbitmap_test_check(data, L1); + hbitmap_test_check(data, L1 * 2 - 1); + hbitmap_test_check(data, L2 - 1); + hbitmap_test_check(data, L2); + hbitmap_test_check(data, L2 + 1); + hbitmap_test_check(data, L2 + L1); + hbitmap_test_check(data, L2 + L1 * 2 - 1); + hbitmap_test_check(data, L2 * 2 - 1); + hbitmap_test_check(data, L2 * 2); + hbitmap_test_check(data, L2 * 2 + 1); + hbitmap_test_check(data, L2 * 2 + L1); + hbitmap_test_check(data, L2 * 2 + L1 * 2 - 1); + hbitmap_test_check(data, L3 / 2); +} + +static void test_hbitmap_set_all(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3, 0); + hbitmap_test_set(data, 0, L3); +} + +static void test_hbitmap_get_all(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3, 0); + hbitmap_test_set(data, 0, L3); + hbitmap_test_check_get(data); +} + +static void test_hbitmap_get_some(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, 2 * L2, 0); + hbitmap_test_set(data, 10, 1); + hbitmap_test_check_get(data); + hbitmap_test_set(data, L1 - 1, 1); + hbitmap_test_check_get(data); + hbitmap_test_set(data, L1, 1); + hbitmap_test_check_get(data); + hbitmap_test_set(data, L2 - 1, 1); + hbitmap_test_check_get(data); + hbitmap_test_set(data, L2, 1); + hbitmap_test_check_get(data); +} + +static void test_hbitmap_set_one(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, 2 * L2, 0); + hbitmap_test_set(data, 10, 1); + hbitmap_test_set(data, L1 - 1, 1); + hbitmap_test_set(data, L1, 1); + hbitmap_test_set(data, L2 - 1, 1); + hbitmap_test_set(data, L2, 1); +} + +static void test_hbitmap_set_two_elem(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, 2 * L2, 0); + hbitmap_test_set(data, L1 - 1, 2); + hbitmap_test_set(data, L1 * 2 - 1, 4); + hbitmap_test_set(data, L1 * 4, L1 + 1); + hbitmap_test_set(data, L1 * 8 - 1, L1 + 1); + hbitmap_test_set(data, L2 - 1, 2); + hbitmap_test_set(data, L2 + L1 - 1, 8); + hbitmap_test_set(data, L2 + L1 * 4, L1 + 1); + hbitmap_test_set(data, L2 + L1 * 8 - 1, L1 + 1); +} + +static void test_hbitmap_set(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3 * 2, 0); + hbitmap_test_set(data, L1 - 1, L1 + 2); + hbitmap_test_set(data, L1 * 3 - 1, L1 + 2); + hbitmap_test_set(data, L1 * 5, L1 * 2 + 1); + hbitmap_test_set(data, L1 * 8 - 1, L1 * 2 + 1); + hbitmap_test_set(data, L2 - 1, L1 + 2); + hbitmap_test_set(data, L2 + L1 * 2 - 1, L1 + 2); + hbitmap_test_set(data, L2 + L1 * 4, L1 * 2 + 1); + hbitmap_test_set(data, L2 + L1 * 7 - 1, L1 * 2 + 1); + hbitmap_test_set(data, L2 * 2 - 1, L3 * 2 - L2 * 2); +} + +static void test_hbitmap_set_twice(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L1 * 3, 0); + hbitmap_test_set(data, 0, L1 * 3); + hbitmap_test_set(data, L1, 1); +} + +static void test_hbitmap_set_overlap(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3 * 2, 0); + hbitmap_test_set(data, L1 - 1, L1 + 2); + hbitmap_test_set(data, L1 * 2 - 1, L1 * 2 + 2); + hbitmap_test_set(data, 0, L1 * 3); + hbitmap_test_set(data, L1 * 8 - 1, L2); + hbitmap_test_set(data, L2, L1); + hbitmap_test_set(data, L2 - L1 - 1, L1 * 8 + 2); + hbitmap_test_set(data, L2, L3 - L2 + 1); + hbitmap_test_set(data, L3 - L1, L1 * 3); + hbitmap_test_set(data, L3 - 1, 3); + hbitmap_test_set(data, L3 - 1, L2); +} + +static void test_hbitmap_reset_empty(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3, 0); + hbitmap_test_reset(data, 0, L3); +} + +static void test_hbitmap_reset(TestHBitmapData *data, + const void *unused) +{ + hbitmap_test_init(data, L3 * 2, 0); + hbitmap_test_set(data, L1 - 1, L1 + 2); + hbitmap_test_reset(data, L1 * 2 - 1, L1 * 2 + 2); + hbitmap_test_set(data, 0, L1 * 3); + hbitmap_test_reset(data, L1 * 8 - 1, L2); + hbitmap_test_set(data, L2, L1); + hbitmap_test_reset(data, L2 - L1 - 1, L1 * 8 + 2); + hbitmap_test_set(data, L2, L3 - L2 + 1); + hbitmap_test_reset(data, L3 - L1, L1 * 3); + hbitmap_test_set(data, L3 - 1, 3); + hbitmap_test_reset(data, L3 - 1, L2); + hbitmap_test_set(data, 0, L3 * 2); + hbitmap_test_reset(data, 0, L1); + hbitmap_test_reset(data, 0, L2); + hbitmap_test_reset(data, L3, L3); + hbitmap_test_set(data, L3 / 2, L3); +} + +static void test_hbitmap_granularity(TestHBitmapData *data, + const void *unused) +{ + /* Note that hbitmap_test_check has to be invoked manually in this test. */ + hbitmap_test_init(data, L1, 1); + hbitmap_test_set(data, 0, 1); + g_assert_cmpint(hbitmap_count(data->hb), ==, 2); + hbitmap_test_check(data, 0); + hbitmap_test_set(data, 2, 1); + g_assert_cmpint(hbitmap_count(data->hb), ==, 4); + hbitmap_test_check(data, 0); + hbitmap_test_set(data, 0, 3); + g_assert_cmpint(hbitmap_count(data->hb), ==, 4); + hbitmap_test_reset(data, 0, 1); + g_assert_cmpint(hbitmap_count(data->hb), ==, 2); +} + +static void test_hbitmap_iter_granularity(TestHBitmapData *data, + const void *unused) +{ + HBitmapIter hbi; + + /* Note that hbitmap_test_check has to be invoked manually in this test. */ + hbitmap_test_init(data, 131072 << 7, 7); + hbitmap_iter_init(&hbi, data->hb, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_test_set(data, (131072 << 7) - 8, 8); + hbitmap_iter_init(&hbi, data->hb, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + + hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); +} + +static void hbitmap_test_add(const char *testpath, + void (*test_func)(TestHBitmapData *data, const void *user_data)) +{ + g_test_add(testpath, TestHBitmapData, NULL, NULL, test_func, + hbitmap_test_teardown); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + hbitmap_test_add("/hbitmap/size/0", test_hbitmap_zero); + hbitmap_test_add("/hbitmap/size/unaligned", test_hbitmap_unaligned); + hbitmap_test_add("/hbitmap/iter/empty", test_hbitmap_iter_empty); + hbitmap_test_add("/hbitmap/iter/partial", test_hbitmap_iter_partial); + hbitmap_test_add("/hbitmap/iter/granularity", test_hbitmap_iter_granularity); + hbitmap_test_add("/hbitmap/get/all", test_hbitmap_get_all); + hbitmap_test_add("/hbitmap/get/some", test_hbitmap_get_some); + hbitmap_test_add("/hbitmap/set/all", test_hbitmap_set_all); + hbitmap_test_add("/hbitmap/set/one", test_hbitmap_set_one); + hbitmap_test_add("/hbitmap/set/two-elem", test_hbitmap_set_two_elem); + hbitmap_test_add("/hbitmap/set/general", test_hbitmap_set); + hbitmap_test_add("/hbitmap/set/twice", test_hbitmap_set_twice); + hbitmap_test_add("/hbitmap/set/overlap", test_hbitmap_set_overlap); + hbitmap_test_add("/hbitmap/reset/empty", test_hbitmap_reset_empty); + hbitmap_test_add("/hbitmap/reset/general", test_hbitmap_reset); + hbitmap_test_add("/hbitmap/granularity", test_hbitmap_granularity); + g_test_run(); + + return 0; +} diff --git a/tests/test-iov.c b/tests/test-iov.c index a480bc8725..46e4dddc55 100644 --- a/tests/test-iov.c +++ b/tests/test-iov.c @@ -250,11 +250,161 @@ static void test_io(void) #endif } +static void test_discard_front(void) +{ + struct iovec *iov; + struct iovec *iov_tmp; + unsigned int iov_cnt; + unsigned int iov_cnt_tmp; + void *old_base; + size_t size; + size_t ret; + + /* Discard zero bytes */ + iov_random(&iov, &iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, 0); + g_assert(ret == 0); + g_assert(iov_tmp == iov); + g_assert(iov_cnt_tmp == iov_cnt); + iov_free(iov, iov_cnt); + + /* Discard more bytes than vector size */ + iov_random(&iov, &iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + size = iov_size(iov, iov_cnt); + ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size + 1); + g_assert(ret == size); + g_assert(iov_cnt_tmp == 0); + iov_free(iov, iov_cnt); + + /* Discard entire vector */ + iov_random(&iov, &iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + size = iov_size(iov, iov_cnt); + ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_cnt_tmp == 0); + iov_free(iov, iov_cnt); + + /* Discard within first element */ + iov_random(&iov, &iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + old_base = iov->iov_base; + size = g_test_rand_int_range(1, iov->iov_len); + ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_tmp == iov); + g_assert(iov_cnt_tmp == iov_cnt); + g_assert(iov_tmp->iov_base == old_base + size); + iov_tmp->iov_base = old_base; /* undo before g_free() */ + iov_free(iov, iov_cnt); + + /* Discard entire first element */ + iov_random(&iov, &iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, iov->iov_len); + g_assert(ret == iov->iov_len); + g_assert(iov_tmp == iov + 1); + g_assert(iov_cnt_tmp == iov_cnt - 1); + iov_free(iov, iov_cnt); + + /* Discard within second element */ + iov_random(&iov, &iov_cnt); + iov_tmp = iov; + iov_cnt_tmp = iov_cnt; + old_base = iov[1].iov_base; + size = iov->iov_len + g_test_rand_int_range(1, iov[1].iov_len); + ret = iov_discard_front(&iov_tmp, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_tmp == iov + 1); + g_assert(iov_cnt_tmp == iov_cnt - 1); + g_assert(iov_tmp->iov_base == old_base + (size - iov->iov_len)); + iov_tmp->iov_base = old_base; /* undo before g_free() */ + iov_free(iov, iov_cnt); +} + +static void test_discard_back(void) +{ + struct iovec *iov; + unsigned int iov_cnt; + unsigned int iov_cnt_tmp; + void *old_base; + size_t size; + size_t ret; + + /* Discard zero bytes */ + iov_random(&iov, &iov_cnt); + iov_cnt_tmp = iov_cnt; + ret = iov_discard_back(iov, &iov_cnt_tmp, 0); + g_assert(ret == 0); + g_assert(iov_cnt_tmp == iov_cnt); + iov_free(iov, iov_cnt); + + /* Discard more bytes than vector size */ + iov_random(&iov, &iov_cnt); + iov_cnt_tmp = iov_cnt; + size = iov_size(iov, iov_cnt); + ret = iov_discard_back(iov, &iov_cnt_tmp, size + 1); + g_assert(ret == size); + g_assert(iov_cnt_tmp == 0); + iov_free(iov, iov_cnt); + + /* Discard entire vector */ + iov_random(&iov, &iov_cnt); + iov_cnt_tmp = iov_cnt; + size = iov_size(iov, iov_cnt); + ret = iov_discard_back(iov, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_cnt_tmp == 0); + iov_free(iov, iov_cnt); + + /* Discard within last element */ + iov_random(&iov, &iov_cnt); + iov_cnt_tmp = iov_cnt; + old_base = iov[iov_cnt - 1].iov_base; + size = g_test_rand_int_range(1, iov[iov_cnt - 1].iov_len); + ret = iov_discard_back(iov, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_cnt_tmp == iov_cnt); + g_assert(iov[iov_cnt - 1].iov_base == old_base); + iov_free(iov, iov_cnt); + + /* Discard entire last element */ + iov_random(&iov, &iov_cnt); + iov_cnt_tmp = iov_cnt; + old_base = iov[iov_cnt - 1].iov_base; + size = iov[iov_cnt - 1].iov_len; + ret = iov_discard_back(iov, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_cnt_tmp == iov_cnt - 1); + iov_free(iov, iov_cnt); + + /* Discard within second-to-last element */ + iov_random(&iov, &iov_cnt); + iov_cnt_tmp = iov_cnt; + old_base = iov[iov_cnt - 2].iov_base; + size = iov[iov_cnt - 1].iov_len + + g_test_rand_int_range(1, iov[iov_cnt - 2].iov_len); + ret = iov_discard_back(iov, &iov_cnt_tmp, size); + g_assert(ret == size); + g_assert(iov_cnt_tmp == iov_cnt - 1); + g_assert(iov[iov_cnt - 2].iov_base == old_base); + iov_free(iov, iov_cnt); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_rand_int(); g_test_add_func("/basic/iov/from-to-buf", test_to_from_buf); g_test_add_func("/basic/iov/io", test_io); + g_test_add_func("/basic/iov/discard-front", test_discard_front); + g_test_add_func("/basic/iov/discard-back", test_discard_back); return g_test_run(); } diff --git a/tests/test-mul64.c b/tests/test-mul64.c new file mode 100644 index 0000000000..a0a17f7775 --- /dev/null +++ b/tests/test-mul64.c @@ -0,0 +1,70 @@ +/* + * Test 64x64 -> 128 multiply subroutines + * + * 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 +#include +#include "qemu/host-utils.h" +#include "qemu/osdep.h" + + +typedef struct { + uint64_t a, b; + uint64_t rh, rl; +} Test; + +static const Test test_u_data[] = { + { 1, 1, 0, 1 }, + { 10000, 10000, 0, 100000000 }, + { 0xffffffffffffffffULL, 2, 1, 0xfffffffffffffffeULL }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, + 0xfffffffffffffffeULL, 0x0000000000000001ULL }, + { 0x1122334455667788ull, 0x8877665544332211ull, + 0x092228fb777ae38full, 0x0a3e963337c60008ull }, +}; + +static const Test test_s_data[] = { + { 1, 1, 0, 1 }, + { 1, -1, -1, -1 }, + { -10, -10, 0, 100 }, + { 10000, 10000, 0, 100000000 }, + { -1, 2, -1, -2 }, + { 0x1122334455667788ULL, 0x1122334455667788ULL, + 0x01258f60bbc2975cULL, 0x1eace4a3c82fb840ULL }, +}; + +static void test_u(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_u_data); ++i) { + uint64_t rl, rh; + mulu64(&rl, &rh, test_u_data[i].a, test_u_data[i].b); + g_assert_cmpuint(rl, ==, test_u_data[i].rl); + g_assert_cmpuint(rh, ==, test_u_data[i].rh); + } +} + +static void test_s(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_s_data); ++i) { + uint64_t rl, rh; + muls64(&rl, &rh, test_s_data[i].a, test_s_data[i].b); + g_assert_cmpuint(rl, ==, test_s_data[i].rl); + g_assert_cmpint(rh, ==, test_s_data[i].rh); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/host-utils/mulu64", test_u); + g_test_add_func("/host-utils/muls64", test_s); + return g_test_run(); +} diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c index 899feda579..5989f8118e 100644 --- a/tests/test-string-input-visitor.c +++ b/tests/test-string-input-visitor.c @@ -165,6 +165,53 @@ static void test_visitor_in_enum(TestInputVisitorData *data, data->siv = NULL; } +/* Try to crash the visitors */ +static void test_visitor_in_fuzz(TestInputVisitorData *data, + const void *unused) +{ + int64_t ires; + bool bres; + double nres; + char *sres; + EnumOne eres; + Visitor *v; + unsigned int i; + char buf[10000]; + + for (i = 0; i < 100; i++) { + unsigned int j; + + j = g_test_rand_int_range(0, sizeof(buf) - 1); + + buf[j] = '\0'; + + if (j != 0) { + for (j--; j != 0; j--) { + buf[j - 1] = (char)g_test_rand_int_range(0, 256); + } + } + + v = visitor_input_test_init(data, buf); + visit_type_int(v, &ires, NULL, NULL); + + v = visitor_input_test_init(data, buf); + visit_type_bool(v, &bres, NULL, NULL); + visitor_input_teardown(data, NULL); + + v = visitor_input_test_init(data, buf); + visit_type_number(v, &nres, NULL, NULL); + + v = visitor_input_test_init(data, buf); + sres = NULL; + visit_type_str(v, &sres, NULL, NULL); + g_free(sres); + + v = visitor_input_test_init(data, buf); + visit_type_EnumOne(v, &eres, NULL, NULL); + visitor_input_teardown(data, NULL); + } +} + static void input_visitor_test_add(const char *testpath, TestInputVisitorData *data, void (*test_func)(TestInputVisitorData *data, const void *user_data)) @@ -189,6 +236,8 @@ int main(int argc, char **argv) &in_visitor_data, test_visitor_in_string); input_visitor_test_add("/string-visitor/input/enum", &in_visitor_data, test_visitor_in_enum); + input_visitor_test_add("/string-visitor/input/fuzz", + &in_visitor_data, test_visitor_in_fuzz); g_test_run(); diff --git a/tests/test-thread-pool.c b/tests/test-thread-pool.c index 9998e031f2..22915aac10 100644 --- a/tests/test-thread-pool.c +++ b/tests/test-thread-pool.c @@ -4,6 +4,8 @@ #include "block/thread-pool.h" #include "block/block.h" +static AioContext *ctx; +static ThreadPool *pool; static int active; typedef struct { @@ -38,19 +40,10 @@ static void done_cb(void *opaque, int ret) active--; } -/* A non-blocking poll of the main AIO context (we cannot use aio_poll - * because we do not know the AioContext). - */ -static void qemu_aio_wait_nonblocking(void) -{ - qemu_notify_event(); - qemu_aio_wait(); -} - /* Wait until all aio and bh activity has finished */ static void qemu_aio_wait_all(void) { - while (qemu_aio_wait()) { + while (aio_poll(ctx, true)) { /* Do nothing */ } } @@ -58,7 +51,7 @@ static void qemu_aio_wait_all(void) static void test_submit(void) { WorkerTestData data = { .n = 0 }; - thread_pool_submit(worker_cb, &data); + thread_pool_submit(pool, worker_cb, &data); qemu_aio_wait_all(); g_assert_cmpint(data.n, ==, 1); } @@ -66,7 +59,8 @@ static void test_submit(void) static void test_submit_aio(void) { WorkerTestData data = { .n = 0, .ret = -EINPROGRESS }; - data.aiocb = thread_pool_submit_aio(worker_cb, &data, done_cb, &data); + data.aiocb = thread_pool_submit_aio(pool, worker_cb, &data, + done_cb, &data); /* The callbacks are not called until after the first wait. */ active = 1; @@ -84,7 +78,7 @@ static void co_test_cb(void *opaque) active = 1; data->n = 0; data->ret = -EINPROGRESS; - thread_pool_submit_co(worker_cb, data); + thread_pool_submit_co(pool, worker_cb, data); /* The test continues in test_submit_co, after qemu_coroutine_enter... */ @@ -126,12 +120,12 @@ static void test_submit_many(void) for (i = 0; i < 100; i++) { data[i].n = 0; data[i].ret = -EINPROGRESS; - thread_pool_submit_aio(worker_cb, &data[i], done_cb, &data[i]); + thread_pool_submit_aio(pool, worker_cb, &data[i], done_cb, &data[i]); } active = 100; while (active > 0) { - qemu_aio_wait(); + aio_poll(ctx, true); } for (i = 0; i < 100; i++) { g_assert_cmpint(data[i].n, ==, 1); @@ -154,7 +148,7 @@ static void test_cancel(void) for (i = 0; i < 100; i++) { data[i].n = 0; data[i].ret = -EINPROGRESS; - data[i].aiocb = thread_pool_submit_aio(long_cb, &data[i], + data[i].aiocb = thread_pool_submit_aio(pool, long_cb, &data[i], done_cb, &data[i]); } @@ -162,7 +156,8 @@ static void test_cancel(void) * run, but do not waste too much time... */ active = 100; - qemu_aio_wait_nonblocking(); + aio_notify(ctx); + aio_poll(ctx, false); /* Wait some time for the threads to start, with some sanity * testing on the behavior of the scheduler... @@ -208,11 +203,10 @@ static void test_cancel(void) int main(int argc, char **argv) { - /* These should be removed once each AioContext has its thread pool. - * The test should create its own AioContext. - */ - qemu_init_main_loop(); - bdrv_init(); + int ret; + + ctx = aio_context_new(); + pool = aio_get_thread_pool(ctx); g_test_init(&argc, &argv, NULL); g_test_add_func("/thread-pool/submit", test_submit); @@ -220,5 +214,9 @@ int main(int argc, char **argv) g_test_add_func("/thread-pool/submit-co", test_submit_co); g_test_add_func("/thread-pool/submit-many", test_submit_many); g_test_add_func("/thread-pool/cancel", test_cancel); - return g_test_run(); + + ret = g_test_run(); + + aio_context_unref(ctx); + return ret; } diff --git a/tests/test-x86-cpuid.c b/tests/test-x86-cpuid.c new file mode 100644 index 0000000000..8d9f96a113 --- /dev/null +++ b/tests/test-x86-cpuid.c @@ -0,0 +1,110 @@ +/* + * Test code for x86 CPUID and Topology functions + * + * Copyright (c) 2012 Red Hat Inc. + * + * 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 + +#include "topology.h" + +static void test_topo_bits(void) +{ + /* simple tests for 1 thread per core, 1 core per socket */ + g_assert_cmpuint(apicid_smt_width(1, 1), ==, 0); + g_assert_cmpuint(apicid_core_width(1, 1), ==, 0); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 0), ==, 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 1), ==, 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 2), ==, 2); + g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 3), ==, 3); + + + /* Test field width calculation for multiple values + */ + g_assert_cmpuint(apicid_smt_width(1, 2), ==, 1); + g_assert_cmpuint(apicid_smt_width(1, 3), ==, 2); + g_assert_cmpuint(apicid_smt_width(1, 4), ==, 2); + + g_assert_cmpuint(apicid_smt_width(1, 14), ==, 4); + g_assert_cmpuint(apicid_smt_width(1, 15), ==, 4); + g_assert_cmpuint(apicid_smt_width(1, 16), ==, 4); + g_assert_cmpuint(apicid_smt_width(1, 17), ==, 5); + + + g_assert_cmpuint(apicid_core_width(30, 2), ==, 5); + g_assert_cmpuint(apicid_core_width(31, 2), ==, 5); + g_assert_cmpuint(apicid_core_width(32, 2), ==, 5); + g_assert_cmpuint(apicid_core_width(33, 2), ==, 6); + + + /* build a weird topology and see if IDs are calculated correctly + */ + + /* This will use 2 bits for thread ID and 3 bits for core ID + */ + g_assert_cmpuint(apicid_smt_width(6, 3), ==, 2); + g_assert_cmpuint(apicid_core_width(6, 3), ==, 3); + g_assert_cmpuint(apicid_pkg_offset(6, 3), ==, 5); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 0), ==, 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 1), ==, 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 2), ==, 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 1 * 3 + 0), ==, + (1 << 2) | 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 1 * 3 + 1), ==, + (1 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 1 * 3 + 2), ==, + (1 << 2) | 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 2 * 3 + 0), ==, + (2 << 2) | 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 2 * 3 + 1), ==, + (2 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 2 * 3 + 2), ==, + (2 << 2) | 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 5 * 3 + 0), ==, + (5 << 2) | 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 5 * 3 + 1), ==, + (5 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 5 * 3 + 2), ==, + (5 << 2) | 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 1 * 6 * 3 + 0 * 3 + 0), ==, + (1 << 5)); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 1 * 6 * 3 + 1 * 3 + 1), ==, + (1 << 5) | (1 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(6, 3, 3 * 6 * 3 + 5 * 3 + 2), ==, + (3 << 5) | (5 << 2) | 2); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/cpuid/topology/basic", test_topo_bits); + + g_test_run(); + + return 0; +} diff --git a/tests/test-xbzrle.c b/tests/test-xbzrle.c new file mode 100644 index 0000000000..db93b0a3d2 --- /dev/null +++ b/tests/test-xbzrle.c @@ -0,0 +1,196 @@ +/* + * Xor Based Zero Run Length Encoding unit tests. + * + * Copyright 2013 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Orit Wasserman + * + * 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 +#include +#include +#include +#include +#include +#include +#include "qemu-common.h" +#include "include/migration/migration.h" + +#define PAGE_SIZE 4096 + +static void test_uleb(void) +{ + uint32_t i, val; + uint8_t buf[2]; + int encode_ret, decode_ret; + + for (i = 0; i <= 0x3fff; i++) { + encode_ret = uleb128_encode_small(&buf[0], i); + decode_ret = uleb128_decode_small(&buf[0], &val); + g_assert(encode_ret == decode_ret); + g_assert(i == val); + } + + /* decode invalid value */ + buf[0] = 0x80; + buf[1] = 0x80; + + decode_ret = uleb128_decode_small(&buf[0], &val); + g_assert(decode_ret == -1); + g_assert(val == 0); +} + +static void test_encode_decode_zero(void) +{ + uint8_t *buffer = g_malloc0(PAGE_SIZE); + uint8_t *compressed = g_malloc0(PAGE_SIZE); + int i = 0; + int dlen = 0; + int diff_len = g_test_rand_int_range(0, PAGE_SIZE - 1006); + + for (i = diff_len; i > 0; i--) { + buffer[1000 + i] = i; + } + + buffer[1000 + diff_len + 3] = 103; + buffer[1000 + diff_len + 5] = 105; + + /* encode zero page */ + dlen = xbzrle_encode_buffer(buffer, buffer, PAGE_SIZE, compressed, + PAGE_SIZE); + g_assert(dlen == 0); + + g_free(buffer); + g_free(compressed); +} + +static void test_encode_decode_unchanged(void) +{ + uint8_t *compressed = g_malloc0(PAGE_SIZE); + uint8_t *test = g_malloc0(PAGE_SIZE); + int i = 0; + int dlen = 0; + int diff_len = g_test_rand_int_range(0, PAGE_SIZE - 1006); + + for (i = diff_len; i > 0; i--) { + test[1000 + i] = i + 4; + } + + test[1000 + diff_len + 3] = 107; + test[1000 + diff_len + 5] = 109; + + /* test unchanged buffer */ + dlen = xbzrle_encode_buffer(test, test, PAGE_SIZE, compressed, + PAGE_SIZE); + g_assert(dlen == 0); + + g_free(test); + g_free(compressed); +} + +static void test_encode_decode_1_byte(void) +{ + uint8_t *buffer = g_malloc0(PAGE_SIZE); + uint8_t *test = g_malloc0(PAGE_SIZE); + uint8_t *compressed = g_malloc(PAGE_SIZE); + int dlen = 0, rc = 0; + uint8_t buf[2]; + + test[PAGE_SIZE - 1] = 1; + + dlen = xbzrle_encode_buffer(buffer, test, PAGE_SIZE, compressed, + PAGE_SIZE); + g_assert(dlen == (uleb128_encode_small(&buf[0], 4095) + 2)); + + rc = xbzrle_decode_buffer(compressed, dlen, buffer, PAGE_SIZE); + g_assert(rc == PAGE_SIZE); + g_assert(memcmp(test, buffer, PAGE_SIZE) == 0); + + g_free(buffer); + g_free(compressed); + g_free(test); +} + +static void test_encode_decode_overflow(void) +{ + uint8_t *compressed = g_malloc0(PAGE_SIZE); + uint8_t *test = g_malloc0(PAGE_SIZE); + uint8_t *buffer = g_malloc0(PAGE_SIZE); + int i = 0, rc = 0; + + for (i = 0; i < PAGE_SIZE / 2 - 1; i++) { + test[i * 2] = 1; + } + + /* encode overflow */ + rc = xbzrle_encode_buffer(buffer, test, PAGE_SIZE, compressed, + PAGE_SIZE); + g_assert(rc == -1); + + g_free(buffer); + g_free(compressed); + g_free(test); +} + +static void encode_decode_range(void) +{ + uint8_t *buffer = g_malloc0(PAGE_SIZE); + uint8_t *compressed = g_malloc(PAGE_SIZE); + uint8_t *test = g_malloc0(PAGE_SIZE); + int i = 0, rc = 0; + int dlen = 0; + + int diff_len = g_test_rand_int_range(0, PAGE_SIZE - 1006); + + for (i = diff_len; i > 0; i--) { + buffer[1000 + i] = i; + test[1000 + i] = i + 4; + } + + buffer[1000 + diff_len + 3] = 103; + test[1000 + diff_len + 3] = 107; + + buffer[1000 + diff_len + 5] = 105; + test[1000 + diff_len + 5] = 109; + + /* test encode/decode */ + dlen = xbzrle_encode_buffer(test, buffer, PAGE_SIZE, compressed, + PAGE_SIZE); + + rc = xbzrle_decode_buffer(compressed, dlen, test, PAGE_SIZE); + g_assert(rc < PAGE_SIZE); + g_assert(memcmp(test, buffer, PAGE_SIZE) == 0); + + g_free(buffer); + g_free(compressed); + g_free(test); +} + +static void test_encode_decode(void) +{ + int i; + + for (i = 0; i < 10000; i++) { + encode_decode_range(); + } +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_rand_int(); + g_test_add_func("/xbzrle/uleb", test_uleb); + g_test_add_func("/xbzrle/encode_decode_zero", test_encode_decode_zero); + g_test_add_func("/xbzrle/encode_decode_unchanged", + test_encode_decode_unchanged); + g_test_add_func("/xbzrle/encode_decode_1_byte", test_encode_decode_1_byte); + g_test_add_func("/xbzrle/encode_decode_overflow", + test_encode_decode_overflow); + g_test_add_func("/xbzrle/encode_decode", test_encode_decode); + + return g_test_run(); +} diff --git a/tests/tmp105-test.c b/tests/tmp105-test.c new file mode 100644 index 0000000000..a6ad213de8 --- /dev/null +++ b/tests/tmp105-test.c @@ -0,0 +1,76 @@ +/* + * QTest testcase for the TMP105 temperature sensor + * + * Copyright (c) 2012 Andreas Färber + * + * 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 "libqtest.h" +#include "libi2c.h" +#include "hw/tmp105_regs.h" + +#include + +#define OMAP2_I2C_1_BASE 0x48070000 + +#define N8X0_ADDR 0x48 + +static I2CAdapter *i2c; +static uint8_t addr; + +static void send_and_receive(void) +{ + uint8_t cmd[3]; + uint8_t resp[2]; + + cmd[0] = TMP105_REG_TEMPERATURE; + i2c_send(i2c, addr, cmd, 1); + i2c_recv(i2c, addr, resp, 2); + g_assert_cmpuint(((uint16_t)resp[0] << 8) | resp[1], ==, 0); + + cmd[0] = TMP105_REG_CONFIG; + cmd[1] = 0x0; /* matches the reset value */ + i2c_send(i2c, addr, cmd, 2); + i2c_recv(i2c, addr, resp, 1); + g_assert_cmphex(resp[0], ==, cmd[1]); + + cmd[0] = TMP105_REG_T_LOW; + cmd[1] = 0x12; + cmd[2] = 0x34; + i2c_send(i2c, addr, cmd, 3); + i2c_recv(i2c, addr, resp, 2); + g_assert_cmphex(resp[0], ==, cmd[1]); + g_assert_cmphex(resp[1], ==, cmd[2]); + + cmd[0] = TMP105_REG_T_HIGH; + cmd[1] = 0x42; + cmd[2] = 0x31; + i2c_send(i2c, addr, cmd, 3); + i2c_recv(i2c, addr, resp, 2); + g_assert_cmphex(resp[0], ==, cmd[1]); + g_assert_cmphex(resp[1], ==, cmd[2]); +} + +int main(int argc, char **argv) +{ + QTestState *s = NULL; + int ret; + + g_test_init(&argc, &argv, NULL); + + s = qtest_start("-display none -machine n800"); + i2c = omap_i2c_create(OMAP2_I2C_1_BASE); + addr = N8X0_ADDR; + + qtest_add_func("/tmp105/tx-rx", send_and_receive); + + ret = g_test_run(); + + if (s) { + qtest_quit(s); + } + g_free(i2c); + + return ret; +} diff --git a/thread-pool.c b/thread-pool.c index e3ca64d790..0ebd4c2964 100644 --- a/thread-pool.c +++ b/thread-pool.c @@ -24,7 +24,7 @@ #include "qemu/event_notifier.h" #include "block/thread-pool.h" -static void do_spawn_thread(void); +static void do_spawn_thread(ThreadPool *pool); typedef struct ThreadPoolElement ThreadPoolElement; @@ -37,6 +37,7 @@ enum ThreadState { struct ThreadPoolElement { BlockDriverAIOCB common; + ThreadPool *pool; ThreadPoolFunc *func; void *arg; @@ -54,49 +55,56 @@ struct ThreadPoolElement { QLIST_ENTRY(ThreadPoolElement) all; }; -static EventNotifier notifier; -static QemuMutex lock; -static QemuCond check_cancel; -static QemuSemaphore sem; -static int max_threads = 64; -static QEMUBH *new_thread_bh; +struct ThreadPool { + EventNotifier notifier; + AioContext *ctx; + QemuMutex lock; + QemuCond check_cancel; + QemuCond worker_stopped; + QemuSemaphore sem; + int max_threads; + QEMUBH *new_thread_bh; -/* The following variables are protected by the global mutex. */ -static QLIST_HEAD(, ThreadPoolElement) head; + /* The following variables are only accessed from one AioContext. */ + QLIST_HEAD(, ThreadPoolElement) head; -/* The following variables are protected by lock. */ -static QTAILQ_HEAD(, ThreadPoolElement) request_list; -static int cur_threads; -static int idle_threads; -static int new_threads; /* backlog of threads we need to create */ -static int pending_threads; /* threads created but not running yet */ -static int pending_cancellations; /* whether we need a cond_broadcast */ + /* The following variables are protected by lock. */ + QTAILQ_HEAD(, ThreadPoolElement) request_list; + int cur_threads; + int idle_threads; + int new_threads; /* backlog of threads we need to create */ + int pending_threads; /* threads created but not running yet */ + int pending_cancellations; /* whether we need a cond_broadcast */ + bool stopping; +}; -static void *worker_thread(void *unused) +static void *worker_thread(void *opaque) { - qemu_mutex_lock(&lock); - pending_threads--; - do_spawn_thread(); + ThreadPool *pool = opaque; - while (1) { + qemu_mutex_lock(&pool->lock); + pool->pending_threads--; + do_spawn_thread(pool); + + while (!pool->stopping) { ThreadPoolElement *req; int ret; do { - idle_threads++; - qemu_mutex_unlock(&lock); - ret = qemu_sem_timedwait(&sem, 10000); - qemu_mutex_lock(&lock); - idle_threads--; - } while (ret == -1 && !QTAILQ_EMPTY(&request_list)); - if (ret == -1) { + pool->idle_threads++; + qemu_mutex_unlock(&pool->lock); + ret = qemu_sem_timedwait(&pool->sem, 10000); + qemu_mutex_lock(&pool->lock); + pool->idle_threads--; + } while (ret == -1 && !QTAILQ_EMPTY(&pool->request_list)); + if (ret == -1 || pool->stopping) { break; } - req = QTAILQ_FIRST(&request_list); - QTAILQ_REMOVE(&request_list, req, reqs); + req = QTAILQ_FIRST(&pool->request_list); + QTAILQ_REMOVE(&pool->request_list, req, reqs); req->state = THREAD_ACTIVE; - qemu_mutex_unlock(&lock); + qemu_mutex_unlock(&pool->lock); ret = req->func(req->arg); @@ -105,45 +113,48 @@ static void *worker_thread(void *unused) smp_wmb(); req->state = THREAD_DONE; - qemu_mutex_lock(&lock); - if (pending_cancellations) { - qemu_cond_broadcast(&check_cancel); + qemu_mutex_lock(&pool->lock); + if (pool->pending_cancellations) { + qemu_cond_broadcast(&pool->check_cancel); } - event_notifier_set(¬ifier); + event_notifier_set(&pool->notifier); } - cur_threads--; - qemu_mutex_unlock(&lock); + pool->cur_threads--; + qemu_cond_signal(&pool->worker_stopped); + qemu_mutex_unlock(&pool->lock); return NULL; } -static void do_spawn_thread(void) +static void do_spawn_thread(ThreadPool *pool) { QemuThread t; /* Runs with lock taken. */ - if (!new_threads) { + if (!pool->new_threads) { return; } - new_threads--; - pending_threads++; + pool->new_threads--; + pool->pending_threads++; - qemu_thread_create(&t, worker_thread, NULL, QEMU_THREAD_DETACHED); + qemu_thread_create(&t, worker_thread, pool, QEMU_THREAD_DETACHED); } static void spawn_thread_bh_fn(void *opaque) { - qemu_mutex_lock(&lock); - do_spawn_thread(); - qemu_mutex_unlock(&lock); + ThreadPool *pool = opaque; + + qemu_mutex_lock(&pool->lock); + do_spawn_thread(pool); + qemu_mutex_unlock(&pool->lock); } -static void spawn_thread(void) +static void spawn_thread(ThreadPool *pool) { - cur_threads++; - new_threads++; + pool->cur_threads++; + pool->new_threads++; /* If there are threads being created, they will spawn new workers, so * we don't spend time creating many threads in a loop holding a mutex or * starving the current vcpu. @@ -151,23 +162,25 @@ static void spawn_thread(void) * If there are no idle threads, ask the main thread to create one, so we * inherit the correct affinity instead of the vcpu affinity. */ - if (!pending_threads) { - qemu_bh_schedule(new_thread_bh); + if (!pool->pending_threads) { + qemu_bh_schedule(pool->new_thread_bh); } } static void event_notifier_ready(EventNotifier *notifier) { + ThreadPool *pool = container_of(notifier, ThreadPool, notifier); ThreadPoolElement *elem, *next; event_notifier_test_and_clear(notifier); restart: - QLIST_FOREACH_SAFE(elem, &head, all, next) { + QLIST_FOREACH_SAFE(elem, &pool->head, all, next) { if (elem->state != THREAD_CANCELED && elem->state != THREAD_DONE) { continue; } if (elem->state == THREAD_DONE) { - trace_thread_pool_complete(elem, elem->common.opaque, elem->ret); + trace_thread_pool_complete(pool, elem, elem->common.opaque, + elem->ret); } if (elem->state == THREAD_DONE && elem->common.cb) { QLIST_REMOVE(elem, all); @@ -186,34 +199,36 @@ restart: static int thread_pool_active(EventNotifier *notifier) { - return !QLIST_EMPTY(&head); + ThreadPool *pool = container_of(notifier, ThreadPool, notifier); + return !QLIST_EMPTY(&pool->head); } static void thread_pool_cancel(BlockDriverAIOCB *acb) { ThreadPoolElement *elem = (ThreadPoolElement *)acb; + ThreadPool *pool = elem->pool; trace_thread_pool_cancel(elem, elem->common.opaque); - qemu_mutex_lock(&lock); + qemu_mutex_lock(&pool->lock); if (elem->state == THREAD_QUEUED && /* No thread has yet started working on elem. we can try to "steal" * the item from the worker if we can get a signal from the * semaphore. Because this is non-blocking, we can do it with * the lock taken and ensure that elem will remain THREAD_QUEUED. */ - qemu_sem_timedwait(&sem, 0) == 0) { - QTAILQ_REMOVE(&request_list, elem, reqs); + qemu_sem_timedwait(&pool->sem, 0) == 0) { + QTAILQ_REMOVE(&pool->request_list, elem, reqs); elem->state = THREAD_CANCELED; - event_notifier_set(¬ifier); + event_notifier_set(&pool->notifier); } else { - pending_cancellations++; + pool->pending_cancellations++; while (elem->state != THREAD_CANCELED && elem->state != THREAD_DONE) { - qemu_cond_wait(&check_cancel, &lock); + qemu_cond_wait(&pool->check_cancel, &pool->lock); } - pending_cancellations--; + pool->pending_cancellations--; } - qemu_mutex_unlock(&lock); + qemu_mutex_unlock(&pool->lock); } static const AIOCBInfo thread_pool_aiocb_info = { @@ -221,7 +236,8 @@ static const AIOCBInfo thread_pool_aiocb_info = { .cancel = thread_pool_cancel, }; -BlockDriverAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, +BlockDriverAIOCB *thread_pool_submit_aio(ThreadPool *pool, + ThreadPoolFunc *func, void *arg, BlockDriverCompletionFunc *cb, void *opaque) { ThreadPoolElement *req; @@ -230,18 +246,19 @@ BlockDriverAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, req->func = func; req->arg = arg; req->state = THREAD_QUEUED; + req->pool = pool; - QLIST_INSERT_HEAD(&head, req, all); + QLIST_INSERT_HEAD(&pool->head, req, all); - trace_thread_pool_submit(req, arg); + trace_thread_pool_submit(pool, req, arg); - qemu_mutex_lock(&lock); - if (idle_threads == 0 && cur_threads < max_threads) { - spawn_thread(); + qemu_mutex_lock(&pool->lock); + if (pool->idle_threads == 0 && pool->cur_threads < pool->max_threads) { + spawn_thread(pool); } - QTAILQ_INSERT_TAIL(&request_list, req, reqs); - qemu_mutex_unlock(&lock); - qemu_sem_post(&sem); + QTAILQ_INSERT_TAIL(&pool->request_list, req, reqs); + qemu_mutex_unlock(&pool->lock); + qemu_sem_post(&pool->sem); return &req->common; } @@ -258,32 +275,80 @@ static void thread_pool_co_cb(void *opaque, int ret) qemu_coroutine_enter(co->co, NULL); } -int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg) +int coroutine_fn thread_pool_submit_co(ThreadPool *pool, ThreadPoolFunc *func, + void *arg) { ThreadPoolCo tpc = { .co = qemu_coroutine_self(), .ret = -EINPROGRESS }; assert(qemu_in_coroutine()); - thread_pool_submit_aio(func, arg, thread_pool_co_cb, &tpc); + thread_pool_submit_aio(pool, func, arg, thread_pool_co_cb, &tpc); qemu_coroutine_yield(); return tpc.ret; } -void thread_pool_submit(ThreadPoolFunc *func, void *arg) +void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg) { - thread_pool_submit_aio(func, arg, NULL, NULL); + thread_pool_submit_aio(pool, func, arg, NULL, NULL); } -static void thread_pool_init(void) +static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx) { - QLIST_INIT(&head); - event_notifier_init(¬ifier, false); - qemu_mutex_init(&lock); - qemu_cond_init(&check_cancel); - qemu_sem_init(&sem, 0); - qemu_aio_set_event_notifier(¬ifier, event_notifier_ready, - thread_pool_active); + if (!ctx) { + ctx = qemu_get_aio_context(); + } - QTAILQ_INIT(&request_list); - new_thread_bh = qemu_bh_new(spawn_thread_bh_fn, NULL); + memset(pool, 0, sizeof(*pool)); + event_notifier_init(&pool->notifier, false); + pool->ctx = ctx; + qemu_mutex_init(&pool->lock); + qemu_cond_init(&pool->check_cancel); + qemu_cond_init(&pool->worker_stopped); + qemu_sem_init(&pool->sem, 0); + pool->max_threads = 64; + pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool); + + QLIST_INIT(&pool->head); + QTAILQ_INIT(&pool->request_list); + + aio_set_event_notifier(ctx, &pool->notifier, event_notifier_ready, + thread_pool_active); } -block_init(thread_pool_init) +ThreadPool *thread_pool_new(AioContext *ctx) +{ + ThreadPool *pool = g_new(ThreadPool, 1); + thread_pool_init_one(pool, ctx); + return pool; +} + +void thread_pool_free(ThreadPool *pool) +{ + if (!pool) { + return; + } + + assert(QLIST_EMPTY(&pool->head)); + + qemu_mutex_lock(&pool->lock); + + /* Stop new threads from spawning */ + qemu_bh_delete(pool->new_thread_bh); + pool->cur_threads -= pool->new_threads; + pool->new_threads = 0; + + /* Wait for worker threads to terminate */ + pool->stopping = true; + while (pool->cur_threads > 0) { + qemu_sem_post(&pool->sem); + qemu_cond_wait(&pool->worker_stopped, &pool->lock); + } + + qemu_mutex_unlock(&pool->lock); + + aio_set_event_notifier(pool->ctx, &pool->notifier, NULL, NULL); + qemu_sem_destroy(&pool->sem); + qemu_cond_destroy(&pool->check_cancel); + qemu_cond_destroy(&pool->worker_stopped); + qemu_mutex_destroy(&pool->lock); + event_notifier_cleanup(&pool->notifier); + g_free(pool); +} diff --git a/tpm/Makefile.objs b/tpm/Makefile.objs new file mode 100644 index 0000000000..86768244e4 --- /dev/null +++ b/tpm/Makefile.objs @@ -0,0 +1,6 @@ +common-obj-y = tpm.o +ifeq ($(CONFIG_TPM),y) +common-obj-y += tpm_backend.o +common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o +common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o +endif diff --git a/tpm/tpm.c b/tpm/tpm.c new file mode 100644 index 0000000000..ffd24956d7 --- /dev/null +++ b/tpm/tpm.c @@ -0,0 +1,357 @@ +/* + * TPM configuration + * + * Copyright (C) 2011-2013 IBM Corporation + * + * Authors: + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Based on net.c + */ +#include "config-host.h" + +#include "monitor/monitor.h" +#include "qapi/qmp/qerror.h" +#include "tpm_int.h" +#include "tpm/tpm.h" +#include "qemu/config-file.h" +#include "qmp-commands.h" + +static QLIST_HEAD(, TPMBackend) tpm_backends = + QLIST_HEAD_INITIALIZER(tpm_backends); + + +#define TPM_MAX_MODELS 1 +#define TPM_MAX_DRIVERS 1 + +static TPMDriverOps const *be_drivers[TPM_MAX_DRIVERS] = { + NULL, +}; + +static enum TpmModel tpm_models[TPM_MAX_MODELS] = { + -1, +}; + +int tpm_register_model(enum TpmModel model) +{ + int i; + + for (i = 0; i < TPM_MAX_MODELS; i++) { + if (tpm_models[i] == -1) { + tpm_models[i] = model; + return 0; + } + } + error_report("Could not register TPM model"); + return 1; +} + +static bool tpm_model_is_registered(enum TpmModel model) +{ + int i; + + for (i = 0; i < TPM_MAX_MODELS; i++) { + if (tpm_models[i] == model) { + return true; + } + } + return false; +} + +/* + * Write an error message in the given output buffer. + */ +void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len) +{ + if (out_len >= sizeof(struct tpm_resp_hdr)) { + struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out; + + resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND); + resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr)); + resp->errcode = cpu_to_be32(TPM_FAIL); + } +} + +const TPMDriverOps *tpm_get_backend_driver(const char *type) +{ + int i; + + for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) { + if (!strcmp(TpmType_lookup[be_drivers[i]->type], type)) { + return be_drivers[i]; + } + } + + return NULL; +} + +#ifdef CONFIG_TPM + +int tpm_register_driver(const TPMDriverOps *tdo) +{ + int i; + + for (i = 0; i < TPM_MAX_DRIVERS; i++) { + if (!be_drivers[i]) { + be_drivers[i] = tdo; + return 0; + } + } + error_report("Could not register TPM driver"); + return 1; +} + +/* + * Walk the list of available TPM backend drivers and display them on the + * screen. + */ +void tpm_display_backend_drivers(void) +{ + int i; + + fprintf(stderr, "Supported TPM types (choose only one):\n"); + + for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) { + fprintf(stderr, "%12s %s\n", + TpmType_lookup[be_drivers[i]->type], be_drivers[i]->desc()); + } + fprintf(stderr, "\n"); +} + +/* + * Find the TPM with the given Id + */ +TPMBackend *qemu_find_tpm(const char *id) +{ + TPMBackend *drv; + + if (id) { + QLIST_FOREACH(drv, &tpm_backends, list) { + if (!strcmp(drv->id, id)) { + return drv; + } + } + } + + return NULL; +} + +static int configure_tpm(QemuOpts *opts) +{ + const char *value; + const char *id; + const TPMDriverOps *be; + TPMBackend *drv; + + if (!QLIST_EMPTY(&tpm_backends)) { + error_report("Only one TPM is allowed.\n"); + return 1; + } + + id = qemu_opts_id(opts); + if (id == NULL) { + qerror_report(QERR_MISSING_PARAMETER, "id"); + return 1; + } + + value = qemu_opt_get(opts, "type"); + if (!value) { + qerror_report(QERR_MISSING_PARAMETER, "type"); + tpm_display_backend_drivers(); + return 1; + } + + be = tpm_get_backend_driver(value); + if (be == NULL) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "type", + "a TPM backend type"); + tpm_display_backend_drivers(); + return 1; + } + + drv = be->create(opts, id); + if (!drv) { + return 1; + } + + QLIST_INSERT_HEAD(&tpm_backends, drv, list); + + return 0; +} + +static int tpm_init_tpmdev(QemuOpts *opts, void *dummy) +{ + return configure_tpm(opts); +} + +/* + * Walk the list of TPM backend drivers that are in use and call their + * destroy function to have them cleaned up. + */ +void tpm_cleanup(void) +{ + TPMBackend *drv, *next; + + QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) { + QLIST_REMOVE(drv, list); + drv->ops->destroy(drv); + } +} + +/* + * Initialize the TPM. Process the tpmdev command line options describing the + * TPM backend. + */ +int tpm_init(void) +{ + if (qemu_opts_foreach(qemu_find_opts("tpmdev"), + tpm_init_tpmdev, NULL, 1) != 0) { + return -1; + } + + atexit(tpm_cleanup); + + return 0; +} + +/* + * Parse the TPM configuration options. + * To display all available TPM backends the user may use '-tpmdev help' + */ +int tpm_config_parse(QemuOptsList *opts_list, const char *optarg) +{ + QemuOpts *opts; + + if (!strcmp(optarg, "help")) { + tpm_display_backend_drivers(); + return -1; + } + opts = qemu_opts_parse(opts_list, optarg, 1); + if (!opts) { + return -1; + } + return 0; +} + +#endif /* CONFIG_TPM */ + +static const TPMDriverOps *tpm_driver_find_by_type(enum TpmType type) +{ + int i; + + for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) { + if (be_drivers[i]->type == type) { + return be_drivers[i]; + } + } + return NULL; +} + +static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv) +{ + TPMInfo *res = g_new0(TPMInfo, 1); + TPMPassthroughOptions *tpo; + + res->id = g_strdup(drv->id); + res->model = drv->fe_model; + res->type = drv->ops->type; + res->tpm_options = g_new0(TpmTypeOptions, 1); + + switch (res->type) { + case TPM_TYPE_PASSTHROUGH: + res->tpm_options->kind = TPM_TYPE_OPTIONS_KIND_TPM_PASSTHROUGH_OPTIONS; + tpo = g_new0(TPMPassthroughOptions, 1); + res->tpm_options->tpm_passthrough_options = tpo; + if (drv->path) { + tpo->path = g_strdup(drv->path); + tpo->has_path = true; + } + if (drv->cancel_path) { + tpo->cancel_path = g_strdup(drv->cancel_path); + tpo->has_cancel_path = true; + } + break; + case TPM_TYPE_MAX: + break; + } + + return res; +} + +/* + * Walk the list of active TPM backends and collect information about them + * following the schema description in qapi-schema.json. + */ +TPMInfoList *qmp_query_tpm(Error **errp) +{ + TPMBackend *drv; + TPMInfoList *info, *head = NULL, *cur_item = NULL; + + QLIST_FOREACH(drv, &tpm_backends, list) { + if (!tpm_model_is_registered(drv->fe_model)) { + continue; + } + info = g_new0(TPMInfoList, 1); + info->value = qmp_query_tpm_inst(drv); + + if (!cur_item) { + head = cur_item = info; + } else { + cur_item->next = info; + cur_item = info; + } + } + + return head; +} + +TpmTypeList *qmp_query_tpm_types(Error **errp) +{ + unsigned int i = 0; + TpmTypeList *head = NULL, *prev = NULL, *cur_item; + + for (i = 0; i < TPM_TYPE_MAX; i++) { + if (!tpm_driver_find_by_type(i)) { + continue; + } + cur_item = g_new0(TpmTypeList, 1); + cur_item->value = i; + + if (prev) { + prev->next = cur_item; + } + if (!head) { + head = cur_item; + } + prev = cur_item; + } + + return head; +} + +TpmModelList *qmp_query_tpm_models(Error **errp) +{ + unsigned int i = 0; + TpmModelList *head = NULL, *prev = NULL, *cur_item; + + for (i = 0; i < TPM_MODEL_MAX; i++) { + if (!tpm_model_is_registered(i)) { + continue; + } + cur_item = g_new0(TpmModelList, 1); + cur_item->value = i; + + if (prev) { + prev->next = cur_item; + } + if (!head) { + head = cur_item; + } + prev = cur_item; + } + + return head; +} diff --git a/tpm/tpm_backend.c b/tpm/tpm_backend.c new file mode 100644 index 0000000000..4144ef7d76 --- /dev/null +++ b/tpm/tpm_backend.c @@ -0,0 +1,58 @@ +/* + * common TPM backend driver functions + * + * Copyright (c) 2012-2013 IBM Corporation + * Authors: + * Stefan Berger + * + * 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 + */ + +#include "tpm/tpm.h" +#include "qemu/thread.h" +#include "tpm_backend.h" + +void tpm_backend_thread_deliver_request(TPMBackendThread *tbt) +{ + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NULL); +} + +void tpm_backend_thread_create(TPMBackendThread *tbt, + GFunc func, gpointer user_data) +{ + if (!tbt->pool) { + tbt->pool = g_thread_pool_new(func, user_data, 1, TRUE, NULL); + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL); + } +} + +void tpm_backend_thread_end(TPMBackendThread *tbt) +{ + if (tbt->pool) { + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_END, NULL); + g_thread_pool_free(tbt->pool, FALSE, TRUE); + tbt->pool = NULL; + } +} + +void tpm_backend_thread_tpm_reset(TPMBackendThread *tbt, + GFunc func, gpointer user_data) +{ + if (!tbt->pool) { + tpm_backend_thread_create(tbt, func, user_data); + } else { + g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_TPM_RESET, + NULL); + } +} diff --git a/tpm/tpm_backend.h b/tpm/tpm_backend.h new file mode 100644 index 0000000000..05d94d0f5b --- /dev/null +++ b/tpm/tpm_backend.h @@ -0,0 +1,45 @@ +/* + * common TPM backend driver functions + * + * Copyright (c) 2012-2013 IBM Corporation + * Authors: + * Stefan Berger + * + * 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 + */ + +#ifndef TPM_TPM_BACKEND_H +#define TPM_TPM_BACKEND_H + +#include + +typedef struct TPMBackendThread { + GThreadPool *pool; +} TPMBackendThread; + +void tpm_backend_thread_deliver_request(TPMBackendThread *tbt); +void tpm_backend_thread_create(TPMBackendThread *tbt, + GFunc func, gpointer user_data); +void tpm_backend_thread_end(TPMBackendThread *tbt); +void tpm_backend_thread_tpm_reset(TPMBackendThread *tbt, + GFunc func, gpointer user_data); + +typedef enum TPMBackendCmd { + TPM_BACKEND_CMD_INIT = 1, + TPM_BACKEND_CMD_PROCESS_CMD, + TPM_BACKEND_CMD_END, + TPM_BACKEND_CMD_TPM_RESET, +} TPMBackendCmd; + +#endif /* TPM_TPM_BACKEND_H */ diff --git a/tpm/tpm_int.h b/tpm/tpm_int.h new file mode 100644 index 0000000000..f7056436cc --- /dev/null +++ b/tpm/tpm_int.h @@ -0,0 +1,116 @@ +/* + * TPM configuration + * + * Copyright (C) 2011-2013 IBM Corporation + * + * Authors: + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef TPM_TPM_INT_H +#define TPM_TPM_INT_H + +#include "exec/memory.h" +#include "tpm/tpm_tis.h" + +struct TPMDriverOps; +typedef struct TPMDriverOps TPMDriverOps; + +typedef struct TPMPassthruState TPMPassthruState; + +typedef struct TPMBackend { + char *id; + enum TpmModel fe_model; + char *path; + char *cancel_path; + const TPMDriverOps *ops; + + union { + TPMPassthruState *tpm_pt; + } s; + + QLIST_ENTRY(TPMBackend) list; +} TPMBackend; + +/* overall state of the TPM interface */ +typedef struct TPMState { + ISADevice busdev; + MemoryRegion mmio; + + union { + TPMTISEmuState tis; + } s; + + uint8_t locty_number; + TPMLocality *locty_data; + + char *backend; + TPMBackend *be_driver; +} TPMState; + +#define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS) + +typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty); + +struct TPMDriverOps { + enum TpmType type; + /* get a descriptive text of the backend to display to the user */ + const char *(*desc)(void); + + TPMBackend *(*create)(QemuOpts *opts, const char *id); + void (*destroy)(TPMBackend *t); + + /* initialize the backend */ + int (*init)(TPMBackend *t, TPMState *s, TPMRecvDataCB *datacb); + /* start up the TPM on the backend */ + int (*startup_tpm)(TPMBackend *t); + /* returns true if nothing will ever answer TPM requests */ + bool (*had_startup_error)(TPMBackend *t); + + size_t (*realloc_buffer)(TPMSizedBuffer *sb); + + void (*deliver_request)(TPMBackend *t); + + void (*reset)(TPMBackend *t); + + void (*cancel_cmd)(TPMBackend *t); + + bool (*get_tpm_established_flag)(TPMBackend *t); +}; + +struct tpm_req_hdr { + uint16_t tag; + uint32_t len; + uint32_t ordinal; +} QEMU_PACKED; + +struct tpm_resp_hdr { + uint16_t tag; + uint32_t len; + uint32_t errcode; +} QEMU_PACKED; + +#define TPM_TAG_RQU_COMMAND 0xc1 +#define TPM_TAG_RQU_AUTH1_COMMAND 0xc2 +#define TPM_TAG_RQU_AUTH2_COMMAND 0xc3 + +#define TPM_TAG_RSP_COMMAND 0xc4 +#define TPM_TAG_RSP_AUTH1_COMMAND 0xc5 +#define TPM_TAG_RSP_AUTH2_COMMAND 0xc6 + +#define TPM_FAIL 9 + +#define TPM_ORD_GetTicks 0xf1 + +TPMBackend *qemu_find_tpm(const char *id); +int tpm_register_model(enum TpmModel model); +int tpm_register_driver(const TPMDriverOps *tdo); +void tpm_display_backend_drivers(void); +const TPMDriverOps *tpm_get_backend_driver(const char *type); +void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len); + +extern const TPMDriverOps tpm_passthrough_driver; + +#endif /* TPM_TPM_INT_H */ diff --git a/tpm/tpm_passthrough.c b/tpm/tpm_passthrough.c new file mode 100644 index 0000000000..24aff4d69c --- /dev/null +++ b/tpm/tpm_passthrough.c @@ -0,0 +1,530 @@ +/* + * passthrough TPM driver + * + * Copyright (c) 2010 - 2013 IBM Corporation + * Authors: + * Stefan Berger + * + * Copyright (C) 2011 IAIK, Graz University of Technology + * Author: Andreas Niederl + * + * 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 + */ + +#include + +#include "qemu-common.h" +#include "qapi/error.h" +#include "qemu/sockets.h" +#include "tpm_int.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "tpm_tis.h" +#include "tpm_backend.h" + +/* #define DEBUG_TPM */ + +#ifdef DEBUG_TPM +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +/* data structures */ + +typedef struct TPMPassthruThreadParams { + TPMState *tpm_state; + + TPMRecvDataCB *recv_data_callback; + TPMBackend *tb; +} TPMPassthruThreadParams; + +struct TPMPassthruState { + TPMBackendThread tbt; + + TPMPassthruThreadParams tpm_thread_params; + + char *tpm_dev; + int tpm_fd; + bool tpm_executing; + bool tpm_op_canceled; + int cancel_fd; + bool had_startup_error; +}; + +#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0" + +/* functions */ + +static void tpm_passthrough_cancel_cmd(TPMBackend *tb); + +static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len) +{ + return send_all(fd, buf, len); +} + +static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) +{ + return recv_all(fd, buf, len, true); +} + +static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf) +{ + struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf; + + return be32_to_cpu(resp->len); +} + +static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, + const uint8_t *in, uint32_t in_len, + uint8_t *out, uint32_t out_len) +{ + int ret; + + tpm_pt->tpm_op_canceled = false; + tpm_pt->tpm_executing = true; + + ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len); + if (ret != in_len) { + if (!tpm_pt->tpm_op_canceled || + (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { + error_report("tpm_passthrough: error while transmitting data " + "to TPM: %s (%i)\n", + strerror(errno), errno); + } + goto err_exit; + } + + tpm_pt->tpm_executing = false; + + ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len); + if (ret < 0) { + if (!tpm_pt->tpm_op_canceled || + (tpm_pt->tpm_op_canceled && errno != ECANCELED)) { + error_report("tpm_passthrough: error while reading data from " + "TPM: %s (%i)\n", + strerror(errno), errno); + } + } else if (ret < sizeof(struct tpm_resp_hdr) || + tpm_passthrough_get_size_from_buffer(out) != ret) { + ret = -1; + error_report("tpm_passthrough: received invalid response " + "packet from TPM\n"); + } + +err_exit: + if (ret < 0) { + tpm_write_fatal_error_response(out, out_len); + } + + tpm_pt->tpm_executing = false; + + return ret; +} + +static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt, + const TPMLocality *locty_data) +{ + return tpm_passthrough_unix_tx_bufs(tpm_pt, + locty_data->w_buffer.buffer, + locty_data->w_offset, + locty_data->r_buffer.buffer, + locty_data->r_buffer.size); +} + +static void tpm_passthrough_worker_thread(gpointer data, + gpointer user_data) +{ + TPMPassthruThreadParams *thr_parms = user_data; + TPMPassthruState *tpm_pt = thr_parms->tb->s.tpm_pt; + TPMBackendCmd cmd = (TPMBackendCmd)data; + + DPRINTF("tpm_passthrough: processing command type %d\n", cmd); + + switch (cmd) { + case TPM_BACKEND_CMD_PROCESS_CMD: + tpm_passthrough_unix_transfer(tpm_pt, + thr_parms->tpm_state->locty_data); + + thr_parms->recv_data_callback(thr_parms->tpm_state, + thr_parms->tpm_state->locty_number); + break; + case TPM_BACKEND_CMD_INIT: + case TPM_BACKEND_CMD_END: + case TPM_BACKEND_CMD_TPM_RESET: + /* nothing to do */ + break; + } +} + +/* + * Start the TPM (thread). If it had been started before, then terminate + * and start it again. + */ +static int tpm_passthrough_startup_tpm(TPMBackend *tb) +{ + TPMPassthruState *tpm_pt = tb->s.tpm_pt; + + /* terminate a running TPM */ + tpm_backend_thread_end(&tpm_pt->tbt); + + tpm_backend_thread_create(&tpm_pt->tbt, + tpm_passthrough_worker_thread, + &tb->s.tpm_pt->tpm_thread_params); + + return 0; +} + +static void tpm_passthrough_reset(TPMBackend *tb) +{ + TPMPassthruState *tpm_pt = tb->s.tpm_pt; + + DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n"); + + tpm_passthrough_cancel_cmd(tb); + + tpm_backend_thread_end(&tpm_pt->tbt); + + tpm_pt->had_startup_error = false; +} + +static int tpm_passthrough_init(TPMBackend *tb, TPMState *s, + TPMRecvDataCB *recv_data_cb) +{ + TPMPassthruState *tpm_pt = tb->s.tpm_pt; + + tpm_pt->tpm_thread_params.tpm_state = s; + tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb; + tpm_pt->tpm_thread_params.tb = tb; + + return 0; +} + +static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb) +{ + return false; +} + +static bool tpm_passthrough_get_startup_error(TPMBackend *tb) +{ + TPMPassthruState *tpm_pt = tb->s.tpm_pt; + + return tpm_pt->had_startup_error; +} + +static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb) +{ + size_t wanted_size = 4096; /* Linux tpm.c buffer size */ + + if (sb->size != wanted_size) { + sb->buffer = g_realloc(sb->buffer, wanted_size); + sb->size = wanted_size; + } + return sb->size; +} + +static void tpm_passthrough_deliver_request(TPMBackend *tb) +{ + TPMPassthruState *tpm_pt = tb->s.tpm_pt; + + tpm_backend_thread_deliver_request(&tpm_pt->tbt); +} + +static void tpm_passthrough_cancel_cmd(TPMBackend *tb) +{ + TPMPassthruState *tpm_pt = tb->s.tpm_pt; + int n; + + /* + * As of Linux 3.7 the tpm_tis driver does not properly cancel + * commands on all TPM manufacturers' TPMs. + * Only cancel if we're busy so we don't cancel someone else's + * command, e.g., a command executed on the host. + */ + if (tpm_pt->tpm_executing) { + if (tpm_pt->cancel_fd >= 0) { + n = write(tpm_pt->cancel_fd, "-", 1); + if (n != 1) { + error_report("Canceling TPM command failed: %s\n", + strerror(errno)); + } else { + tpm_pt->tpm_op_canceled = true; + } + } else { + error_report("Cannot cancel TPM command due to missing " + "TPM sysfs cancel entry"); + } + } +} + +static const char *tpm_passthrough_create_desc(void) +{ + return "Passthrough TPM backend driver"; +} + +/* + * A basic test of a TPM device. We expect a well formatted response header + * (error response is fine) within one second. + */ +static int tpm_passthrough_test_tpmdev(int fd) +{ + struct tpm_req_hdr req = { + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), + .len = cpu_to_be32(sizeof(req)), + .ordinal = cpu_to_be32(TPM_ORD_GetTicks), + }; + struct tpm_resp_hdr *resp; + fd_set readfds; + int n; + struct timeval tv = { + .tv_sec = 1, + .tv_usec = 0, + }; + unsigned char buf[1024]; + + n = write(fd, &req, sizeof(req)); + if (n < 0) { + return errno; + } + if (n != sizeof(req)) { + return EFAULT; + } + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + /* wait for a second */ + n = select(fd + 1, &readfds, NULL, NULL, &tv); + if (n != 1) { + return errno; + } + + n = read(fd, &buf, sizeof(buf)); + if (n < sizeof(struct tpm_resp_hdr)) { + return EFAULT; + } + + resp = (struct tpm_resp_hdr *)buf; + /* check the header */ + if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND || + be32_to_cpu(resp->len) != n) { + return EBADMSG; + } + + return 0; +} + +/* + * Check whether the given base path, e.g., /sys/class/misc/tpm0/device, + * is the sysfs directory of a TPM. A TPM sysfs directory should be uniquely + * recognizable by the file entries 'pcrs' and 'cancel'. + * Upon success 'true' is returned and the basebath buffer has '/cancel' + * appended. + */ +static bool tpm_passthrough_check_sysfs_cancel(char *basepath, size_t bufsz) +{ + char path[PATH_MAX]; + struct stat statbuf; + + snprintf(path, sizeof(path), "%s/pcrs", basepath); + if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) { + return false; + } + + snprintf(path, sizeof(path), "%s/cancel", basepath); + if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) { + return false; + } + + strncpy(basepath, path, bufsz); + + return true; +} + +/* + * Unless path or file descriptor set has been provided by user, + * determine the sysfs cancel file following kernel documentation + * in Documentation/ABI/stable/sysfs-class-tpm. + */ +static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) +{ + int fd = -1; + unsigned int idx; + DIR *pnp_dir; + char path[PATH_MAX]; + struct dirent entry, *result; + int len; + + if (tb->cancel_path) { + fd = qemu_open(tb->cancel_path, O_WRONLY); + if (fd < 0) { + error_report("Could not open TPM cancel path : %s", + strerror(errno)); + } + return fd; + } + + snprintf(path, sizeof(path), "/sys/class/misc"); + pnp_dir = opendir(path); + if (pnp_dir != NULL) { + while (readdir_r(pnp_dir, &entry, &result) == 0 && + result != NULL) { + /* + * only allow /sys/class/misc/tpm%u type of paths + */ + if (sscanf(entry.d_name, "tpm%u%n", &idx, &len) < 1 || + len <= strlen("tpm") || + len != strlen(entry.d_name)) { + continue; + } + + snprintf(path, sizeof(path), "/sys/class/misc/%s/device", + entry.d_name); + if (!tpm_passthrough_check_sysfs_cancel(path, sizeof(path))) { + continue; + } + + fd = qemu_open(path, O_WRONLY); + break; + } + closedir(pnp_dir); + } + + if (fd >= 0) { + tb->cancel_path = g_strdup(path); + } + + return fd; +} + +static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) +{ + const char *value; + + value = qemu_opt_get(opts, "cancel-path"); + if (value) { + tb->cancel_path = g_strdup(value); + } + + value = qemu_opt_get(opts, "path"); + if (!value) { + value = TPM_PASSTHROUGH_DEFAULT_DEVICE; + } + + tb->s.tpm_pt->tpm_dev = g_strdup(value); + + tb->path = g_strdup(tb->s.tpm_pt->tpm_dev); + + tb->s.tpm_pt->tpm_fd = qemu_open(tb->s.tpm_pt->tpm_dev, O_RDWR); + if (tb->s.tpm_pt->tpm_fd < 0) { + error_report("Cannot access TPM device using '%s': %s\n", + tb->s.tpm_pt->tpm_dev, strerror(errno)); + goto err_free_parameters; + } + + if (tpm_passthrough_test_tpmdev(tb->s.tpm_pt->tpm_fd)) { + error_report("'%s' is not a TPM device.\n", + tb->s.tpm_pt->tpm_dev); + goto err_close_tpmdev; + } + + return 0; + + err_close_tpmdev: + qemu_close(tb->s.tpm_pt->tpm_fd); + tb->s.tpm_pt->tpm_fd = -1; + + err_free_parameters: + g_free(tb->path); + tb->path = NULL; + + g_free(tb->s.tpm_pt->tpm_dev); + tb->s.tpm_pt->tpm_dev = NULL; + + return 1; +} + +static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id) +{ + TPMBackend *tb; + + tb = g_new0(TPMBackend, 1); + tb->s.tpm_pt = g_new0(TPMPassthruState, 1); + tb->id = g_strdup(id); + /* let frontend set the fe_model to proper value */ + tb->fe_model = -1; + + tb->ops = &tpm_passthrough_driver; + + if (tpm_passthrough_handle_device_opts(opts, tb)) { + goto err_exit; + } + + tb->s.tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb); + if (tb->s.tpm_pt->cancel_fd < 0) { + goto err_exit; + } + + return tb; + +err_exit: + g_free(tb->id); + g_free(tb->s.tpm_pt); + g_free(tb); + + return NULL; +} + +static void tpm_passthrough_destroy(TPMBackend *tb) +{ + TPMPassthruState *tpm_pt = tb->s.tpm_pt; + + tpm_passthrough_cancel_cmd(tb); + + tpm_backend_thread_end(&tpm_pt->tbt); + + qemu_close(tpm_pt->tpm_fd); + qemu_close(tb->s.tpm_pt->cancel_fd); + + g_free(tb->id); + g_free(tb->path); + g_free(tb->cancel_path); + g_free(tb->s.tpm_pt->tpm_dev); + g_free(tb->s.tpm_pt); + g_free(tb); +} + +const TPMDriverOps tpm_passthrough_driver = { + .type = TPM_TYPE_PASSTHROUGH, + .desc = tpm_passthrough_create_desc, + .create = tpm_passthrough_create, + .destroy = tpm_passthrough_destroy, + .init = tpm_passthrough_init, + .startup_tpm = tpm_passthrough_startup_tpm, + .realloc_buffer = tpm_passthrough_realloc_buffer, + .reset = tpm_passthrough_reset, + .had_startup_error = tpm_passthrough_get_startup_error, + .deliver_request = tpm_passthrough_deliver_request, + .cancel_cmd = tpm_passthrough_cancel_cmd, + .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag, +}; + +static void tpm_passthrough_register(void) +{ + tpm_register_driver(&tpm_passthrough_driver); +} + +type_init(tpm_passthrough_register) diff --git a/tpm/tpm_tis.c b/tpm/tpm_tis.c new file mode 100644 index 0000000000..e93825eec1 --- /dev/null +++ b/tpm/tpm_tis.c @@ -0,0 +1,929 @@ +/* + * tpm_tis.c - QEMU's TPM TIS interface emulator + * + * Copyright (C) 2006,2010-2013 IBM Corporation + * + * Authors: + * Stefan Berger + * David Safford + * + * Xen 4 support: Andrease Niederl + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Implementation of the TIS interface according to specs found at + * http://www.trustedcomputinggroup.org. This implementation currently + * supports version 1.21, revision 1.0. + * In the developers menu choose the PC Client section then find the TIS + * specification. + */ + +#include "tpm_int.h" +#include "block/block.h" +#include "exec/address-spaces.h" +#include "hw/hw.h" +#include "hw/pc.h" +#include "hw/pci/pci_ids.h" +#include "tpm/tpm_tis.h" +#include "qemu-common.h" + +/*#define DEBUG_TIS */ + +#ifdef DEBUG_TIS +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +/* whether the STS interrupt is supported */ +#define RAISE_STS_IRQ + +/* tis registers */ +#define TPM_TIS_REG_ACCESS 0x00 +#define TPM_TIS_REG_INT_ENABLE 0x08 +#define TPM_TIS_REG_INT_VECTOR 0x0c +#define TPM_TIS_REG_INT_STATUS 0x10 +#define TPM_TIS_REG_INTF_CAPABILITY 0x14 +#define TPM_TIS_REG_STS 0x18 +#define TPM_TIS_REG_DATA_FIFO 0x24 +#define TPM_TIS_REG_DID_VID 0xf00 +#define TPM_TIS_REG_RID 0xf04 + +/* vendor-specific registers */ +#define TPM_TIS_REG_DEBUG 0xf90 + +#define TPM_TIS_STS_VALID (1 << 7) +#define TPM_TIS_STS_COMMAND_READY (1 << 6) +#define TPM_TIS_STS_TPM_GO (1 << 5) +#define TPM_TIS_STS_DATA_AVAILABLE (1 << 4) +#define TPM_TIS_STS_EXPECT (1 << 3) +#define TPM_TIS_STS_RESPONSE_RETRY (1 << 1) + +#define TPM_TIS_BURST_COUNT_SHIFT 8 +#define TPM_TIS_BURST_COUNT(X) \ + ((X) << TPM_TIS_BURST_COUNT_SHIFT) + +#define TPM_TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) +#define TPM_TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) +#define TPM_TIS_ACCESS_BEEN_SEIZED (1 << 4) +#define TPM_TIS_ACCESS_SEIZE (1 << 3) +#define TPM_TIS_ACCESS_PENDING_REQUEST (1 << 2) +#define TPM_TIS_ACCESS_REQUEST_USE (1 << 1) +#define TPM_TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) + +#define TPM_TIS_INT_ENABLED (1 << 31) +#define TPM_TIS_INT_DATA_AVAILABLE (1 << 0) +#define TPM_TIS_INT_STS_VALID (1 << 1) +#define TPM_TIS_INT_LOCALITY_CHANGED (1 << 2) +#define TPM_TIS_INT_COMMAND_READY (1 << 7) + +#define TPM_TIS_INT_POLARITY_MASK (3 << 3) +#define TPM_TIS_INT_POLARITY_LOW_LEVEL (1 << 3) + +#ifndef RAISE_STS_IRQ + +#define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \ + TPM_TIS_INT_DATA_AVAILABLE | \ + TPM_TIS_INT_COMMAND_READY) + +#else + +#define TPM_TIS_INTERRUPTS_SUPPORTED (TPM_TIS_INT_LOCALITY_CHANGED | \ + TPM_TIS_INT_DATA_AVAILABLE | \ + TPM_TIS_INT_STS_VALID | \ + TPM_TIS_INT_COMMAND_READY) + +#endif + +#define TPM_TIS_CAP_INTERRUPT_LOW_LEVEL (1 << 4) /* support is mandatory */ +#define TPM_TIS_CAPABILITIES_SUPPORTED (TPM_TIS_CAP_INTERRUPT_LOW_LEVEL | \ + TPM_TIS_INTERRUPTS_SUPPORTED) + +#define TPM_TIS_TPM_DID 0x0001 +#define TPM_TIS_TPM_VID PCI_VENDOR_ID_IBM +#define TPM_TIS_TPM_RID 0x0001 + +#define TPM_TIS_NO_DATA_BYTE 0xff + +/* local prototypes */ + +static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, + unsigned size); + +/* utility functions */ + +static uint8_t tpm_tis_locality_from_addr(hwaddr addr) +{ + return (uint8_t)((addr >> TPM_TIS_LOCALITY_SHIFT) & 0x7); +} + +static uint32_t tpm_tis_get_size_from_buffer(const TPMSizedBuffer *sb) +{ + return be32_to_cpu(*(uint32_t *)&sb->buffer[2]); +} + +static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string) +{ +#ifdef DEBUG_TIS + uint32_t len, i; + + len = tpm_tis_get_size_from_buffer(sb); + DPRINTF("tpm_tis: %s length = %d\n", string, len); + for (i = 0; i < len; i++) { + if (i && !(i % 16)) { + DPRINTF("\n"); + } + DPRINTF("%.2X ", sb->buffer[i]); + } + DPRINTF("\n"); +#endif +} + +/* + * Send a request to the TPM. + */ +static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) +{ + TPMTISEmuState *tis = &s->s.tis; + + tpm_tis_show_buffer(&tis->loc[locty].w_buffer, "tpm_tis: To TPM"); + + s->locty_number = locty; + s->locty_data = &tis->loc[locty]; + + /* + * w_offset serves as length indicator for length of data; + * it's reset when the response comes back + */ + tis->loc[locty].state = TPM_TIS_STATE_EXECUTION; + + s->be_driver->ops->deliver_request(s->be_driver); +} + +/* raise an interrupt if allowed */ +static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask) +{ + TPMTISEmuState *tis = &s->s.tis; + + if (!TPM_TIS_IS_VALID_LOCTY(locty)) { + return; + } + + if ((tis->loc[locty].inte & TPM_TIS_INT_ENABLED) && + (tis->loc[locty].inte & irqmask)) { + DPRINTF("tpm_tis: Raising IRQ for flag %08x\n", irqmask); + qemu_irq_raise(s->s.tis.irq); + tis->loc[locty].ints |= irqmask; + } +} + +static uint32_t tpm_tis_check_request_use_except(TPMState *s, uint8_t locty) +{ + uint8_t l; + + for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { + if (l == locty) { + continue; + } + if ((s->s.tis.loc[l].access & TPM_TIS_ACCESS_REQUEST_USE)) { + return 1; + } + } + + return 0; +} + +static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) +{ + TPMTISEmuState *tis = &s->s.tis; + bool change = (s->s.tis.active_locty != new_active_locty); + bool is_seize; + uint8_t mask; + + if (change && TPM_TIS_IS_VALID_LOCTY(s->s.tis.active_locty)) { + is_seize = TPM_TIS_IS_VALID_LOCTY(new_active_locty) && + tis->loc[new_active_locty].access & TPM_TIS_ACCESS_SEIZE; + + if (is_seize) { + mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY); + } else { + mask = ~(TPM_TIS_ACCESS_ACTIVE_LOCALITY| + TPM_TIS_ACCESS_REQUEST_USE); + } + /* reset flags on the old active locality */ + tis->loc[s->s.tis.active_locty].access &= mask; + + if (is_seize) { + tis->loc[tis->active_locty].access |= TPM_TIS_ACCESS_BEEN_SEIZED; + } + } + + tis->active_locty = new_active_locty; + + DPRINTF("tpm_tis: Active locality is now %d\n", s->s.tis.active_locty); + + if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) { + /* set flags on the new active locality */ + tis->loc[new_active_locty].access |= TPM_TIS_ACCESS_ACTIVE_LOCALITY; + tis->loc[new_active_locty].access &= ~(TPM_TIS_ACCESS_REQUEST_USE | + TPM_TIS_ACCESS_SEIZE); + } + + if (change) { + tpm_tis_raise_irq(s, tis->active_locty, TPM_TIS_INT_LOCALITY_CHANGED); + } +} + +/* abort -- this function switches the locality */ +static void tpm_tis_abort(TPMState *s, uint8_t locty) +{ + TPMTISEmuState *tis = &s->s.tis; + + tis->loc[locty].r_offset = 0; + tis->loc[locty].w_offset = 0; + + DPRINTF("tpm_tis: tis_abort: new active locality is %d\n", tis->next_locty); + + /* + * Need to react differently depending on who's aborting now and + * which locality will become active afterwards. + */ + if (tis->aborting_locty == tis->next_locty) { + tis->loc[tis->aborting_locty].state = TPM_TIS_STATE_READY; + tis->loc[tis->aborting_locty].sts = TPM_TIS_STS_COMMAND_READY; + tpm_tis_raise_irq(s, tis->aborting_locty, TPM_TIS_INT_COMMAND_READY); + } + + /* locality after abort is another one than the current one */ + tpm_tis_new_active_locality(s, tis->next_locty); + + tis->next_locty = TPM_TIS_NO_LOCALITY; + /* nobody's aborting a command anymore */ + tis->aborting_locty = TPM_TIS_NO_LOCALITY; +} + +/* prepare aborting current command */ +static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) +{ + TPMTISEmuState *tis = &s->s.tis; + uint8_t busy_locty; + + tis->aborting_locty = locty; + tis->next_locty = newlocty; /* locality after successful abort */ + + /* + * only abort a command using an interrupt if currently executing + * a command AND if there's a valid connection to the vTPM. + */ + for (busy_locty = 0; busy_locty < TPM_TIS_NUM_LOCALITIES; busy_locty++) { + if (tis->loc[busy_locty].state == TPM_TIS_STATE_EXECUTION) { + /* + * request the backend to cancel. Some backends may not + * support it + */ + s->be_driver->ops->cancel_cmd(s->be_driver); + return; + } + } + + tpm_tis_abort(s, locty); +} + +static void tpm_tis_receive_bh(void *opaque) +{ + TPMState *s = opaque; + TPMTISEmuState *tis = &s->s.tis; + uint8_t locty = s->locty_number; + + tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE; + tis->loc[locty].state = TPM_TIS_STATE_COMPLETION; + tis->loc[locty].r_offset = 0; + tis->loc[locty].w_offset = 0; + + if (TPM_TIS_IS_VALID_LOCTY(tis->next_locty)) { + tpm_tis_abort(s, locty); + } + +#ifndef RAISE_STS_IRQ + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_DATA_AVAILABLE); +#else + tpm_tis_raise_irq(s, locty, + TPM_TIS_INT_DATA_AVAILABLE | TPM_TIS_INT_STS_VALID); +#endif +} + +/* + * Callback from the TPM to indicate that the response was received. + */ +static void tpm_tis_receive_cb(TPMState *s, uint8_t locty) +{ + TPMTISEmuState *tis = &s->s.tis; + + assert(s->locty_number == locty); + + qemu_bh_schedule(tis->bh); +} + +/* + * Read a byte of response data + */ +static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty) +{ + TPMTISEmuState *tis = &s->s.tis; + uint32_t ret = TPM_TIS_NO_DATA_BYTE; + uint16_t len; + + if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { + len = tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer); + + ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++]; + if (tis->loc[locty].r_offset >= len) { + /* got last byte */ + tis->loc[locty].sts = TPM_TIS_STS_VALID; +#ifdef RAISE_STS_IRQ + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); +#endif + } + DPRINTF("tpm_tis: tpm_tis_data_read byte 0x%02x [%d]\n", + ret, tis->loc[locty].r_offset-1); + } + + return ret; +} + +#ifdef DEBUG_TIS +static void tpm_tis_dump_state(void *opaque, hwaddr addr) +{ + static const unsigned regs[] = { + TPM_TIS_REG_ACCESS, + TPM_TIS_REG_INT_ENABLE, + TPM_TIS_REG_INT_VECTOR, + TPM_TIS_REG_INT_STATUS, + TPM_TIS_REG_INTF_CAPABILITY, + TPM_TIS_REG_STS, + TPM_TIS_REG_DID_VID, + TPM_TIS_REG_RID, + 0xfff}; + int idx; + uint8_t locty = tpm_tis_locality_from_addr(addr); + hwaddr base = addr & ~0xfff; + TPMState *s = opaque; + TPMTISEmuState *tis = &s->s.tis; + + DPRINTF("tpm_tis: active locality : %d\n" + "tpm_tis: state of locality %d : %d\n" + "tpm_tis: register dump:\n", + tis->active_locty, + locty, tis->loc[locty].state); + + for (idx = 0; regs[idx] != 0xfff; idx++) { + DPRINTF("tpm_tis: 0x%04x : 0x%08x\n", regs[idx], + (uint32_t)tpm_tis_mmio_read(opaque, base + regs[idx], 4)); + } + + DPRINTF("tpm_tis: read offset : %d\n" + "tpm_tis: result buffer : ", + tis->loc[locty].r_offset); + for (idx = 0; + idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer); + idx++) { + DPRINTF("%c%02x%s", + tis->loc[locty].r_offset == idx ? '>' : ' ', + tis->loc[locty].r_buffer.buffer[idx], + ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); + } + DPRINTF("\n" + "tpm_tis: write offset : %d\n" + "tpm_tis: request buffer: ", + tis->loc[locty].w_offset); + for (idx = 0; + idx < tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer); + idx++) { + DPRINTF("%c%02x%s", + tis->loc[locty].w_offset == idx ? '>' : ' ', + tis->loc[locty].w_buffer.buffer[idx], + ((idx & 0xf) == 0xf) ? "\ntpm_tis: " : ""); + } + DPRINTF("\n"); +} +#endif + +/* + * Read a register of the TIS interface + * See specs pages 33-63 for description of the registers + */ +static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, + unsigned size) +{ + TPMState *s = opaque; + TPMTISEmuState *tis = &s->s.tis; + uint16_t offset = addr & 0xffc; + uint8_t shift = (addr & 0x3) * 8; + uint32_t val = 0xffffffff; + uint8_t locty = tpm_tis_locality_from_addr(addr); + uint32_t avail; + + if (s->be_driver->ops->had_startup_error(s->be_driver)) { + return val; + } + + switch (offset) { + case TPM_TIS_REG_ACCESS: + /* never show the SEIZE flag even though we use it internally */ + val = tis->loc[locty].access & ~TPM_TIS_ACCESS_SEIZE; + /* the pending flag is always calculated */ + if (tpm_tis_check_request_use_except(s, locty)) { + val |= TPM_TIS_ACCESS_PENDING_REQUEST; + } + val |= !s->be_driver->ops->get_tpm_established_flag(s->be_driver); + break; + case TPM_TIS_REG_INT_ENABLE: + val = tis->loc[locty].inte; + break; + case TPM_TIS_REG_INT_VECTOR: + val = tis->irq_num; + break; + case TPM_TIS_REG_INT_STATUS: + val = tis->loc[locty].ints; + break; + case TPM_TIS_REG_INTF_CAPABILITY: + val = TPM_TIS_CAPABILITIES_SUPPORTED; + break; + case TPM_TIS_REG_STS: + if (tis->active_locty == locty) { + if ((tis->loc[locty].sts & TPM_TIS_STS_DATA_AVAILABLE)) { + val = TPM_TIS_BURST_COUNT( + tpm_tis_get_size_from_buffer(&tis->loc[locty].r_buffer) + - tis->loc[locty].r_offset) | tis->loc[locty].sts; + } else { + avail = tis->loc[locty].w_buffer.size + - tis->loc[locty].w_offset; + /* + * byte-sized reads should not return 0x00 for 0x100 + * available bytes. + */ + if (size == 1 && avail > 0xff) { + avail = 0xff; + } + val = TPM_TIS_BURST_COUNT(avail) | tis->loc[locty].sts; + } + } + break; + case TPM_TIS_REG_DATA_FIFO: + if (tis->active_locty == locty) { + switch (tis->loc[locty].state) { + case TPM_TIS_STATE_COMPLETION: + val = tpm_tis_data_read(s, locty); + break; + default: + val = TPM_TIS_NO_DATA_BYTE; + break; + } + } + break; + case TPM_TIS_REG_DID_VID: + val = (TPM_TIS_TPM_DID << 16) | TPM_TIS_TPM_VID; + break; + case TPM_TIS_REG_RID: + val = TPM_TIS_TPM_RID; + break; +#ifdef DEBUG_TIS + case TPM_TIS_REG_DEBUG: + tpm_tis_dump_state(opaque, addr); + break; +#endif + } + + if (shift) { + val >>= shift; + } + + DPRINTF("tpm_tis: read.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val); + + return val; +} + +/* + * Write a value to a register of the TIS interface + * See specs pages 33-63 for description of the registers + */ +static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + bool hw_access) +{ + TPMState *s = opaque; + TPMTISEmuState *tis = &s->s.tis; + uint16_t off = addr & 0xfff; + uint8_t locty = tpm_tis_locality_from_addr(addr); + uint8_t active_locty, l; + int c, set_new_locty = 1; + uint16_t len; + + DPRINTF("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (uint32_t)val); + + if (locty == 4 && !hw_access) { + DPRINTF("tpm_tis: Access to locality 4 only allowed from hardware\n"); + return; + } + + if (s->be_driver->ops->had_startup_error(s->be_driver)) { + return; + } + + switch (off) { + case TPM_TIS_REG_ACCESS: + + if ((val & TPM_TIS_ACCESS_SEIZE)) { + val &= ~(TPM_TIS_ACCESS_REQUEST_USE | + TPM_TIS_ACCESS_ACTIVE_LOCALITY); + } + + active_locty = tis->active_locty; + + if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) { + /* give up locality if currently owned */ + if (tis->active_locty == locty) { + DPRINTF("tpm_tis: Releasing locality %d\n", locty); + + uint8_t newlocty = TPM_TIS_NO_LOCALITY; + /* anybody wants the locality ? */ + for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) { + if ((tis->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) { + DPRINTF("tpm_tis: Locality %d requests use.\n", c); + newlocty = c; + break; + } + } + DPRINTF("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: " + "Next active locality: %d\n", + newlocty); + + if (TPM_TIS_IS_VALID_LOCTY(newlocty)) { + set_new_locty = 0; + tpm_tis_prep_abort(s, locty, newlocty); + } else { + active_locty = TPM_TIS_NO_LOCALITY; + } + } else { + /* not currently the owner; clear a pending request */ + tis->loc[locty].access &= ~TPM_TIS_ACCESS_REQUEST_USE; + } + } + + if ((val & TPM_TIS_ACCESS_BEEN_SEIZED)) { + tis->loc[locty].access &= ~TPM_TIS_ACCESS_BEEN_SEIZED; + } + + if ((val & TPM_TIS_ACCESS_SEIZE)) { + /* + * allow seize if a locality is active and the requesting + * locality is higher than the one that's active + * OR + * allow seize for requesting locality if no locality is + * active + */ + while ((TPM_TIS_IS_VALID_LOCTY(tis->active_locty) && + locty > tis->active_locty) || + !TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) { + bool higher_seize = FALSE; + + /* already a pending SEIZE ? */ + if ((tis->loc[locty].access & TPM_TIS_ACCESS_SEIZE)) { + break; + } + + /* check for ongoing seize by a higher locality */ + for (l = locty + 1; l < TPM_TIS_NUM_LOCALITIES; l++) { + if ((tis->loc[l].access & TPM_TIS_ACCESS_SEIZE)) { + higher_seize = TRUE; + break; + } + } + + if (higher_seize) { + break; + } + + /* cancel any seize by a lower locality */ + for (l = 0; l < locty - 1; l++) { + tis->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; + } + + tis->loc[locty].access |= TPM_TIS_ACCESS_SEIZE; + DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: " + "Locality %d seized from locality %d\n", + locty, tis->active_locty); + DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n"); + set_new_locty = 0; + tpm_tis_prep_abort(s, tis->active_locty, locty); + break; + } + } + + if ((val & TPM_TIS_ACCESS_REQUEST_USE)) { + if (tis->active_locty != locty) { + if (TPM_TIS_IS_VALID_LOCTY(tis->active_locty)) { + tis->loc[locty].access |= TPM_TIS_ACCESS_REQUEST_USE; + } else { + /* no locality active -> make this one active now */ + active_locty = locty; + } + } + } + + if (set_new_locty) { + tpm_tis_new_active_locality(s, active_locty); + } + + break; + case TPM_TIS_REG_INT_ENABLE: + if (tis->active_locty != locty) { + break; + } + + tis->loc[locty].inte = (val & (TPM_TIS_INT_ENABLED | + TPM_TIS_INT_POLARITY_MASK | + TPM_TIS_INTERRUPTS_SUPPORTED)); + break; + case TPM_TIS_REG_INT_VECTOR: + /* hard wired -- ignore */ + break; + case TPM_TIS_REG_INT_STATUS: + if (tis->active_locty != locty) { + break; + } + + /* clearing of interrupt flags */ + if (((val & TPM_TIS_INTERRUPTS_SUPPORTED)) && + (tis->loc[locty].ints & TPM_TIS_INTERRUPTS_SUPPORTED)) { + tis->loc[locty].ints &= ~val; + if (tis->loc[locty].ints == 0) { + qemu_irq_lower(tis->irq); + DPRINTF("tpm_tis: Lowering IRQ\n"); + } + } + tis->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED); + break; + case TPM_TIS_REG_STS: + if (tis->active_locty != locty) { + break; + } + + val &= (TPM_TIS_STS_COMMAND_READY | TPM_TIS_STS_TPM_GO | + TPM_TIS_STS_RESPONSE_RETRY); + + if (val == TPM_TIS_STS_COMMAND_READY) { + switch (tis->loc[locty].state) { + + case TPM_TIS_STATE_READY: + tis->loc[locty].w_offset = 0; + tis->loc[locty].r_offset = 0; + break; + + case TPM_TIS_STATE_IDLE: + tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY; + tis->loc[locty].state = TPM_TIS_STATE_READY; + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); + break; + + case TPM_TIS_STATE_EXECUTION: + case TPM_TIS_STATE_RECEPTION: + /* abort currently running command */ + DPRINTF("tpm_tis: %s: Initiating abort.\n", + __func__); + tpm_tis_prep_abort(s, locty, locty); + break; + + case TPM_TIS_STATE_COMPLETION: + tis->loc[locty].w_offset = 0; + tis->loc[locty].r_offset = 0; + /* shortcut to ready state with C/R set */ + tis->loc[locty].state = TPM_TIS_STATE_READY; + if (!(tis->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) { + tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY; + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); + } + tis->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); + break; + + } + } else if (val == TPM_TIS_STS_TPM_GO) { + switch (tis->loc[locty].state) { + case TPM_TIS_STATE_RECEPTION: + if ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT) == 0) { + tpm_tis_tpm_send(s, locty); + } + break; + default: + /* ignore */ + break; + } + } else if (val == TPM_TIS_STS_RESPONSE_RETRY) { + switch (tis->loc[locty].state) { + case TPM_TIS_STATE_COMPLETION: + tis->loc[locty].r_offset = 0; + tis->loc[locty].sts = TPM_TIS_STS_VALID | + TPM_TIS_STS_DATA_AVAILABLE; + break; + default: + /* ignore */ + break; + } + } + break; + case TPM_TIS_REG_DATA_FIFO: + /* data fifo */ + if (tis->active_locty != locty) { + break; + } + + if (tis->loc[locty].state == TPM_TIS_STATE_IDLE || + tis->loc[locty].state == TPM_TIS_STATE_EXECUTION || + tis->loc[locty].state == TPM_TIS_STATE_COMPLETION) { + /* drop the byte */ + } else { + DPRINTF("tpm_tis: Byte to send to TPM: %02x\n", (uint8_t)val); + if (tis->loc[locty].state == TPM_TIS_STATE_READY) { + tis->loc[locty].state = TPM_TIS_STATE_RECEPTION; + tis->loc[locty].sts = TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID; + } + + if ((tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) { + if (tis->loc[locty].w_offset < tis->loc[locty].w_buffer.size) { + tis->loc[locty].w_buffer. + buffer[tis->loc[locty].w_offset++] = (uint8_t)val; + } else { + tis->loc[locty].sts = TPM_TIS_STS_VALID; + } + } + + /* check for complete packet */ + if (tis->loc[locty].w_offset > 5 && + (tis->loc[locty].sts & TPM_TIS_STS_EXPECT)) { + /* we have a packet length - see if we have all of it */ +#ifdef RAISE_STS_IRQ + bool needIrq = !(tis->loc[locty].sts & TPM_TIS_STS_VALID); +#endif + len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer); + if (len > tis->loc[locty].w_offset) { + tis->loc[locty].sts = TPM_TIS_STS_EXPECT | + TPM_TIS_STS_VALID; + } else { + /* packet complete */ + tis->loc[locty].sts = TPM_TIS_STS_VALID; + } +#ifdef RAISE_STS_IRQ + if (needIrq) { + tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); + } +#endif + } + } + break; + } +} + +static void tpm_tis_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + return tpm_tis_mmio_write_intern(opaque, addr, val, size, false); +} + +static const MemoryRegionOps tpm_tis_memory_ops = { + .read = tpm_tis_mmio_read, + .write = tpm_tis_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static int tpm_tis_do_startup_tpm(TPMState *s) +{ + return s->be_driver->ops->startup_tpm(s->be_driver); +} + +/* + * This function is called when the machine starts, resets or due to + * S3 resume. + */ +static void tpm_tis_reset(DeviceState *dev) +{ + TPMState *s = TPM(dev); + TPMTISEmuState *tis = &s->s.tis; + int c; + + s->be_driver->ops->reset(s->be_driver); + + tis->active_locty = TPM_TIS_NO_LOCALITY; + tis->next_locty = TPM_TIS_NO_LOCALITY; + tis->aborting_locty = TPM_TIS_NO_LOCALITY; + + for (c = 0; c < TPM_TIS_NUM_LOCALITIES; c++) { + tis->loc[c].access = TPM_TIS_ACCESS_TPM_REG_VALID_STS; + tis->loc[c].sts = 0; + tis->loc[c].inte = TPM_TIS_INT_POLARITY_LOW_LEVEL; + tis->loc[c].ints = 0; + tis->loc[c].state = TPM_TIS_STATE_IDLE; + + tis->loc[c].w_offset = 0; + s->be_driver->ops->realloc_buffer(&tis->loc[c].w_buffer); + tis->loc[c].r_offset = 0; + s->be_driver->ops->realloc_buffer(&tis->loc[c].r_buffer); + } + + tpm_tis_do_startup_tpm(s); +} + +static const VMStateDescription vmstate_tpm_tis = { + .name = "tpm", + .unmigratable = 1, +}; + +static Property tpm_tis_properties[] = { + DEFINE_PROP_UINT32("irq", TPMState, + s.tis.irq_num, TPM_TIS_IRQ), + DEFINE_PROP_STRING("tpmdev", TPMState, backend), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tpm_tis_realizefn(DeviceState *dev, Error **errp) +{ + TPMState *s = TPM(dev); + TPMTISEmuState *tis = &s->s.tis; + + s->be_driver = qemu_find_tpm(s->backend); + if (!s->be_driver) { + error_setg(errp, "tpm_tis: backend driver with id %s could not be " + "found", s->backend); + return; + } + + s->be_driver->fe_model = TPM_MODEL_TPM_TIS; + + if (s->be_driver->ops->init(s->be_driver, s, tpm_tis_receive_cb)) { + error_setg(errp, "tpm_tis: backend driver with id %s could not be " + "initialized", s->backend); + return; + } + + if (tis->irq_num > 15) { + error_setg(errp, "tpm_tis: IRQ %d for TPM TIS is outside valid range " + "of 0 to 15.\n", tis->irq_num); + return; + } + + tis->bh = qemu_bh_new(tpm_tis_receive_bh, s); + + isa_init_irq(&s->busdev, &tis->irq, tis->irq_num); +} + +static void tpm_tis_initfn(Object *obj) +{ + ISADevice *dev = ISA_DEVICE(obj); + TPMState *s = TPM(obj); + + memory_region_init_io(&s->mmio, &tpm_tis_memory_ops, s, "tpm-tis-mmio", + TPM_TIS_NUM_LOCALITIES << TPM_TIS_LOCALITY_SHIFT); + memory_region_add_subregion(isa_address_space(dev), TPM_TIS_ADDR_BASE, + &s->mmio); +} + +static void tpm_tis_uninitfn(Object *obj) +{ + TPMState *s = TPM(obj); + + memory_region_del_subregion(get_system_memory(), &s->mmio); + memory_region_destroy(&s->mmio); +} + +static void tpm_tis_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = tpm_tis_realizefn; + dc->props = tpm_tis_properties; + dc->reset = tpm_tis_reset; + dc->vmsd = &vmstate_tpm_tis; +} + +static const TypeInfo tpm_tis_info = { + .name = TYPE_TPM_TIS, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(TPMState), + .instance_init = tpm_tis_initfn, + .instance_finalize = tpm_tis_uninitfn, + .class_init = tpm_tis_class_init, +}; + +static void tpm_tis_register(void) +{ + type_register_static(&tpm_tis_info); + tpm_register_model(TPM_MODEL_TPM_TIS); +} + +type_init(tpm_tis_register) diff --git a/tpm/tpm_tis.h b/tpm/tpm_tis.h new file mode 100644 index 0000000000..0c8df80cce --- /dev/null +++ b/tpm/tpm_tis.h @@ -0,0 +1,80 @@ +/* + * tpm_tis.h - QEMU's TPM TIS interface emulator + * + * Copyright (C) 2006, 2010-2013 IBM Corporation + * + * Authors: + * Stefan Berger + * David Safford + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Implementation of the TIS interface according to specs found at + * http://www.trustedcomputinggroup.org + * + */ +#ifndef TPM_TPM_TIS_H +#define TPM_TPM_TIS_H + +#include "hw/isa.h" +#include "qemu-common.h" + +#define TPM_TIS_ADDR_BASE 0xFED40000 + +#define TPM_TIS_NUM_LOCALITIES 5 /* per spec */ +#define TPM_TIS_LOCALITY_SHIFT 12 +#define TPM_TIS_NO_LOCALITY 0xff + +#define TPM_TIS_IS_VALID_LOCTY(x) ((x) < TPM_TIS_NUM_LOCALITIES) + +#define TPM_TIS_IRQ 5 + +#define TPM_TIS_BUFFER_MAX 4096 + +#define TYPE_TPM_TIS "tpm-tis" + + +typedef struct TPMSizedBuffer { + uint32_t size; + uint8_t *buffer; +} TPMSizedBuffer; + +typedef enum { + TPM_TIS_STATE_IDLE = 0, + TPM_TIS_STATE_READY, + TPM_TIS_STATE_COMPLETION, + TPM_TIS_STATE_EXECUTION, + TPM_TIS_STATE_RECEPTION, +} TPMTISState; + +/* locality data -- all fields are persisted */ +typedef struct TPMLocality { + TPMTISState state; + uint8_t access; + uint8_t sts; + uint32_t inte; + uint32_t ints; + + uint16_t w_offset; + uint16_t r_offset; + TPMSizedBuffer w_buffer; + TPMSizedBuffer r_buffer; +} TPMLocality; + +typedef struct TPMTISEmuState { + QEMUBH *bh; + uint32_t offset; + uint8_t buf[TPM_TIS_BUFFER_MAX]; + + uint8_t active_locty; + uint8_t aborting_locty; + uint8_t next_locty; + + TPMLocality loc[TPM_TIS_NUM_LOCALITIES]; + + qemu_irq irq; + uint32_t irq_num; +} TPMTISEmuState; + +#endif /* TPM_TPM_TIS_H */ diff --git a/trace-events b/trace-events index bb7621eeb6..cd73b7f3ea 100644 --- a/trace-events +++ b/trace-events @@ -79,10 +79,17 @@ commit_start(void *bs, void *base, void *top, void *s, void *co, void *opaque) " # block/mirror.c mirror_start(void *bs, void *s, void *co, void *opaque) "bs %p s %p co %p opaque %p" +mirror_restart_iter(void *s, int64_t cnt) "s %p dirty count %"PRId64 mirror_before_flush(void *s) "s %p" mirror_before_drain(void *s, int64_t cnt) "s %p dirty count %"PRId64 mirror_before_sleep(void *s, int64_t cnt, int synced) "s %p dirty count %"PRId64" synced %d" mirror_one_iteration(void *s, int64_t sector_num, int nb_sectors) "s %p sector_num %"PRId64" nb_sectors %d" +mirror_cow(void *s, int64_t sector_num) "s %p sector_num %"PRId64 +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" # blockdev.c qmp_block_job_cancel(void *job) "job %p" @@ -98,9 +105,18 @@ virtio_blk_rw_complete(void *req, int ret) "req %p ret %d" virtio_blk_handle_write(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu" virtio_blk_handle_read(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu" +# hw/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" +virtio_blk_data_plane_complete_request(void *s, unsigned int head, int ret) "dataplane %p head %u ret %d" + +# hw/dataplane/vring.c +vring_setup(uint64_t physical, void *desc, void *avail, void *used) "vring physical %#"PRIx64" desc %p avail %p used %p" + # thread-pool.c -thread_pool_submit(void *req, void *opaque) "req %p opaque %p" -thread_pool_complete(void *req, void *opaque, int ret) "req %p opaque %p ret %d" +thread_pool_submit(void *pool, void *req, void *opaque) "pool %p req %p opaque %p" +thread_pool_complete(void *pool, void *req, void *opaque, int ret) "pool %p req %p opaque %p ret %d" thread_pool_cancel(void *req, void *opaque) "req %p opaque %p" # posix-aio-compat.c @@ -158,6 +174,13 @@ ecc_mem_readl_ecr1(uint32_t ret) "Read event count 2 %08x" ecc_diag_mem_writeb(uint64_t addr, uint32_t val) "Write diagnostic %"PRId64" = %02x" ecc_diag_mem_readb(uint64_t addr, uint32_t ret) "Read diagnostic %"PRId64"= %02x" +# hw/fw_cfg.c +fw_cfg_write(void *s, uint8_t value) "%p %d" +fw_cfg_select(void *s, uint16_t key, int ret) "%p key %d = %d" +fw_cfg_read(void *s, uint8_t ret) "%p = %d" +fw_cfg_add_file_dupe(void *s, char *name) "%p %s" +fw_cfg_add_file(void *s, int index, char *name, size_t len) "%p #%d: %s (%zd bytes)" + # hw/hd-geometry.c hd_geometry_lchs_guess(void *bs, int cyls, int heads, int secs) "bs %p LCHS %d %d %d" hd_geometry_guess(void *bs, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "bs %p CHS %u %u %u trans %d" @@ -298,8 +321,6 @@ usb_uhci_frame_loop_stop_idle(void) "" usb_uhci_frame_loop_continue(void) "" usb_uhci_mmio_readw(uint32_t addr, uint32_t val) "addr 0x%04x, ret 0x%04x" usb_uhci_mmio_writew(uint32_t addr, uint32_t val) "addr 0x%04x, val 0x%04x" -usb_uhci_mmio_readl(uint32_t addr, uint32_t val) "addr 0x%04x, ret 0x%08x" -usb_uhci_mmio_writel(uint32_t addr, uint32_t val) "addr 0x%04x, val 0x%08x" usb_uhci_queue_add(uint32_t token) "token 0x%x" usb_uhci_queue_del(uint32_t token, const char *reason) "token 0x%x: %s" usb_uhci_packet_add(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x" @@ -349,11 +370,11 @@ usb_xhci_slot_evaluate(uint32_t slotid) "slotid %d" usb_xhci_slot_reset(uint32_t slotid) "slotid %d" usb_xhci_ep_enable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" -usb_xhci_ep_set_dequeue(uint32_t slotid, uint32_t epid, uint64_t param) "slotid %d, epid %d, ptr %016" PRIx64 -usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" +usb_xhci_ep_set_dequeue(uint32_t slotid, uint32_t epid, uint32_t streamid, uint64_t param) "slotid %d, epid %d, streamid %d, ptr %016" PRIx64 +usb_xhci_ep_kick(uint32_t slotid, uint32_t epid, uint32_t streamid) "slotid %d, epid %d, streamid %d" usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" -usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid) "%p: slotid %d, epid %d" +usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid, uint32_t streamid) "%p: slotid %d, epid %d, streamid %d" usb_xhci_xfer_async(void *xfer) "%p" usb_xhci_xfer_nak(void *xfer) "%p" usb_xhci_xfer_retry(void *xfer) "%p" @@ -439,6 +460,7 @@ scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d le scsi_req_data_canceled(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d" scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d" scsi_req_continue(int target, int lun, int tag) "target %d lun %d tag %d" +scsi_req_continue_canceled(int target, int lun, int tag) "target %d lun %d tag %d" scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer) "target %d lun %d tag %d command %d dir %d length %d" scsi_req_parsed_lba(int target, int lun, int tag, int cmd, uint64_t lba) "target %d lun %d tag %d command %d lba %"PRIu64 scsi_req_parse_bad(int target, int lun, int tag, int cmd) "target %d lun %d tag %d command %d" @@ -451,6 +473,7 @@ scsi_request_sense(int target, int lun, int tag) "target %d lun %d tag %d" # vl.c vm_state_notify(int running, int reason) "running %d reason %d" +load_file(const char *name, const char *path) "name %s location %s" # block/qcow2.c qcow2_writev_start_req(void *co, int64_t sector, int nb_sectors) "co %p sector %" PRIx64 " nb_sectors %d" @@ -726,6 +749,14 @@ mipsnet_read(uint64_t addr, uint32_t val) "read addr=0x%" PRIx64 " val=0x%x" mipsnet_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 "" mipsnet_irq(uint32_t isr, uint32_t intctl) "set irq to %d (%02x)" +# hw/pc87312.c +pc87312_io_read(uint32_t addr, uint32_t val) "read addr=%x val=%x" +pc87312_io_write(uint32_t addr, uint32_t val) "write addr=%x val=%x" +pc87312_info_floppy(uint32_t base) "base 0x%x" +pc87312_info_ide(uint32_t base) "base 0x%x" +pc87312_info_parallel(uint32_t base, uint32_t irq) "base 0x%x, irq %u" +pc87312_info_serial(int n, uint32_t base, uint32_t irq) "id=%d, base 0x%x, irq %u" + # xen-all.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" @@ -1007,8 +1038,10 @@ qxl_send_events_vm_stopped(int qid, uint32_t events) "%d %d" qxl_set_guest_bug(int qid) "%d" qxl_interrupt_client_monitors_config(int qid, int num_heads, void *heads) "%d %d %p" qxl_client_monitors_config_unsupported_by_guest(int qid, uint32_t int_mask, void *client_monitors_config) "%d %X %p" +qxl_client_monitors_config_unsupported_by_device(int qid, int revision) "%d revision=%d" qxl_client_monitors_config_capped(int qid, int requested, int limit) "%d %d %d" qxl_client_monitors_config_crc(int qid, unsigned size, uint32_t crc32) "%d %u %u" +qxl_set_client_capabilities_unsupported_by_revision(int qid, int revision) "%d revision=%d" # hw/qxl-render.c qxl_render_blit_guest_primary_initialized(void) "" @@ -1036,3 +1069,29 @@ xics_set_irq_lsi(int srcno, int nr) "set_irq_lsi: srcno %d [irq %#x]" xics_ics_write_xive(int nr, int srcno, int server, uint8_t priority) "ics_write_xive: irq %#x [src %d] server %#x prio %#x" xics_ics_reject(int nr, int srcno) "reject irq %#x [src %d]" xics_ics_eoi(int nr) "ics_eoi: irq %#x" + +# hbitmap.c +hbitmap_iter_skip_words(const void *hb, void *hbi, uint64_t pos, unsigned long cur) "hb %p hbi %p pos %"PRId64" cur 0x%lx" +hbitmap_reset(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64 +hbitmap_set(void *hb, uint64_t start, uint64_t count, uint64_t sbit, uint64_t ebit) "hb %p items %"PRIu64",%"PRIu64" bits %"PRIu64"..%"PRIu64 + +# target-s390x/ioinst.c +ioinst(const char *insn) "IOINST: %s" +ioinst_sch_id(const char *insn, int cssid, int ssid, int schid) "IOINST: %s (%x.%x.%04x)" +ioinst_chp_id(const char *insn, int cssid, int chpid) "IOINST: %s (%x.%02x)" +ioinst_chsc_cmd(uint16_t cmd, uint16_t len) "IOINST: chsc command %04x, len %04x" + +# hw/s390x/css.c +css_enable_facility(const char *facility) "CSS: enable %s" +css_crw(uint8_t rsc, uint8_t erc, uint16_t rsid, const char *chained) "CSS: queueing crw: rsc=%x, erc=%x, rsid=%x %s" +css_chpid_add(uint8_t cssid, uint8_t chpid, uint8_t type) "CSS: add chpid %x.%02x (type %02x)" +css_new_image(uint8_t cssid, const char *default_cssid) "CSS: add css image %02x %s" +css_assign_subch(const char *do_assign, uint8_t cssid, uint8_t ssid, uint16_t schid, uint16_t devno) "CSS: %s %x.%x.%04x (devno %04x)" +css_io_interrupt(int cssid, int ssid, int schid, uint32_t intparm, uint8_t isc, const char *conditional) "CSS: I/O interrupt on sch %x.%x.%04x (intparm %08x, isc %x) %s" + +# hw/s390x/virtio-ccw.c +virtio_ccw_interpret_ccw(int cssid, int ssid, int schid, int cmd_code) "VIRTIO-CCW: %x.%x.%04x: interpret command %x" +virtio_ccw_new_device(int cssid, int ssid, int schid, int devno, const char *devno_mode) "VIRTIO-CCW: add subchannel %x.%x.%04x, devno %04x (%s)" + +# migration.c +migrate_set_state(int new_state) "new state %d" diff --git a/trace/Makefile.objs b/trace/Makefile.objs new file mode 100644 index 0000000000..dde9d5784e --- /dev/null +++ b/trace/Makefile.objs @@ -0,0 +1,58 @@ +# -*- mode: makefile -*- + +###################################################################### +# Auto-generated header for tracing routines + +$(obj)/generated-tracers.h: $(obj)/generated-tracers.h-timestamp + @cmp -s $< $@ || cp $< $@ +$(obj)/generated-tracers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak + $(call quiet-command,$(TRACETOOL) \ + --format=h \ + --backend=$(TRACE_BACKEND) \ + < $< > $@," GEN $(patsubst %-timestamp,%,$@)") + +###################################################################### +# Auto-generated tracing routines (non-DTrace) + +ifneq ($(TRACE_BACKEND),dtrace) +$(obj)/generated-tracers.c: $(obj)/generated-tracers.c-timestamp + @cmp -s $< $@ || cp $< $@ +$(obj)/generated-tracers.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak + $(call quiet-command,$(TRACETOOL) \ + --format=c \ + --backend=$(TRACE_BACKEND) \ + < $< > $@," GEN $(patsubst %-timestamp,%,$@)") + +$(obj)/generated-tracers.o: $(obj)/generated-tracers.c $(obj)/generated-tracers.h +endif + + +###################################################################### +# Auto-generated DTrace code + +# 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 ($(TRACE_BACKEND),dtrace) +$(obj)/generated-tracers.dtrace: $(obj)/generated-tracers.dtrace-timestamp +$(obj)/generated-tracers.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak + $(call quiet-command,$(TRACETOOL) \ + --format=d \ + --backend=$(TRACE_BACKEND) \ + < $< > $@," GEN $(patsubst %-timestamp,%,$@)") + @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@) + +$(obj)/generated-tracers-dtrace.h: $(obj)/generated-tracers.dtrace + $(call quiet-command,dtrace -o $@ -h -s $<, " GEN $@") + +$(obj)/generated-tracers.o: $(obj)/generated-tracers.dtrace +endif + +###################################################################### +# Backend code + +util-obj-$(CONFIG_TRACE_DEFAULT) += default.o +util-obj-$(CONFIG_TRACE_SIMPLE) += simple.o +util-obj-$(CONFIG_TRACE_STDERR) += stderr.o +util-obj-y += control.o +util-obj-y += generated-tracers.o diff --git a/trace/simple.c b/trace/simple.c index ce17d64bd7..375d98f70b 100644 --- a/trace/simple.c +++ b/trace/simple.c @@ -40,8 +40,18 @@ * records to become available, writes them out, and then waits again. */ static GStaticMutex trace_lock = G_STATIC_MUTEX_INIT; + +/* g_cond_new() was deprecated in glib 2.31 but we still need to support it */ +#if GLIB_CHECK_VERSION(2, 31, 0) +static GCond the_trace_available_cond; +static GCond the_trace_empty_cond; +static GCond *trace_available_cond = &the_trace_available_cond; +static GCond *trace_empty_cond = &the_trace_empty_cond; +#else static GCond *trace_available_cond; static GCond *trace_empty_cond; +#endif + static bool trace_available; static bool trace_writeout_enabled; @@ -51,9 +61,9 @@ enum { }; uint8_t trace_buf[TRACE_BUF_LEN]; -static unsigned int trace_idx; +static volatile gint trace_idx; static unsigned int writeout_idx; -static uint64_t dropped_events; +static volatile gint dropped_events; static FILE *trace_fp; static char *trace_file_name; @@ -63,7 +73,7 @@ typedef struct { uint64_t timestamp_ns; uint32_t length; /* in bytes */ uint32_t reserved; /* unused */ - uint8_t arguments[]; + uint64_t arguments[]; } TraceRecord; typedef struct { @@ -160,25 +170,22 @@ static gpointer writeout_thread(gpointer opaque) uint8_t bytes[sizeof(TraceRecord) + sizeof(uint64_t)]; } dropped; unsigned int idx = 0; - uint64_t dropped_count; + int dropped_count; size_t unused __attribute__ ((unused)); for (;;) { wait_for_trace_records_available(); - if (dropped_events) { + if (g_atomic_int_get(&dropped_events)) { dropped.rec.event = DROPPED_EVENT_ID, dropped.rec.timestamp_ns = get_clock(); - dropped.rec.length = sizeof(TraceRecord) + sizeof(dropped_events), + dropped.rec.length = sizeof(TraceRecord) + sizeof(uint64_t), dropped.rec.reserved = 0; - while (1) { - dropped_count = dropped_events; - if (g_atomic_int_compare_and_exchange((gint *)&dropped_events, - dropped_count, 0)) { - break; - } - } - memcpy(dropped.rec.arguments, &dropped_count, sizeof(uint64_t)); + do { + dropped_count = g_atomic_int_get(&dropped_events); + } while (!g_atomic_int_compare_and_exchange(&dropped_events, + dropped_count, 0)); + dropped.rec.arguments[0] = dropped_count; unused = fwrite(&dropped.rec, dropped.rec.length, 1, trace_fp); } @@ -213,22 +220,17 @@ int trace_record_start(TraceBufferRecord *rec, TraceEventID event, size_t datasi uint32_t rec_len = sizeof(TraceRecord) + datasize; uint64_t timestamp_ns = get_clock(); - while (1) { - old_idx = trace_idx; + do { + old_idx = g_atomic_int_get(&trace_idx); smp_rmb(); new_idx = old_idx + rec_len; if (new_idx - writeout_idx > TRACE_BUF_LEN) { /* Trace Buffer Full, Event dropped ! */ - g_atomic_int_inc((gint *)&dropped_events); + g_atomic_int_inc(&dropped_events); return -ENOSPC; } - - if (g_atomic_int_compare_and_exchange((gint *)&trace_idx, - old_idx, new_idx)) { - break; - } - } + } while (!g_atomic_int_compare_and_exchange(&trace_idx, old_idx, new_idx)); idx = old_idx % TRACE_BUF_LEN; @@ -275,7 +277,8 @@ void trace_record_finish(TraceBufferRecord *rec) record.event |= TRACE_RECORD_VALID; write_to_buffer(rec->tbuf_idx, &record, sizeof(TraceRecord)); - if ((trace_idx - writeout_idx) > TRACE_BUF_FLUSH_THRESHOLD) { + if (((unsigned int)g_atomic_int_get(&trace_idx) - writeout_idx) + > TRACE_BUF_FLUSH_THRESHOLD) { flush_trace_file(false); } } @@ -404,7 +407,13 @@ static GThread *trace_thread_create(GThreadFunc fn) sigfillset(&set); pthread_sigmask(SIG_SETMASK, &set, &oldset); #endif + +#if GLIB_CHECK_VERSION(2, 31, 0) + thread = g_thread_new("trace-thread", fn, NULL); +#else thread = g_thread_create(fn, NULL, FALSE, NULL); +#endif + #ifndef _WIN32 pthread_sigmask(SIG_SETMASK, &oldset, NULL); #endif @@ -425,8 +434,10 @@ bool trace_backend_init(const char *events, const char *file) #endif } +#if !GLIB_CHECK_VERSION(2, 31, 0) trace_available_cond = g_cond_new(); trace_empty_cond = g_cond_new(); +#endif thread = trace_thread_create(writeout_thread); if (!thread) { diff --git a/translate-all.c b/translate-all.c index d367fc4d11..1f3237e60e 100644 --- a/translate-all.c +++ b/translate-all.c @@ -72,21 +72,6 @@ #define SMC_BITMAP_USE_THRESHOLD 10 -/* Code generation and translation blocks */ -static TranslationBlock *tbs; -static int code_gen_max_blocks; -TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; -static int nb_tbs; -/* any access to the tbs or the page table must use this lock */ -spinlock_t tb_lock = SPIN_LOCK_UNLOCKED; - -uint8_t *code_gen_prologue; -static uint8_t *code_gen_buffer; -static size_t code_gen_buffer_size; -/* threshold to flush the translated code buffer */ -static size_t code_gen_buffer_max_size; -static uint8_t *code_gen_ptr; - typedef struct PageDesc { /* list of TBs intersecting this ram page */ TranslationBlock *first_tb; @@ -133,10 +118,6 @@ uintptr_t qemu_host_page_mask; The bottom level has pointers to PageDesc. */ static void *l1_map[V_L1_SIZE]; -/* statistics */ -static int tb_flush_count; -static int tb_phys_invalidate_count; - /* code generation context */ TCGContext tcg_ctx; @@ -514,7 +495,7 @@ static inline size_t size_code_gen_buffer(size_t tb_size) if (tb_size > MAX_CODE_GEN_BUFFER_SIZE) { tb_size = MAX_CODE_GEN_BUFFER_SIZE; } - code_gen_buffer_size = tb_size; + tcg_ctx.code_gen_buffer_size = tb_size; return tb_size; } @@ -524,7 +505,7 @@ static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE] static inline void *alloc_code_gen_buffer(void) { - map_exec(static_code_gen_buffer, code_gen_buffer_size); + map_exec(static_code_gen_buffer, tcg_ctx.code_gen_buffer_size); return static_code_gen_buffer; } #elif defined(USE_MMAP) @@ -547,8 +528,8 @@ static inline void *alloc_code_gen_buffer(void) Leave the choice of exact location with the kernel. */ flags |= MAP_32BIT; /* Cannot expect to map more than 800MB in low memory. */ - if (code_gen_buffer_size > 800u * 1024 * 1024) { - code_gen_buffer_size = 800u * 1024 * 1024; + if (tcg_ctx.code_gen_buffer_size > 800u * 1024 * 1024) { + tcg_ctx.code_gen_buffer_size = 800u * 1024 * 1024; } # elif defined(__sparc__) start = 0x40000000ul; @@ -556,17 +537,17 @@ static inline void *alloc_code_gen_buffer(void) start = 0x90000000ul; # endif - buf = mmap((void *)start, code_gen_buffer_size, + buf = mmap((void *)start, tcg_ctx.code_gen_buffer_size, PROT_WRITE | PROT_READ | PROT_EXEC, flags, -1, 0); return buf == MAP_FAILED ? NULL : buf; } #else static inline void *alloc_code_gen_buffer(void) { - void *buf = g_malloc(code_gen_buffer_size); + void *buf = g_malloc(tcg_ctx.code_gen_buffer_size); if (buf) { - map_exec(buf, code_gen_buffer_size); + map_exec(buf, tcg_ctx.code_gen_buffer_size); } return buf; } @@ -574,27 +555,31 @@ static inline void *alloc_code_gen_buffer(void) static inline void code_gen_alloc(size_t tb_size) { - code_gen_buffer_size = size_code_gen_buffer(tb_size); - code_gen_buffer = alloc_code_gen_buffer(); - if (code_gen_buffer == NULL) { + tcg_ctx.code_gen_buffer_size = size_code_gen_buffer(tb_size); + tcg_ctx.code_gen_buffer = alloc_code_gen_buffer(); + if (tcg_ctx.code_gen_buffer == NULL) { fprintf(stderr, "Could not allocate dynamic translator buffer\n"); exit(1); } - qemu_madvise(code_gen_buffer, code_gen_buffer_size, QEMU_MADV_HUGEPAGE); + qemu_madvise(tcg_ctx.code_gen_buffer, tcg_ctx.code_gen_buffer_size, + QEMU_MADV_HUGEPAGE); /* Steal room for the prologue at the end of the buffer. This ensures (via the MAX_CODE_GEN_BUFFER_SIZE limits above) that direct branches from TB's to the prologue are going to be in range. It also means that we don't need to mark (additional) portions of the data segment as executable. */ - code_gen_prologue = code_gen_buffer + code_gen_buffer_size - 1024; - code_gen_buffer_size -= 1024; + tcg_ctx.code_gen_prologue = tcg_ctx.code_gen_buffer + + tcg_ctx.code_gen_buffer_size - 1024; + tcg_ctx.code_gen_buffer_size -= 1024; - code_gen_buffer_max_size = code_gen_buffer_size - + tcg_ctx.code_gen_buffer_max_size = tcg_ctx.code_gen_buffer_size - (TCG_MAX_OP_SIZE * OPC_BUF_SIZE); - code_gen_max_blocks = code_gen_buffer_size / CODE_GEN_AVG_BLOCK_SIZE; - tbs = g_malloc(code_gen_max_blocks * sizeof(TranslationBlock)); + tcg_ctx.code_gen_max_blocks = tcg_ctx.code_gen_buffer_size / + CODE_GEN_AVG_BLOCK_SIZE; + tcg_ctx.tb_ctx.tbs = + g_malloc(tcg_ctx.code_gen_max_blocks * sizeof(TranslationBlock)); } /* Must be called before using the QEMU cpus. 'tb_size' is the size @@ -604,8 +589,8 @@ void tcg_exec_init(unsigned long tb_size) { cpu_gen_init(); code_gen_alloc(tb_size); - code_gen_ptr = code_gen_buffer; - tcg_register_jit(code_gen_buffer, code_gen_buffer_size); + tcg_ctx.code_gen_ptr = tcg_ctx.code_gen_buffer; + tcg_register_jit(tcg_ctx.code_gen_buffer, tcg_ctx.code_gen_buffer_size); page_init(); #if !defined(CONFIG_USER_ONLY) || !defined(CONFIG_USE_GUEST_BASE) /* There's no guest base to take into account, so go ahead and @@ -616,7 +601,7 @@ void tcg_exec_init(unsigned long tb_size) bool tcg_enabled(void) { - return code_gen_buffer != NULL; + return tcg_ctx.code_gen_buffer != NULL; } /* Allocate a new translation block. Flush the translation buffer if @@ -625,11 +610,12 @@ static TranslationBlock *tb_alloc(target_ulong pc) { TranslationBlock *tb; - if (nb_tbs >= code_gen_max_blocks || - (code_gen_ptr - code_gen_buffer) >= code_gen_buffer_max_size) { + if (tcg_ctx.tb_ctx.nb_tbs >= tcg_ctx.code_gen_max_blocks || + (tcg_ctx.code_gen_ptr - tcg_ctx.code_gen_buffer) >= + tcg_ctx.code_gen_buffer_max_size) { return NULL; } - tb = &tbs[nb_tbs++]; + tb = &tcg_ctx.tb_ctx.tbs[tcg_ctx.tb_ctx.nb_tbs++]; tb->pc = pc; tb->cflags = 0; return tb; @@ -640,9 +626,10 @@ void tb_free(TranslationBlock *tb) /* In practice this is mostly used for single use temporary TB Ignore the hard cases and just back up if this TB happens to be the last one generated. */ - if (nb_tbs > 0 && tb == &tbs[nb_tbs - 1]) { - code_gen_ptr = tb->tc_ptr; - nb_tbs--; + if (tcg_ctx.tb_ctx.nb_tbs > 0 && + tb == &tcg_ctx.tb_ctx.tbs[tcg_ctx.tb_ctx.nb_tbs - 1]) { + tcg_ctx.code_gen_ptr = tb->tc_ptr; + tcg_ctx.tb_ctx.nb_tbs--; } } @@ -696,27 +683,29 @@ void tb_flush(CPUArchState *env1) #if defined(DEBUG_FLUSH) printf("qemu: flush code_size=%ld nb_tbs=%d avg_tb_size=%ld\n", - (unsigned long)(code_gen_ptr - code_gen_buffer), - nb_tbs, nb_tbs > 0 ? - ((unsigned long)(code_gen_ptr - code_gen_buffer)) / nb_tbs : 0); + (unsigned long)(tcg_ctx.code_gen_ptr - tcg_ctx.code_gen_buffer), + tcg_ctx.tb_ctx.nb_tbs, tcg_ctx.tb_ctx.nb_tbs > 0 ? + ((unsigned long)(tcg_ctx.code_gen_ptr - tcg_ctx.code_gen_buffer)) / + tcg_ctx.tb_ctx.nb_tbs : 0); #endif - if ((unsigned long)(code_gen_ptr - code_gen_buffer) - > code_gen_buffer_size) { + if ((unsigned long)(tcg_ctx.code_gen_ptr - tcg_ctx.code_gen_buffer) + > tcg_ctx.code_gen_buffer_size) { cpu_abort(env1, "Internal error: code buffer overflow\n"); } - nb_tbs = 0; + tcg_ctx.tb_ctx.nb_tbs = 0; for (env = first_cpu; env != NULL; env = env->next_cpu) { memset(env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof(void *)); } - memset(tb_phys_hash, 0, CODE_GEN_PHYS_HASH_SIZE * sizeof(void *)); + memset(tcg_ctx.tb_ctx.tb_phys_hash, 0, + CODE_GEN_PHYS_HASH_SIZE * sizeof(void *)); page_flush_tb(); - code_gen_ptr = code_gen_buffer; + tcg_ctx.code_gen_ptr = tcg_ctx.code_gen_buffer; /* XXX: flush processor icache at this point if cache flush is expensive */ - tb_flush_count++; + tcg_ctx.tb_ctx.tb_flush_count++; } #ifdef DEBUG_TB_CHECK @@ -728,7 +717,7 @@ static void tb_invalidate_check(target_ulong address) address &= TARGET_PAGE_MASK; for (i = 0; i < CODE_GEN_PHYS_HASH_SIZE; i++) { - for (tb = tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) { + for (tb = tb_ctx.tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) { if (!(address + TARGET_PAGE_SIZE <= tb->pc || address >= tb->pc + tb->size)) { printf("ERROR invalidate: address=" TARGET_FMT_lx @@ -746,7 +735,8 @@ static void tb_page_check(void) int i, flags1, flags2; for (i = 0; i < CODE_GEN_PHYS_HASH_SIZE; i++) { - for (tb = tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) { + for (tb = tcg_ctx.tb_ctx.tb_phys_hash[i]; tb != NULL; + tb = tb->phys_hash_next) { flags1 = page_get_flags(tb->pc); flags2 = page_get_flags(tb->pc + tb->size - 1); if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) { @@ -838,7 +828,7 @@ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) /* remove the TB from the hash list */ phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK); h = tb_phys_hash_func(phys_pc); - tb_hash_remove(&tb_phys_hash[h], tb); + tb_hash_remove(&tcg_ctx.tb_ctx.tb_phys_hash[h], tb); /* remove the TB from the page list */ if (tb->page_addr[0] != page_addr) { @@ -852,7 +842,7 @@ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) invalidate_page_bitmap(p); } - tb_invalidated_flag = 1; + tcg_ctx.tb_ctx.tb_invalidated_flag = 1; /* remove the TB from the hash list */ h = tb_jmp_cache_hash_func(tb->pc); @@ -881,7 +871,7 @@ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) } tb->jmp_first = (TranslationBlock *)((uintptr_t)tb | 2); /* fail safe */ - tb_phys_invalidate_count++; + tcg_ctx.tb_ctx.tb_phys_invalidate_count++; } static inline void set_bits(uint8_t *tab, int start, int len) @@ -958,16 +948,16 @@ TranslationBlock *tb_gen_code(CPUArchState *env, /* cannot fail at this point */ tb = tb_alloc(pc); /* Don't forget to invalidate previous TB info. */ - tb_invalidated_flag = 1; + tcg_ctx.tb_ctx.tb_invalidated_flag = 1; } - tc_ptr = code_gen_ptr; + tc_ptr = tcg_ctx.code_gen_ptr; tb->tc_ptr = tc_ptr; tb->cs_base = cs_base; tb->flags = flags; tb->cflags = cflags; cpu_gen_code(env, tb, &code_gen_size); - code_gen_ptr = (void *)(((uintptr_t)code_gen_ptr + code_gen_size + - CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); + tcg_ctx.code_gen_ptr = (void *)(((uintptr_t)tcg_ctx.code_gen_ptr + + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); /* check next page if needed */ virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; @@ -1008,6 +998,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, { TranslationBlock *tb, *tb_next, *saved_tb; CPUArchState *env = cpu_single_env; + CPUState *cpu = NULL; tb_page_addr_t tb_start, tb_end; PageDesc *p; int n; @@ -1030,6 +1021,9 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, /* build code bitmap */ build_page_bitmap(p); } + if (env != NULL) { + cpu = ENV_GET_CPU(env); + } /* we remove all the TBs in the range [start, end[ */ /* XXX: see if in some cases it could be faster to invalidate all @@ -1076,15 +1070,15 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, /* we need to do that to handle the case where a signal occurs while doing tb_phys_invalidate() */ saved_tb = NULL; - if (env) { - saved_tb = env->current_tb; - env->current_tb = NULL; + if (cpu != NULL) { + saved_tb = cpu->current_tb; + cpu->current_tb = NULL; } tb_phys_invalidate(tb, -1); - if (env) { - env->current_tb = saved_tb; - if (env->interrupt_request && env->current_tb) { - cpu_interrupt(env, env->interrupt_request); + if (cpu != NULL) { + cpu->current_tb = saved_tb; + if (cpu->interrupt_request && cpu->current_tb) { + cpu_interrupt(cpu, cpu->interrupt_request); } } } @@ -1104,7 +1098,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, /* we generate a block containing just the instruction modifying the memory. It will ensure that it cannot modify itself */ - env->current_tb = NULL; + cpu->current_tb = NULL; tb_gen_code(env, current_pc, current_cs_base, current_flags, 1); cpu_resume_from_signal(env, NULL); } @@ -1152,6 +1146,7 @@ static void tb_invalidate_phys_page(tb_page_addr_t addr, #ifdef TARGET_HAS_PRECISE_SMC TranslationBlock *current_tb = NULL; CPUArchState *env = cpu_single_env; + CPUState *cpu = NULL; int current_tb_modified = 0; target_ulong current_pc = 0; target_ulong current_cs_base = 0; @@ -1168,6 +1163,9 @@ static void tb_invalidate_phys_page(tb_page_addr_t addr, if (tb && pc != 0) { current_tb = tb_find_pc(pc); } + if (env != NULL) { + cpu = ENV_GET_CPU(env); + } #endif while (tb != NULL) { n = (uintptr_t)tb & 3; @@ -1196,7 +1194,7 @@ static void tb_invalidate_phys_page(tb_page_addr_t addr, /* we generate a block containing just the instruction modifying the memory. It will ensure that it cannot modify itself */ - env->current_tb = NULL; + cpu->current_tb = NULL; tb_gen_code(env, current_pc, current_cs_base, current_flags, 1); cpu_resume_from_signal(env, puc); } @@ -1276,7 +1274,7 @@ static void tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, mmap_lock(); /* add in the physical hash table */ h = tb_phys_hash_func(phys_pc); - ptb = &tb_phys_hash[h]; + ptb = &tcg_ctx.tb_ctx.tb_phys_hash[h]; tb->phys_hash_next = *ptb; *ptb = tb; @@ -1312,8 +1310,9 @@ bool is_tcg_gen_code(uintptr_t tc_ptr) { /* This can be called during code generation, code_gen_buffer_max_size is used instead of code_gen_ptr for upper boundary checking */ - return (tc_ptr >= (uintptr_t)code_gen_buffer && - tc_ptr < (uintptr_t)(code_gen_buffer + code_gen_buffer_max_size)); + return (tc_ptr >= (uintptr_t)tcg_ctx.code_gen_buffer && + tc_ptr < (uintptr_t)(tcg_ctx.code_gen_buffer + + tcg_ctx.code_gen_buffer_max_size)); } #endif @@ -1325,19 +1324,19 @@ static TranslationBlock *tb_find_pc(uintptr_t tc_ptr) uintptr_t v; TranslationBlock *tb; - if (nb_tbs <= 0) { + if (tcg_ctx.tb_ctx.nb_tbs <= 0) { return NULL; } - if (tc_ptr < (uintptr_t)code_gen_buffer || - tc_ptr >= (uintptr_t)code_gen_ptr) { + if (tc_ptr < (uintptr_t)tcg_ctx.code_gen_buffer || + tc_ptr >= (uintptr_t)tcg_ctx.code_gen_ptr) { return NULL; } /* binary search (cf Knuth) */ m_min = 0; - m_max = nb_tbs - 1; + m_max = tcg_ctx.tb_ctx.nb_tbs - 1; while (m_min <= m_max) { m = (m_min + m_max) >> 1; - tb = &tbs[m]; + tb = &tcg_ctx.tb_ctx.tbs[m]; v = (uintptr_t)tb->tc_ptr; if (v == tc_ptr) { return tb; @@ -1347,56 +1346,7 @@ static TranslationBlock *tb_find_pc(uintptr_t tc_ptr) m_min = m + 1; } } - return &tbs[m_max]; -} - -static void tb_reset_jump_recursive(TranslationBlock *tb); - -static inline void tb_reset_jump_recursive2(TranslationBlock *tb, int n) -{ - TranslationBlock *tb1, *tb_next, **ptb; - unsigned int n1; - - tb1 = tb->jmp_next[n]; - if (tb1 != NULL) { - /* find head of list */ - for (;;) { - n1 = (uintptr_t)tb1 & 3; - tb1 = (TranslationBlock *)((uintptr_t)tb1 & ~3); - if (n1 == 2) { - break; - } - tb1 = tb1->jmp_next[n1]; - } - /* we are now sure now that tb jumps to tb1 */ - tb_next = tb1; - - /* remove tb from the jmp_first list */ - ptb = &tb_next->jmp_first; - for (;;) { - tb1 = *ptb; - n1 = (uintptr_t)tb1 & 3; - tb1 = (TranslationBlock *)((uintptr_t)tb1 & ~3); - if (n1 == n && tb1 == tb) { - break; - } - ptb = &tb1->jmp_next[n1]; - } - *ptb = tb->jmp_next[n]; - tb->jmp_next[n] = NULL; - - /* suppress the jump to next tb in generated code */ - tb_reset_jump(tb, n); - - /* suppress jumps in the tb on which we could have jumped */ - tb_reset_jump_recursive(tb_next); - } -} - -static void tb_reset_jump_recursive(TranslationBlock *tb) -{ - tb_reset_jump_recursive2(tb, 0); - tb_reset_jump_recursive2(tb, 1); + return &tcg_ctx.tb_ctx.tbs[m_max]; } #if defined(TARGET_HAS_ICE) && !defined(CONFIG_USER_ONLY) @@ -1417,26 +1367,6 @@ void tb_invalidate_phys_addr(hwaddr addr) } #endif /* TARGET_HAS_ICE && !defined(CONFIG_USER_ONLY) */ -void cpu_unlink_tb(CPUArchState *env) -{ - /* FIXME: TB unchaining isn't SMP safe. For now just ignore the - problem and hope the cpu will stop of its own accord. For userspace - emulation this often isn't actually as bad as it sounds. Often - signals are used primarily to interrupt blocking syscalls. */ - TranslationBlock *tb; - static spinlock_t interrupt_lock = SPIN_LOCK_UNLOCKED; - - spin_lock(&interrupt_lock); - tb = env->current_tb; - /* if the cpu is currently executing code, we must unlink it and - all the potentially executing TB */ - if (tb) { - env->current_tb = NULL; - tb_reset_jump_recursive(tb); - } - spin_unlock(&interrupt_lock); -} - void tb_check_watchpoint(CPUArchState *env) { TranslationBlock *tb; @@ -1452,13 +1382,13 @@ void tb_check_watchpoint(CPUArchState *env) #ifndef CONFIG_USER_ONLY /* mask must never be zero, except for A20 change call */ -static void tcg_handle_interrupt(CPUArchState *env, int mask) +static void tcg_handle_interrupt(CPUState *cpu, int mask) { - CPUState *cpu = ENV_GET_CPU(env); + CPUArchState *env = cpu->env_ptr; int old_mask; - old_mask = env->interrupt_request; - env->interrupt_request |= mask; + old_mask = cpu->interrupt_request; + cpu->interrupt_request |= mask; /* * If called from iothread context, wake the target cpu in @@ -1476,7 +1406,7 @@ static void tcg_handle_interrupt(CPUArchState *env, int mask) cpu_abort(env, "Raised interrupt while not in I/O function"); } } else { - cpu_unlink_tb(env); + cpu->tcg_exit_req = 1; } } @@ -1568,8 +1498,8 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) cross_page = 0; direct_jmp_count = 0; direct_jmp2_count = 0; - for (i = 0; i < nb_tbs; i++) { - tb = &tbs[i]; + for (i = 0; i < tcg_ctx.tb_ctx.nb_tbs; i++) { + tb = &tcg_ctx.tb_ctx.tbs[i]; target_code_size += tb->size; if (tb->size > max_target_code_size) { max_target_code_size = tb->size; @@ -1587,37 +1517,45 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) /* XXX: avoid using doubles ? */ cpu_fprintf(f, "Translation buffer state:\n"); cpu_fprintf(f, "gen code size %td/%zd\n", - code_gen_ptr - code_gen_buffer, code_gen_buffer_max_size); + tcg_ctx.code_gen_ptr - tcg_ctx.code_gen_buffer, + tcg_ctx.code_gen_buffer_max_size); cpu_fprintf(f, "TB count %d/%d\n", - nb_tbs, code_gen_max_blocks); + tcg_ctx.tb_ctx.nb_tbs, tcg_ctx.code_gen_max_blocks); cpu_fprintf(f, "TB avg target size %d max=%d bytes\n", - nb_tbs ? target_code_size / nb_tbs : 0, - max_target_code_size); + tcg_ctx.tb_ctx.nb_tbs ? target_code_size / + tcg_ctx.tb_ctx.nb_tbs : 0, + max_target_code_size); cpu_fprintf(f, "TB avg host size %td bytes (expansion ratio: %0.1f)\n", - nb_tbs ? (code_gen_ptr - code_gen_buffer) / nb_tbs : 0, - target_code_size ? (double) (code_gen_ptr - code_gen_buffer) - / target_code_size : 0); - cpu_fprintf(f, "cross page TB count %d (%d%%)\n", - cross_page, - nb_tbs ? (cross_page * 100) / nb_tbs : 0); + tcg_ctx.tb_ctx.nb_tbs ? (tcg_ctx.code_gen_ptr - + tcg_ctx.code_gen_buffer) / + tcg_ctx.tb_ctx.nb_tbs : 0, + target_code_size ? (double) (tcg_ctx.code_gen_ptr - + tcg_ctx.code_gen_buffer) / + target_code_size : 0); + cpu_fprintf(f, "cross page TB count %d (%d%%)\n", cross_page, + tcg_ctx.tb_ctx.nb_tbs ? (cross_page * 100) / + tcg_ctx.tb_ctx.nb_tbs : 0); cpu_fprintf(f, "direct jump count %d (%d%%) (2 jumps=%d %d%%)\n", direct_jmp_count, - nb_tbs ? (direct_jmp_count * 100) / nb_tbs : 0, + tcg_ctx.tb_ctx.nb_tbs ? (direct_jmp_count * 100) / + tcg_ctx.tb_ctx.nb_tbs : 0, direct_jmp2_count, - nb_tbs ? (direct_jmp2_count * 100) / nb_tbs : 0); + tcg_ctx.tb_ctx.nb_tbs ? (direct_jmp2_count * 100) / + tcg_ctx.tb_ctx.nb_tbs : 0); cpu_fprintf(f, "\nStatistics:\n"); - cpu_fprintf(f, "TB flush count %d\n", tb_flush_count); - cpu_fprintf(f, "TB invalidate count %d\n", tb_phys_invalidate_count); + cpu_fprintf(f, "TB flush count %d\n", tcg_ctx.tb_ctx.tb_flush_count); + cpu_fprintf(f, "TB invalidate count %d\n", + tcg_ctx.tb_ctx.tb_phys_invalidate_count); cpu_fprintf(f, "TLB flush count %d\n", tlb_flush_count); tcg_dump_info(f, cpu_fprintf); } #else /* CONFIG_USER_ONLY */ -void cpu_interrupt(CPUArchState *env, int mask) +void cpu_interrupt(CPUState *cpu, int mask) { - env->interrupt_request |= mask; - cpu_unlink_tb(env); + cpu->interrupt_request |= mask; + cpu->tcg_exit_req = 1; } /* diff --git a/translate-all.h b/translate-all.h index b181fb48ad..5c38819eb8 100644 --- a/translate-all.h +++ b/translate-all.h @@ -28,7 +28,7 @@ /* translate-all.c */ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); -void cpu_unlink_tb(CPUArchState *env); +void cpu_unlink_tb(CPUState *cpu); void tb_check_watchpoint(CPUArchState *env); #endif /* TRANSLATE_ALL_H */ diff --git a/ui/Makefile.objs b/ui/Makefile.objs index 6768bb7f7e..6ddc0def6d 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -4,6 +4,7 @@ vnc-obj-y += vnc-enc-tight.o vnc-palette.o vnc-obj-y += vnc-enc-zrle.o vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o +vnc-obj-$(CONFIG_VNC_WS) += vnc-ws.o vnc-obj-y += vnc-jobs.o common-obj-y += keymaps.o console.o cursor.o input.o qemu-pixman.o @@ -12,7 +13,10 @@ common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o common-obj-$(CONFIG_COCOA) += cocoa.o common-obj-$(CONFIG_CURSES) += curses.o common-obj-$(CONFIG_VNC) += $(vnc-obj-y) +common-obj-$(CONFIG_GTK) += gtk.o x_keymap.o $(obj)/sdl.o $(obj)/sdl_zoom.o: QEMU_CFLAGS += $(SDL_CFLAGS) $(obj)/cocoa.o: $(SRC_PATH)/$(obj)/cocoa.m + +$(obj)/gtk.o: QEMU_CFLAGS += $(GTK_CFLAGS) $(VTE_CFLAGS) diff --git a/ui/cocoa.m b/ui/cocoa.m index 3bf1c6e890..ca42413b34 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -265,6 +265,7 @@ static int cocoa_keycode_to_qemu(int keycode) BOOL isTabletEnabled; } - (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds; +- (void) updateDataOffset:(DisplayState *)ds; - (void) grabMouse; - (void) ungrabMouse; - (void) toggleFullScreen:(id)sender; @@ -429,6 +430,20 @@ QemuCocoaView *cocoaView; [self setFrame:NSMakeRect(cx, cy, cw, ch)]; } +- (void) updateDataOffset:(DisplayState *)ds +{ + COCOA_DEBUG("QemuCocoaView: UpdateDataOffset\n"); + + // update screenBuffer + if (dataProviderRef) { + CGDataProviderRelease(dataProviderRef); + } + + size_t size = ds_get_width(ds) * 4 * ds_get_height(ds); + dataProviderRef = CGDataProviderCreateWithData(NULL, ds_get_data(ds), + size, NULL); +} + - (void) toggleFullScreen:(id)sender { COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n"); @@ -813,9 +828,9 @@ QemuCocoaView *cocoaView; [sheet close]; - asprintf(&argv[0], "%s", bin); - asprintf(&argv[1], "-hda"); - asprintf(&argv[2], "%s", img); + argv[0] = g_strdup_printf("%s", bin); + argv[1] = g_strdup_printf("-hda"); + argv[2] = g_strdup_printf("%s", img); printf("Using argc %d argv %s -hda %s\n", 3, bin, img); @@ -1004,6 +1019,11 @@ static void cocoa_refresh(DisplayState *ds) vga_hw_update(); } +static void cocoa_setdata(DisplayState *ds) +{ + [cocoaView updateDataOffset:ds]; +} + static void cocoa_cleanup(void) { COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n"); @@ -1020,6 +1040,7 @@ void cocoa_display_init(DisplayState *ds, int full_screen) dcl->dpy_gfx_update = cocoa_update; dcl->dpy_gfx_resize = cocoa_resize; dcl->dpy_refresh = cocoa_refresh; + dcl->dpy_gfx_setdata = cocoa_setdata; register_displaychangelistener(ds, dcl); diff --git a/ui/console.c b/ui/console.c index d41cbbc835..ace79aa63e 100644 --- a/ui/console.c +++ b/ui/console.c @@ -194,7 +194,7 @@ void qmp_screendump(const char *filename, Error **errp) if (consoles[0] && consoles[0]->hw_screen_dump) { consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch, errp); } else { - error_setg(errp, "device doesn't support screendump\n"); + error_setg(errp, "device doesn't support screendump"); } if (cswitch) { @@ -1341,11 +1341,16 @@ DisplaySurface *qemu_resize_displaysurface(DisplayState *ds, } DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp, - int linesize, uint8_t *data) + int linesize, uint8_t *data, + bool byteswap) { DisplaySurface *surface = g_new0(DisplaySurface, 1); - surface->pf = qemu_default_pixelformat(bpp); + if (byteswap) { + surface->pf = qemu_different_endianness_pixelformat(bpp); + } else { + surface->pf = qemu_default_pixelformat(bpp); + } surface->format = qemu_pixman_get_format(&surface->pf); assert(surface->format != 0); @@ -1534,22 +1539,26 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) chr->init(chr); } -CharDriverState *text_console_init(QemuOpts *opts) +static CharDriverState *text_console_init(ChardevVC *vc) { CharDriverState *chr; QemuConsole *s; - unsigned width; - unsigned height; + unsigned width = 0; + unsigned height = 0; chr = g_malloc0(sizeof(CharDriverState)); - width = qemu_opt_get_number(opts, "width", 0); - if (width == 0) - width = qemu_opt_get_number(opts, "cols", 0) * FONT_WIDTH; + if (vc->has_width) { + width = vc->width; + } else if (vc->has_cols) { + width = vc->cols * FONT_WIDTH; + } - height = qemu_opt_get_number(opts, "height", 0); - if (height == 0) - height = qemu_opt_get_number(opts, "rows", 0) * FONT_HEIGHT; + if (vc->has_height) { + height = vc->height; + } else if (vc->has_rows) { + height = vc->rows * FONT_HEIGHT; + } if (width == 0 || height == 0) { s = new_console(NULL, TEXT_CONSOLE); @@ -1570,6 +1579,18 @@ CharDriverState *text_console_init(QemuOpts *opts) return chr; } +static VcHandler *vc_handler = text_console_init; + +CharDriverState *vc_init(ChardevVC *vc) +{ + return vc_handler(vc); +} + +void register_vc_handler(VcHandler *handler) +{ + vc_handler = handler; +} + void text_consoles_set_display(DisplayState *ds) { int i; @@ -1724,3 +1745,43 @@ PixelFormat qemu_default_pixelformat(int bpp) } return pf; } + +static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + int val; + + backend->vc = g_new0(ChardevVC, 1); + + val = qemu_opt_get_number(opts, "width", 0); + if (val != 0) { + backend->vc->has_width = true; + backend->vc->width = val; + } + + val = qemu_opt_get_number(opts, "height", 0); + if (val != 0) { + backend->vc->has_height = true; + backend->vc->height = val; + } + + val = qemu_opt_get_number(opts, "cols", 0); + if (val != 0) { + backend->vc->has_cols = true; + backend->vc->cols = val; + } + + val = qemu_opt_get_number(opts, "rows", 0); + if (val != 0) { + backend->vc->has_rows = true; + backend->vc->rows = val; + } +} + +static void register_types(void) +{ + register_char_driver_qapi("vc", CHARDEV_BACKEND_KIND_VC, + qemu_chr_parse_vc); +} + +type_init(register_types); diff --git a/ui/gtk.c b/ui/gtk.c new file mode 100644 index 0000000000..794dab15b1 --- /dev/null +++ b/ui/gtk.c @@ -0,0 +1,1353 @@ +/* + * GTK UI + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Portions from gtk-vnc: + * + * GTK VNC Widget + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2009-2010 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define GETTEXT_PACKAGE "qemu" +#define LOCALEDIR "po" + +#include "qemu-common.h" + +#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE +/* Work around an -Wstrict-prototypes warning in GTK headers */ +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif +#include +#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE +#pragma GCC diagnostic error "-Wstrict-prototypes" +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ui/console.h" +#include "sysemu/sysemu.h" +#include "qmp-commands.h" +#include "x_keymap.h" +#include "keymaps.h" +#include "char/char.h" + +//#define DEBUG_GTK + +#ifdef DEBUG_GTK +#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) do { } while (0) +#endif + +#define MAX_VCS 10 + + +/* Compatibility define to let us build on both Gtk2 and Gtk3 */ +#if GTK_CHECK_VERSION(3, 0, 0) +static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh) +{ + *ww = gdk_window_get_width(w); + *wh = gdk_window_get_height(w); +} +#endif + +#if !GTK_CHECK_VERSION(2, 20, 0) +#define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget) +#endif + +#ifndef GDK_KEY_0 +#define GDK_KEY_0 GDK_0 +#define GDK_KEY_1 GDK_1 +#define GDK_KEY_2 GDK_2 +#define GDK_KEY_f GDK_f +#define GDK_KEY_g GDK_g +#define GDK_KEY_plus GDK_plus +#define GDK_KEY_minus GDK_minus +#endif + +typedef struct VirtualConsole +{ + GtkWidget *menu_item; + GtkWidget *terminal; + GtkWidget *scrolled_window; + CharDriverState *chr; + int fd; +} VirtualConsole; + +typedef struct GtkDisplayState +{ + GtkWidget *window; + + GtkWidget *menu_bar; + + GtkAccelGroup *accel_group; + + GtkWidget *machine_menu_item; + GtkWidget *machine_menu; + GtkWidget *pause_item; + GtkWidget *reset_item; + GtkWidget *powerdown_item; + GtkWidget *quit_item; + + GtkWidget *view_menu_item; + GtkWidget *view_menu; + GtkWidget *full_screen_item; + GtkWidget *zoom_in_item; + GtkWidget *zoom_out_item; + GtkWidget *zoom_fixed_item; + GtkWidget *zoom_fit_item; + GtkWidget *grab_item; + GtkWidget *grab_on_hover_item; + GtkWidget *vga_item; + + int nb_vcs; + VirtualConsole vc[MAX_VCS]; + + GtkWidget *show_tabs_item; + + GtkWidget *vbox; + GtkWidget *notebook; + GtkWidget *drawing_area; + cairo_surface_t *surface; + DisplayChangeListener dcl; + DisplayState *ds; + int button_mask; + int last_x; + int last_y; + + double scale_x; + double scale_y; + gboolean full_screen; + + GdkCursor *null_cursor; + Notifier mouse_mode_notifier; + gboolean free_scale; + + bool external_pause_update; +} GtkDisplayState; + +static GtkDisplayState *global_state; + +/** Utility Functions **/ + +static bool gd_is_grab_active(GtkDisplayState *s) +{ + return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item)); +} + +static bool gd_grab_on_hover(GtkDisplayState *s) +{ + return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item)); +} + +static bool gd_on_vga(GtkDisplayState *s) +{ + return gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)) == 0; +} + +static void gd_update_cursor(GtkDisplayState *s, gboolean override) +{ + GdkWindow *window; + bool on_vga; + + window = gtk_widget_get_window(GTK_WIDGET(s->drawing_area)); + + on_vga = gd_on_vga(s); + + if ((override || on_vga) && + (s->full_screen || kbd_mouse_is_absolute() || gd_is_grab_active(s))) { + gdk_window_set_cursor(window, s->null_cursor); + } else { + gdk_window_set_cursor(window, NULL); + } +} + +static void gd_update_caption(GtkDisplayState *s) +{ + const char *status = ""; + gchar *title; + const char *grab = ""; + bool is_paused = !runstate_is_running(); + + if (gd_is_grab_active(s)) { + grab = " - Press Ctrl+Alt+G to release grab"; + } + + if (is_paused) { + status = " [Paused]"; + } + s->external_pause_update = true; + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item), + is_paused); + s->external_pause_update = false; + + if (qemu_name) { + title = g_strdup_printf("QEMU (%s)%s%s", qemu_name, status, grab); + } else { + title = g_strdup_printf("QEMU%s%s", status, grab); + } + + gtk_window_set_title(GTK_WINDOW(s->window), title); + + g_free(title); +} + +/** DisplayState Callbacks **/ + +static void gd_update(DisplayState *ds, int x, int y, int w, int h) +{ + GtkDisplayState *s = ds->opaque; + int x1, x2, y1, y2; + int mx, my; + int fbw, fbh; + int ww, wh; + + DPRINTF("update(x=%d, y=%d, w=%d, h=%d)\n", x, y, w, h); + + x1 = floor(x * s->scale_x); + y1 = floor(y * s->scale_y); + + x2 = ceil(x * s->scale_x + w * s->scale_x); + y2 = ceil(y * s->scale_y + h * s->scale_y); + + fbw = ds_get_width(s->ds) * s->scale_x; + fbh = ds_get_height(s->ds) * s->scale_y; + + gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); + + mx = my = 0; + if (ww > fbw) { + mx = (ww - fbw) / 2; + } + if (wh > fbh) { + my = (wh - fbh) / 2; + } + + gtk_widget_queue_draw_area(s->drawing_area, mx + x1, my + y1, (x2 - x1), (y2 - y1)); +} + +static void gd_refresh(DisplayState *ds) +{ + vga_hw_update(); +} + +static void gd_resize(DisplayState *ds) +{ + GtkDisplayState *s = ds->opaque; + cairo_format_t kind; + int stride; + + DPRINTF("resize(width=%d, height=%d)\n", + ds_get_width(ds), ds_get_height(ds)); + + if (s->surface) { + cairo_surface_destroy(s->surface); + } + + switch (ds->surface->pf.bits_per_pixel) { + case 8: + kind = CAIRO_FORMAT_A8; + break; + case 16: + kind = CAIRO_FORMAT_RGB16_565; + break; + case 32: + kind = CAIRO_FORMAT_RGB24; + break; + default: + g_assert_not_reached(); + break; + } + + stride = cairo_format_stride_for_width(kind, ds_get_width(ds)); + g_assert(ds_get_linesize(ds) == stride); + + s->surface = cairo_image_surface_create_for_data(ds_get_data(ds), + kind, + ds_get_width(ds), + ds_get_height(ds), + ds_get_linesize(ds)); + + if (!s->full_screen) { + GtkRequisition req; + double sx, sy; + + if (s->free_scale) { + sx = s->scale_x; + sy = s->scale_y; + + s->scale_y = 1.0; + s->scale_x = 1.0; + } else { + sx = 1.0; + sy = 1.0; + } + + gtk_widget_set_size_request(s->drawing_area, + ds_get_width(ds) * s->scale_x, + ds_get_height(ds) * s->scale_y); +#if GTK_CHECK_VERSION(3, 0, 0) + gtk_widget_get_preferred_size(s->vbox, NULL, &req); +#else + gtk_widget_size_request(s->vbox, &req); +#endif + + gtk_window_resize(GTK_WINDOW(s->window), + req.width * sx, req.height * sy); + } +} + +/** QEMU Events **/ + +static void gd_change_runstate(void *opaque, int running, RunState state) +{ + GtkDisplayState *s = opaque; + + gd_update_caption(s); +} + +static void gd_mouse_mode_change(Notifier *notify, void *data) +{ + gd_update_cursor(container_of(notify, GtkDisplayState, mouse_mode_notifier), + FALSE); +} + +/** GTK Events **/ + +static gboolean gd_window_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) +{ + GtkDisplayState *s = opaque; + GtkAccelGroupEntry *entries; + guint n_entries = 0; + gboolean propagate_accel = TRUE; + gboolean handled = FALSE; + + entries = gtk_accel_group_query(s->accel_group, key->keyval, + key->state, &n_entries); + if (n_entries) { + const char *quark = g_quark_to_string(entries[0].accel_path_quark); + + if (gd_is_grab_active(s) && strstart(quark, "/File/", NULL)) { + propagate_accel = FALSE; + } + } + + if (!handled && propagate_accel) { + handled = gtk_window_activate_key(GTK_WINDOW(widget), key); + } + + if (!handled) { + handled = gtk_window_propagate_key_event(GTK_WINDOW(widget), key); + } + + return handled; +} + +static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event, + void *opaque) +{ + GtkDisplayState *s = opaque; + + if (!no_quit) { + unregister_displaychangelistener(s->ds, &s->dcl); + qmp_quit(NULL); + return FALSE; + } + + return TRUE; +} + +static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) +{ + GtkDisplayState *s = opaque; + int mx, my; + int ww, wh; + int fbw, fbh; + + if (!gtk_widget_get_realized(widget)) { + return FALSE; + } + + fbw = ds_get_width(s->ds); + fbh = ds_get_height(s->ds); + + gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh); + + if (s->full_screen) { + s->scale_x = (double)ww / fbw; + s->scale_y = (double)wh / fbh; + } else if (s->free_scale) { + double sx, sy; + + sx = (double)ww / fbw; + sy = (double)wh / fbh; + + s->scale_x = s->scale_y = MIN(sx, sy); + } + + fbw *= s->scale_x; + fbh *= s->scale_y; + + mx = my = 0; + if (ww > fbw) { + mx = (ww - fbw) / 2; + } + if (wh > fbh) { + my = (wh - fbh) / 2; + } + + cairo_rectangle(cr, 0, 0, ww, wh); + + /* Optionally cut out the inner area where the pixmap + will be drawn. This avoids 'flashing' since we're + not double-buffering. Note we're using the undocumented + behaviour of drawing the rectangle from right to left + to cut out the whole */ + cairo_rectangle(cr, mx + fbw, my, + -1 * fbw, fbh); + cairo_fill(cr); + + cairo_scale(cr, s->scale_x, s->scale_y); + cairo_set_source_surface(cr, s->surface, mx / s->scale_x, my / s->scale_y); + cairo_paint(cr); + + return TRUE; +} + +#if !GTK_CHECK_VERSION(3, 0, 0) +static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose, + void *opaque) +{ + cairo_t *cr; + gboolean ret; + + cr = gdk_cairo_create(gtk_widget_get_window(widget)); + cairo_rectangle(cr, + expose->area.x, + expose->area.y, + expose->area.width, + expose->area.height); + cairo_clip(cr); + + ret = gd_draw_event(widget, cr, opaque); + + cairo_destroy(cr); + + return ret; +} +#endif + +static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, + void *opaque) +{ + GtkDisplayState *s = opaque; + int dx, dy; + int x, y; + int mx, my; + int fbh, fbw; + int ww, wh; + + fbw = ds_get_width(s->ds) * s->scale_x; + fbh = ds_get_height(s->ds) * s->scale_y; + + gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); + + mx = my = 0; + if (ww > fbw) { + mx = (ww - fbw) / 2; + } + if (wh > fbh) { + my = (wh - fbh) / 2; + } + + x = (motion->x - mx) / s->scale_x; + y = (motion->y - my) / s->scale_y; + + if (x < 0 || y < 0 || + x >= ds_get_width(s->ds) || + y >= ds_get_height(s->ds)) { + return TRUE; + } + + if (kbd_mouse_is_absolute()) { + dx = x * 0x7FFF / (ds_get_width(s->ds) - 1); + dy = y * 0x7FFF / (ds_get_height(s->ds) - 1); + } else if (s->last_x == -1 || s->last_y == -1) { + dx = 0; + dy = 0; + } else { + dx = x - s->last_x; + dy = y - s->last_y; + } + + s->last_x = x; + s->last_y = y; + + if (kbd_mouse_is_absolute() || gd_is_grab_active(s)) { + kbd_mouse_event(dx, dy, 0, s->button_mask); + } + + if (!kbd_mouse_is_absolute() && gd_is_grab_active(s)) { + GdkScreen *screen = gtk_widget_get_screen(s->drawing_area); + int x = (int)motion->x_root; + int y = (int)motion->y_root; + + /* In relative mode check to see if client pointer hit + * one of the screen edges, and if so move it back by + * 200 pixels. This is important because the pointer + * in the server doesn't correspond 1-for-1, and so + * may still be only half way across the screen. Without + * this warp, the server pointer would thus appear to hit + * an invisible wall */ + if (x == 0) { + x += 200; + } + if (y == 0) { + y += 200; + } + if (x == (gdk_screen_get_width(screen) - 1)) { + x -= 200; + } + if (y == (gdk_screen_get_height(screen) - 1)) { + y -= 200; + } + + if (x != (int)motion->x_root || y != (int)motion->y_root) { +#if GTK_CHECK_VERSION(3, 0, 0) + GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion); + gdk_device_warp(dev, screen, x, y); +#else + GdkDisplay *display = gtk_widget_get_display(widget); + gdk_display_warp_pointer(display, screen, x, y); +#endif + s->last_x = -1; + s->last_y = -1; + return FALSE; + } + } + return TRUE; +} + +static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button, + void *opaque) +{ + GtkDisplayState *s = opaque; + int dx, dy; + int n; + + if (button->button == 1) { + n = 0x01; + } else if (button->button == 2) { + n = 0x04; + } else if (button->button == 3) { + n = 0x02; + } else { + n = 0x00; + } + + if (button->type == GDK_BUTTON_PRESS) { + s->button_mask |= n; + } else if (button->type == GDK_BUTTON_RELEASE) { + s->button_mask &= ~n; + } + + if (kbd_mouse_is_absolute()) { + dx = s->last_x * 0x7FFF / (ds_get_width(s->ds) - 1); + dy = s->last_y * 0x7FFF / (ds_get_height(s->ds) - 1); + } else { + dx = 0; + dy = 0; + } + + kbd_mouse_event(dx, dy, 0, s->button_mask); + + return TRUE; +} + +static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) +{ + int gdk_keycode; + int qemu_keycode; + + gdk_keycode = key->hardware_keycode; + + if (gdk_keycode < 9) { + qemu_keycode = 0; + } else if (gdk_keycode < 97) { + qemu_keycode = gdk_keycode - 8; + } else if (gdk_keycode < 158) { + qemu_keycode = translate_evdev_keycode(gdk_keycode - 97); + } else if (gdk_keycode == 208) { /* Hiragana_Katakana */ + qemu_keycode = 0x70; + } else if (gdk_keycode == 211) { /* backslash */ + qemu_keycode = 0x73; + } else { + qemu_keycode = 0; + } + + DPRINTF("translated GDK keycode %d to QEMU keycode %d (%s)\n", + gdk_keycode, qemu_keycode, + (key->type == GDK_KEY_PRESS) ? "down" : "up"); + + if (qemu_keycode & SCANCODE_GREY) { + kbd_put_keycode(SCANCODE_EMUL0); + } + + if (key->type == GDK_KEY_PRESS) { + kbd_put_keycode(qemu_keycode & SCANCODE_KEYCODEMASK); + } else if (key->type == GDK_KEY_RELEASE) { + kbd_put_keycode(qemu_keycode | SCANCODE_UP); + } else { + g_assert_not_reached(); + } + + return TRUE; +} + +/** Window Menu Actions **/ + +static void gd_menu_pause(GtkMenuItem *item, void *opaque) +{ + GtkDisplayState *s = opaque; + + if (s->external_pause_update) { + return; + } + if (runstate_is_running()) { + qmp_stop(NULL); + } else { + qmp_cont(NULL); + } +} + +static void gd_menu_reset(GtkMenuItem *item, void *opaque) +{ + qmp_system_reset(NULL); +} + +static void gd_menu_powerdown(GtkMenuItem *item, void *opaque) +{ + qmp_system_powerdown(NULL); +} + +static void gd_menu_quit(GtkMenuItem *item, void *opaque) +{ + qmp_quit(NULL); +} + +static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque) +{ + GtkDisplayState *s = opaque; + + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) { + gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), 0); + } else { + int i; + + for (i = 0; i < s->nb_vcs; i++) { + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) { + gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), i + 1); + break; + } + } + } +} + +static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque) +{ + GtkDisplayState *s = opaque; + + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) { + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE); + } else { + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); + } +} + +static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) +{ + GtkDisplayState *s = opaque; + + if (!s->full_screen) { + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); + gtk_widget_set_size_request(s->menu_bar, 0, 0); + gtk_widget_set_size_request(s->drawing_area, -1, -1); + gtk_window_fullscreen(GTK_WINDOW(s->window)); + if (gd_on_vga(s)) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE); + } + s->full_screen = TRUE; + } else { + gtk_window_unfullscreen(GTK_WINDOW(s->window)); + gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s); + gtk_widget_set_size_request(s->menu_bar, -1, -1); + gtk_widget_set_size_request(s->drawing_area, + ds_get_width(s->ds), ds_get_height(s->ds)); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE); + s->full_screen = FALSE; + s->scale_x = 1.0; + s->scale_y = 1.0; + } + + gd_update_cursor(s, FALSE); +} + +static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque) +{ + GtkDisplayState *s = opaque; + + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), + FALSE); + + s->scale_x += .25; + s->scale_y += .25; + + gd_resize(s->ds); +} + +static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque) +{ + GtkDisplayState *s = opaque; + + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item), + FALSE); + + s->scale_x -= .25; + s->scale_y -= .25; + + s->scale_x = MAX(s->scale_x, .25); + s->scale_y = MAX(s->scale_y, .25); + + gd_resize(s->ds); +} + +static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque) +{ + GtkDisplayState *s = opaque; + + s->scale_x = 1.0; + s->scale_y = 1.0; + + gd_resize(s->ds); +} + +static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque) +{ + GtkDisplayState *s = opaque; + int ww, wh; + + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) { + s->free_scale = TRUE; + } else { + s->free_scale = FALSE; + } + + gd_resize(s->ds); + + gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh); + gtk_widget_queue_draw_area(s->drawing_area, 0, 0, ww, wh); +} + +static void gd_grab_keyboard(GtkDisplayState *s) +{ +#if GTK_CHECK_VERSION(3, 0, 0) + GdkDisplay *display = gtk_widget_get_display(s->drawing_area); + GdkDeviceManager *mgr = gdk_display_get_device_manager(display); + GList *devices = gdk_device_manager_list_devices(mgr, + GDK_DEVICE_TYPE_MASTER); + GList *tmp = devices; + while (tmp) { + GdkDevice *dev = tmp->data; + if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) { + gdk_device_grab(dev, + gtk_widget_get_window(s->drawing_area), + GDK_OWNERSHIP_NONE, + FALSE, + GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, + NULL, + GDK_CURRENT_TIME); + } + tmp = tmp->next; + } + g_list_free(devices); +#else + gdk_keyboard_grab(gtk_widget_get_window(s->drawing_area), + FALSE, + GDK_CURRENT_TIME); +#endif +} + +static void gd_ungrab_keyboard(GtkDisplayState *s) +{ +#if GTK_CHECK_VERSION(3, 0, 0) + GdkDisplay *display = gtk_widget_get_display(s->drawing_area); + GdkDeviceManager *mgr = gdk_display_get_device_manager(display); + GList *devices = gdk_device_manager_list_devices(mgr, + GDK_DEVICE_TYPE_MASTER); + GList *tmp = devices; + while (tmp) { + GdkDevice *dev = tmp->data; + if (gdk_device_get_source(dev) == GDK_SOURCE_KEYBOARD) { + gdk_device_ungrab(dev, + GDK_CURRENT_TIME); + } + tmp = tmp->next; + } + g_list_free(devices); +#else + gdk_keyboard_ungrab(GDK_CURRENT_TIME); +#endif +} + +static void gd_grab_pointer(GtkDisplayState *s) +{ +#if GTK_CHECK_VERSION(3, 0, 0) + GdkDisplay *display = gtk_widget_get_display(s->drawing_area); + GdkDeviceManager *mgr = gdk_display_get_device_manager(display); + GList *devices = gdk_device_manager_list_devices(mgr, + GDK_DEVICE_TYPE_MASTER); + GList *tmp = devices; + while (tmp) { + GdkDevice *dev = tmp->data; + if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) { + gdk_device_grab(dev, + gtk_widget_get_window(s->drawing_area), + GDK_OWNERSHIP_NONE, + FALSE, /* All events to come to our + window directly */ + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_MOTION_MASK | + GDK_SCROLL_MASK, + s->null_cursor, + GDK_CURRENT_TIME); + } + tmp = tmp->next; + } + g_list_free(devices); +#else + gdk_pointer_grab(gtk_widget_get_window(s->drawing_area), + FALSE, /* All events to come to our window directly */ + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_MOTION_MASK | + GDK_SCROLL_MASK, + NULL, /* Allow cursor to move over entire desktop */ + s->null_cursor, + GDK_CURRENT_TIME); +#endif +} + +static void gd_ungrab_pointer(GtkDisplayState *s) +{ +#if GTK_CHECK_VERSION(3, 0, 0) + GdkDisplay *display = gtk_widget_get_display(s->drawing_area); + GdkDeviceManager *mgr = gdk_display_get_device_manager(display); + GList *devices = gdk_device_manager_list_devices(mgr, + GDK_DEVICE_TYPE_MASTER); + GList *tmp = devices; + while (tmp) { + GdkDevice *dev = tmp->data; + if (gdk_device_get_source(dev) == GDK_SOURCE_MOUSE) { + gdk_device_ungrab(dev, + GDK_CURRENT_TIME); + } + tmp = tmp->next; + } + g_list_free(devices); +#else + gdk_pointer_ungrab(GDK_CURRENT_TIME); +#endif +} + +static void gd_menu_grab_input(GtkMenuItem *item, void *opaque) +{ + GtkDisplayState *s = opaque; + + if (gd_is_grab_active(s)) { + gd_grab_keyboard(s); + gd_grab_pointer(s); + } else { + gd_ungrab_keyboard(s); + gd_ungrab_pointer(s); + } + + gd_update_caption(s); + gd_update_cursor(s, FALSE); +} + +static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, + gpointer data) +{ + GtkDisplayState *s = data; + guint last_page; + gboolean on_vga; + + if (!gtk_widget_get_realized(s->notebook)) { + return; + } + + last_page = gtk_notebook_get_current_page(nb); + + if (last_page) { + gtk_widget_set_size_request(s->vc[last_page - 1].terminal, -1, -1); + } + + on_vga = arg2 == 0; + + if (!on_vga) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), + FALSE); + } else if (s->full_screen) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), + TRUE); + } + + if (arg2 == 0) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE); + } else { + VirtualConsole *vc = &s->vc[arg2 - 1]; + VteTerminal *term = VTE_TERMINAL(vc->terminal); + int width, height; + + width = 80 * vte_terminal_get_char_width(term); + height = 25 * vte_terminal_get_char_height(term); + + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); + gtk_widget_set_size_request(vc->terminal, width, height); + } + + gtk_widget_set_sensitive(s->grab_item, on_vga); + + gd_update_cursor(s, TRUE); +} + +static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data) +{ + GtkDisplayState *s = data; + + if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) { + gd_grab_keyboard(s); + } + + return TRUE; +} + +static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data) +{ + GtkDisplayState *s = data; + + if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) { + gd_ungrab_keyboard(s); + } + + return TRUE; +} + +/** Virtual Console Callbacks **/ + +static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + VirtualConsole *vc = chr->opaque; + + return write(vc->fd, buf, len); +} + +static int nb_vcs; +static CharDriverState *vcs[MAX_VCS]; + +static CharDriverState *gd_vc_handler(ChardevVC *unused) +{ + CharDriverState *chr; + + chr = g_malloc0(sizeof(*chr)); + chr->chr_write = gd_vc_chr_write; + + vcs[nb_vcs++] = chr; + + return chr; +} + +void early_gtk_display_init(void) +{ + register_vc_handler(gd_vc_handler); +} + +static gboolean gd_vc_in(GIOChannel *chan, GIOCondition cond, void *opaque) +{ + VirtualConsole *vc = opaque; + uint8_t buffer[1024]; + ssize_t len; + + len = read(vc->fd, buffer, sizeof(buffer)); + if (len <= 0) { + return FALSE; + } + + qemu_chr_be_write(vc->chr, buffer, len); + + return TRUE; +} + +static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group) +{ + const char *label; + char buffer[32]; + char path[32]; +#if VTE_CHECK_VERSION(0, 26, 0) + VtePty *pty; +#endif + GIOChannel *chan; + GtkWidget *scrolled_window; + GtkAdjustment *vadjustment; + int master_fd, slave_fd, ret; + struct termios tty; + + snprintf(buffer, sizeof(buffer), "vc%d", index); + snprintf(path, sizeof(path), "/View/VC%d", index); + + vc->chr = vcs[index]; + + if (vc->chr->label) { + label = vc->chr->label; + } else { + label = buffer; + } + + vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label); + group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item)); + gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path); + gtk_accel_map_add_entry(path, GDK_KEY_2 + index, GDK_CONTROL_MASK | GDK_MOD1_MASK); + + vc->terminal = vte_terminal_new(); + + ret = openpty(&master_fd, &slave_fd, NULL, NULL, NULL); + g_assert(ret != -1); + + /* Set raw attributes on the pty. */ + tcgetattr(slave_fd, &tty); + cfmakeraw(&tty); + tcsetattr(slave_fd, TCSAFLUSH, &tty); + +#if VTE_CHECK_VERSION(0, 26, 0) + pty = vte_pty_new_foreign(master_fd, NULL); + vte_terminal_set_pty_object(VTE_TERMINAL(vc->terminal), pty); +#else + vte_terminal_set_pty(VTE_TERMINAL(vc->terminal), master_fd); +#endif + + vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1); + + vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal)); + + scrolled_window = gtk_scrolled_window_new(NULL, vadjustment); + gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal); + + vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25); + + vc->fd = slave_fd; + vc->chr->opaque = vc; + vc->scrolled_window = scrolled_window; + + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc->scrolled_window), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), scrolled_window, gtk_label_new(label)); + g_signal_connect(vc->menu_item, "activate", + G_CALLBACK(gd_menu_switch_vc), s); + + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), vc->menu_item); + + qemu_chr_generic_open(vc->chr); + if (vc->chr->init) { + vc->chr->init(vc->chr); + } + + chan = g_io_channel_unix_new(vc->fd); + g_io_add_watch(chan, G_IO_IN, gd_vc_in, vc); + + return group; +} + +/** Window Creation **/ + +static void gd_connect_signals(GtkDisplayState *s) +{ + g_signal_connect(s->show_tabs_item, "activate", + G_CALLBACK(gd_menu_show_tabs), s); + + g_signal_connect(s->window, "key-press-event", + G_CALLBACK(gd_window_key_event), s); + g_signal_connect(s->window, "delete-event", + G_CALLBACK(gd_window_close), s); + +#if GTK_CHECK_VERSION(3, 0, 0) + g_signal_connect(s->drawing_area, "draw", + G_CALLBACK(gd_draw_event), s); +#else + g_signal_connect(s->drawing_area, "expose-event", + G_CALLBACK(gd_expose_event), s); +#endif + g_signal_connect(s->drawing_area, "motion-notify-event", + G_CALLBACK(gd_motion_event), s); + g_signal_connect(s->drawing_area, "button-press-event", + G_CALLBACK(gd_button_event), s); + g_signal_connect(s->drawing_area, "button-release-event", + G_CALLBACK(gd_button_event), s); + g_signal_connect(s->drawing_area, "key-press-event", + G_CALLBACK(gd_key_event), s); + g_signal_connect(s->drawing_area, "key-release-event", + G_CALLBACK(gd_key_event), s); + + g_signal_connect(s->pause_item, "activate", + G_CALLBACK(gd_menu_pause), s); + g_signal_connect(s->reset_item, "activate", + G_CALLBACK(gd_menu_reset), s); + g_signal_connect(s->powerdown_item, "activate", + G_CALLBACK(gd_menu_powerdown), s); + g_signal_connect(s->quit_item, "activate", + G_CALLBACK(gd_menu_quit), s); + g_signal_connect(s->full_screen_item, "activate", + G_CALLBACK(gd_menu_full_screen), s); + g_signal_connect(s->zoom_in_item, "activate", + G_CALLBACK(gd_menu_zoom_in), s); + g_signal_connect(s->zoom_out_item, "activate", + G_CALLBACK(gd_menu_zoom_out), s); + g_signal_connect(s->zoom_fixed_item, "activate", + G_CALLBACK(gd_menu_zoom_fixed), s); + g_signal_connect(s->zoom_fit_item, "activate", + G_CALLBACK(gd_menu_zoom_fit), s); + g_signal_connect(s->vga_item, "activate", + G_CALLBACK(gd_menu_switch_vc), s); + g_signal_connect(s->grab_item, "activate", + G_CALLBACK(gd_menu_grab_input), s); + g_signal_connect(s->notebook, "switch-page", + G_CALLBACK(gd_change_page), s); + g_signal_connect(s->drawing_area, "enter-notify-event", + G_CALLBACK(gd_enter_event), s); + g_signal_connect(s->drawing_area, "leave-notify-event", + G_CALLBACK(gd_leave_event), s); +} + +static void gd_create_menus(GtkDisplayState *s) +{ + GtkStockItem item; + GtkAccelGroup *accel_group; + GSList *group = NULL; + GtkWidget *separator; + int i; + + accel_group = gtk_accel_group_new(); + s->machine_menu = gtk_menu_new(); + gtk_menu_set_accel_group(GTK_MENU(s->machine_menu), accel_group); + s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine")); + + s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause")); + gtk_menu_shell_append(GTK_MENU_SHELL(s->machine_menu), s->pause_item); + + separator = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(s->machine_menu), separator); + + s->reset_item = gtk_image_menu_item_new_with_mnemonic(_("_Reset")); + gtk_menu_shell_append(GTK_MENU_SHELL(s->machine_menu), s->reset_item); + + s->powerdown_item = gtk_image_menu_item_new_with_mnemonic(_("Power _Down")); + gtk_menu_shell_append(GTK_MENU_SHELL(s->machine_menu), s->powerdown_item); + + separator = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(s->machine_menu), separator); + + s->quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL); + gtk_stock_lookup(GTK_STOCK_QUIT, &item); + gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item), + "/Machine/Quit"); + gtk_accel_map_add_entry("/Machine/Quit", item.keyval, item.modifier); + gtk_menu_shell_append(GTK_MENU_SHELL(s->machine_menu), s->quit_item); + + s->view_menu = gtk_menu_new(); + gtk_menu_set_accel_group(GTK_MENU(s->view_menu), accel_group); + s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View")); + + s->full_screen_item = + gtk_image_menu_item_new_from_stock(GTK_STOCK_FULLSCREEN, NULL); + gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->full_screen_item), + "/View/Full Screen"); + gtk_accel_map_add_entry("/View/Full Screen", GDK_KEY_f, GDK_CONTROL_MASK | GDK_MOD1_MASK); + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->full_screen_item); + + separator = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), separator); + + s->zoom_in_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_IN, NULL); + gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item), + "/View/Zoom In"); + gtk_accel_map_add_entry("/View/Zoom In", GDK_KEY_plus, GDK_CONTROL_MASK | GDK_MOD1_MASK); + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->zoom_in_item); + + s->zoom_out_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_OUT, NULL); + gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item), + "/View/Zoom Out"); + gtk_accel_map_add_entry("/View/Zoom Out", GDK_KEY_minus, GDK_CONTROL_MASK | GDK_MOD1_MASK); + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->zoom_out_item); + + s->zoom_fixed_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_100, NULL); + gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item), + "/View/Zoom Fixed"); + gtk_accel_map_add_entry("/View/Zoom Fixed", GDK_KEY_0, GDK_CONTROL_MASK | GDK_MOD1_MASK); + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->zoom_fixed_item); + + s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit")); + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->zoom_fit_item); + + separator = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), separator); + + s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover")); + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->grab_on_hover_item); + + s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input")); + gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item), + "/View/Grab Input"); + gtk_accel_map_add_entry("/View/Grab Input", GDK_KEY_g, GDK_CONTROL_MASK | GDK_MOD1_MASK); + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->grab_item); + + separator = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), separator); + + s->vga_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA"); + group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s->vga_item)); + gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->vga_item), + "/View/VGA"); + gtk_accel_map_add_entry("/View/VGA", GDK_KEY_1, GDK_CONTROL_MASK | GDK_MOD1_MASK); + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->vga_item); + + for (i = 0; i < nb_vcs; i++) { + VirtualConsole *vc = &s->vc[i]; + + group = gd_vc_init(s, vc, i, group); + s->nb_vcs++; + } + + separator = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), separator); + + s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs")); + gtk_menu_shell_append(GTK_MENU_SHELL(s->view_menu), s->show_tabs_item); + + g_object_set_data(G_OBJECT(s->window), "accel_group", accel_group); + gtk_window_add_accel_group(GTK_WINDOW(s->window), accel_group); + s->accel_group = accel_group; + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item), + s->machine_menu); + gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item); + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu); + gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item); +} + +void gtk_display_init(DisplayState *ds) +{ + GtkDisplayState *s = g_malloc0(sizeof(*s)); + + gtk_init(NULL, NULL); + + ds->opaque = s; + s->ds = ds; + s->dcl.dpy_gfx_update = gd_update; + s->dcl.dpy_gfx_resize = gd_resize; + s->dcl.dpy_refresh = gd_refresh; + + s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); +#if GTK_CHECK_VERSION(3, 2, 0) + s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); +#else + s->vbox = gtk_vbox_new(FALSE, 0); +#endif + s->notebook = gtk_notebook_new(); + s->drawing_area = gtk_drawing_area_new(); + s->menu_bar = gtk_menu_bar_new(); + + s->scale_x = 1.0; + s->scale_y = 1.0; + s->free_scale = FALSE; + + setlocale(LC_ALL, ""); + bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR); + textdomain("qemu"); + + s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR); + + s->mouse_mode_notifier.notify = gd_mouse_mode_change; + qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier); + qemu_add_vm_change_state_handler(gd_change_runstate, s); + + gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), s->drawing_area, gtk_label_new("VGA")); + + gd_create_menus(s); + + gd_connect_signals(s); + + gtk_widget_add_events(s->drawing_area, + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_MOTION_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_SCROLL_MASK | + GDK_KEY_PRESS_MASK); + gtk_widget_set_double_buffered(s->drawing_area, FALSE); + gtk_widget_set_can_focus(s->drawing_area, TRUE); + + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE); + gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE); + + gd_update_caption(s); + + gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0); + + gtk_container_add(GTK_CONTAINER(s->window), s->vbox); + + gtk_widget_show_all(s->window); + + register_displaychangelistener(ds, &s->dcl); + + global_state = s; +} diff --git a/ui/input.c b/ui/input.c index 259fd1808d..9abef0cd78 100644 --- a/ui/input.c +++ b/ui/input.c @@ -269,7 +269,7 @@ void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time, /* key down events */ keycode = keycode_from_keyvalue(p->value); if (keycode < 0x01 || keycode > 0xff) { - error_setg(errp, "invalid hex keycode 0x%x\n", keycode); + error_setg(errp, "invalid hex keycode 0x%x", keycode); free_keycodes(); return; } diff --git a/ui/keymaps.c b/ui/keymaps.c index 9625d82fa1..f373cc53d9 100644 --- a/ui/keymaps.c +++ b/ui/keymaps.c @@ -127,25 +127,27 @@ static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table, // fprintf(stderr, "Warning: unknown keysym %s\n", line); } else { const char *rest = end_of_keysym + 1; - char *rest2; - int keycode = strtol(rest, &rest2, 0); + int keycode = strtol(rest, NULL, 0); - if (rest && strstr(rest, "numlock")) { + if (strstr(rest, "numlock")) { add_to_key_range(&k->keypad_range, keycode); add_to_key_range(&k->numlock_range, keysym); //fprintf(stderr, "keypad keysym %04x keycode %d\n", keysym, keycode); } - if (rest && strstr(rest, "shift")) + if (strstr(rest, "shift")) { keycode |= SCANCODE_SHIFT; - if (rest && strstr(rest, "altgr")) + } + if (strstr(rest, "altgr")) { keycode |= SCANCODE_ALTGR; - if (rest && strstr(rest, "ctrl")) + } + if (strstr(rest, "ctrl")) { keycode |= SCANCODE_CTRL; + } add_keysym(line, keysym, keycode, k); - if (rest && strstr(rest, "addupper")) { + if (strstr(rest, "addupper")) { char *c; for (c = line; *c; c++) *c = qemu_toupper(*c); diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index 609335ab11..6dcbe90546 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -3,7 +3,8 @@ * See the COPYING file in the top-level directory. */ -#include "ui/qemu-pixman.h" +#include "qemu-common.h" +#include "ui/console.h" int qemu_pixman_get_type(int rshift, int gshift, int bshift) { diff --git a/ui/sdl_zoom.c b/ui/sdl_zoom.c index 122027cb36..2625c4557e 100644 --- a/ui/sdl_zoom.c +++ b/ui/sdl_zoom.c @@ -13,13 +13,14 @@ #include "sdl_zoom.h" #include "qemu/osdep.h" +#include #include #include -static int sdl_zoom_rgb16(SDL_Surface *src, SDL_Surface *dst, int smooth, - SDL_Rect *dst_rect); -static int sdl_zoom_rgb32(SDL_Surface *src, SDL_Surface *dst, int smooth, - SDL_Rect *dst_rect); +static void sdl_zoom_rgb16(SDL_Surface *src, SDL_Surface *dst, int smooth, + SDL_Rect *dst_rect); +static void sdl_zoom_rgb32(SDL_Surface *src, SDL_Surface *dst, int smooth, + SDL_Rect *dst_rect); #define BPP 32 #include "sdl_zoom_template.h" diff --git a/ui/sdl_zoom_template.h b/ui/sdl_zoom_template.h index 64bbca849b..3bb508b51e 100644 --- a/ui/sdl_zoom_template.h +++ b/ui/sdl_zoom_template.h @@ -51,7 +51,7 @@ (((a) & (dpf->Amask >> dpf->Ashift)) << dpf->Ashift); \ } while (0); -static int glue(sdl_zoom_rgb, BPP)(SDL_Surface *src, SDL_Surface *dst, int smooth, +static void glue(sdl_zoom_rgb, BPP)(SDL_Surface *src, SDL_Surface *dst, int smooth, SDL_Rect *dst_rect) { int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep, sstep_jump; @@ -71,13 +71,8 @@ static int glue(sdl_zoom_rgb, BPP)(SDL_Surface *src, SDL_Surface *dst, int smoot sy = (int) (65536.0 * (float) src->h / (float) dst->h); } - if ((sax = (int *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) { - return (-1); - } - if ((say = (int *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) { - free(sax); - return (-1); - } + sax = g_new(int, dst->w + 1); + say = g_new(int, dst->h + 1); sp = csp = (SDL_TYPE *) src->pixels; dp = (SDL_TYPE *) (dst->pixels + dst_rect->y * dst->pitch + @@ -216,9 +211,8 @@ static int glue(sdl_zoom_rgb, BPP)(SDL_Surface *src, SDL_Surface *dst, int smoot } } - free(sax); - free(say); - return (0); + g_free(sax); + g_free(say); } #undef SDL_TYPE diff --git a/ui/spice-core.c b/ui/spice-core.c index 3e44779107..bcc4199e7a 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -417,6 +417,90 @@ static SpiceChannelList *qmp_query_spice_channels(void) return head; } +static QemuOptsList qemu_spice_opts = { + .name = "spice", + .head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head), + .desc = { + { + .name = "port", + .type = QEMU_OPT_NUMBER, + },{ + .name = "tls-port", + .type = QEMU_OPT_NUMBER, + },{ + .name = "addr", + .type = QEMU_OPT_STRING, + },{ + .name = "ipv4", + .type = QEMU_OPT_BOOL, + },{ + .name = "ipv6", + .type = QEMU_OPT_BOOL, + },{ + .name = "password", + .type = QEMU_OPT_STRING, + },{ + .name = "disable-ticketing", + .type = QEMU_OPT_BOOL, + },{ + .name = "disable-copy-paste", + .type = QEMU_OPT_BOOL, + },{ + .name = "sasl", + .type = QEMU_OPT_BOOL, + },{ + .name = "x509-dir", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-key-file", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-key-password", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-cert-file", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-cacert-file", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-dh-key-file", + .type = QEMU_OPT_STRING, + },{ + .name = "tls-ciphers", + .type = QEMU_OPT_STRING, + },{ + .name = "tls-channel", + .type = QEMU_OPT_STRING, + },{ + .name = "plaintext-channel", + .type = QEMU_OPT_STRING, + },{ + .name = "image-compression", + .type = QEMU_OPT_STRING, + },{ + .name = "jpeg-wan-compression", + .type = QEMU_OPT_STRING, + },{ + .name = "zlib-glz-wan-compression", + .type = QEMU_OPT_STRING, + },{ + .name = "streaming-video", + .type = QEMU_OPT_STRING, + },{ + .name = "agent-mouse", + .type = QEMU_OPT_BOOL, + },{ + .name = "playback-compression", + .type = QEMU_OPT_BOOL, + }, { + .name = "seamless-migration", + .type = QEMU_OPT_BOOL, + }, + { /* end of list */ } + }, +}; + SpiceInfo *qmp_query_spice(Error **errp) { QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head); @@ -709,7 +793,7 @@ void qemu_spice_init(void) qemu_spice_input_init(); qemu_spice_audio_init(); - qemu_add_vm_change_state_handler(vm_change_state_handler, &spice_server); + qemu_add_vm_change_state_handler(vm_change_state_handler, NULL); g_free(x509_key_file); g_free(x509_cert_file); @@ -736,8 +820,7 @@ int qemu_spice_add_interface(SpiceBaseInstance *sin) */ spice_server = spice_server_new(); spice_server_init(spice_server, &core_interface); - qemu_add_vm_change_state_handler(vm_change_state_handler, - &spice_server); + qemu_add_vm_change_state_handler(vm_change_state_handler, NULL); } return spice_server_add_interface(spice_server, sin); @@ -765,8 +848,8 @@ static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn) int qemu_spice_set_passwd(const char *passwd, bool fail_if_conn, bool disconnect_if_conn) { - free(auth_passwd); - auth_passwd = strdup(passwd); + g_free(auth_passwd); + auth_passwd = g_strdup(passwd); return qemu_spice_set_ticket(fail_if_conn, disconnect_if_conn); } diff --git a/ui/vnc-tls.c b/ui/vnc-tls.c index 56292636d7..8d4cc8e47c 100644 --- a/ui/vnc-tls.c +++ b/ui/vnc-tls.c @@ -99,9 +99,9 @@ static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, } -static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void) +static gnutls_anon_server_credentials_t vnc_tls_initialize_anon_cred(void) { - gnutls_anon_server_credentials anon_cred; + gnutls_anon_server_credentials_t anon_cred; int ret; if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) { @@ -382,7 +382,7 @@ int vnc_tls_client_setup(struct VncState *vs, } } else { - gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred(); + gnutls_anon_server_credentials_t anon_cred = vnc_tls_initialize_anon_cred(); if (!anon_cred) { gnutls_deinit(vs->tls.session); vs->tls.session = NULL; diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c new file mode 100644 index 0000000000..3e3020916c --- /dev/null +++ b/ui/vnc-ws.c @@ -0,0 +1,285 @@ +/* + * QEMU VNC display driver: Websockets support + * + * Copyright (C) 2010 Joel Martin + * Copyright (C) 2012 Tim Hardeck + * + * This 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) any later version. + * + * This software 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 software; if not, see . + */ + +#include "vnc.h" + +void vncws_handshake_read(void *opaque) +{ + VncState *vs = opaque; + uint8_t *handshake_end; + long ret; + buffer_reserve(&vs->ws_input, 4096); + ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), 4096); + + if (!ret) { + if (vs->csock == -1) { + vnc_disconnect_finish(vs); + } + return; + } + vs->ws_input.offset += ret; + + handshake_end = (uint8_t *)g_strstr_len((char *)vs->ws_input.buffer, + vs->ws_input.offset, WS_HANDSHAKE_END); + if (handshake_end) { + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset); + buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer + + strlen(WS_HANDSHAKE_END)); + } +} + + +long vnc_client_read_ws(VncState *vs) +{ + int ret, err; + uint8_t *payload; + size_t payload_size, frame_size; + VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs->ws_input.buffer, + vs->ws_input.capacity, vs->ws_input.offset); + buffer_reserve(&vs->ws_input, 4096); + ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), 4096); + if (!ret) { + return 0; + } + vs->ws_input.offset += ret; + + /* make sure that nothing is left in the ws_input buffer */ + do { + err = vncws_decode_frame(&vs->ws_input, &payload, + &payload_size, &frame_size); + if (err <= 0) { + return err; + } + + buffer_reserve(&vs->input, payload_size); + buffer_append(&vs->input, payload, payload_size); + + buffer_advance(&vs->ws_input, frame_size); + } while (vs->ws_input.offset > 0); + + return ret; +} + +long vnc_client_write_ws(VncState *vs) +{ + long ret; + VNC_DEBUG("Write WS: Pending output %p size %zd offset %zd\n", + vs->output.buffer, vs->output.capacity, vs->output.offset); + vncws_encode_frame(&vs->ws_output, vs->output.buffer, vs->output.offset); + buffer_reset(&vs->output); + ret = vnc_client_write_buf(vs, vs->ws_output.buffer, vs->ws_output.offset); + if (!ret) { + return 0; + } + + buffer_advance(&vs->ws_output, ret); + + if (vs->ws_output.offset == 0) { + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + } + + return ret; +} + +static char *vncws_extract_handshake_entry(const char *handshake, + size_t handshake_len, const char *name) +{ + char *begin, *end, *ret = NULL; + char *line = g_strdup_printf("%s%s: ", WS_HANDSHAKE_DELIM, name); + begin = g_strstr_len(handshake, handshake_len, line); + if (begin != NULL) { + begin += strlen(line); + end = g_strstr_len(begin, handshake_len - (begin - handshake), + WS_HANDSHAKE_DELIM); + if (end != NULL) { + ret = g_strndup(begin, end - begin); + } + } + g_free(line); + return ret; +} + +static void vncws_send_handshake_response(VncState *vs, const char* key) +{ + char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1]; + unsigned char hash[SHA1_DIGEST_LEN]; + size_t hash_size = sizeof(hash); + char *accept = NULL, *response = NULL; + gnutls_datum_t in; + int ret; + + g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1); + g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1); + + /* hash and encode it */ + in.data = (void *)combined_key; + in.size = WS_CLIENT_KEY_LEN + WS_GUID_LEN; + ret = gnutls_fingerprint(GNUTLS_DIG_SHA1, &in, hash, &hash_size); + if (ret == GNUTLS_E_SUCCESS && hash_size <= SHA1_DIGEST_LEN) { + accept = g_base64_encode(hash, hash_size); + } + if (accept == NULL) { + VNC_DEBUG("Hashing Websocket combined key failed\n"); + vnc_client_error(vs); + return; + } + + response = g_strdup_printf(WS_HANDSHAKE, accept); + vnc_write(vs, response, strlen(response)); + vnc_flush(vs); + + g_free(accept); + g_free(response); + + vs->encode_ws = 1; + vnc_init_state(vs); +} + +void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size) +{ + char *protocols = vncws_extract_handshake_entry((const char *)line, size, + "Sec-WebSocket-Protocol"); + char *version = vncws_extract_handshake_entry((const char *)line, size, + "Sec-WebSocket-Version"); + char *key = vncws_extract_handshake_entry((const char *)line, size, + "Sec-WebSocket-Key"); + + if (protocols && version && key + && g_strrstr(protocols, "binary") + && !strcmp(version, WS_SUPPORTED_VERSION) + && strlen(key) == WS_CLIENT_KEY_LEN) { + vncws_send_handshake_response(vs, key); + } else { + VNC_DEBUG("Defective Websockets header or unsupported protocol\n"); + vnc_client_error(vs); + } + + g_free(protocols); + g_free(version); + g_free(key); +} + +void vncws_encode_frame(Buffer *output, const void *payload, + const size_t payload_size) +{ + size_t header_size = 0; + unsigned char opcode = WS_OPCODE_BINARY_FRAME; + union { + char buf[WS_HEAD_MAX_LEN]; + WsHeader ws; + } header; + + if (!payload_size) { + return; + } + + header.ws.b0 = 0x80 | (opcode & 0x0f); + if (payload_size <= 125) { + header.ws.b1 = (uint8_t)payload_size; + header_size = 2; + } else if (payload_size < 65536) { + header.ws.b1 = 0x7e; + header.ws.u.s16.l16 = cpu_to_be16((uint16_t)payload_size); + header_size = 4; + } else { + header.ws.b1 = 0x7f; + header.ws.u.s64.l64 = cpu_to_be64(payload_size); + header_size = 10; + } + + buffer_reserve(output, header_size + payload_size); + buffer_append(output, header.buf, header_size); + buffer_append(output, payload, payload_size); +} + +int vncws_decode_frame(Buffer *input, uint8_t **payload, + size_t *payload_size, size_t *frame_size) +{ + unsigned char opcode = 0, fin = 0, has_mask = 0; + size_t header_size = 0; + uint32_t *payload32; + WsHeader *header = (WsHeader *)input->buffer; + WsMask mask; + int i; + + if (input->offset < WS_HEAD_MIN_LEN + 4) { + /* header not complete */ + return 0; + } + + fin = (header->b0 & 0x80) >> 7; + opcode = header->b0 & 0x0f; + has_mask = (header->b1 & 0x80) >> 7; + *payload_size = header->b1 & 0x7f; + + if (opcode == WS_OPCODE_CLOSE) { + /* disconnect */ + return -1; + } + + /* Websocket frame sanity check: + * * Websocket fragmentation is not supported. + * * All websockets frames sent by a client have to be masked. + * * Only binary encoding is supported. + */ + if (!fin || !has_mask || opcode != WS_OPCODE_BINARY_FRAME) { + VNC_DEBUG("Received faulty/unsupported Websocket frame\n"); + return -2; + } + + if (*payload_size < 126) { + header_size = 6; + mask = header->u.m; + } else if (*payload_size == 126 && input->offset >= 8) { + *payload_size = be16_to_cpu(header->u.s16.l16); + header_size = 8; + mask = header->u.s16.m16; + } else if (*payload_size == 127 && input->offset >= 14) { + *payload_size = be64_to_cpu(header->u.s64.l64); + header_size = 14; + mask = header->u.s64.m64; + } else { + /* header not complete */ + return 0; + } + + *frame_size = header_size + *payload_size; + + if (input->offset < *frame_size) { + /* frame not complete */ + return 0; + } + + *payload = input->buffer + header_size; + + /* unmask frame */ + /* process 1 frame (32 bit op) */ + payload32 = (uint32_t *)(*payload); + for (i = 0; i < *payload_size / 4; i++) { + payload32[i] ^= mask.u; + } + /* process the remaining bytes (if any) */ + for (i *= 4; i < *payload_size; i++) { + (*payload)[i] ^= mask.c[i % 4]; + } + + return 1; +} diff --git a/ui/vnc-ws.h b/ui/vnc-ws.h new file mode 100644 index 0000000000..039a58765c --- /dev/null +++ b/ui/vnc-ws.h @@ -0,0 +1,86 @@ +/* + * QEMU VNC display driver: Websockets support + * + * Copyright (C) 2010 Joel Martin + * Copyright (C) 2012 Tim Hardeck + * + * This 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) any later version. + * + * This software 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 software; if not, see . + */ + +#ifndef __QEMU_UI_VNC_WS_H +#define __QEMU_UI_VNC_WS_H + +#include + +#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3) +#define SHA1_DIGEST_LEN 20 + +#define WS_ACCEPT_LEN (B64LEN(SHA1_DIGEST_LEN) + 1) +#define WS_CLIENT_KEY_LEN 24 +#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define WS_GUID_LEN strlen(WS_GUID) + +#define WS_HANDSHAKE "HTTP/1.1 101 Switching Protocols\r\n\ +Upgrade: websocket\r\n\ +Connection: Upgrade\r\n\ +Sec-WebSocket-Accept: %s\r\n\ +Sec-WebSocket-Protocol: binary\r\n\ +\r\n" +#define WS_HANDSHAKE_DELIM "\r\n" +#define WS_HANDSHAKE_END "\r\n\r\n" +#define WS_SUPPORTED_VERSION "13" + +#define WS_HEAD_MIN_LEN sizeof(uint16_t) +#define WS_HEAD_MAX_LEN (WS_HEAD_MIN_LEN + sizeof(uint64_t) + sizeof(uint32_t)) + +typedef union WsMask { + char c[4]; + uint32_t u; +} WsMask; + +typedef struct QEMU_PACKED WsHeader { + unsigned char b0; + unsigned char b1; + union { + struct QEMU_PACKED { + uint16_t l16; + WsMask m16; + } s16; + struct QEMU_PACKED { + uint64_t l64; + WsMask m64; + } s64; + WsMask m; + } u; +} WsHeader; + +enum { + WS_OPCODE_CONTINUATION = 0x0, + WS_OPCODE_TEXT_FRAME = 0x1, + WS_OPCODE_BINARY_FRAME = 0x2, + WS_OPCODE_CLOSE = 0x8, + WS_OPCODE_PING = 0x9, + WS_OPCODE_PONG = 0xA +}; + +void vncws_handshake_read(void *opaque); +long vnc_client_write_ws(VncState *vs); +long vnc_client_read_ws(VncState *vs); +void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size); +void vncws_encode_frame(Buffer *output, const void *payload, + const size_t payload_size); +int vncws_decode_frame(Buffer *input, uint8_t **payload, + size_t *payload_size, size_t *frame_size); + +#endif /* __QEMU_UI_VNC_WS_H */ diff --git a/ui/vnc.c b/ui/vnc.c index 8912b78945..ff4e2ae586 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -420,7 +420,6 @@ out_error: static int vnc_update_client(VncState *vs, int has_dirty); static int vnc_update_client_sync(VncState *vs, int has_dirty); static void vnc_disconnect_start(VncState *vs); -static void vnc_disconnect_finish(VncState *vs); static void vnc_init_timer(VncDisplay *vd); static void vnc_remove_timer(VncDisplay *vd); @@ -486,7 +485,7 @@ static int buffer_empty(Buffer *buffer) return buffer->offset == 0; } -static uint8_t *buffer_end(Buffer *buffer) +uint8_t *buffer_end(Buffer *buffer) { return buffer->buffer + buffer->offset; } @@ -510,6 +509,13 @@ void buffer_append(Buffer *buffer, const void *data, size_t len) buffer->offset += len; } +void buffer_advance(Buffer *buf, size_t len) +{ + memmove(buf->buffer, buf->buffer + len, + (buf->offset - len)); + buf->offset -= len; +} + static void vnc_desktop_resize(VncState *vs) { DisplayState *ds = vs->ds; @@ -1016,7 +1022,7 @@ static void vnc_disconnect_start(VncState *vs) vs->csock = -1; } -static void vnc_disconnect_finish(VncState *vs) +void vnc_disconnect_finish(VncState *vs) { int i; @@ -1027,6 +1033,10 @@ static void vnc_disconnect_finish(VncState *vs) buffer_free(&vs->input); buffer_free(&vs->output); +#ifdef CONFIG_VNC_WS + buffer_free(&vs->ws_input); + buffer_free(&vs->ws_output); +#endif /* CONFIG_VNC_WS */ qobject_decref(vs->info); @@ -1043,20 +1053,24 @@ static void vnc_disconnect_finish(VncState *vs) audio_del(vs); vnc_release_modifiers(vs); - QTAILQ_REMOVE(&vs->vd->clients, vs, next); + if (vs->initialized) { + QTAILQ_REMOVE(&vs->vd->clients, vs, next); + qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier); + } if (QTAILQ_EMPTY(&vs->vd->clients)) { dcl->idle = 1; } - qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier); vnc_remove_timer(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); - qemu_bh_delete(vs->bh); + if (vs->bh != NULL) { + qemu_bh_delete(vs->bh); + } buffer_free(&vs->jobs_buffer); for (i = 0; i < VNC_STAT_ROWS; ++i) { @@ -1166,8 +1180,7 @@ static long vnc_client_write_plain(VncState *vs) if (!ret) return 0; - memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret)); - vs->output.offset -= ret; + buffer_advance(&vs->output, ret); if (vs->output.offset == 0) { qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); @@ -1193,7 +1206,16 @@ static void vnc_client_write_locked(void *opaque) vnc_client_write_sasl(vs); } else #endif /* CONFIG_VNC_SASL */ - vnc_client_write_plain(vs); + { +#ifdef CONFIG_VNC_WS + if (vs->encode_ws) { + vnc_client_write_ws(vs); + } else +#endif /* CONFIG_VNC_WS */ + { + vnc_client_write_plain(vs); + } + } } void vnc_client_write(void *opaque) @@ -1201,7 +1223,11 @@ void vnc_client_write(void *opaque) VncState *vs = opaque; vnc_lock_output(vs); - if (vs->output.offset) { + if (vs->output.offset +#ifdef CONFIG_VNC_WS + || vs->ws_output.offset +#endif + ) { vnc_client_write_locked(opaque); } else if (vs->csock != -1) { qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); @@ -1295,7 +1321,21 @@ void vnc_client_read(void *opaque) ret = vnc_client_read_sasl(vs); else #endif /* CONFIG_VNC_SASL */ +#ifdef CONFIG_VNC_WS + if (vs->encode_ws) { + ret = vnc_client_read_ws(vs); + if (ret == -1) { + vnc_disconnect_start(vs); + return; + } else if (ret == -2) { + vnc_client_error(vs); + return; + } + } else +#endif /* CONFIG_VNC_WS */ + { ret = vnc_client_read_plain(vs); + } if (!ret) { if (vs->csock == -1) vnc_disconnect_finish(vs); @@ -1313,8 +1353,7 @@ void vnc_client_read(void *opaque) } if (!ret) { - memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len)); - vs->input.offset -= len; + buffer_advance(&vs->input, len); } else { vs->read_handler_expect = ret; } @@ -1367,7 +1406,11 @@ void vnc_write_u8(VncState *vs, uint8_t value) void vnc_flush(VncState *vs) { vnc_lock_output(vs); - if (vs->csock != -1 && vs->output.offset) { + if (vs->csock != -1 && (vs->output.offset +#ifdef CONFIG_VNC_WS + || vs->ws_output.offset +#endif + )) { vnc_client_write_locked(vs); } vnc_unlock_output(vs); @@ -2657,7 +2700,7 @@ static void vnc_remove_timer(VncDisplay *vd) } } -static void vnc_connect(VncDisplay *vd, int csock, int skipauth) +static void vnc_connect(VncDisplay *vd, int csock, int skipauth, bool websocket) { VncState *vs = g_malloc0(sizeof(VncState)); int i; @@ -2684,13 +2727,35 @@ static void vnc_connect(VncDisplay *vd, int csock, int skipauth) VNC_DEBUG("New client on socket %d\n", csock); dcl->idle = 0; socket_set_nonblock(vs->csock); - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); +#ifdef CONFIG_VNC_WS + if (websocket) { + vs->websocket = 1; + qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs); + } else +#endif /* CONFIG_VNC_WS */ + { + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + } vnc_client_cache_addr(vs); vnc_qmp_event(vs, QEVENT_VNC_CONNECTED); vnc_set_share_mode(vs, VNC_SHARE_MODE_CONNECTING); vs->vd = vd; + +#ifdef CONFIG_VNC_WS + if (!vs->websocket) +#endif + { + vnc_init_state(vs); + } +} + +void vnc_init_state(VncState *vs) +{ + vs->initialized = true; + VncDisplay *vd = vs->vd; + vs->ds = vd->ds; vs->last_x = -1; vs->last_y = -1; @@ -2722,21 +2787,41 @@ static void vnc_connect(VncDisplay *vd, int csock, int skipauth) /* vs might be free()ed here */ } -static void vnc_listen_read(void *opaque) +static void vnc_listen_read(void *opaque, bool websocket) { VncDisplay *vs = opaque; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); + int csock; /* Catch-up */ vga_hw_update(); +#ifdef CONFIG_VNC_WS + if (websocket) { + csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen); + } else +#endif /* CONFIG_VNC_WS */ + { + csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); + } - int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); if (csock != -1) { - vnc_connect(vs, csock, 0); + vnc_connect(vs, csock, 0, websocket); } } +static void vnc_listen_regular_read(void *opaque) +{ + vnc_listen_read(opaque, 0); +} + +#ifdef CONFIG_VNC_WS +static void vnc_listen_websocket_read(void *opaque) +{ + vnc_listen_read(opaque, 1); +} +#endif /* CONFIG_VNC_WS */ + void vnc_display_init(DisplayState *ds) { VncDisplay *vs = g_malloc0(sizeof(*vs)); @@ -2748,6 +2833,9 @@ void vnc_display_init(DisplayState *ds) vnc_display = vs; vs->lsock = -1; +#ifdef CONFIG_VNC_WS + vs->lwebsock = -1; +#endif vs->ds = ds; QTAILQ_INIT(&vs->clients); @@ -2789,6 +2877,15 @@ static void vnc_display_close(DisplayState *ds) close(vs->lsock); vs->lsock = -1; } +#ifdef CONFIG_VNC_WS + g_free(vs->ws_display); + vs->ws_display = NULL; + if (vs->lwebsock != -1) { + qemu_set_fd_handler2(vs->lwebsock, NULL, NULL, NULL, NULL); + close(vs->lwebsock); + vs->lwebsock = -1; + } +#endif /* CONFIG_VNC_WS */ vs->auth = VNC_AUTH_INVALID; #ifdef CONFIG_VNC_TLS vs->subauth = VNC_AUTH_INVALID; @@ -2910,6 +3007,36 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) } else if (strncmp(options, "sasl", 4) == 0) { sasl = 1; /* Require SASL auth */ #endif +#ifdef CONFIG_VNC_WS + } else if (strncmp(options, "websocket", 9) == 0) { + char *start, *end; + vs->websocket = 1; + + /* Check for 'websocket=' */ + start = strchr(options, '='); + end = strchr(options, ','); + if (start && (!end || (start < end))) { + int len = end ? end-(start+1) : strlen(start+1); + if (len < 6) { + /* extract the host specification from display */ + char *host = NULL, *port = NULL, *host_end = NULL; + port = g_strndup(start + 1, len); + + /* ipv6 hosts have colons */ + end = strchr(display, ','); + host_end = g_strrstr_len(display, end - display, ":"); + + if (host_end) { + host = g_strndup(display, host_end - display + 1); + } else { + host = g_strndup(":", 1); + } + vs->ws_display = g_strconcat(host, port, NULL); + g_free(host); + g_free(port); + } + } +#endif /* CONFIG_VNC_WS */ #ifdef CONFIG_VNC_TLS } else if (strncmp(options, "tls", 3) == 0) { tls = 1; /* Require TLS */ @@ -3068,6 +3195,9 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) /* connect to viewer */ int csock; vs->lsock = -1; +#ifdef CONFIG_VNC_WS + vs->lwebsock = -1; +#endif if (strncmp(display, "unix:", 5) == 0) { csock = unix_connect(display+5, errp); } else { @@ -3076,7 +3206,7 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) if (csock < 0) { goto fail; } - vnc_connect(vs, csock, 0); + vnc_connect(vs, csock, 0, 0); } else { /* listen for connects */ char *dpy; @@ -3087,25 +3217,56 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp) } else { vs->lsock = inet_listen(display, dpy, 256, SOCK_STREAM, 5900, errp); - } - if (vs->lsock < 0) { - g_free(dpy); - goto fail; + if (vs->lsock < 0) { + g_free(dpy); + goto fail; + } +#ifdef CONFIG_VNC_WS + if (vs->websocket) { + if (vs->ws_display) { + vs->lwebsock = inet_listen(vs->ws_display, NULL, 256, + SOCK_STREAM, 0, errp); + } else { + vs->lwebsock = inet_listen(vs->display, NULL, 256, + SOCK_STREAM, 5700, errp); + } + + if (vs->lwebsock < 0) { + if (vs->lsock) { + close(vs->lsock); + vs->lsock = -1; + } + g_free(dpy); + goto fail; + } + } +#endif /* CONFIG_VNC_WS */ } g_free(vs->display); vs->display = dpy; - qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs); + qemu_set_fd_handler2(vs->lsock, NULL, + vnc_listen_regular_read, NULL, vs); +#ifdef CONFIG_VNC_WS + if (vs->websocket) { + qemu_set_fd_handler2(vs->lwebsock, NULL, + vnc_listen_websocket_read, NULL, vs); + } +#endif /* CONFIG_VNC_WS */ } return; fail: g_free(vs->display); vs->display = NULL; +#ifdef CONFIG_VNC_WS + g_free(vs->ws_display); + vs->ws_display = NULL; +#endif /* CONFIG_VNC_WS */ } void vnc_display_add_client(DisplayState *ds, int csock, int skipauth) { VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; - vnc_connect(vs, csock, skipauth); + vnc_connect(vs, csock, skipauth, 0); } diff --git a/ui/vnc.h b/ui/vnc.h index 8b40f09117..45d7686843 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -99,6 +99,9 @@ typedef struct VncDisplay VncDisplay; #ifdef CONFIG_VNC_SASL #include "vnc-auth-sasl.h" #endif +#ifdef CONFIG_VNC_WS +#include "vnc-ws.h" +#endif struct VncRectStat { @@ -142,6 +145,11 @@ struct VncDisplay QEMUTimer *timer; int timer_interval; int lsock; +#ifdef CONFIG_VNC_WS + int lwebsock; + bool websocket; + char *ws_display; +#endif DisplayState *ds; kbd_layout_t *kbd_layout; int lock_key_sync; @@ -269,11 +277,19 @@ struct VncState #ifdef CONFIG_VNC_SASL VncStateSASL sasl; #endif +#ifdef CONFIG_VNC_WS + bool encode_ws; + bool websocket; +#endif QObject *info; Buffer output; Buffer input; +#ifdef CONFIG_VNC_WS + Buffer ws_input; + Buffer ws_output; +#endif /* current output mode information */ VncWritePixels *write_pixels; PixelFormat client_pf; @@ -290,6 +306,7 @@ struct VncState QEMUPutLEDEntry *led; bool abort; + bool initialized; QemuMutex output_mutex; QEMUBH *bh; Buffer jobs_buffer; @@ -493,6 +510,8 @@ void vnc_write_u16(VncState *vs, uint16_t value); void vnc_write_u8(VncState *vs, uint8_t value); void vnc_flush(VncState *vs); void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting); +void vnc_disconnect_finish(VncState *vs); +void vnc_init_state(VncState *vs); /* Buffer I/O functions */ @@ -510,6 +529,8 @@ void buffer_reserve(Buffer *buffer, size_t len); void buffer_reset(Buffer *buffer); void buffer_free(Buffer *buffer); void buffer_append(Buffer *buffer, const void *data, size_t len); +void buffer_advance(Buffer *buf, size_t len); +uint8_t *buffer_end(Buffer *buffer); /* Misc helpers */ diff --git a/ui/vnc_keysym.h b/ui/vnc_keysym.h index df33cfe53c..6250bec692 100644 --- a/ui/vnc_keysym.h +++ b/ui/vnc_keysym.h @@ -215,10 +215,14 @@ static const name2keysym_t name2keysym[]={ { "Zabovedot", 0x1af}, { "zacute", 0x1bc}, { "Zacute", 0x1ac}, +{ "Odoubleacute", 0x1d5}, +{ "Udoubleacute", 0x1db}, { "cacute", 0x1e6}, { "Cacute", 0x1c6}, { "nacute", 0x1f1}, { "Nacute", 0x1d1}, +{ "odoubleacute", 0x1f5}, +{ "udoubleacute", 0x1fb}, /* modifiers */ {"ISO_Level3_Shift", 0xfe03}, /* XK_ISO_Level3_Shift */ diff --git a/user-exec.c b/user-exec.c index c71acbc503..71bd6c531c 100644 --- a/user-exec.c +++ b/user-exec.c @@ -70,7 +70,7 @@ void cpu_resume_from_signal(CPUArchState *env1, void *puc) #endif } env1->exception_index = -1; - longjmp(env1->jmp_env, 1); + siglongjmp(env1->jmp_env, 1); } /* 'pc' is the host PC at which the exception was raised. 'address' is diff --git a/util/Makefile.objs b/util/Makefile.objs new file mode 100644 index 0000000000..cad5ce87db --- /dev/null +++ b/util/Makefile.objs @@ -0,0 +1,11 @@ +util-obj-y = osdep.o cutils.o qemu-timer-common.o +util-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o event_notifier-win32.o +util-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o event_notifier-posix.o +util-obj-y += envlist.o path.o host-utils.o cache-utils.o module.o +util-obj-y += bitmap.o bitops.o hbitmap.o +util-obj-y += fifo8.o +util-obj-y += acl.o +util-obj-y += error.o qemu-error.o +util-obj-$(CONFIG_POSIX) += compatfd.o +util-obj-y += iov.o aes.o qemu-config.o qemu-sockets.o uri.o notify.o +util-obj-y += qemu-option.o qemu-progress.o diff --git a/acl.c b/util/acl.c similarity index 96% rename from acl.c rename to util/acl.c index 81ac25599b..a7f33ff7bb 100644 --- a/acl.c +++ b/util/acl.c @@ -103,8 +103,8 @@ void qemu_acl_reset(qemu_acl *acl) acl->defaultDeny = 1; QTAILQ_FOREACH_SAFE(entry, &acl->entries, next, next_entry) { QTAILQ_REMOVE(&acl->entries, entry, next); - free(entry->match); - free(entry); + g_free(entry->match); + g_free(entry); } acl->nentries = 0; } @@ -168,6 +168,9 @@ int qemu_acl_remove(qemu_acl *acl, i++; if (strcmp(entry->match, match) == 0) { QTAILQ_REMOVE(&acl->entries, entry, next); + acl->nentries--; + g_free(entry->match); + g_free(entry); return i; } } diff --git a/aes.c b/util/aes.c similarity index 100% rename from aes.c rename to util/aes.c diff --git a/bitmap.c b/util/bitmap.c similarity index 100% rename from bitmap.c rename to util/bitmap.c diff --git a/bitops.c b/util/bitops.c similarity index 96% rename from bitops.c rename to util/bitops.c index 4c3a836a01..e72237ab2b 100644 --- a/bitops.c +++ b/util/bitops.c @@ -60,7 +60,7 @@ found_first: return result + size; /* Nope. */ } found_middle: - return result + bitops_ffsl(tmp); + return result + ctzl(tmp); } /* @@ -109,7 +109,7 @@ found_first: return result + size; /* Nope. */ } found_middle: - return result + ffz(tmp); + return result + ctzl(~tmp); } unsigned long find_last_bit(const unsigned long *addr, unsigned long size) @@ -133,7 +133,7 @@ unsigned long find_last_bit(const unsigned long *addr, unsigned long size) tmp = addr[--words]; if (tmp) { found: - return words * BITS_PER_LONG + bitops_flsl(tmp); + return words * BITS_PER_LONG + BITS_PER_LONG - 1 - clzl(tmp); } } diff --git a/cache-utils.c b/util/cache-utils.c similarity index 100% rename from cache-utils.c rename to util/cache-utils.c diff --git a/compatfd.c b/util/compatfd.c similarity index 100% rename from compatfd.c rename to util/compatfd.c diff --git a/cutils.c b/util/cutils.c similarity index 74% rename from cutils.c rename to util/cutils.c index d06590b330..1439da4f99 100644 --- a/cutils.c +++ b/util/cutils.c @@ -214,12 +214,13 @@ static int64_t suffix_mul(char suffix, int64_t unit) /* * Convert string to bytes, allowing either B/b for bytes, K/k for KB, * M/m for MB, G/g for GB or T/t for TB. End pointer will be returned - * in *end, if not NULL. Return -1 on error. + * in *end, if not NULL. Return -ERANGE on overflow, Return -EINVAL on + * other error. */ int64_t strtosz_suffix_unit(const char *nptr, char **end, const char default_suffix, int64_t unit) { - int64_t retval = -1; + int64_t retval = -EINVAL; char *endptr; unsigned char c; int mul_required = 0; @@ -246,6 +247,7 @@ int64_t strtosz_suffix_unit(const char *nptr, char **end, goto fail; } if ((val * mul >= INT64_MAX) || val < 0) { + retval = -ERANGE; goto fail; } retval = val * mul; @@ -268,6 +270,105 @@ int64_t strtosz(const char *nptr, char **end) return strtosz_suffix(nptr, end, STRTOSZ_DEFSUFFIX_MB); } +/** + * parse_uint: + * + * @s: String to parse + * @value: Destination for parsed integer value + * @endptr: Destination for pointer to first character not consumed + * @base: integer base, between 2 and 36 inclusive, or 0 + * + * Parse unsigned integer + * + * Parsed syntax is like strtoull()'s: arbitrary whitespace, a single optional + * '+' or '-', an optional "0x" if @base is 0 or 16, one or more digits. + * + * If @s is null, or @base is invalid, or @s doesn't start with an + * integer in the syntax above, set *@value to 0, *@endptr to @s, and + * return -EINVAL. + * + * Set *@endptr to point right beyond the parsed integer (even if the integer + * overflows or is negative, all digits will be parsed and *@endptr will + * point right beyond them). + * + * If the integer is negative, set *@value to 0, and return -ERANGE. + * + * If the integer overflows unsigned long long, set *@value to + * ULLONG_MAX, and return -ERANGE. + * + * Else, set *@value to the parsed integer, and return 0. + */ +int parse_uint(const char *s, unsigned long long *value, char **endptr, + int base) +{ + int r = 0; + char *endp = (char *)s; + unsigned long long val = 0; + + if (!s) { + r = -EINVAL; + goto out; + } + + errno = 0; + val = strtoull(s, &endp, base); + if (errno) { + r = -errno; + goto out; + } + + if (endp == s) { + r = -EINVAL; + goto out; + } + + /* make sure we reject negative numbers: */ + while (isspace((unsigned char)*s)) { + s++; + } + if (*s == '-') { + val = 0; + r = -ERANGE; + goto out; + } + +out: + *value = val; + *endptr = endp; + return r; +} + +/** + * parse_uint_full: + * + * @s: String to parse + * @value: Destination for parsed integer value + * @base: integer base, between 2 and 36 inclusive, or 0 + * + * Parse unsigned integer from entire string + * + * Have the same behavior of parse_uint(), but with an additional check + * for additional data after the parsed number. If extra characters are present + * after the parsed number, the function will return -EINVAL, and *@v will + * be set to 0. + */ +int parse_uint_full(const char *s, unsigned long long *value, int base) +{ + char *endp; + int r; + + r = parse_uint(s, value, &endp, base); + if (r < 0) { + return r; + } + if (*endp) { + *value = 0; + return -EINVAL; + } + + return 0; +} + int qemu_parse_fd(const char *param) { int fd; diff --git a/envlist.c b/util/envlist.c similarity index 98% rename from envlist.c rename to util/envlist.c index ff99fc44e9..ebc06cf0f3 100644 --- a/envlist.c +++ b/util/envlist.c @@ -1,9 +1,4 @@ -#include -#include -#include -#include -#include - +#include "qemu-common.h" #include "qemu/queue.h" #include "qemu/envlist.h" diff --git a/error.c b/util/error.c similarity index 100% rename from error.c rename to util/error.c diff --git a/event_notifier-posix.c b/util/event_notifier-posix.c similarity index 100% rename from event_notifier-posix.c rename to util/event_notifier-posix.c diff --git a/event_notifier-win32.c b/util/event_notifier-win32.c similarity index 100% rename from event_notifier-win32.c rename to util/event_notifier-win32.c diff --git a/hw/fifo.c b/util/fifo8.c similarity index 97% rename from hw/fifo.c rename to util/fifo8.c index 68a955a77b..013e903c6e 100644 --- a/hw/fifo.c +++ b/util/fifo8.c @@ -12,7 +12,8 @@ * with this program; if not, see . */ -#include "fifo.h" +#include "qemu-common.h" +#include "qemu/fifo8.h" void fifo8_create(Fifo8 *fifo, uint32_t capacity) { diff --git a/util/hbitmap.c b/util/hbitmap.c new file mode 100644 index 0000000000..d93683128b --- /dev/null +++ b/util/hbitmap.c @@ -0,0 +1,402 @@ +/* + * Hierarchical Bitmap Data Type + * + * Copyright Red Hat, Inc., 2012 + * + * Author: Paolo Bonzini + * + * 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 +#include +#include +#include "qemu/osdep.h" +#include "qemu/hbitmap.h" +#include "qemu/host-utils.h" +#include "trace.h" + +/* HBitmaps provides an array of bits. The bits are stored as usual in an + * array of unsigned longs, but HBitmap is also optimized to provide fast + * iteration over set bits; going from one bit to the next is O(logB n) + * worst case, with B = sizeof(long) * CHAR_BIT: the result is low enough + * that the number of levels is in fact fixed. + * + * In order to do this, it stacks multiple bitmaps with progressively coarser + * granularity; in all levels except the last, bit N is set iff the N-th + * unsigned long is nonzero in the immediately next level. When iteration + * completes on the last level it can examine the 2nd-last level to quickly + * skip entire words, and even do so recursively to skip blocks of 64 words or + * powers thereof (32 on 32-bit machines). + * + * Given an index in the bitmap, it can be split in group of bits like + * this (for the 64-bit case): + * + * bits 0-57 => word in the last bitmap | bits 58-63 => bit in the word + * bits 0-51 => word in the 2nd-last bitmap | bits 52-57 => bit in the word + * bits 0-45 => word in the 3rd-last bitmap | bits 46-51 => bit in the word + * + * So it is easy to move up simply by shifting the index right by + * log2(BITS_PER_LONG) bits. To move down, you shift the index left + * similarly, and add the word index within the group. Iteration uses + * ffs (find first set bit) to find the next word to examine; this + * operation can be done in constant time in most current architectures. + * + * Setting or clearing a range of m bits on all levels, the work to perform + * is O(m + m/W + m/W^2 + ...), which is O(m) like on a regular bitmap. + * + * When iterating on a bitmap, each bit (on any level) is only visited + * once. Hence, The total cost of visiting a bitmap with m bits in it is + * the number of bits that are set in all bitmaps. Unless the bitmap is + * extremely sparse, this is also O(m + m/W + m/W^2 + ...), so the amortized + * cost of advancing from one bit to the next is usually constant (worst case + * O(logB n) as in the non-amortized complexity). + */ + +struct HBitmap { + /* Number of total bits in the bottom level. */ + uint64_t size; + + /* Number of set bits in the bottom level. */ + uint64_t count; + + /* A scaling factor. Given a granularity of G, each bit in the bitmap will + * will actually represent a group of 2^G elements. Each operation on a + * range of bits first rounds the bits to determine which group they land + * in, and then affect the entire page; iteration will only visit the first + * bit of each group. Here is an example of operations in a size-16, + * granularity-1 HBitmap: + * + * initial state 00000000 + * set(start=0, count=9) 11111000 (iter: 0, 2, 4, 6, 8) + * reset(start=1, count=3) 00111000 (iter: 4, 6, 8) + * set(start=9, count=2) 00111100 (iter: 4, 6, 8, 10) + * reset(start=5, count=5) 00000000 + * + * From an implementation point of view, when setting or resetting bits, + * the bitmap will scale bit numbers right by this amount of bits. When + * iterating, the bitmap will scale bit numbers left by this amount of + * bits. + */ + int granularity; + + /* A number of progressively less coarse bitmaps (i.e. level 0 is the + * coarsest). Each bit in level N represents a word in level N+1 that + * has a set bit, except the last level where each bit represents the + * actual bitmap. + * + * Note that all bitmaps have the same number of levels. Even a 1-bit + * bitmap will still allocate HBITMAP_LEVELS arrays. + */ + unsigned long *levels[HBITMAP_LEVELS]; +}; + +static inline int popcountl(unsigned long l) +{ + return BITS_PER_LONG == 32 ? ctpop32(l) : ctpop64(l); +} + +/* Advance hbi to the next nonzero word and return it. hbi->pos + * is updated. Returns zero if we reach the end of the bitmap. + */ +unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) +{ + size_t pos = hbi->pos; + const HBitmap *hb = hbi->hb; + unsigned i = HBITMAP_LEVELS - 1; + + unsigned long cur; + do { + cur = hbi->cur[--i]; + pos >>= BITS_PER_LEVEL; + } while (cur == 0); + + /* Check for end of iteration. We always use fewer than BITS_PER_LONG + * bits in the level 0 bitmap; thus we can repurpose the most significant + * bit as a sentinel. The sentinel is set in hbitmap_alloc and ensures + * that the above loop ends even without an explicit check on i. + */ + + if (i == 0 && cur == (1UL << (BITS_PER_LONG - 1))) { + return 0; + } + for (; i < HBITMAP_LEVELS - 1; i++) { + /* Shift back pos to the left, matching the right shifts above. + * The index of this word's least significant set bit provides + * the low-order bits. + */ + assert(cur); + pos = (pos << BITS_PER_LEVEL) + ctzl(cur); + hbi->cur[i] = cur & (cur - 1); + + /* Set up next level for iteration. */ + cur = hb->levels[i + 1][pos]; + } + + hbi->pos = pos; + trace_hbitmap_iter_skip_words(hbi->hb, hbi, pos, cur); + + assert(cur); + return cur; +} + +void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first) +{ + unsigned i, bit; + uint64_t pos; + + hbi->hb = hb; + pos = first >> hb->granularity; + assert(pos < hb->size); + hbi->pos = pos >> BITS_PER_LEVEL; + hbi->granularity = hb->granularity; + + for (i = HBITMAP_LEVELS; i-- > 0; ) { + bit = pos & (BITS_PER_LONG - 1); + pos >>= BITS_PER_LEVEL; + + /* Drop bits representing items before first. */ + hbi->cur[i] = hb->levels[i][pos] & ~((1UL << bit) - 1); + + /* We have already added level i+1, so the lowest set bit has + * been processed. Clear it. + */ + if (i != HBITMAP_LEVELS - 1) { + hbi->cur[i] &= ~(1UL << bit); + } + } +} + +bool hbitmap_empty(const HBitmap *hb) +{ + return hb->count == 0; +} + +int hbitmap_granularity(const HBitmap *hb) +{ + return hb->granularity; +} + +uint64_t hbitmap_count(const HBitmap *hb) +{ + return hb->count << hb->granularity; +} + +/* Count the number of set bits between start and end, not accounting for + * the granularity. Also an example of how to use hbitmap_iter_next_word. + */ +static uint64_t hb_count_between(HBitmap *hb, uint64_t start, uint64_t last) +{ + HBitmapIter hbi; + uint64_t count = 0; + uint64_t end = last + 1; + unsigned long cur; + size_t pos; + + hbitmap_iter_init(&hbi, hb, start << hb->granularity); + for (;;) { + pos = hbitmap_iter_next_word(&hbi, &cur); + if (pos >= (end >> BITS_PER_LEVEL)) { + break; + } + count += popcountl(cur); + } + + if (pos == (end >> BITS_PER_LEVEL)) { + /* Drop bits representing the END-th and subsequent items. */ + int bit = end & (BITS_PER_LONG - 1); + cur &= (1UL << bit) - 1; + count += popcountl(cur); + } + + return count; +} + +/* Setting starts at the last layer and propagates up if an element + * changes from zero to non-zero. + */ +static inline bool hb_set_elem(unsigned long *elem, uint64_t start, uint64_t last) +{ + unsigned long mask; + bool changed; + + assert((last >> BITS_PER_LEVEL) == (start >> BITS_PER_LEVEL)); + assert(start <= last); + + mask = 2UL << (last & (BITS_PER_LONG - 1)); + mask -= 1UL << (start & (BITS_PER_LONG - 1)); + changed = (*elem == 0); + *elem |= mask; + return changed; +} + +/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... */ +static void hb_set_between(HBitmap *hb, int level, uint64_t start, uint64_t last) +{ + size_t pos = start >> BITS_PER_LEVEL; + size_t lastpos = last >> BITS_PER_LEVEL; + bool changed = false; + size_t i; + + i = pos; + if (i < lastpos) { + uint64_t next = (start | (BITS_PER_LONG - 1)) + 1; + changed |= hb_set_elem(&hb->levels[level][i], start, next - 1); + for (;;) { + start = next; + next += BITS_PER_LONG; + if (++i == lastpos) { + break; + } + changed |= (hb->levels[level][i] == 0); + hb->levels[level][i] = ~0UL; + } + } + changed |= hb_set_elem(&hb->levels[level][i], start, last); + + /* If there was any change in this layer, we may have to update + * the one above. + */ + if (level > 0 && changed) { + hb_set_between(hb, level - 1, pos, lastpos); + } +} + +void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count) +{ + /* Compute range in the last layer. */ + uint64_t last = start + count - 1; + + trace_hbitmap_set(hb, start, count, + start >> hb->granularity, last >> hb->granularity); + + start >>= hb->granularity; + last >>= hb->granularity; + count = last - start + 1; + + hb->count += count - hb_count_between(hb, start, last); + hb_set_between(hb, HBITMAP_LEVELS - 1, start, last); +} + +/* Resetting works the other way round: propagate up if the new + * value is zero. + */ +static inline bool hb_reset_elem(unsigned long *elem, uint64_t start, uint64_t last) +{ + unsigned long mask; + bool blanked; + + assert((last >> BITS_PER_LEVEL) == (start >> BITS_PER_LEVEL)); + assert(start <= last); + + mask = 2UL << (last & (BITS_PER_LONG - 1)); + mask -= 1UL << (start & (BITS_PER_LONG - 1)); + blanked = *elem != 0 && ((*elem & ~mask) == 0); + *elem &= ~mask; + return blanked; +} + +/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... */ +static void hb_reset_between(HBitmap *hb, int level, uint64_t start, uint64_t last) +{ + size_t pos = start >> BITS_PER_LEVEL; + size_t lastpos = last >> BITS_PER_LEVEL; + bool changed = false; + size_t i; + + i = pos; + if (i < lastpos) { + uint64_t next = (start | (BITS_PER_LONG - 1)) + 1; + + /* Here we need a more complex test than when setting bits. Even if + * something was changed, we must not blank bits in the upper level + * unless the lower-level word became entirely zero. So, remove pos + * from the upper-level range if bits remain set. + */ + if (hb_reset_elem(&hb->levels[level][i], start, next - 1)) { + changed = true; + } else { + pos++; + } + + for (;;) { + start = next; + next += BITS_PER_LONG; + if (++i == lastpos) { + break; + } + changed |= (hb->levels[level][i] != 0); + hb->levels[level][i] = 0UL; + } + } + + /* Same as above, this time for lastpos. */ + if (hb_reset_elem(&hb->levels[level][i], start, last)) { + changed = true; + } else { + lastpos--; + } + + if (level > 0 && changed) { + hb_reset_between(hb, level - 1, pos, lastpos); + } +} + +void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count) +{ + /* Compute range in the last layer. */ + uint64_t last = start + count - 1; + + trace_hbitmap_reset(hb, start, count, + start >> hb->granularity, last >> hb->granularity); + + start >>= hb->granularity; + last >>= hb->granularity; + + hb->count -= hb_count_between(hb, start, last); + hb_reset_between(hb, HBITMAP_LEVELS - 1, start, last); +} + +bool hbitmap_get(const HBitmap *hb, uint64_t item) +{ + /* Compute position and bit in the last layer. */ + uint64_t pos = item >> hb->granularity; + unsigned long bit = 1UL << (pos & (BITS_PER_LONG - 1)); + + return (hb->levels[HBITMAP_LEVELS - 1][pos >> BITS_PER_LEVEL] & bit) != 0; +} + +void hbitmap_free(HBitmap *hb) +{ + unsigned i; + for (i = HBITMAP_LEVELS; i-- > 0; ) { + g_free(hb->levels[i]); + } + g_free(hb); +} + +HBitmap *hbitmap_alloc(uint64_t size, int granularity) +{ + HBitmap *hb = g_malloc0(sizeof (struct HBitmap)); + unsigned i; + + assert(granularity >= 0 && granularity < 64); + size = (size + (1ULL << granularity) - 1) >> granularity; + assert(size <= ((uint64_t)1 << HBITMAP_LOG_MAX_SIZE)); + + hb->size = size; + hb->granularity = granularity; + for (i = HBITMAP_LEVELS; i-- > 0; ) { + size = MAX((size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1); + hb->levels[i] = g_malloc0(size * sizeof(unsigned long)); + } + + /* We necessarily have free bits in level 0 due to the definition + * of HBITMAP_LEVELS, so use one for a sentinel. This speeds up + * hbitmap_iter_skip_words. + */ + assert(size == 1); + hb->levels[0][0] |= 1UL << (BITS_PER_LONG - 1); + return hb; +} diff --git a/host-utils.c b/util/host-utils.c similarity index 55% rename from host-utils.c rename to util/host-utils.c index 5e3915abba..f0784d6335 100644 --- a/host-utils.c +++ b/util/host-utils.c @@ -27,79 +27,63 @@ #include #include "qemu/host-utils.h" -//#define DEBUG_MULDIV - /* Long integer helpers */ -#if !defined(__x86_64__) -static void add128 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b) +#ifndef CONFIG_INT128 +static inline void mul64(uint64_t *plow, uint64_t *phigh, + uint64_t a, uint64_t b) { - *plow += a; - /* carry test */ - if (*plow < a) - (*phigh)++; - *phigh += b; -} + typedef union { + uint64_t ll; + struct { +#ifdef HOST_WORDS_BIGENDIAN + uint32_t high, low; +#else + uint32_t low, high; +#endif + } l; + } LL; + LL rl, rm, rn, rh, a0, b0; + uint64_t c; -static void neg128 (uint64_t *plow, uint64_t *phigh) -{ - *plow = ~*plow; - *phigh = ~*phigh; - add128(plow, phigh, 1, 0); -} + a0.ll = a; + b0.ll = b; -static void mul64 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b) -{ - uint32_t a0, a1, b0, b1; - uint64_t v; + rl.ll = (uint64_t)a0.l.low * b0.l.low; + rm.ll = (uint64_t)a0.l.low * b0.l.high; + rn.ll = (uint64_t)a0.l.high * b0.l.low; + rh.ll = (uint64_t)a0.l.high * b0.l.high; - a0 = a; - a1 = a >> 32; + c = (uint64_t)rl.l.high + rm.l.low + rn.l.low; + rl.l.high = c; + c >>= 32; + c = c + rm.l.high + rn.l.high + rh.l.low; + rh.l.low = c; + rh.l.high += (uint32_t)(c >> 32); - b0 = b; - b1 = b >> 32; - - v = (uint64_t)a0 * (uint64_t)b0; - *plow = v; - *phigh = 0; - - v = (uint64_t)a0 * (uint64_t)b1; - add128(plow, phigh, v << 32, v >> 32); - - v = (uint64_t)a1 * (uint64_t)b0; - add128(plow, phigh, v << 32, v >> 32); - - v = (uint64_t)a1 * (uint64_t)b1; - *phigh += v; + *plow = rl.ll; + *phigh = rh.ll; } /* Unsigned 64x64 -> 128 multiplication */ void mulu64 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b) { mul64(plow, phigh, a, b); -#if defined(DEBUG_MULDIV) - printf("mulu64: 0x%016llx * 0x%016llx = 0x%016llx%016llx\n", - a, b, *phigh, *plow); -#endif } /* Signed 64x64 -> 128 multiplication */ void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b) { - int sa, sb; + uint64_t rh; - sa = (a < 0); - if (sa) - a = -a; - sb = (b < 0); - if (sb) - b = -b; - mul64(plow, phigh, a, b); - if (sa ^ sb) { - neg128(plow, phigh); + mul64(plow, &rh, a, b); + + /* Adjust for signs. */ + if (b < 0) { + rh -= a; } -#if defined(DEBUG_MULDIV) - printf("muls64: 0x%016llx * 0x%016llx = 0x%016llx%016llx\n", - a, b, *phigh, *plow); -#endif + if (a < 0) { + rh -= b; + } + *phigh = rh; } -#endif /* !defined(__x86_64__) */ +#endif /* !CONFIG_INT128 */ diff --git a/iov.c b/util/iov.c similarity index 81% rename from iov.c rename to util/iov.c index 419e419969..fbe675d373 100644 --- a/iov.c +++ b/util/iov.c @@ -288,6 +288,40 @@ void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len) ++qiov->niov; } +/* + * Concatenates (partial) iovecs from src_iov to the end of dst. + * It starts copying after skipping `soffset' bytes at the + * beginning of src and adds individual vectors from src to + * dst copies up to `sbytes' bytes total, or up to the end + * of src_iov if it comes first. This way, it is okay to specify + * very large value for `sbytes' to indicate "up to the end + * of src". + * Only vector pointers are processed, not the actual data buffers. + */ +void qemu_iovec_concat_iov(QEMUIOVector *dst, + struct iovec *src_iov, unsigned int src_cnt, + size_t soffset, size_t sbytes) +{ + int i; + size_t done; + + if (!sbytes) { + return; + } + assert(dst->nalloc != -1); + for (i = 0, done = 0; done < sbytes && i < src_cnt; i++) { + if (soffset < src_iov[i].iov_len) { + size_t len = MIN(src_iov[i].iov_len - soffset, sbytes - done); + qemu_iovec_add(dst, src_iov[i].iov_base + soffset, len); + done += len; + soffset = 0; + } else { + soffset -= src_iov[i].iov_len; + } + } + assert(soffset == 0); /* offset beyond end of src */ +} + /* * Concatenates (partial) iovecs from src to the end of dst. * It starts copying after skipping `soffset' bytes at the @@ -301,22 +335,7 @@ void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len) void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t soffset, size_t sbytes) { - int i; - size_t done; - struct iovec *siov = src->iov; - assert(dst->nalloc != -1); - assert(src->size >= soffset); - for (i = 0, done = 0; done < sbytes && i < src->niov; i++) { - if (soffset < siov[i].iov_len) { - size_t len = MIN(siov[i].iov_len - soffset, sbytes - done); - qemu_iovec_add(dst, siov[i].iov_base + soffset, len); - done += len; - soffset = 0; - } else { - soffset -= siov[i].iov_len; - } - } - /* return done; */ + qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes); } void qemu_iovec_destroy(QEMUIOVector *qiov) @@ -354,3 +373,54 @@ size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset, { return iov_memset(qiov->iov, qiov->niov, offset, fillc, bytes); } + +size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt, + size_t bytes) +{ + size_t total = 0; + struct iovec *cur; + + for (cur = *iov; *iov_cnt > 0; cur++) { + if (cur->iov_len > bytes) { + cur->iov_base += bytes; + cur->iov_len -= bytes; + total += bytes; + break; + } + + bytes -= cur->iov_len; + total += cur->iov_len; + *iov_cnt -= 1; + } + + *iov = cur; + return total; +} + +size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt, + size_t bytes) +{ + size_t total = 0; + struct iovec *cur; + + if (*iov_cnt == 0) { + return 0; + } + + cur = iov + (*iov_cnt - 1); + + while (*iov_cnt > 0) { + if (cur->iov_len > bytes) { + cur->iov_len -= bytes; + total += bytes; + break; + } + + bytes -= cur->iov_len; + total += cur->iov_len; + cur--; + *iov_cnt -= 1; + } + + return total; +} diff --git a/module.c b/util/module.c similarity index 100% rename from module.c rename to util/module.c diff --git a/notify.c b/util/notify.c similarity index 100% rename from notify.c rename to util/notify.c diff --git a/osdep.c b/util/osdep.c similarity index 97% rename from osdep.c rename to util/osdep.c index 5b51a0322e..bd59ac90c1 100644 --- a/osdep.c +++ b/util/osdep.c @@ -57,12 +57,18 @@ static const char *qemu_version = QEMU_VERSION; int socket_set_cork(int fd, int v) { #if defined(SOL_TCP) && defined(TCP_CORK) - return setsockopt(fd, SOL_TCP, TCP_CORK, &v, sizeof(v)); + return qemu_setsockopt(fd, SOL_TCP, TCP_CORK, &v, sizeof(v)); #else return 0; #endif } +int socket_set_nodelay(int fd) +{ + int v = 1; + return qemu_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); +} + int qemu_madvise(void *addr, size_t len, int advice) { if (advice == QEMU_MADV_INVALID) { diff --git a/oslib-posix.c b/util/oslib-posix.c similarity index 97% rename from oslib-posix.c rename to util/oslib-posix.c index 4f5ec6788b..433dd6888b 100644 --- a/oslib-posix.c +++ b/util/oslib-posix.c @@ -35,7 +35,7 @@ extern int daemon(int, int); #endif -#if defined(__linux__) && defined(__x86_64__) +#if defined(__linux__) && (defined(__x86_64__) || defined(__arm__)) /* Use 2 MiB alignment so transparent hugepages can be used by KVM. Valgrind does not support alignments larger than 1 MiB, therefore we need special code which handles running on Valgrind. */ @@ -105,8 +105,6 @@ void *qemu_memalign(size_t alignment, size_t size) return ptr; } -/* conflicts with qemu_vmalloc in bsd-user/mmap.c */ -#if !defined(CONFIG_BSD_USER) /* alloc shared memory pages */ void *qemu_vmalloc(size_t size) { @@ -129,7 +127,6 @@ void *qemu_vmalloc(size_t size) trace_qemu_vmalloc(size, ptr); return ptr; } -#endif void qemu_vfree(void *ptr) { diff --git a/oslib-win32.c b/util/oslib-win32.c similarity index 98% rename from oslib-win32.c rename to util/oslib-win32.c index e7e283e875..640194c0cf 100644 --- a/oslib-win32.c +++ b/util/oslib-win32.c @@ -71,7 +71,9 @@ void *qemu_vmalloc(size_t size) void qemu_vfree(void *ptr) { trace_qemu_vfree(ptr); - VirtualFree(ptr, 0, MEM_RELEASE); + if (ptr) { + VirtualFree(ptr, 0, MEM_RELEASE); + } } /* FIXME: add proper locking */ diff --git a/path.c b/util/path.c similarity index 94% rename from path.c rename to util/path.c index ef3f277f17..f0c69627c7 100644 --- a/path.c +++ b/util/path.c @@ -47,10 +47,7 @@ static struct pathelem *new_entry(const char *root, { struct pathelem *new = malloc(sizeof(*new)); new->name = strdup(name); - if (asprintf(&new->pathname, "%s/%s", root, name) == -1) { - printf("Cannot allocate memory\n"); - exit(1); - } + new->pathname = g_strdup_printf("%s/%s", root, name); new->num_entries = 0; return new; } @@ -58,9 +55,10 @@ static struct pathelem *new_entry(const char *root, #define streq(a,b) (strcmp((a), (b)) == 0) /* Not all systems provide this feature */ -#if defined(DT_DIR) && defined(DT_UNKNOWN) +#if defined(DT_DIR) && defined(DT_UNKNOWN) && defined(DT_LNK) # define dirent_type(dirent) ((dirent)->d_type) -# define is_dir_maybe(type) ((type) == DT_DIR || (type) == DT_UNKNOWN) +# define is_dir_maybe(type) \ + ((type) == DT_DIR || (type) == DT_UNKNOWN || (type) == DT_LNK) #else # define dirent_type(dirent) (1) # define is_dir_maybe(type) (type) diff --git a/util/qemu-config.c b/util/qemu-config.c new file mode 100644 index 0000000000..01ca8901cf --- /dev/null +++ b/util/qemu-config.c @@ -0,0 +1,216 @@ +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "qemu/option.h" +#include "qemu/config-file.h" +#include "qapi/qmp/qerror.h" +#include "hw/qdev.h" +#include "qapi/error.h" + +static QemuOptsList *vm_config_groups[32]; + +static QemuOptsList *find_list(QemuOptsList **lists, const char *group, + Error **errp) +{ + int i; + + for (i = 0; lists[i] != NULL; i++) { + if (strcmp(lists[i]->name, group) == 0) + break; + } + if (lists[i] == NULL) { + error_set(errp, QERR_INVALID_OPTION_GROUP, group); + } + return lists[i]; +} + +QemuOptsList *qemu_find_opts(const char *group) +{ + QemuOptsList *ret; + Error *local_err = NULL; + + ret = find_list(vm_config_groups, group, &local_err); + if (error_is_set(&local_err)) { + error_report("%s", error_get_pretty(local_err)); + error_free(local_err); + } + + return ret; +} + +QemuOptsList *qemu_find_opts_err(const char *group, Error **errp) +{ + return find_list(vm_config_groups, group, errp); +} + +void qemu_add_opts(QemuOptsList *list) +{ + int entries, i; + + entries = ARRAY_SIZE(vm_config_groups); + entries--; /* keep list NULL terminated */ + for (i = 0; i < entries; i++) { + if (vm_config_groups[i] == NULL) { + vm_config_groups[i] = list; + return; + } + } + fprintf(stderr, "ran out of space in vm_config_groups"); + abort(); +} + +int qemu_set_option(const char *str) +{ + char group[64], id[64], arg[64]; + QemuOptsList *list; + QemuOpts *opts; + int rc, offset; + + rc = sscanf(str, "%63[^.].%63[^.].%63[^=]%n", group, id, arg, &offset); + if (rc < 3 || str[offset] != '=') { + error_report("can't parse: \"%s\"", str); + return -1; + } + + list = qemu_find_opts(group); + if (list == NULL) { + return -1; + } + + opts = qemu_opts_find(list, id); + if (!opts) { + error_report("there is no %s \"%s\" defined", + list->name, id); + return -1; + } + + if (qemu_opt_set(opts, arg, str+offset+1) == -1) { + return -1; + } + return 0; +} + +struct ConfigWriteData { + QemuOptsList *list; + FILE *fp; +}; + +static int config_write_opt(const char *name, const char *value, void *opaque) +{ + struct ConfigWriteData *data = opaque; + + fprintf(data->fp, " %s = \"%s\"\n", name, value); + return 0; +} + +static int config_write_opts(QemuOpts *opts, void *opaque) +{ + struct ConfigWriteData *data = opaque; + const char *id = qemu_opts_id(opts); + + if (id) { + fprintf(data->fp, "[%s \"%s\"]\n", data->list->name, id); + } else { + fprintf(data->fp, "[%s]\n", data->list->name); + } + qemu_opt_foreach(opts, config_write_opt, data, 0); + fprintf(data->fp, "\n"); + return 0; +} + +void qemu_config_write(FILE *fp) +{ + struct ConfigWriteData data = { .fp = fp }; + QemuOptsList **lists = vm_config_groups; + int i; + + fprintf(fp, "# qemu config file\n\n"); + for (i = 0; lists[i] != NULL; i++) { + data.list = lists[i]; + qemu_opts_foreach(data.list, config_write_opts, &data, 0); + } +} + +int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname) +{ + char line[1024], group[64], id[64], arg[64], value[1024]; + Location loc; + QemuOptsList *list = NULL; + Error *local_err = NULL; + QemuOpts *opts = NULL; + int res = -1, lno = 0; + + loc_push_none(&loc); + while (fgets(line, sizeof(line), fp) != NULL) { + loc_set_file(fname, ++lno); + if (line[0] == '\n') { + /* skip empty lines */ + continue; + } + if (line[0] == '#') { + /* comment */ + continue; + } + if (sscanf(line, "[%63s \"%63[^\"]\"]", group, id) == 2) { + /* group with id */ + list = find_list(lists, group, &local_err); + if (error_is_set(&local_err)) { + error_report("%s", error_get_pretty(local_err)); + error_free(local_err); + goto out; + } + opts = qemu_opts_create(list, id, 1, NULL); + continue; + } + if (sscanf(line, "[%63[^]]]", group) == 1) { + /* group without id */ + list = find_list(lists, group, &local_err); + if (error_is_set(&local_err)) { + error_report("%s", error_get_pretty(local_err)); + error_free(local_err); + goto out; + } + opts = qemu_opts_create_nofail(list); + continue; + } + if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2) { + /* arg = value */ + if (opts == NULL) { + error_report("no group defined"); + goto out; + } + if (qemu_opt_set(opts, arg, value) != 0) { + goto out; + } + continue; + } + error_report("parse error"); + goto out; + } + if (ferror(fp)) { + error_report("error reading file"); + goto out; + } + res = 0; +out: + loc_pop(&loc); + return res; +} + +int qemu_read_config_file(const char *filename) +{ + FILE *f = fopen(filename, "r"); + int ret; + + if (f == NULL) { + return -errno; + } + + ret = qemu_config_parse(f, vm_config_groups, filename); + fclose(f); + + if (ret == 0) { + return 0; + } else { + return -EINVAL; + } +} diff --git a/qemu-error.c b/util/qemu-error.c similarity index 100% rename from qemu-error.c rename to util/qemu-error.c diff --git a/qemu-option.c b/util/qemu-option.c similarity index 96% rename from qemu-option.c rename to util/qemu-option.c index f532b765a0..8b74bf1f7a 100644 --- a/qemu-option.c +++ b/util/qemu-option.c @@ -231,8 +231,10 @@ static void parse_option_size(const char *name, const char *value, break; default: error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, "a size"); +#if 0 /* conversion from qerror_report() to error_set() broke this: */ error_printf_unless_qmp("You may use k, M, G or T suffixes for " "kilobytes, megabytes, gigabytes and terabytes.\n"); +#endif return; } } else { @@ -643,9 +645,7 @@ static void opt_set(QemuOpts *opts, const char *name, const char *value, QTAILQ_INSERT_TAIL(&opts->head, opt, next); } opt->desc = desc; - if (value) { - opt->str = g_strdup(value); - } + opt->str = g_strdup(value); qemu_opt_parse(opt, &local_err); if (error_is_set(&local_err)) { error_propagate(errp, local_err); @@ -773,7 +773,9 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, if (id) { if (!id_wellformed(id)) { error_set(errp,QERR_INVALID_PARAMETER_VALUE, "id", "an identifier"); +#if 0 /* conversion from qerror_report() to error_set() broke this: */ error_printf_unless_qmp("Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.\n"); +#endif return NULL; } opts = qemu_opts_find(list, id); @@ -792,9 +794,7 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, } } opts = g_malloc0(sizeof(*opts)); - if (id) { - opts->id = g_strdup(id); - } + opts->id = g_strdup(id); opts->list = list; loc_save(&opts->loc); QTAILQ_INIT(&opts->head); @@ -1066,6 +1066,40 @@ QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict, return opts; } +/* + * Adds all QDict entries to the QemuOpts that can be added and removes them + * from the QDict. When this function returns, the QDict contains only those + * entries that couldn't be added to the QemuOpts. + */ +void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp) +{ + const QDictEntry *entry, *next; + + entry = qdict_first(qdict); + + while (entry != NULL) { + Error *local_err = NULL; + OptsFromQDictState state = { + .errp = &local_err, + .opts = opts, + }; + + next = qdict_next(qdict, entry); + + if (find_desc_by_name(opts->list->desc, entry->key)) { + qemu_opts_from_qdict_1(entry->key, entry->value, &state); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + return; + } else { + qdict_del(qdict, entry->key); + } + } + + entry = next; + } +} + /* * Convert from QemuOpts to QDict. * The QDict values are of type QString. diff --git a/qemu-progress.c b/util/qemu-progress.c similarity index 100% rename from qemu-progress.c rename to util/qemu-progress.c diff --git a/qemu-sockets.c b/util/qemu-sockets.c similarity index 96% rename from qemu-sockets.c rename to util/qemu-sockets.c index 3537bf3d45..83e4e08e85 100644 --- a/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -373,6 +373,10 @@ int inet_connect_opts(QemuOpts *opts, Error **errp, } for (e = res; e != NULL; e = e->ai_next) { + if (error_is_set(errp)) { + error_free(*errp); + *errp = NULL; + } if (connect_state != NULL) { connect_state->current_addr = e; } @@ -720,7 +724,7 @@ int unix_connect_opts(QemuOpts *opts, Error **errp, int sock, rc; if (NULL == path) { - error_setg(errp, "unix connect: no path specified\n"); + error_setg(errp, "unix connect: no path specified"); return -1; } @@ -854,7 +858,7 @@ SocketAddress *socket_parse(const char *str, Error **errp) addr = g_new(SocketAddress, 1); if (strstart(str, "unix:", NULL)) { if (str[5] == '\0') { - error_setg(errp, "invalid Unix socket address\n"); + error_setg(errp, "invalid Unix socket address"); goto fail; } else { addr->kind = SOCKET_ADDRESS_KIND_UNIX; @@ -863,7 +867,7 @@ SocketAddress *socket_parse(const char *str, Error **errp) } } else if (strstart(str, "fd:", NULL)) { if (str[3] == '\0') { - error_setg(errp, "invalid file descriptor address\n"); + error_setg(errp, "invalid file descriptor address"); goto fail; } else { addr->kind = SOCKET_ADDRESS_KIND_FD; @@ -945,6 +949,31 @@ int socket_listen(SocketAddress *addr, Error **errp) return fd; } +int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp) +{ + QemuOpts *opts; + int fd; + + opts = qemu_opts_create_nofail(&dummy_opts); + switch (remote->kind) { + case SOCKET_ADDRESS_KIND_INET: + qemu_opt_set(opts, "host", remote->inet->host); + qemu_opt_set(opts, "port", remote->inet->port); + if (local) { + qemu_opt_set(opts, "localaddr", local->inet->host); + qemu_opt_set(opts, "localport", local->inet->port); + } + fd = inet_dgram_opts(opts, errp); + break; + + default: + error_setg(errp, "socket type unsupported for datagram"); + return -1; + } + qemu_opts_del(opts); + return fd; +} + #ifdef _WIN32 static void socket_cleanup(void) { diff --git a/qemu-thread-posix.c b/util/qemu-thread-posix.c similarity index 95% rename from qemu-thread-posix.c rename to util/qemu-thread-posix.c index 7be292ed68..4489abf1d8 100644 --- a/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -122,7 +122,7 @@ void qemu_sem_init(QemuSemaphore *sem, int init) { int rc; -#if defined(__OpenBSD__) || defined(__APPLE__) || defined(__NetBSD__) +#if defined(__APPLE__) || defined(__NetBSD__) rc = pthread_mutex_init(&sem->lock, NULL); if (rc != 0) { error_exit(rc, __func__); @@ -147,7 +147,7 @@ void qemu_sem_destroy(QemuSemaphore *sem) { int rc; -#if defined(__OpenBSD__) || defined(__APPLE__) || defined(__NetBSD__) +#if defined(__APPLE__) || defined(__NetBSD__) rc = pthread_cond_destroy(&sem->cond); if (rc < 0) { error_exit(rc, __func__); @@ -168,7 +168,7 @@ void qemu_sem_post(QemuSemaphore *sem) { int rc; -#if defined(__OpenBSD__) || defined(__APPLE__) || defined(__NetBSD__) +#if defined(__APPLE__) || defined(__NetBSD__) pthread_mutex_lock(&sem->lock); if (sem->count == INT_MAX) { rc = EINVAL; @@ -206,13 +206,14 @@ int qemu_sem_timedwait(QemuSemaphore *sem, int ms) int rc; struct timespec ts; -#if defined(__OpenBSD__) || defined(__APPLE__) || defined(__NetBSD__) +#if defined(__APPLE__) || defined(__NetBSD__) compute_abs_deadline(&ts, ms); pthread_mutex_lock(&sem->lock); --sem->count; while (sem->count < 0) { rc = pthread_cond_timedwait(&sem->cond, &sem->lock, &ts); if (rc == ETIMEDOUT) { + ++sem->count; break; } if (rc != 0) { @@ -248,7 +249,7 @@ int qemu_sem_timedwait(QemuSemaphore *sem, int ms) void qemu_sem_wait(QemuSemaphore *sem) { -#if defined(__OpenBSD__) || defined(__APPLE__) || defined(__NetBSD__) +#if defined(__APPLE__) || defined(__NetBSD__) pthread_mutex_lock(&sem->lock); --sem->count; while (sem->count < 0) { diff --git a/qemu-thread-win32.c b/util/qemu-thread-win32.c similarity index 94% rename from qemu-thread-win32.c rename to util/qemu-thread-win32.c index 8037b39045..517878dcc1 100644 --- a/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -239,7 +239,7 @@ struct QemuThreadData { CRITICAL_SECTION cs; }; -static int qemu_thread_tls_index = TLS_OUT_OF_INDEXES; +static __thread QemuThreadData *qemu_thread_data; static unsigned __stdcall win32_start_routine(void *arg) { @@ -251,14 +251,15 @@ static unsigned __stdcall win32_start_routine(void *arg) g_free(data); data = NULL; } - TlsSetValue(qemu_thread_tls_index, data); + qemu_thread_data = data; qemu_thread_exit(start_routine(thread_arg)); abort(); } void qemu_thread_exit(void *arg) { - QemuThreadData *data = TlsGetValue(qemu_thread_tls_index); + QemuThreadData *data = qemu_thread_data; + if (data) { assert(data->mode != QEMU_THREAD_DETACHED); data->ret = arg; @@ -298,25 +299,13 @@ void *qemu_thread_join(QemuThread *thread) return ret; } -static inline void qemu_thread_init(void) -{ - if (qemu_thread_tls_index == TLS_OUT_OF_INDEXES) { - qemu_thread_tls_index = TlsAlloc(); - if (qemu_thread_tls_index == TLS_OUT_OF_INDEXES) { - error_exit(ERROR_NO_SYSTEM_RESOURCES, __func__); - } - } -} - - void qemu_thread_create(QemuThread *thread, void *(*start_routine)(void *), void *arg, int mode) { HANDLE hThread; - struct QemuThreadData *data; - qemu_thread_init(); + data = g_malloc(sizeof *data); data->start_routine = start_routine; data->arg = arg; @@ -338,8 +327,7 @@ void qemu_thread_create(QemuThread *thread, void qemu_thread_get_self(QemuThread *thread) { - qemu_thread_init(); - thread->data = TlsGetValue(qemu_thread_tls_index); + thread->data = qemu_thread_data; thread->tid = GetCurrentThreadId(); } diff --git a/qemu-timer-common.c b/util/qemu-timer-common.c similarity index 100% rename from qemu-timer-common.c rename to util/qemu-timer-common.c diff --git a/uri.c b/util/uri.c similarity index 100% rename from uri.c rename to util/uri.c diff --git a/vl.c b/vl.c index e6a8d89acc..a621aec0a4 100644 --- a/vl.c +++ b/vl.c @@ -119,13 +119,13 @@ int main(int argc, char **argv) #include "hw/pcmcia.h" #include "hw/pc.h" #include "hw/isa.h" -#include "hw/baum.h" #include "hw/bt.h" #include "hw/watchdog.h" #include "hw/smbios.h" #include "hw/xen.h" #include "hw/qdev.h" #include "hw/loader.h" +#include "monitor/qdev.h" #include "bt/bt.h" #include "net/net.h" #include "net/slirp.h" @@ -139,6 +139,7 @@ int main(int argc, char **argv) #include "sysemu/blockdev.h" #include "hw/block-common.h" #include "migration/block.h" +#include "tpm/tpm.h" #include "sysemu/dma.h" #include "audio/audio.h" #include "migration/migration.h" @@ -176,8 +177,10 @@ int main(int argc, char **argv) #define DEFAULT_RAM_SIZE 128 #define MAX_VIRTIO_CONSOLES 1 +#define MAX_SCLP_CONSOLES 1 -static const char *data_dir; +static const char *data_dir[16]; +static int data_dir_idx; const char *bios_name = NULL; enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB; DisplayType display_type = DT_DEFAULT; @@ -203,6 +206,7 @@ int no_quit = 0; CharDriverState *serial_hds[MAX_SERIAL_PORTS]; CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES]; +CharDriverState *sclp_hds[MAX_SCLP_CONSOLES]; int win2k_install_hack = 0; int singlestep = 0; int smp_cpus = 1; @@ -231,7 +235,7 @@ unsigned int nb_prom_envs = 0; const char *prom_envs[MAX_PROM_ENVS]; int boot_menu; uint8_t *boot_splash_filedata; -int boot_splash_filedata_size; +size_t boot_splash_filedata_size; uint8_t qemu_extra_params_fw[2]; typedef struct FWBootEntry FWBootEntry; @@ -261,9 +265,9 @@ static NotifierList exit_notifiers = static NotifierList machine_init_done_notifiers = NOTIFIER_LIST_INITIALIZER(machine_init_done_notifiers); -static int tcg_allowed = 1; -int kvm_allowed = 0; -int xen_allowed = 0; +static bool tcg_allowed = true; +bool kvm_allowed; +bool xen_allowed; uint32_t xen_domid; enum xen_mode xen_mode = XEN_EMULATE; static int tcg_tb_size; @@ -271,6 +275,7 @@ static int tcg_tb_size; static int default_serial = 1; static int default_parallel = 1; static int default_virtcon = 1; +static int default_sclp = 1; static int default_monitor = 1; static int default_floppy = 1; static int default_cdrom = 1; @@ -299,6 +304,219 @@ static struct { { .driver = "qxl-vga", .flag = &default_vga }, }; +static QemuOptsList qemu_rtc_opts = { + .name = "rtc", + .head = QTAILQ_HEAD_INITIALIZER(qemu_rtc_opts.head), + .desc = { + { + .name = "base", + .type = QEMU_OPT_STRING, + },{ + .name = "clock", + .type = QEMU_OPT_STRING, + },{ + .name = "driftfix", + .type = QEMU_OPT_STRING, + }, + { /* end of list */ } + }, +}; + +static QemuOptsList qemu_sandbox_opts = { + .name = "sandbox", + .implied_opt_name = "enable", + .head = QTAILQ_HEAD_INITIALIZER(qemu_sandbox_opts.head), + .desc = { + { + .name = "enable", + .type = QEMU_OPT_BOOL, + }, + { /* end of list */ } + }, +}; + +static QemuOptsList qemu_trace_opts = { + .name = "trace", + .implied_opt_name = "trace", + .head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head), + .desc = { + { + .name = "events", + .type = QEMU_OPT_STRING, + },{ + .name = "file", + .type = QEMU_OPT_STRING, + }, + { /* end of list */ } + }, +}; + +static QemuOptsList qemu_option_rom_opts = { + .name = "option-rom", + .implied_opt_name = "romfile", + .head = QTAILQ_HEAD_INITIALIZER(qemu_option_rom_opts.head), + .desc = { + { + .name = "bootindex", + .type = QEMU_OPT_NUMBER, + }, { + .name = "romfile", + .type = QEMU_OPT_STRING, + }, + { /* end of list */ } + }, +}; + +static QemuOptsList qemu_machine_opts = { + .name = "machine", + .implied_opt_name = "type", + .merge_lists = true, + .head = QTAILQ_HEAD_INITIALIZER(qemu_machine_opts.head), + .desc = { + { + .name = "type", + .type = QEMU_OPT_STRING, + .help = "emulated machine" + }, { + .name = "accel", + .type = QEMU_OPT_STRING, + .help = "accelerator list", + }, { + .name = "kernel_irqchip", + .type = QEMU_OPT_BOOL, + .help = "use KVM in-kernel irqchip", + }, { + .name = "kvm_shadow_mem", + .type = QEMU_OPT_SIZE, + .help = "KVM shadow MMU size", + }, { + .name = "kernel", + .type = QEMU_OPT_STRING, + .help = "Linux kernel image file", + }, { + .name = "initrd", + .type = QEMU_OPT_STRING, + .help = "Linux initial ramdisk file", + }, { + .name = "append", + .type = QEMU_OPT_STRING, + .help = "Linux kernel command line", + }, { + .name = "dtb", + .type = QEMU_OPT_STRING, + .help = "Linux kernel device tree file", + }, { + .name = "dumpdtb", + .type = QEMU_OPT_STRING, + .help = "Dump current dtb to a file and quit", + }, { + .name = "phandle_start", + .type = QEMU_OPT_STRING, + .help = "The first phandle ID we may generate dynamically", + }, { + .name = "dt_compatible", + .type = QEMU_OPT_STRING, + .help = "Overrides the \"compatible\" property of the dt root node", + }, { + .name = "dump-guest-core", + .type = QEMU_OPT_BOOL, + .help = "Include guest memory in a core dump", + }, { + .name = "mem-merge", + .type = QEMU_OPT_BOOL, + .help = "enable/disable memory merge support", + },{ + .name = "usb", + .type = QEMU_OPT_BOOL, + .help = "Set on/off to enable/disable usb", + }, + { /* End of list */ } + }, +}; + +static QemuOptsList qemu_boot_opts = { + .name = "boot-opts", + .head = QTAILQ_HEAD_INITIALIZER(qemu_boot_opts.head), + .desc = { + /* the three names below are not used now */ + { + .name = "order", + .type = QEMU_OPT_STRING, + }, { + .name = "once", + .type = QEMU_OPT_STRING, + }, { + .name = "menu", + .type = QEMU_OPT_STRING, + /* following are really used */ + }, { + .name = "splash", + .type = QEMU_OPT_STRING, + }, { + .name = "splash-time", + .type = QEMU_OPT_STRING, + }, { + .name = "reboot-timeout", + .type = QEMU_OPT_STRING, + }, + { /*End of list */ } + }, +}; + +static QemuOptsList qemu_add_fd_opts = { + .name = "add-fd", + .head = QTAILQ_HEAD_INITIALIZER(qemu_add_fd_opts.head), + .desc = { + { + .name = "fd", + .type = QEMU_OPT_NUMBER, + .help = "file descriptor of which a duplicate is added to fd set", + },{ + .name = "set", + .type = QEMU_OPT_NUMBER, + .help = "ID of the fd set to add fd to", + },{ + .name = "opaque", + .type = QEMU_OPT_STRING, + .help = "free-form string used to describe fd", + }, + { /* end of list */ } + }, +}; + +static QemuOptsList qemu_object_opts = { + .name = "object", + .implied_opt_name = "qom-type", + .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head), + .desc = { + { } + }, +}; + +static QemuOptsList qemu_tpmdev_opts = { + .name = "tpmdev", + .implied_opt_name = "type", + .head = QTAILQ_HEAD_INITIALIZER(qemu_tpmdev_opts.head), + .desc = { + { + .name = "type", + .type = QEMU_OPT_STRING, + .help = "Type of TPM backend", + }, + { + .name = "cancel-path", + .type = QEMU_OPT_STRING, + .help = "Sysfs file entry for canceling TPM commands", + }, + { + .name = "path", + .type = QEMU_OPT_STRING, + .help = "Path to TPM device on the host", + }, + { /* end of list */ } + }, +}; + const char *qemu_get_vm_name(void) { return qemu_name; @@ -448,21 +666,18 @@ StatusInfo *qmp_query_status(Error **errp) void qemu_get_timedate(struct tm *tm, int offset) { time_t ti; - struct tm *ret; time(&ti); ti += offset; if (rtc_date_offset == -1) { if (rtc_utc) - ret = gmtime(&ti); + gmtime_r(&ti, tm); else - ret = localtime(&ti); + localtime_r(&ti, tm); } else { ti -= rtc_date_offset; - ret = gmtime(&ti); + gmtime_r(&ti, tm); } - - memcpy(tm, ret, sizeof(struct tm)); } int qemu_timedate_diff(struct tm *tm) @@ -1012,15 +1227,15 @@ void add_boot_device_path(int32_t bootindex, DeviceState *dev, * memory pointed by "size" is assigned total length of the array in bytes * */ -char *get_boot_devices_list(uint32_t *size) +char *get_boot_devices_list(size_t *size) { FWBootEntry *i; - uint32_t total = 0; + size_t total = 0; char *list = NULL; QTAILQ_FOREACH(i, &fw_boot_order, link) { char *devpath = NULL, *bootpath; - int len; + size_t len; if (i->dev) { devpath = qdev_get_fw_dev_path(i->dev); @@ -1055,21 +1270,79 @@ char *get_boot_devices_list(uint32_t *size) return list; } +static void numa_node_parse_cpus(int nodenr, const char *cpus) +{ + char *endptr; + unsigned long long value, endvalue; + + /* Empty CPU range strings will be considered valid, they will simply + * not set any bit in the CPU bitmap. + */ + if (!*cpus) { + return; + } + + if (parse_uint(cpus, &value, &endptr, 10) < 0) { + goto error; + } + if (*endptr == '-') { + if (parse_uint_full(endptr + 1, &endvalue, 10) < 0) { + goto error; + } + } else if (*endptr == '\0') { + endvalue = value; + } else { + goto error; + } + + if (endvalue >= MAX_CPUMASK_BITS) { + endvalue = MAX_CPUMASK_BITS - 1; + fprintf(stderr, + "qemu: NUMA: A max of %d VCPUs are supported\n", + MAX_CPUMASK_BITS); + } + + if (endvalue < value) { + goto error; + } + + bitmap_set(node_cpumask[nodenr], value, endvalue-value+1); + return; + +error: + fprintf(stderr, "qemu: Invalid NUMA CPU range: %s\n", cpus); + exit(1); +} + static void numa_add(const char *optarg) { char option[128]; char *endptr; - unsigned long long value, endvalue; - int nodenr; + unsigned long long nodenr; - value = endvalue = 0ULL; - - optarg = get_opt_name(option, 128, optarg, ',') + 1; + optarg = get_opt_name(option, 128, optarg, ','); + if (*optarg == ',') { + optarg++; + } if (!strcmp(option, "node")) { + + if (nb_numa_nodes >= MAX_NODES) { + fprintf(stderr, "qemu: too many NUMA nodes\n"); + exit(1); + } + if (get_param_value(option, 128, "nodeid", optarg) == 0) { nodenr = nb_numa_nodes; } else { - nodenr = strtoull(option, NULL, 10); + if (parse_uint_full(option, &nodenr, 10) < 0) { + fprintf(stderr, "qemu: Invalid NUMA nodeid: %s\n", option); + exit(1); + } + } + + if (nodenr >= MAX_NODES) { + fprintf(stderr, "qemu: invalid NUMA nodeid: %llu\n", nodenr); + exit(1); } if (get_param_value(option, 128, "mem", optarg) == 0) { @@ -1084,23 +1357,12 @@ static void numa_add(const char *optarg) node_mem[nodenr] = sval; } if (get_param_value(option, 128, "cpus", optarg) != 0) { - value = strtoull(option, &endptr, 10); - if (*endptr == '-') { - endvalue = strtoull(endptr+1, &endptr, 10); - } else { - endvalue = value; - } - - if (!(endvalue < MAX_CPUMASK_BITS)) { - endvalue = MAX_CPUMASK_BITS - 1; - fprintf(stderr, - "A max of %d CPUs are supported in a guest\n", - MAX_CPUMASK_BITS); - } - - bitmap_set(node_cpumask[nodenr], value, endvalue-value+1); + numa_node_parse_cpus(nodenr, option); } nb_numa_nodes++; + } else { + fprintf(stderr, "Invalid -numa option: %s\n", option); + exit(1); } } @@ -1191,8 +1453,9 @@ static int usb_device_del(const char *devname) int bus_num, addr; const char *p; - if (strstart(devname, "host:", &p)) - return usb_host_device_close(p); + if (strstart(devname, "host:", &p)) { + return -1; + } if (!usb_enabled(false)) { return -1; @@ -1263,7 +1526,7 @@ void pcmcia_socket_unregister(PCMCIASocket *socket) } } -void pcmcia_info(Monitor *mon) +void pcmcia_info(Monitor *mon, const QDict *qdict) { struct pcmcia_socket_entry_s *iter; @@ -1969,6 +2232,13 @@ static DisplayType select_display(const char *p) #else fprintf(stderr, "Curses support is disabled\n"); exit(1); +#endif + } else if (strstart(p, "gtk", &opts)) { +#ifdef CONFIG_GTK + display = DT_GTK; +#else + fprintf(stderr, "GTK support is disabled\n"); + exit(1); #endif } else if (strstart(p, "none", &opts)) { display = DT_NONE; @@ -2007,14 +2277,16 @@ static int balloon_parse(const char *arg) char *qemu_find_file(int type, const char *name) { - int len; + int i; const char *subdir; char *buf; /* Try the name as a straight path first */ if (access(name, R_OK) == 0) { + trace_load_file(name, name); return g_strdup(name); } + switch (type) { case QEMU_FILE_TYPE_BIOS: subdir = ""; @@ -2025,14 +2297,16 @@ char *qemu_find_file(int type, const char *name) default: abort(); } - len = strlen(data_dir) + strlen(name) + strlen(subdir) + 2; - buf = g_malloc0(len); - snprintf(buf, len, "%s/%s%s", data_dir, subdir, name); - if (access(buf, R_OK)) { + + for (i = 0; i < data_dir_idx; i++) { + buf = g_strdup_printf("%s/%s%s", data_dir[i], subdir, name); + if (access(buf, R_OK) == 0) { + trace_load_file(name, buf); + return buf; + } g_free(buf); - return NULL; } - return buf; + return NULL; } static int device_help_func(QemuOpts *opts, void *opaque) @@ -2047,16 +2321,20 @@ static int device_init_func(QemuOpts *opts, void *opaque) dev = qdev_device_add(opts); if (!dev) return -1; + object_unref(OBJECT(dev)); return 0; } static int chardev_init_func(QemuOpts *opts, void *opaque) { - CharDriverState *chr; + Error *local_err = NULL; - chr = qemu_chr_new_from_opts(opts, NULL); - if (!chr) + qemu_chr_new_from_opts(opts, NULL, &local_err); + if (error_is_set(&local_err)) { + fprintf(stderr, "%s\n", error_get_pretty(local_err)); + error_free(local_err); return -1; + } return 0; } @@ -2151,6 +2429,7 @@ struct device_config { DEV_VIRTCON, /* -virtioconsole */ DEV_DEBUGCON, /* -debugcon */ DEV_GDB, /* -gdb, -s */ + DEV_SCLP, /* s390 sclp */ } type; const char *cmdline; Location loc; @@ -2269,6 +2548,39 @@ static int virtcon_parse(const char *devname) return 0; } +static int sclp_parse(const char *devname) +{ + QemuOptsList *device = qemu_find_opts("device"); + static int index = 0; + char label[32]; + QemuOpts *dev_opts; + + if (strcmp(devname, "none") == 0) { + return 0; + } + if (index == MAX_SCLP_CONSOLES) { + fprintf(stderr, "qemu: too many sclp consoles\n"); + exit(1); + } + + assert(arch_type == QEMU_ARCH_S390X); + + dev_opts = qemu_opts_create(device, NULL, 0, NULL); + qemu_opt_set(dev_opts, "driver", "sclpconsole"); + + snprintf(label, sizeof(label), "sclpcon%d", index); + sclp_hds[index] = qemu_chr_new(label, devname, NULL); + if (!sclp_hds[index]) { + fprintf(stderr, "qemu: could not connect sclp console" + " to character backend '%s'\n", devname); + return -1; + } + qemu_opt_set(dev_opts, "chardev", label); + + index++; + return 0; +} + static int debugcon_parse(const char *devname) { QemuOpts *opts; @@ -2318,7 +2630,7 @@ static struct { const char *name; int (*available)(void); int (*init)(void); - int *allowed; + bool *allowed; } accel_list[] = { { "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed }, { "xen", "Xen", xen_available, xen_init, &xen_allowed }, @@ -2331,8 +2643,8 @@ static int configure_accelerator(void) const char *p = NULL; char buf[10]; int i, ret; - bool accel_initialised = 0; - bool init_failed = 0; + bool accel_initialised = false; + bool init_failed = false; QemuOptsList *list = qemu_find_opts("machine"); if (!QTAILQ_EMPTY(&list->head)) { @@ -2351,21 +2663,21 @@ static int configure_accelerator(void) p = get_opt_name(buf, sizeof (buf), p, ':'); for (i = 0; i < ARRAY_SIZE(accel_list); i++) { if (strcmp(accel_list[i].opt_name, buf) == 0) { - *(accel_list[i].allowed) = 1; + if (!accel_list[i].available()) { + printf("%s not supported for this target\n", + accel_list[i].name); + continue; + } + *(accel_list[i].allowed) = true; ret = accel_list[i].init(); if (ret < 0) { - init_failed = 1; - if (!accel_list[i].available()) { - printf("%s not supported for this target\n", - accel_list[i].name); - } else { - fprintf(stderr, "failed to initialize %s: %s\n", - accel_list[i].name, - strerror(-ret)); - } - *(accel_list[i].allowed) = 0; + init_failed = true; + fprintf(stderr, "failed to initialize %s: %s\n", + accel_list[i].name, + strerror(-ret)); + *(accel_list[i].allowed) = false; } else { - accel_initialised = 1; + accel_initialised = true; } break; } @@ -2376,7 +2688,9 @@ static int configure_accelerator(void) } if (!accel_initialised) { - fprintf(stderr, "No accelerator found!\n"); + if (!init_failed) { + fprintf(stderr, "No accelerator found!\n"); + } exit(1); } @@ -2526,7 +2840,7 @@ int main(int argc, char **argv, char **envp) const char *icount_option = NULL; const char *initrd_filename; const char *kernel_filename, *kernel_cmdline; - char boot_devices[33] = "cad"; /* default to HD->floppy->CD-ROM */ + char boot_devices[33] = ""; DisplayState *ds; int cyls, heads, secs, translation; QemuOpts *hda_opts = NULL, *opts, *machine_opts; @@ -2569,6 +2883,23 @@ int main(int argc, char **argv, char **envp) module_call_init(MODULE_INIT_QOM); + qemu_add_opts(&qemu_drive_opts); + qemu_add_opts(&qemu_chardev_opts); + qemu_add_opts(&qemu_device_opts); + qemu_add_opts(&qemu_netdev_opts); + qemu_add_opts(&qemu_net_opts); + qemu_add_opts(&qemu_rtc_opts); + qemu_add_opts(&qemu_global_opts); + qemu_add_opts(&qemu_mon_opts); + qemu_add_opts(&qemu_trace_opts); + qemu_add_opts(&qemu_option_rom_opts); + qemu_add_opts(&qemu_machine_opts); + qemu_add_opts(&qemu_boot_opts); + qemu_add_opts(&qemu_sandbox_opts); + qemu_add_opts(&qemu_add_fd_opts); + qemu_add_opts(&qemu_object_opts); + qemu_add_opts(&qemu_tpmdev_opts); + runstate_init(); init_clocks(); @@ -2694,7 +3025,7 @@ int main(int argc, char **argv, char **envp) drive_add(IF_MTD, -1, optarg, MTD_OPTS); break; case QEMU_OPTION_sd: - drive_add(IF_SD, 0, optarg, SD_OPTS); + drive_add(IF_SD, -1, optarg, SD_OPTS); break; case QEMU_OPTION_pflash: drive_add(IF_PFLASH, -1, optarg, PFLASH_OPTS); @@ -2752,10 +3083,6 @@ int main(int argc, char **argv, char **envp) } break; case QEMU_OPTION_numa: - if (nb_numa_nodes >= MAX_NODES) { - fprintf(stderr, "qemu: too many NUMA nodes\n"); - exit(1); - } numa_add(optarg); break; case QEMU_OPTION_display: @@ -2847,8 +3174,10 @@ int main(int argc, char **argv, char **envp) exit(1); } } - qemu_opts_parse(qemu_find_opts("boot-opts"), - optarg, 0); + if (!qemu_opts_parse(qemu_find_opts("boot-opts"), + optarg, 0)) { + exit(1); + } } } break; @@ -2933,6 +3262,13 @@ int main(int argc, char **argv, char **envp) } break; } +#ifdef CONFIG_TPM + case QEMU_OPTION_tpmdev: + if (tpm_config_parse(qemu_find_opts("tpmdev"), optarg) < 0) { + exit(1); + } + break; +#endif case QEMU_OPTION_mempath: mem_path = optarg; break; @@ -2954,7 +3290,9 @@ int main(int argc, char **argv, char **envp) add_device_config(DEV_GDB, optarg); break; case QEMU_OPTION_L: - data_dir = optarg; + if (data_dir_idx < ARRAY_SIZE(data_dir)) { + data_dir[data_dir_idx++] = optarg; + } break; case QEMU_OPTION_bios: bios_name = optarg; @@ -3046,7 +3384,6 @@ int main(int argc, char **argv, char **envp) } opts = qemu_opts_parse(olist, optarg, 1); if (!opts) { - fprintf(stderr, "parse error: %s\n", optarg); exit(1); } break; @@ -3062,7 +3399,6 @@ int main(int argc, char **argv, char **envp) } opts = qemu_opts_parse(olist, optarg, 1); if (!opts) { - fprintf(stderr, "parse error: %s\n", optarg); exit(1); } @@ -3233,7 +3569,6 @@ int main(int argc, char **argv, char **envp) olist = qemu_find_opts("machine"); opts = qemu_opts_parse(olist, optarg, 1); if (!opts) { - fprintf(stderr, "parse error: %s\n", optarg); exit(1); } optarg = qemu_opt_get(opts, "type"); @@ -3338,6 +3673,9 @@ int main(int argc, char **argv, char **envp) exit(1); } opts = qemu_opts_parse(qemu_find_opts("option-rom"), optarg, 1); + if (!opts) { + exit(1); + } option_rom[nb_option_roms].name = qemu_opt_get(opts, "romfile"); option_rom[nb_option_roms].bootindex = qemu_opt_get_number(opts, "bootindex", -1); @@ -3410,6 +3748,7 @@ int main(int argc, char **argv, char **envp) default_serial = 0; default_parallel = 0; default_virtcon = 0; + default_sclp = 0; default_monitor = 0; default_net = 0; default_floppy = 0; @@ -3466,9 +3805,9 @@ int main(int argc, char **argv, char **envp) } opts = qemu_opts_parse(olist, optarg, 0); if (!opts) { - fprintf(stderr, "parse error: %s\n", optarg); exit(1); } + display_remote++; break; case QEMU_OPTION_writeconfig: { @@ -3495,14 +3834,14 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_sandbox: opts = qemu_opts_parse(qemu_find_opts("sandbox"), optarg, 1); if (!opts) { - exit(0); + exit(1); } break; case QEMU_OPTION_add_fd: #ifndef _WIN32 opts = qemu_opts_parse(qemu_find_opts("add-fd"), optarg, 0); if (!opts) { - exit(0); + exit(1); } #else error_report("File descriptor passing is disabled on this " @@ -3512,6 +3851,9 @@ int main(int argc, char **argv, char **envp) break; case QEMU_OPTION_object: opts = qemu_opts_parse(qemu_find_opts("object"), optarg, 1); + if (!opts) { + exit(1); + } break; default: os_parse_cmd_args(popt->index, optarg); @@ -3571,10 +3913,17 @@ int main(int argc, char **argv, char **envp) * location or level of logging. */ if (log_mask) { + int mask; if (log_file) { - set_cpu_log_filename(log_file); + qemu_set_log_filename(log_file); } - set_cpu_log(log_mask); + + mask = qemu_str_to_log_mask(log_mask); + if (!mask) { + qemu_print_log_usage(stdout); + exit(1); + } + qemu_set_log(mask); } if (!trace_backend_init(trace_events, trace_file)) { @@ -3583,12 +3932,15 @@ int main(int argc, char **argv, char **envp) /* If no data_dir is specified then try to find it relative to the executable path. */ - if (!data_dir) { - data_dir = os_find_datadir(argv[0]); + if (data_dir_idx < ARRAY_SIZE(data_dir)) { + data_dir[data_dir_idx] = os_find_datadir(argv[0]); + if (data_dir[data_dir_idx] != NULL) { + data_dir_idx++; + } } /* If all else fails use the install path specified when building. */ - if (!data_dir) { - data_dir = CONFIG_QEMU_DATADIR; + if (data_dir_idx < ARRAY_SIZE(data_dir)) { + data_dir[data_dir_idx++] = CONFIG_QEMU_DATADIR; } /* @@ -3627,6 +3979,9 @@ int main(int argc, char **argv, char **envp) if (!machine->use_virtcon) { default_virtcon = 0; } + if (!machine->use_sclp) { + default_sclp = 0; + } if (machine->no_floppy) { default_floppy = 0; } @@ -3637,6 +3992,30 @@ int main(int argc, char **argv, char **envp) default_sdcard = 0; } + if (is_daemonized()) { + /* According to documentation and historically, -nographic redirects + * serial port, parallel port and monitor to stdio, which does not work + * with -daemonize. We can redirect these to null instead, but since + * -nographic is legacy, let's just error out. + * We disallow -nographic only if all other ports are not redirected + * explicitly, to not break existing legacy setups which uses + * -nographic _and_ redirects all ports explicitly - this is valid + * usage, -nographic is just a no-op in this case. + */ + if (display_type == DT_NOGRAPHIC + && (default_parallel || default_serial + || default_monitor || default_virtcon)) { + fprintf(stderr, "-nographic can not be used with -daemonize\n"); + exit(1); + } +#ifdef CONFIG_CURSES + if (display_type == DT_CURSES) { + fprintf(stderr, "curses display can not be used with -daemonize\n"); + exit(1); + } +#endif + } + if (display_type == DT_NOGRAPHIC) { if (default_parallel) add_device_config(DEV_PARALLEL, "null"); @@ -3644,11 +4023,16 @@ int main(int argc, char **argv, char **envp) add_device_config(DEV_SERIAL, "mon:stdio"); } else if (default_virtcon && default_monitor) { add_device_config(DEV_VIRTCON, "mon:stdio"); + } else if (default_sclp && default_monitor) { + add_device_config(DEV_SCLP, "mon:stdio"); } else { if (default_serial) add_device_config(DEV_SERIAL, "stdio"); if (default_virtcon) add_device_config(DEV_VIRTCON, "stdio"); + if (default_sclp) { + add_device_config(DEV_SCLP, "stdio"); + } if (default_monitor) monitor_parse("stdio", "readline"); } @@ -3661,8 +4045,30 @@ int main(int argc, char **argv, char **envp) monitor_parse("vc:80Cx24C", "readline"); if (default_virtcon) add_device_config(DEV_VIRTCON, "vc:80Cx24C"); + if (default_sclp) { + add_device_config(DEV_SCLP, "vc:80Cx24C"); + } } + if (display_type == DT_DEFAULT && !display_remote) { +#if defined(CONFIG_GTK) + display_type = DT_GTK; +#elif defined(CONFIG_SDL) || defined(CONFIG_COCOA) + display_type = DT_SDL; +#elif defined(CONFIG_VNC) + vnc_display = "localhost:0,to=99"; + show_vnc_port = 1; +#else + display_type = DT_NONE; +#endif + } + +#if defined(CONFIG_GTK) + if (display_type == DT_GTK) { + early_gtk_display_init(); + } +#endif + socket_init(); if (qemu_opts_foreach(qemu_find_opts("chardev"), chardev_init_func, NULL, 1) != 0) @@ -3738,10 +4144,19 @@ int main(int argc, char **argv, char **envp) } configure_icount(icount_option); + /* clean up network at qemu process termination */ + atexit(&net_cleanup); + if (net_init_clients() < 0) { exit(1); } +#ifdef CONFIG_TPM + if (tpm_init() < 0) { + exit(1); + } +#endif + /* init the bluetooth world */ if (foreach_device_config(DEV_BT, bt_parse)) exit(1); @@ -3828,6 +4243,9 @@ int main(int argc, char **argv, char **envp) exit(1); if (foreach_device_config(DEV_VIRTCON, virtcon_parse) < 0) exit(1); + if (foreach_device_config(DEV_SCLP, sclp_parse) < 0) { + exit(1); + } if (foreach_device_config(DEV_DEBUGCON, debugcon_parse) < 0) exit(1); @@ -3855,7 +4273,9 @@ int main(int argc, char **argv, char **envp) qdev_machine_init(); QEMUMachineInitArgs args = { .ram_size = ram_size, - .boot_device = boot_devices, + .boot_device = (boot_devices[0] == '\0') ? + machine->boot_order : + boot_devices, .kernel_filename = kernel_filename, .kernel_cmdline = kernel_cmdline, .initrd_filename = initrd_filename, @@ -3883,29 +4303,13 @@ int main(int argc, char **argv, char **envp) /* just use the first displaystate for the moment */ ds = get_displaystate(); - if (using_spice) - display_remote++; - if (display_type == DT_DEFAULT && !display_remote) { -#if defined(CONFIG_SDL) || defined(CONFIG_COCOA) - display_type = DT_SDL; -#elif defined(CONFIG_VNC) - vnc_display = "localhost:0,to=99"; - show_vnc_port = 1; -#else - display_type = DT_NONE; -#endif - } - - /* init local displays */ switch (display_type) { case DT_NOGRAPHIC: break; #if defined(CONFIG_CURSES) case DT_CURSES: - if (!is_daemonized()) { - curses_display_init(ds, full_screen); - } + curses_display_init(ds, full_screen); break; #endif #if defined(CONFIG_SDL) @@ -3916,6 +4320,11 @@ int main(int argc, char **argv, char **envp) case DT_SDL: cocoa_display_init(ds, full_screen); break; +#endif +#if defined(CONFIG_GTK) + case DT_GTK: + gtk_display_init(ds); + break; #endif default: break; @@ -3992,8 +4401,10 @@ int main(int argc, char **argv, char **envp) main_loop(); bdrv_close_all(); pause_all_vcpus(); - net_cleanup(); res_free(); +#ifdef CONFIG_TPM + tpm_cleanup(); +#endif return 0; } diff --git a/xbzrle.c b/xbzrle.c new file mode 100644 index 0000000000..fbcb35d0e3 --- /dev/null +++ b/xbzrle.c @@ -0,0 +1,173 @@ +/* + * Xor Based Zero Run Length Encoding + * + * Copyright 2013 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Orit Wasserman + * + * 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-common.h" +#include "include/migration/migration.h" + +/* + page = zrun nzrun + | zrun nzrun page + + zrun = length + + nzrun = length byte... + + length = uleb128 encoded integer + */ +int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen, + uint8_t *dst, int dlen) +{ + uint32_t zrun_len = 0, nzrun_len = 0; + int d = 0, i = 0; + long res, xor; + uint8_t *nzrun_start = NULL; + + g_assert(!(((uintptr_t)old_buf | (uintptr_t)new_buf | slen) % + sizeof(long))); + + while (i < slen) { + /* overflow */ + if (d + 2 > dlen) { + return -1; + } + + /* not aligned to sizeof(long) */ + res = (slen - i) % sizeof(long); + while (res && old_buf[i] == new_buf[i]) { + zrun_len++; + i++; + res--; + } + + /* word at a time for speed */ + if (!res) { + while (i < slen && + (*(long *)(old_buf + i)) == (*(long *)(new_buf + i))) { + i += sizeof(long); + zrun_len += sizeof(long); + } + + /* go over the rest */ + while (i < slen && old_buf[i] == new_buf[i]) { + zrun_len++; + i++; + } + } + + /* buffer unchanged */ + if (zrun_len == slen) { + return 0; + } + + /* skip last zero run */ + if (i == slen) { + return d; + } + + d += uleb128_encode_small(dst + d, zrun_len); + + zrun_len = 0; + nzrun_start = new_buf + i; + + /* overflow */ + if (d + 2 > dlen) { + return -1; + } + /* not aligned to sizeof(long) */ + res = (slen - i) % sizeof(long); + while (res && old_buf[i] != new_buf[i]) { + i++; + nzrun_len++; + res--; + } + + /* word at a time for speed, use of 32-bit long okay */ + if (!res) { + /* truncation to 32-bit long okay */ + long mask = (long)0x0101010101010101ULL; + while (i < slen) { + xor = *(long *)(old_buf + i) ^ *(long *)(new_buf + i); + if ((xor - mask) & ~xor & (mask << 7)) { + /* found the end of an nzrun within the current long */ + while (old_buf[i] != new_buf[i]) { + nzrun_len++; + i++; + } + break; + } else { + i += sizeof(long); + nzrun_len += sizeof(long); + } + } + } + + d += uleb128_encode_small(dst + d, nzrun_len); + /* overflow */ + if (d + nzrun_len > dlen) { + return -1; + } + memcpy(dst + d, nzrun_start, nzrun_len); + d += nzrun_len; + nzrun_len = 0; + } + + return d; +} + +int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen) +{ + int i = 0, d = 0; + int ret; + uint32_t count = 0; + + while (i < slen) { + + /* zrun */ + if ((slen - i) < 2) { + return -1; + } + + ret = uleb128_decode_small(src + i, &count); + if (ret < 0 || (i && !count)) { + return -1; + } + i += ret; + d += count; + + /* overflow */ + if (d > dlen) { + return -1; + } + + /* nzrun */ + if ((slen - i) < 2) { + return -1; + } + + ret = uleb128_decode_small(src + i, &count); + if (ret < 0 || !count) { + return -1; + } + i += ret; + + /* overflow */ + if (d + count > dlen || i + count > slen) { + return -1; + } + + memcpy(dst + d, src + i, count); + d += count; + i += count; + } + + return d; +} diff --git a/xen-all.c b/xen-all.c index 19bcfd1510..8c05843faf 100644 --- a/xen-all.c +++ b/xen-all.c @@ -578,18 +578,18 @@ void qmp_xen_set_global_dirty_log(bool enable, Error **errp) static void xen_reset_vcpu(void *opaque) { - CPUArchState *env = opaque; + CPUState *cpu = opaque; - env->halted = 1; + cpu->halted = 1; } void xen_vcpu_init(void) { - CPUArchState *first_cpu; + if (first_cpu != NULL) { + CPUState *cpu = ENV_GET_CPU(first_cpu); - if ((first_cpu = qemu_get_cpu(0))) { - qemu_register_reset(xen_reset_vcpu, first_cpu); - xen_reset_vcpu(first_cpu); + qemu_register_reset(xen_reset_vcpu, cpu); + xen_reset_vcpu(cpu); } /* if rtc_clock is left to default (host_clock), disable it */ if (rtc_clock == host_clock) {