diff options
524 files changed, 22126 insertions, 6609 deletions
diff --git a/.gitlab-ci-opensbi.yml b/.gitlab-ci-opensbi.yml new file mode 100644 index 0000000000..dd051c0124 --- /dev/null +++ b/.gitlab-ci-opensbi.yml @@ -0,0 +1,63 @@ +docker-opensbi: + stage: build + rules: # Only run this job when the Dockerfile is modified + - changes: + - .gitlab-ci-opensbi.yml + - .gitlab-ci.d/opensbi/Dockerfile + when: always + image: docker:19.03.1 + services: + - docker:19.03.1-dind + variables: + GIT_DEPTH: 3 + IMAGE_TAG: $CI_REGISTRY_IMAGE:opensbi-cross-build + # We don't use TLS + DOCKER_HOST: tcp://docker:2375 + DOCKER_TLS_CERTDIR: "" + before_script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + script: + - docker pull $IMAGE_TAG || true + - docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA + --tag $IMAGE_TAG .gitlab-ci.d/opensbi + - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA + - docker push $IMAGE_TAG + +build-opensbi: + rules: # Only run this job when ... + - changes: # ... roms/opensbi/ is modified (submodule updated) + - roms/opensbi/* + when: always + - if: '$CI_COMMIT_REF_NAME =~ /^opensbi/' # or the branch/tag starts with 'opensbi' + when: always + - if: '$CI_COMMIT_MESSAGE =~ /opensbi/i' # or last commit description contains 'OpenSBI' + when: always + artifacts: + paths: # 'artifacts.zip' will contains the following files: + - pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin + - pc-bios/opensbi-riscv32-virt-fw_jump.bin + - pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin + - pc-bios/opensbi-riscv64-virt-fw_jump.bin + - opensbi32-virt-stdout.log + - opensbi32-virt-stderr.log + - opensbi64-virt-stdout.log + - opensbi64-virt-stderr.log + - opensbi32-sifive_u-stdout.log + - opensbi32-sifive_u-stderr.log + - opensbi64-sifive_u-stdout.log + - opensbi64-sifive_u-stderr.log + image: $CI_REGISTRY_IMAGE:opensbi-cross-build + variables: + GIT_DEPTH: 3 + script: # Clone the required submodules and build OpenSBI + - git submodule update --init roms/opensbi + - export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1)) + - echo "=== Using ${JOBS} simultaneous jobs ===" + - make -j${JOBS} -C roms/opensbi clean + - make -j${JOBS} -C roms opensbi32-virt 2>&1 1>opensbi32-virt-stdout.log | tee -a opensbi32-virt-stderr.log >&2 + - make -j${JOBS} -C roms/opensbi clean + - make -j${JOBS} -C roms opensbi64-virt 2>&1 1>opensbi64-virt-stdout.log | tee -a opensbi64-virt-stderr.log >&2 + - make -j${JOBS} -C roms/opensbi clean + - make -j${JOBS} -C roms opensbi32-sifive_u 2>&1 1>opensbi32-sifive_u-stdout.log | tee -a opensbi32-sifive_u-stderr.log >&2 + - make -j${JOBS} -C roms/opensbi clean + - make -j${JOBS} -C roms opensbi64-sifive_u 2>&1 1>opensbi64-sifive_u-stdout.log | tee -a opensbi64-sifive_u-stderr.log >&2 diff --git a/.gitlab-ci.d/opensbi/Dockerfile b/.gitlab-ci.d/opensbi/Dockerfile new file mode 100644 index 0000000000..4ba8a4de86 --- /dev/null +++ b/.gitlab-ci.d/opensbi/Dockerfile @@ -0,0 +1,33 @@ +# +# Docker image to cross-compile OpenSBI firmware binaries +# +FROM ubuntu:18.04 + +MAINTAINER Bin Meng <bmeng.cn@gmail.com> + +# Install packages required to build OpenSBI +RUN apt update \ + && \ + \ + DEBIAN_FRONTEND=noninteractive \ + apt install --assume-yes --no-install-recommends \ + build-essential \ + ca-certificates \ + git \ + make \ + wget \ + && \ + \ + rm -rf /var/lib/apt/lists/* + +# Manually install the kernel.org "Crosstool" based toolchains for gcc-8.3 +RUN wget -O - \ + https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/8.3.0/x86_64-gcc-8.3.0-nolibc-riscv32-linux.tar.xz \ + | tar -C /opt -xJ +RUN wget -O - \ + https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/8.3.0/x86_64-gcc-8.3.0-nolibc-riscv64-linux.tar.xz \ + | tar -C /opt -xJ + +# Export the toolchains to the system path +ENV PATH="/opt/gcc-8.3.0-nolibc/riscv32-linux/bin:${PATH}" +ENV PATH="/opt/gcc-8.3.0-nolibc/riscv64-linux/bin:${PATH}" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 72f8b8aa51..b889fb96b6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,6 @@ include: - local: '/.gitlab-ci-edk2.yml' + - local: '/.gitlab-ci-opensbi.yml' before_script: - apt-get update -qq @@ -39,8 +39,9 @@ Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org> # Next, replace old addresses by a more recent one. -Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com> -Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com> +Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@mips.com> +Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@imgtec.com> +Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com> Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com> <arikalo@wavecomp.com> Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com> James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com> diff --git a/.travis.yml b/.travis.yml index b92798ac3b..5672d129ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -79,6 +79,7 @@ env: - MAIN_SOFTMMU_TARGETS="aarch64-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" - CCACHE_SLOPPINESS="include_file_ctime,include_file_mtime" - CCACHE_MAXSIZE=1G + - G_MESSAGES_DEBUG=error git: @@ -315,7 +316,7 @@ jobs: - name: "GCC check-acceptance" dist: bionic env: - - CONFIG="--target-list=aarch64-softmmu,alpha-softmmu,arm-softmmu,m68k-softmmu,microblaze-softmmu,mips-softmmu,mips64el-softmmu,nios2-softmmu,or1k-softmmu,ppc-softmmu,ppc64-softmmu,s390x-softmmu,sparc-softmmu,x86_64-softmmu,xtensa-softmmu" + - CONFIG="--enable-tools --target-list=aarch64-softmmu,alpha-softmmu,arm-softmmu,m68k-softmmu,microblaze-softmmu,mips-softmmu,mips64el-softmmu,nios2-softmmu,or1k-softmmu,ppc-softmmu,ppc64-softmmu,s390x-softmmu,sparc-softmmu,x86_64-softmmu,xtensa-softmmu" - TEST_CMD="make check-acceptance" after_script: - python3 -c 'import json; r = json.load(open("tests/results/latest/results.json")); [print(t["logfile"]) for t in r["tests"] if t["status"] not in ("PASS", "SKIP")]' | xargs cat diff --git a/MAINTAINERS b/MAINTAINERS index 45d48992b1..2b46f3c6a8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -155,6 +155,7 @@ F: include/hw/cpu/a*mpcore.h F: disas/arm.c F: disas/arm-a64.cc F: disas/libvixl/ +F: docs/system/target-arm.rst ARM SMMU M: Eric Auger <eric.auger@redhat.com> @@ -208,8 +209,8 @@ F: hw/microblaze/ F: disas/microblaze.c MIPS TCG CPUs -M: Aurelien Jarno <aurelien@aurel32.net> -M: Aleksandar Markovic <amarkovic@wavecomp.com> +M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> +R: Aurelien Jarno <aurelien@aurel32.net> R: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com> S: Maintained F: target/mips/ @@ -225,6 +226,7 @@ F: include/hw/mips/ F: include/hw/misc/mips_* F: include/hw/timer/mips_gictimer.h F: tests/acceptance/linux_ssh_mips_malta.py +F: tests/acceptance/machine_mips_malta.py F: tests/tcg/mips/ K: ^Subject:.*(?i)mips @@ -275,6 +277,11 @@ F: include/hw/riscv/ F: linux-user/host/riscv32/ F: linux-user/host/riscv64/ +RENESAS RX CPUs +M: Yoshinori Sato <ysato@users.sourceforge.jp> +S: Maintained +F: target/rx/ + S390 TCG CPUs M: Richard Henderson <rth@twiddle.net> M: David Hildenbrand <david@redhat.com> @@ -366,7 +373,7 @@ S: Maintained F: target/arm/kvm.c MIPS KVM CPUs -M: Aleksandar Markovic <aleksandar.m.mail@gmail.com> +M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> S: Odd Fixes F: target/mips/kvm.c @@ -435,6 +442,17 @@ F: include/hw/block/dataplane/xen* F: include/hw/xen/ F: include/sysemu/xen-mapcache.h +Guest CPU Cores (HAXM) +--------------------- +X86 HAXM CPUs +M: Wenchao Wang <wenchao.wang@intel.com> +M: Colin Xu <colin.xu@intel.com> +L: haxm-team@intel.com +W: https://github.com/intel/haxm/issues +S: Maintained +F: include/sysemu/hax.h +F: target/i386/hax-* + Hosts ----- LINUX @@ -491,6 +509,15 @@ F: hw/*/allwinner* F: include/hw/*/allwinner* F: hw/arm/cubieboard.c +Allwinner-h3 +M: Niek Linnenbank <nieklinnenbank@gmail.com> +L: qemu-arm@nongnu.org +S: Maintained +F: hw/*/allwinner-h3* +F: include/hw/*/allwinner-h3* +F: hw/arm/orangepi.c +F: docs/system/orangepi.rst + ARM PrimeCell and CMSDK devices M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org @@ -615,6 +642,7 @@ F: hw/arm/integratorcp.c F: hw/misc/arm_integrator_debug.c F: include/hw/misc/arm_integrator_debug.h F: tests/acceptance/machine_arm_integratorcp.py +F: docs/system/arm/integratorcp.rst MCIMX6UL EVK / i.MX6ul M: Peter Maydell <peter.maydell@linaro.org> @@ -673,6 +701,7 @@ M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org S: Odd Fixes F: hw/arm/musicpal.c +F: docs/system/arm/musicpal.rst nSeries M: Andrzej Zaborowski <balrogg@gmail.com> @@ -689,6 +718,7 @@ F: include/hw/display/blizzard.h F: include/hw/input/tsc2xxx.h F: include/hw/misc/cbus.h F: tests/acceptance/machine_arm_n8x0.py +F: docs/system/arm/nseries.rst Palm M: Andrzej Zaborowski <balrogg@gmail.com> @@ -698,6 +728,7 @@ S: Odd Fixes F: hw/arm/palm.c F: hw/input/tsc210x.c F: include/hw/input/tsc2xxx.h +F: docs/system/arm/palm.rst Raspberry Pi M: Peter Maydell <peter.maydell@linaro.org> @@ -719,6 +750,7 @@ F: hw/arm/realview* F: hw/cpu/realview_mpcore.c F: hw/intc/realview_gic.c F: include/hw/intc/realview_gic.h +F: docs/system/arm/realview.rst PXA2XX M: Andrzej Zaborowski <balrogg@gmail.com> @@ -738,6 +770,7 @@ F: hw/misc/max111x.c F: include/hw/arm/pxa.h F: include/hw/arm/sharpsl.h F: include/hw/display/tc6393xb.h +F: docs/system/arm/xscale.rst SABRELITE / i.MX6 M: Peter Maydell <peter.maydell@linaro.org> @@ -748,6 +781,8 @@ F: hw/arm/sabrelite.c F: hw/arm/fsl-imx6.c F: hw/misc/imx6_*.c F: hw/ssi/imx_spi.c +F: hw/usb/imx-usb-phy.c +F: include/hw/usb/imx-usb-phy.h F: include/hw/arm/fsl-imx6.h F: include/hw/misc/imx6_*.h F: include/hw/ssi/imx_spi.h @@ -773,6 +808,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/*/stellaris* F: include/hw/input/gamepad.h +F: docs/system/arm/stellaris.rst Versatile Express M: Peter Maydell <peter.maydell@linaro.org> @@ -786,6 +822,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/*/versatile* F: hw/misc/arm_sysctl.c +F: docs/system/arm/versatile.rst Virt M: Peter Maydell <peter.maydell@linaro.org> @@ -998,7 +1035,7 @@ F: hw/display/jazz_led.c F: hw/dma/rc4030.c Malta -M: Aleksandar Markovic <amarkovic@wavecomp.com> +M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> M: Philippe Mathieu-Daudé <f4bug@amsat.org> R: Aurelien Jarno <aurelien@aurel32.net> S: Maintained @@ -1011,21 +1048,22 @@ F: tests/acceptance/linux_ssh_mips_malta.py F: tests/acceptance/machine_mips_malta.py Mipssim -M: Aleksandar Markovic <amarkovic@wavecomp.com> +M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> R: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com> S: Odd Fixes F: hw/mips/mips_mipssim.c F: hw/net/mipsnet.c R4000 -M: Aurelien Jarno <aurelien@aurel32.net> +M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> +R: Aurelien Jarno <aurelien@aurel32.net> R: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com> S: Obsolete F: hw/mips/mips_r4k.c Fulong 2E M: Philippe Mathieu-Daudé <f4bug@amsat.org> -M: Aleksandar Markovic <amarkovic@wavecomp.com> +M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> S: Odd Fixes F: hw/mips/mips_fulong2e.c F: hw/isa/vt82c686.c @@ -1885,6 +1923,8 @@ L: qemu-block@nongnu.org S: Supported F: util/async.c F: util/aio-*.c +F: util/aio-*.h +F: util/fdmon-*.c F: block/io.c F: migration/block* F: include/block/aio.h @@ -1920,6 +1960,7 @@ Block QAPI, monitor, command line M: Markus Armbruster <armbru@redhat.com> S: Supported F: blockdev.c +F: blockdev-hmp-cmds.c F: block/qapi.c F: qapi/block*.json F: qapi/transaction.json @@ -2015,6 +2056,7 @@ F: include/exec/ramblock.h F: memory.c F: include/exec/memory-internal.h F: exec.c +F: scripts/coccinelle/memory-region-housekeeping.cocci SPICE M: Gerd Hoffmann <kraxel@redhat.com> @@ -2105,6 +2147,11 @@ F: python/qemu/*py F: scripts/*.py F: tests/*.py +Benchmark util +M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> +S: Maintained +F: scripts/simplebench/ + QAPI M: Markus Armbruster <armbru@redhat.com> M: Michael Roth <mdroth@linux.vnet.ibm.com> @@ -2489,7 +2536,8 @@ F: tcg/i386/ F: disas/i386.c MIPS TCG target -M: Aurelien Jarno <aurelien@aurel32.net> +M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> +R: Aurelien Jarno <aurelien@aurel32.net> R: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com> S: Maintained F: tcg/mips/ @@ -2817,7 +2865,7 @@ S: Odd Fixes F: scripts/git-submodule.sh UI translations -M: Aleksandar Markovic <aleksandar.m.mail@gmail.com> +M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> F: po/*.po Sphinx documentation configuration and build machinery @@ -795,7 +795,7 @@ rm -f $(MANUAL_BUILDDIR)/$1/objects.inv $(MANUAL_BUILDDIR)/$1/searchindex.js $(M endef distclean: clean - rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi + rm -f config-host.mak config-host.h* config-host.ld $(DOCS) rm -f tests/tcg/config-*.mak rm -f config-all-devices.mak config-all-disas.mak config.status rm -f $(SUBDIR_DEVICES_MAK) @@ -848,7 +848,7 @@ u-boot.e500 u-boot-sam460-20100605.bin \ qemu_vga.ndrv \ edk2-licenses.txt \ hppa-firmware.img \ -opensbi-riscv32-virt-fw_jump.bin \ +opensbi-riscv32-sifive_u-fw_jump.bin opensbi-riscv32-virt-fw_jump.bin \ opensbi-riscv64-sifive_u-fw_jump.bin opensbi-riscv64-virt-fw_jump.bin @@ -1078,9 +1078,10 @@ sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html \ # a single doctree: https://github.com/sphinx-doc/sphinx/issues/2946 build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" $(SPHINX_BUILD) $(if $(V),,-q) -W -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1-$2 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1") # We assume all RST files in the manual's directory are used in it -manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) \ +manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst $(SRC_PATH)/docs/$1/*/*.rst) \ $(SRC_PATH)/docs/defs.rst.inc \ - $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py + $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py \ + $(SRC_PATH)/docs/sphinx/*.py # Macro to write out the rule and dependencies for building manpages # Usage: $(call define-manpage-rule,manualname,manpage1 manpage2...[,extradeps]) # 'extradeps' is optional, and specifies extra files (eg .hx files) that @@ -1122,15 +1123,6 @@ $(MANUAL_BUILDDIR)/index.html: $(SRC_PATH)/docs/index.html.in qemu-version.h $(call quiet-command, sed "s|@@VERSION@@|${VERSION}|g" $< >$@, \ "GEN","$@") -qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool - $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") - -qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/scripts/hxtool - $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") - -qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxtool - $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") - docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi @cp -p $< $@ @@ -1243,50 +1235,57 @@ endif include $(SRC_PATH)/tests/docker/Makefile.include include $(SRC_PATH)/tests/vm/Makefile.include +print-help-run = printf " %-30s - %s\\n" "$1" "$2" +print-help = $(quiet-@)$(call print-help-run,$1,$2) + .PHONY: help help: @echo 'Generic targets:' - @echo ' all - Build all' + $(call print-help,all,Build all) ifdef CONFIG_MODULES - @echo ' modules - Build all modules' + $(call print-help,modules,Build all modules) endif - @echo ' dir/file.o - Build specified target only' - @echo ' install - Install QEMU, documentation and tools' - @echo ' ctags/TAGS - Generate tags file for editors' - @echo ' cscope - Generate cscope index' + $(call print-help,dir/file.o,Build specified target only) + $(call print-help,install,Install QEMU, documentation and tools) + $(call print-help,ctags/TAGS,Generate tags file for editors) + $(call print-help,cscope,Generate cscope index) @echo '' @$(if $(TARGET_DIRS), \ echo 'Architecture specific targets:'; \ $(foreach t, $(TARGET_DIRS), \ - printf " %-30s - Build for %s\\n" $(t)/all $(t);) \ + $(call print-help-run,$(t)/all,Build for $(t));) \ + echo '') + @$(if $(TOOLS), \ + echo 'Tools targets:'; \ + $(foreach t, $(TOOLS), \ + $(call print-help-run,$(t),Build $(shell basename $(t)) tool);) \ echo '') @echo 'Cleaning targets:' - @echo ' clean - Remove most generated files but keep the config' + $(call print-help,clean,Remove most generated files but keep the config) ifdef CONFIG_GCOV - @echo ' clean-coverage - Remove coverage files' + $(call print-help,clean-coverage,Remove coverage files) endif - @echo ' distclean - Remove all generated files' - @echo ' dist - Build a distributable tarball' + $(call print-help,distclean,Remove all generated files) + $(call print-help,dist,Build a distributable tarball) @echo '' @echo 'Test targets:' - @echo ' check - Run all tests (check-help for details)' - @echo ' docker - Help about targets running tests inside containers' - @echo ' vm-help - Help about targets running tests inside VM' + $(call print-help,check,Run all tests (check-help for details)) + $(call print-help,docker,Help about targets running tests inside containers) + $(call print-help,vm-help,Help about targets running tests inside VM) @echo '' @echo 'Documentation targets:' - @echo ' html info pdf txt' - @echo ' - Build documentation in specified format' + $(call print-help,html info pdf txt,Build documentation in specified format) ifdef CONFIG_GCOV - @echo ' coverage-report - Create code coverage report' + $(call print-help,coverage-report,Create code coverage report) endif @echo '' ifdef CONFIG_WIN32 @echo 'Windows targets:' - @echo ' installer - Build NSIS-based installer for QEMU' + $(call print-help,installer,Build NSIS-based installer for QEMU) ifdef QEMU_GA_MSI_ENABLED - @echo ' msi - Build MSI-based installer for qemu-ga' + $(call print-help,msi,Build MSI-based installer for qemu-ga) endif @echo '' endif - @echo ' $(MAKE) [targets] (quiet build, default)' - @echo ' $(MAKE) V=1 [targets] (verbose build)' + $(call print-help,$(MAKE) [targets],(quiet build, default)) + $(call print-help,$(MAKE) V=1 [targets],(verbose build)) diff --git a/Makefile.objs b/Makefile.objs index e288663d89..a7c967633a 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -48,7 +48,7 @@ common-obj-y += dump/ common-obj-y += job-qmp.o common-obj-y += monitor/ common-obj-y += net/ -common-obj-y += qdev-monitor.o device-hotplug.o +common-obj-y += qdev-monitor.o common-obj-$(CONFIG_WIN32) += os-win32.o common-obj-$(CONFIG_POSIX) += os-posix.o @@ -175,6 +175,7 @@ trace-events-subdirs += hw/scsi trace-events-subdirs += hw/sd trace-events-subdirs += hw/sparc trace-events-subdirs += hw/sparc64 +trace-events-subdirs += hw/ssi trace-events-subdirs += hw/timer trace-events-subdirs += hw/tpm trace-events-subdirs += hw/usb diff --git a/Makefile.target b/Makefile.target index cb3a6fa5e7..8ed1eba95b 100644 --- a/Makefile.target +++ b/Makefile.target @@ -12,7 +12,7 @@ endif $(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) ifdef CONFIG_LINUX -QEMU_CFLAGS += -I../linux-headers +QEMU_CFLAGS += -isystem ../linux-headers endif QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H diff --git a/accel/tcg/tcg-runtime-gvec.c b/accel/tcg/tcg-runtime-gvec.c index 5b1902d591..ca449702e6 100644 --- a/accel/tcg/tcg-runtime-gvec.c +++ b/accel/tcg/tcg-runtime-gvec.c @@ -24,48 +24,6 @@ #include "tcg/tcg-gvec-desc.h" -/* Virtually all hosts support 16-byte vectors. Those that don't can emulate - * them via GCC's generic vector extension. This turns out to be simpler and - * more reliable than getting the compiler to autovectorize. - * - * In tcg-op-gvec.c, we asserted that both the size and alignment of the data - * are multiples of 16. - * - * When the compiler does not support all of the operations we require, the - * loops are written so that we can always fall back on the base types. - */ -#ifdef CONFIG_VECTOR16 -typedef uint8_t vec8 __attribute__((vector_size(16))); -typedef uint16_t vec16 __attribute__((vector_size(16))); -typedef uint32_t vec32 __attribute__((vector_size(16))); -typedef uint64_t vec64 __attribute__((vector_size(16))); - -typedef int8_t svec8 __attribute__((vector_size(16))); -typedef int16_t svec16 __attribute__((vector_size(16))); -typedef int32_t svec32 __attribute__((vector_size(16))); -typedef int64_t svec64 __attribute__((vector_size(16))); - -#define DUP16(X) { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X } -#define DUP8(X) { X, X, X, X, X, X, X, X } -#define DUP4(X) { X, X, X, X } -#define DUP2(X) { X, X } -#else -typedef uint8_t vec8; -typedef uint16_t vec16; -typedef uint32_t vec32; -typedef uint64_t vec64; - -typedef int8_t svec8; -typedef int16_t svec16; -typedef int32_t svec32; -typedef int64_t svec64; - -#define DUP16(X) X -#define DUP8(X) X -#define DUP4(X) X -#define DUP2(X) X -#endif /* CONFIG_VECTOR16 */ - static inline void clear_high(void *d, intptr_t oprsz, uint32_t desc) { intptr_t maxsz = simd_maxsz(desc); @@ -83,8 +41,8 @@ void HELPER(gvec_add8)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) + *(vec8 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) + *(uint8_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -94,8 +52,8 @@ void HELPER(gvec_add16)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) + *(vec16 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) + *(uint16_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -105,8 +63,8 @@ void HELPER(gvec_add32)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) + *(vec32 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) + *(uint32_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -116,8 +74,8 @@ void HELPER(gvec_add64)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) + *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) + *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -125,11 +83,10 @@ void HELPER(gvec_add64)(void *d, void *a, void *b, uint32_t desc) void HELPER(gvec_adds8)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec8 vecb = (vec8)DUP16(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) + vecb; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) + (uint8_t)b; } clear_high(d, oprsz, desc); } @@ -137,11 +94,10 @@ void HELPER(gvec_adds8)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_adds16)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec16 vecb = (vec16)DUP8(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) + vecb; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) + (uint16_t)b; } clear_high(d, oprsz, desc); } @@ -149,11 +105,10 @@ void HELPER(gvec_adds16)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_adds32)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec32 vecb = (vec32)DUP4(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) + vecb; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) + (uint32_t)b; } clear_high(d, oprsz, desc); } @@ -161,11 +116,10 @@ void HELPER(gvec_adds32)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_adds64)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) + vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) + b; } clear_high(d, oprsz, desc); } @@ -175,8 +129,8 @@ void HELPER(gvec_sub8)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) - *(vec8 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) - *(uint8_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -186,8 +140,8 @@ void HELPER(gvec_sub16)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) - *(vec16 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) - *(uint16_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -197,8 +151,8 @@ void HELPER(gvec_sub32)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) - *(vec32 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) - *(uint32_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -208,8 +162,8 @@ void HELPER(gvec_sub64)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) - *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) - *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -217,11 +171,10 @@ void HELPER(gvec_sub64)(void *d, void *a, void *b, uint32_t desc) void HELPER(gvec_subs8)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec8 vecb = (vec8)DUP16(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) - vecb; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) - (uint8_t)b; } clear_high(d, oprsz, desc); } @@ -229,11 +182,10 @@ void HELPER(gvec_subs8)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_subs16)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec16 vecb = (vec16)DUP8(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) - vecb; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) - (uint16_t)b; } clear_high(d, oprsz, desc); } @@ -241,11 +193,10 @@ void HELPER(gvec_subs16)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_subs32)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec32 vecb = (vec32)DUP4(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) - vecb; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) - (uint32_t)b; } clear_high(d, oprsz, desc); } @@ -253,11 +204,10 @@ void HELPER(gvec_subs32)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_subs64)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) - vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) - b; } clear_high(d, oprsz, desc); } @@ -267,8 +217,8 @@ void HELPER(gvec_mul8)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) * *(vec8 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) * *(uint8_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -278,8 +228,8 @@ void HELPER(gvec_mul16)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) * *(vec16 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) * *(uint16_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -289,8 +239,8 @@ void HELPER(gvec_mul32)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) * *(vec32 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) * *(uint32_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -300,8 +250,8 @@ void HELPER(gvec_mul64)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) * *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) * *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -309,11 +259,10 @@ void HELPER(gvec_mul64)(void *d, void *a, void *b, uint32_t desc) void HELPER(gvec_muls8)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec8 vecb = (vec8)DUP16(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) * vecb; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) * (uint8_t)b; } clear_high(d, oprsz, desc); } @@ -321,11 +270,10 @@ void HELPER(gvec_muls8)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_muls16)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec16 vecb = (vec16)DUP8(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) * vecb; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) * (uint16_t)b; } clear_high(d, oprsz, desc); } @@ -333,11 +281,10 @@ void HELPER(gvec_muls16)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_muls32)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec32 vecb = (vec32)DUP4(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) * vecb; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) * (uint32_t)b; } clear_high(d, oprsz, desc); } @@ -345,11 +292,10 @@ void HELPER(gvec_muls32)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_muls64)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) * vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) * b; } clear_high(d, oprsz, desc); } @@ -359,8 +305,8 @@ void HELPER(gvec_neg8)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = -*(vec8 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = -*(uint8_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -370,8 +316,8 @@ void HELPER(gvec_neg16)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = -*(vec16 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = -*(uint16_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -381,8 +327,8 @@ void HELPER(gvec_neg32)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = -*(vec32 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = -*(uint32_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -392,8 +338,8 @@ void HELPER(gvec_neg64)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = -*(vec64 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = -*(uint64_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -499,8 +445,8 @@ void HELPER(gvec_not)(void *d, void *a, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = ~*(vec64 *)(a + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = ~*(uint64_t *)(a + i); } clear_high(d, oprsz, desc); } @@ -510,8 +456,8 @@ void HELPER(gvec_and)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) & *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) & *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -521,8 +467,8 @@ void HELPER(gvec_or)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) | *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) | *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -532,8 +478,8 @@ void HELPER(gvec_xor)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) ^ *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) ^ *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -543,8 +489,8 @@ void HELPER(gvec_andc)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) &~ *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) &~ *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -554,8 +500,8 @@ void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) |~ *(vec64 *)(b + i); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) |~ *(uint64_t *)(b + i); } clear_high(d, oprsz, desc); } @@ -565,8 +511,8 @@ void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i)); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) & *(uint64_t *)(b + i)); } clear_high(d, oprsz, desc); } @@ -576,8 +522,8 @@ void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i)); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) | *(uint64_t *)(b + i)); } clear_high(d, oprsz, desc); } @@ -587,8 +533,8 @@ void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i)); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) ^ *(uint64_t *)(b + i)); } clear_high(d, oprsz, desc); } @@ -596,11 +542,10 @@ void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc) void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) & vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) & b; } clear_high(d, oprsz, desc); } @@ -608,11 +553,10 @@ void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) ^ vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) ^ b; } clear_high(d, oprsz, desc); } @@ -620,11 +564,10 @@ void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc) void HELPER(gvec_ors)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); - vec64 vecb = (vec64)DUP2(b); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) | vecb; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) | b; } clear_high(d, oprsz, desc); } @@ -635,8 +578,8 @@ void HELPER(gvec_shl8i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) << shift; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) << shift; } clear_high(d, oprsz, desc); } @@ -647,8 +590,8 @@ void HELPER(gvec_shl16i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) << shift; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) << shift; } clear_high(d, oprsz, desc); } @@ -659,8 +602,8 @@ void HELPER(gvec_shl32i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) << shift; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) << shift; } clear_high(d, oprsz, desc); } @@ -671,8 +614,8 @@ void HELPER(gvec_shl64i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) << shift; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) << shift; } clear_high(d, oprsz, desc); } @@ -683,8 +626,8 @@ void HELPER(gvec_shr8i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(vec8 *)(d + i) = *(vec8 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(uint8_t *)(d + i) = *(uint8_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -695,8 +638,8 @@ void HELPER(gvec_shr16i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(vec16 *)(d + i) = *(vec16 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(uint16_t *)(d + i) = *(uint16_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -707,8 +650,8 @@ void HELPER(gvec_shr32i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(vec32 *)(d + i) = *(vec32 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(uint32_t *)(d + i) = *(uint32_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -719,8 +662,8 @@ void HELPER(gvec_shr64i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(vec64 *)(d + i) = *(vec64 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(uint64_t *)(d + i) = *(uint64_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -731,8 +674,8 @@ void HELPER(gvec_sar8i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec8)) { - *(svec8 *)(d + i) = *(svec8 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + *(int8_t *)(d + i) = *(int8_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -743,8 +686,8 @@ void HELPER(gvec_sar16i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec16)) { - *(svec16 *)(d + i) = *(svec16 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + *(int16_t *)(d + i) = *(int16_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -755,8 +698,8 @@ void HELPER(gvec_sar32i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec32)) { - *(svec32 *)(d + i) = *(svec32 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + *(int32_t *)(d + i) = *(int32_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -767,8 +710,8 @@ void HELPER(gvec_sar64i)(void *d, void *a, uint32_t desc) int shift = simd_data(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - *(svec64 *)(d + i) = *(svec64 *)(a + i) >> shift; + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + *(int64_t *)(d + i) = *(int64_t *)(a + i) >> shift; } clear_high(d, oprsz, desc); } @@ -917,39 +860,30 @@ void HELPER(gvec_sar64v)(void *d, void *a, void *b, uint32_t desc) clear_high(d, oprsz, desc); } -/* If vectors are enabled, the compiler fills in -1 for true. - Otherwise, we must take care of this by hand. */ -#ifdef CONFIG_VECTOR16 -# define DO_CMP0(X) X -#else -# define DO_CMP0(X) -(X) -#endif - #define DO_CMP1(NAME, TYPE, OP) \ void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc) \ { \ intptr_t oprsz = simd_oprsz(desc); \ intptr_t i; \ for (i = 0; i < oprsz; i += sizeof(TYPE)) { \ - *(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \ + *(TYPE *)(d + i) = -(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \ } \ clear_high(d, oprsz, desc); \ } #define DO_CMP2(SZ) \ - DO_CMP1(gvec_eq##SZ, vec##SZ, ==) \ - DO_CMP1(gvec_ne##SZ, vec##SZ, !=) \ - DO_CMP1(gvec_lt##SZ, svec##SZ, <) \ - DO_CMP1(gvec_le##SZ, svec##SZ, <=) \ - DO_CMP1(gvec_ltu##SZ, vec##SZ, <) \ - DO_CMP1(gvec_leu##SZ, vec##SZ, <=) + DO_CMP1(gvec_eq##SZ, uint##SZ##_t, ==) \ + DO_CMP1(gvec_ne##SZ, uint##SZ##_t, !=) \ + DO_CMP1(gvec_lt##SZ, int##SZ##_t, <) \ + DO_CMP1(gvec_le##SZ, int##SZ##_t, <=) \ + DO_CMP1(gvec_ltu##SZ, uint##SZ##_t, <) \ + DO_CMP1(gvec_leu##SZ, uint##SZ##_t, <=) DO_CMP2(8) DO_CMP2(16) DO_CMP2(32) DO_CMP2(64) -#undef DO_CMP0 #undef DO_CMP1 #undef DO_CMP2 @@ -1450,11 +1384,11 @@ void HELPER(gvec_bitsel)(void *d, void *a, void *b, void *c, uint32_t desc) intptr_t oprsz = simd_oprsz(desc); intptr_t i; - for (i = 0; i < oprsz; i += sizeof(vec64)) { - vec64 aa = *(vec64 *)(a + i); - vec64 bb = *(vec64 *)(b + i); - vec64 cc = *(vec64 *)(c + i); - *(vec64 *)(d + i) = (bb & aa) | (cc & ~aa); + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + uint64_t aa = *(uint64_t *)(a + i); + uint64_t bb = *(uint64_t *)(b + i); + uint64_t cc = *(uint64_t *)(c + i); + *(uint64_t *)(d + i) = (bb & aa) | (cc & ~aa); } clear_high(d, oprsz, desc); } diff --git a/arch_init.c b/arch_init.c index 705d0b94ad..d9eb0ec1dd 100644 --- a/arch_init.c +++ b/arch_init.c @@ -77,6 +77,8 @@ int graphic_depth = 32; #define QEMU_ARCH QEMU_ARCH_PPC #elif defined(TARGET_RISCV) #define QEMU_ARCH QEMU_ARCH_RISCV +#elif defined(TARGET_RX) +#define QEMU_ARCH QEMU_ARCH_RX #elif defined(TARGET_S390X) #define QEMU_ARCH QEMU_ARCH_S390X #elif defined(TARGET_SH4) diff --git a/audio/mixeng.c b/audio/mixeng.c index c14b0d874c..739a500449 100644 --- a/audio/mixeng.c +++ b/audio/mixeng.c @@ -268,17 +268,17 @@ f_sample *mixeng_clip[2][2][2][3] = { }; #ifdef FLOAT_MIXENG -#define FLOAT_CONV_TO(x) (x) -#define FLOAT_CONV_FROM(x) (x) +#define CONV_NATURAL_FLOAT(x) (x) +#define CLIP_NATURAL_FLOAT(x) (x) #else -static const float float_scale = UINT_MAX; -#define FLOAT_CONV_TO(x) ((x) * float_scale) +static const float float_scale = UINT_MAX / 2.f; +#define CONV_NATURAL_FLOAT(x) ((x) * float_scale) #ifdef RECIPROCAL -static const float float_scale_reciprocal = 1.f / UINT_MAX; -#define FLOAT_CONV_FROM(x) ((x) * float_scale_reciprocal) +static const float float_scale_reciprocal = 2.f / UINT_MAX; +#define CLIP_NATURAL_FLOAT(x) ((x) * float_scale_reciprocal) #else -#define FLOAT_CONV_FROM(x) ((x) / float_scale) +#define CLIP_NATURAL_FLOAT(x) ((x) / float_scale) #endif #endif @@ -288,7 +288,7 @@ static void conv_natural_float_to_mono(struct st_sample *dst, const void *src, float *in = (float *)src; while (samples--) { - dst->r = dst->l = FLOAT_CONV_TO(*in++); + dst->r = dst->l = CONV_NATURAL_FLOAT(*in++); dst++; } } @@ -299,8 +299,8 @@ static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, float *in = (float *)src; while (samples--) { - dst->l = FLOAT_CONV_TO(*in++); - dst->r = FLOAT_CONV_TO(*in++); + dst->l = CONV_NATURAL_FLOAT(*in++); + dst->r = CONV_NATURAL_FLOAT(*in++); dst++; } } @@ -316,7 +316,7 @@ static void clip_natural_float_from_mono(void *dst, const struct st_sample *src, float *out = (float *)dst; while (samples--) { - *out++ = FLOAT_CONV_FROM(src->l) + FLOAT_CONV_FROM(src->r); + *out++ = CLIP_NATURAL_FLOAT(src->l + src->r); src++; } } @@ -327,8 +327,8 @@ static void clip_natural_float_from_stereo( float *out = (float *)dst; while (samples--) { - *out++ = FLOAT_CONV_FROM(src->l); - *out++ = FLOAT_CONV_FROM(src->r); + *out++ = CLIP_NATURAL_FLOAT(src->l); + *out++ = CLIP_NATURAL_FLOAT(src->r); src++; } } diff --git a/audio/mixeng_template.h b/audio/mixeng_template.h index 77cc89b9e8..bc8509e423 100644 --- a/audio/mixeng_template.h +++ b/audio/mixeng_template.h @@ -41,32 +41,31 @@ static inline mixeng_real glue (conv_, ET) (IN_T v) #ifdef RECIPROCAL #ifdef SIGNED - return nv * (1.f / (mixeng_real) (IN_MAX - IN_MIN)); + return nv * (2.f / ((mixeng_real)IN_MAX - IN_MIN)); #else - return (nv - HALF) * (1.f / (mixeng_real) IN_MAX); + return (nv - HALF) * (2.f / (mixeng_real)IN_MAX); #endif #else /* !RECIPROCAL */ #ifdef SIGNED - return nv / (mixeng_real) ((mixeng_real) IN_MAX - IN_MIN); + return nv / (((mixeng_real)IN_MAX - IN_MIN) / 2.f); #else - return (nv - HALF) / (mixeng_real) IN_MAX; + return (nv - HALF) / ((mixeng_real)IN_MAX / 2.f); #endif #endif } static inline IN_T glue (clip_, ET) (mixeng_real v) { - if (v >= 0.5) { + if (v >= 1.f) { return IN_MAX; - } - else if (v < -0.5) { + } else if (v < -1.f) { return IN_MIN; } #ifdef SIGNED - return ENDIAN_CONVERT ((IN_T) (v * ((mixeng_real) IN_MAX - IN_MIN))); + return ENDIAN_CONVERT((IN_T)(v * (((mixeng_real)IN_MAX - IN_MIN) / 2.f))); #else - return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF)); + return ENDIAN_CONVERT((IN_T)((v * ((mixeng_real)IN_MAX / 2.f)) + HALF)); #endif } @@ -84,10 +83,9 @@ static inline int64_t glue (conv_, ET) (IN_T v) static inline IN_T glue (clip_, ET) (int64_t v) { - if (v >= 0x7f000000) { + if (v >= 0x7fffffffLL) { return IN_MAX; - } - else if (v < -2147483648LL) { + } else if (v < -2147483648LL) { return IN_MIN; } @@ -668,6 +668,32 @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp) } } +int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp) +{ + Error *local_err = NULL; + int ret; + + assert(bs != NULL); + + if (!bs->drv) { + error_setg(errp, "Block node '%s' is not opened", bs->filename); + return -ENOMEDIUM; + } + + if (!bs->drv->bdrv_co_delete_file) { + error_setg(errp, "Driver '%s' does not support image deletion", + bs->drv->format_name); + return -ENOTSUP; + } + + ret = bs->drv->bdrv_co_delete_file(bs, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + } + + return ret; +} + /** * Try to get @bs's logical and physical block size. * On success, store them in @bsz struct and return 0. @@ -1872,8 +1898,6 @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q, bool *tighten_restrictions, Error **errp); static void bdrv_child_abort_perm_update(BdrvChild *c); static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared); -static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, - uint64_t *shared_perm); typedef struct BlockReopenQueueEntry { bool prepared; @@ -2097,8 +2121,8 @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms, } } -static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, - uint64_t *shared_perm) +void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, + uint64_t *shared_perm) { BdrvChild *c; uint64_t cumulative_perms = 0; @@ -4367,6 +4391,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, bdrv_ref(from); assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to)); bdrv_drained_begin(from); /* Put all parents into @list and calculate their cumulative permissions */ diff --git a/block/Makefile.objs b/block/Makefile.objs index cb36ae2503..3635b6b4c1 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -45,6 +45,7 @@ block-obj-y += crypto.o block-obj-y += aio_task.o block-obj-y += backup-top.o block-obj-y += filter-compress.o +common-obj-y += monitor/ block-obj-y += stream.o diff --git a/block/backup-top.c b/block/backup-top.c index 1bfb360bd3..3b50c06e2c 100644 --- a/block/backup-top.c +++ b/block/backup-top.c @@ -38,6 +38,7 @@ typedef struct BDRVBackupTopState { BlockCopyState *bcs; BdrvChild *target; bool active; + int64_t cluster_size; } BDRVBackupTopState; static coroutine_fn int backup_top_co_preadv( @@ -57,8 +58,8 @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset, return 0; } - off = QEMU_ALIGN_DOWN(offset, s->bcs->cluster_size); - end = QEMU_ALIGN_UP(offset + bytes, s->bcs->cluster_size); + off = QEMU_ALIGN_DOWN(offset, s->cluster_size); + end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size); return block_copy(s->bcs, off, end - off, NULL); } @@ -238,6 +239,7 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, goto fail; } + state->cluster_size = cluster_size; state->bcs = block_copy_state_new(top->backing, state->target, cluster_size, write_flags, &local_err); if (local_err) { diff --git a/block/backup.c b/block/backup.c index 1383e219f5..7430ca5883 100644 --- a/block/backup.c +++ b/block/backup.c @@ -57,15 +57,6 @@ static void backup_progress_bytes_callback(int64_t bytes, void *opaque) BackupBlockJob *s = opaque; s->bytes_read += bytes; - job_progress_update(&s->common.job, bytes); -} - -static void backup_progress_reset_callback(void *opaque) -{ - BackupBlockJob *s = opaque; - uint64_t estimate = bdrv_get_dirty_count(s->bcs->copy_bitmap); - - job_progress_set_remaining(&s->common.job, estimate); } static int coroutine_fn backup_do_cow(BackupBlockJob *job, @@ -111,7 +102,7 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) if (ret < 0 && job->bitmap_mode == BITMAP_SYNC_MODE_ALWAYS) { /* If we failed and synced, merge in the bits we didn't copy: */ - bdrv_dirty_bitmap_merge_internal(bm, job->bcs->copy_bitmap, + bdrv_dirty_bitmap_merge_internal(bm, block_copy_dirty_bitmap(job->bcs), NULL, true); } } @@ -154,7 +145,8 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) return; } - bdrv_set_dirty_bitmap(backup_job->bcs->copy_bitmap, 0, backup_job->len); + bdrv_set_dirty_bitmap(block_copy_dirty_bitmap(backup_job->bcs), 0, + backup_job->len); } static BlockErrorAction backup_error_action(BackupBlockJob *job, @@ -199,7 +191,7 @@ static int coroutine_fn backup_loop(BackupBlockJob *job) BdrvDirtyBitmapIter *bdbi; int ret = 0; - bdbi = bdrv_dirty_iter_new(job->bcs->copy_bitmap); + bdbi = bdrv_dirty_iter_new(block_copy_dirty_bitmap(job->bcs)); while ((offset = bdrv_dirty_iter_next(bdbi)) != -1) { do { if (yield_and_check(job)) { @@ -219,14 +211,14 @@ static int coroutine_fn backup_loop(BackupBlockJob *job) return ret; } -static void backup_init_copy_bitmap(BackupBlockJob *job) +static void backup_init_bcs_bitmap(BackupBlockJob *job) { bool ret; uint64_t estimate; + BdrvDirtyBitmap *bcs_bitmap = block_copy_dirty_bitmap(job->bcs); if (job->sync_mode == MIRROR_SYNC_MODE_BITMAP) { - ret = bdrv_dirty_bitmap_merge_internal(job->bcs->copy_bitmap, - job->sync_bitmap, + ret = bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap, NULL, true); assert(ret); } else { @@ -235,12 +227,12 @@ static void backup_init_copy_bitmap(BackupBlockJob *job) * We can't hog the coroutine to initialize this thoroughly. * Set a flag and resume work when we are able to yield safely. */ - job->bcs->skip_unallocated = true; + block_copy_set_skip_unallocated(job->bcs, true); } - bdrv_set_dirty_bitmap(job->bcs->copy_bitmap, 0, job->len); + bdrv_set_dirty_bitmap(bcs_bitmap, 0, job->len); } - estimate = bdrv_get_dirty_count(job->bcs->copy_bitmap); + estimate = bdrv_get_dirty_count(bcs_bitmap); job_progress_set_remaining(&job->common.job, estimate); } @@ -249,7 +241,7 @@ static int coroutine_fn backup_run(Job *job, Error **errp) BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); int ret = 0; - backup_init_copy_bitmap(s); + backup_init_bcs_bitmap(s); if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { int64_t offset = 0; @@ -268,12 +260,12 @@ static int coroutine_fn backup_run(Job *job, Error **errp) offset += count; } - s->bcs->skip_unallocated = false; + block_copy_set_skip_unallocated(s->bcs, false); } if (s->sync_mode == MIRROR_SYNC_MODE_NONE) { /* - * All bits are set in copy_bitmap to allow any cluster to be copied. + * All bits are set in bcs bitmap to allow any cluster to be copied. * This does not actually require them to be copied. */ while (!job_is_cancelled(job)) { @@ -464,8 +456,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, job->cluster_size = cluster_size; job->len = len; - block_copy_set_callbacks(bcs, backup_progress_bytes_callback, - backup_progress_reset_callback, job); + block_copy_set_progress_callback(bcs, backup_progress_bytes_callback, job); + block_copy_set_progress_meter(bcs, &job->common.job.progress); /* Required permissions are already taken by backup-top target */ block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, diff --git a/block/block-copy.c b/block/block-copy.c index 79798a1567..05227e18bf 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -24,37 +24,136 @@ #define BLOCK_COPY_MAX_BUFFER (1 * MiB) #define BLOCK_COPY_MAX_MEM (128 * MiB) -static void coroutine_fn block_copy_wait_inflight_reqs(BlockCopyState *s, - int64_t start, - int64_t end) +typedef struct BlockCopyInFlightReq { + int64_t offset; + int64_t bytes; + QLIST_ENTRY(BlockCopyInFlightReq) list; + CoQueue wait_queue; /* coroutines blocked on this request */ +} BlockCopyInFlightReq; + +typedef struct BlockCopyState { + /* + * BdrvChild objects are not owned or managed by block-copy. They are + * provided by block-copy user and user is responsible for appropriate + * permissions on these children. + */ + BdrvChild *source; + BdrvChild *target; + BdrvDirtyBitmap *copy_bitmap; + int64_t in_flight_bytes; + int64_t cluster_size; + bool use_copy_range; + int64_t copy_size; + uint64_t len; + QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs; + + BdrvRequestFlags write_flags; + + /* + * skip_unallocated: + * + * Used by sync=top jobs, which first scan the source node for unallocated + * areas and clear them in the copy_bitmap. During this process, the bitmap + * is thus not fully initialized: It may still have bits set for areas that + * are unallocated and should actually not be copied. + * + * This is indicated by skip_unallocated. + * + * In this case, block_copy() will query the source’s allocation status, + * skip unallocated regions, clear them in the copy_bitmap, and invoke + * block_copy_reset_unallocated() every time it does. + */ + bool skip_unallocated; + + ProgressMeter *progress; + /* progress_bytes_callback: called when some copying progress is done. */ + ProgressBytesCallbackFunc progress_bytes_callback; + void *progress_opaque; + + SharedResource *mem; +} BlockCopyState; + +static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s, + int64_t offset, + int64_t bytes) { BlockCopyInFlightReq *req; - bool waited; - do { - waited = false; - QLIST_FOREACH(req, &s->inflight_reqs, list) { - if (end > req->start_byte && start < req->end_byte) { - qemu_co_queue_wait(&req->wait_queue, NULL); - waited = true; - break; - } + QLIST_FOREACH(req, &s->inflight_reqs, list) { + if (offset + bytes > req->offset && offset < req->offset + req->bytes) { + return req; } - } while (waited); + } + + return NULL; +} + +/* + * If there are no intersecting requests return false. Otherwise, wait for the + * first found intersecting request to finish and return true. + */ +static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset, + int64_t bytes) +{ + BlockCopyInFlightReq *req = find_conflicting_inflight_req(s, offset, bytes); + + if (!req) { + return false; + } + + qemu_co_queue_wait(&req->wait_queue, NULL); + + return true; } +/* Called only on full-dirty region */ static void block_copy_inflight_req_begin(BlockCopyState *s, BlockCopyInFlightReq *req, - int64_t start, int64_t end) + int64_t offset, int64_t bytes) { - req->start_byte = start; - req->end_byte = end; + assert(!find_conflicting_inflight_req(s, offset, bytes)); + + bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes); + s->in_flight_bytes += bytes; + + req->offset = offset; + req->bytes = bytes; qemu_co_queue_init(&req->wait_queue); QLIST_INSERT_HEAD(&s->inflight_reqs, req, list); } -static void coroutine_fn block_copy_inflight_req_end(BlockCopyInFlightReq *req) +/* + * block_copy_inflight_req_shrink + * + * Drop the tail of the request to be handled later. Set dirty bits back and + * wake up all requests waiting for us (may be some of them are not intersecting + * with shrunk request) + */ +static void coroutine_fn block_copy_inflight_req_shrink(BlockCopyState *s, + BlockCopyInFlightReq *req, int64_t new_bytes) +{ + if (new_bytes == req->bytes) { + return; + } + + assert(new_bytes > 0 && new_bytes < req->bytes); + + s->in_flight_bytes -= req->bytes - new_bytes; + bdrv_set_dirty_bitmap(s->copy_bitmap, + req->offset + new_bytes, req->bytes - new_bytes); + + req->bytes = new_bytes; + qemu_co_queue_restart_all(&req->wait_queue); +} + +static void coroutine_fn block_copy_inflight_req_end(BlockCopyState *s, + BlockCopyInFlightReq *req, + int ret) { + s->in_flight_bytes -= req->bytes; + if (ret < 0) { + bdrv_set_dirty_bitmap(s->copy_bitmap, req->offset, req->bytes); + } QLIST_REMOVE(req, list); qemu_co_queue_restart_all(&req->wait_queue); } @@ -70,16 +169,19 @@ void block_copy_state_free(BlockCopyState *s) g_free(s); } +static uint32_t block_copy_max_transfer(BdrvChild *source, BdrvChild *target) +{ + return MIN_NON_ZERO(INT_MAX, + MIN_NON_ZERO(source->bs->bl.max_transfer, + target->bs->bl.max_transfer)); +} + BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, int64_t cluster_size, BdrvRequestFlags write_flags, Error **errp) { BlockCopyState *s; BdrvDirtyBitmap *copy_bitmap; - uint32_t max_transfer = - MIN_NON_ZERO(INT_MAX, - MIN_NON_ZERO(source->bs->bl.max_transfer, - target->bs->bl.max_transfer)); copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL, errp); @@ -99,7 +201,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, .mem = shres_create(BLOCK_COPY_MAX_MEM), }; - if (max_transfer < cluster_size) { + if (block_copy_max_transfer(source, target) < cluster_size) { /* * copy_range does not respect max_transfer. We don't want to bother * with requests smaller than block-copy cluster size, so fallback to @@ -114,12 +216,11 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, s->copy_size = cluster_size; } else { /* - * copy_range does not respect max_transfer (it's a TODO), so we factor - * that in here. + * We enable copy-range, but keep small copy_size, until first + * successful copy_range (look at block_copy_do_copy). */ s->use_copy_range = true; - s->copy_size = MIN(MAX(cluster_size, BLOCK_COPY_MAX_COPY_RANGE), - QEMU_ALIGN_DOWN(max_transfer, cluster_size)); + s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER); } QLIST_INIT(&s->inflight_reqs); @@ -127,48 +228,83 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, return s; } -void block_copy_set_callbacks( +void block_copy_set_progress_callback( BlockCopyState *s, ProgressBytesCallbackFunc progress_bytes_callback, - ProgressResetCallbackFunc progress_reset_callback, void *progress_opaque) { s->progress_bytes_callback = progress_bytes_callback; - s->progress_reset_callback = progress_reset_callback; s->progress_opaque = progress_opaque; } +void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm) +{ + s->progress = pm; +} + /* * block_copy_do_copy * - * Do copy of cluser-aligned chunk. @end is allowed to exceed s->len only to - * cover last cluster when s->len is not aligned to clusters. + * Do copy of cluster-aligned chunk. Requested region is allowed to exceed + * s->len only to cover last cluster when s->len is not aligned to clusters. * * No sync here: nor bitmap neighter intersecting requests handling, only copy. * * Returns 0 on success. */ static int coroutine_fn block_copy_do_copy(BlockCopyState *s, - int64_t start, int64_t end, - bool *error_is_read) + int64_t offset, int64_t bytes, + bool zeroes, bool *error_is_read) { int ret; - int nbytes = MIN(end, s->len) - start; + int64_t nbytes = MIN(offset + bytes, s->len) - offset; void *bounce_buffer = NULL; - assert(QEMU_IS_ALIGNED(start, s->cluster_size)); - assert(QEMU_IS_ALIGNED(end, s->cluster_size)); - assert(end < s->len || end == QEMU_ALIGN_UP(s->len, s->cluster_size)); + assert(offset >= 0 && bytes > 0 && INT64_MAX - offset >= bytes); + assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); + assert(QEMU_IS_ALIGNED(bytes, s->cluster_size)); + assert(offset < s->len); + assert(offset + bytes <= s->len || + offset + bytes == QEMU_ALIGN_UP(s->len, s->cluster_size)); + assert(nbytes < INT_MAX); + + if (zeroes) { + ret = bdrv_co_pwrite_zeroes(s->target, offset, nbytes, s->write_flags & + ~BDRV_REQ_WRITE_COMPRESSED); + if (ret < 0) { + trace_block_copy_write_zeroes_fail(s, offset, ret); + if (error_is_read) { + *error_is_read = false; + } + } + return ret; + } if (s->use_copy_range) { - ret = bdrv_co_copy_range(s->source, start, s->target, start, nbytes, + ret = bdrv_co_copy_range(s->source, offset, s->target, offset, nbytes, 0, s->write_flags); if (ret < 0) { - trace_block_copy_copy_range_fail(s, start, ret); + trace_block_copy_copy_range_fail(s, offset, ret); s->use_copy_range = false; s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER); /* Fallback to read+write with allocated buffer */ } else { + if (s->use_copy_range) { + /* + * Successful copy-range. Now increase copy_size. copy_range + * does not respect max_transfer (it's a TODO), so we factor + * that in here. + * + * Note: we double-check s->use_copy_range for the case when + * parallel block-copy request unsets it during previous + * bdrv_co_copy_range call. + */ + s->copy_size = + MIN(MAX(s->cluster_size, BLOCK_COPY_MAX_COPY_RANGE), + QEMU_ALIGN_DOWN(block_copy_max_transfer(s->source, + s->target), + s->cluster_size)); + } goto out; } } @@ -176,24 +312,27 @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s, /* * In case of failed copy_range request above, we may proceed with buffered * request larger than BLOCK_COPY_MAX_BUFFER. Still, further requests will - * be properly limited, so don't care too much. + * be properly limited, so don't care too much. Moreover the most likely + * case (copy_range is unsupported for the configuration, so the very first + * copy_range request fails) is handled by setting large copy_size only + * after first successful copy_range. */ bounce_buffer = qemu_blockalign(s->source->bs, nbytes); - ret = bdrv_co_pread(s->source, start, nbytes, bounce_buffer, 0); + ret = bdrv_co_pread(s->source, offset, nbytes, bounce_buffer, 0); if (ret < 0) { - trace_block_copy_read_fail(s, start, ret); + trace_block_copy_read_fail(s, offset, ret); if (error_is_read) { *error_is_read = true; } goto out; } - ret = bdrv_co_pwrite(s->target, start, nbytes, bounce_buffer, + ret = bdrv_co_pwrite(s->target, offset, nbytes, bounce_buffer, s->write_flags); if (ret < 0) { - trace_block_copy_write_fail(s, start, ret); + trace_block_copy_write_fail(s, offset, ret); if (error_is_read) { *error_is_read = false; } @@ -206,6 +345,38 @@ out: return ret; } +static int block_copy_block_status(BlockCopyState *s, int64_t offset, + int64_t bytes, int64_t *pnum) +{ + int64_t num; + BlockDriverState *base; + int ret; + + if (s->skip_unallocated && s->source->bs->backing) { + base = s->source->bs->backing->bs; + } else { + base = NULL; + } + + ret = bdrv_block_status_above(s->source->bs, base, offset, bytes, &num, + NULL, NULL); + if (ret < 0 || num < s->cluster_size) { + /* + * On error or if failed to obtain large enough chunk just fallback to + * copy one cluster. + */ + num = s->cluster_size; + ret = BDRV_BLOCK_ALLOCATED | BDRV_BLOCK_DATA; + } else if (offset + num == s->len) { + num = QEMU_ALIGN_UP(num, s->cluster_size); + } else { + num = QEMU_ALIGN_DOWN(num, s->cluster_size); + } + + *pnum = num; + return ret; +} + /* * Check if the cluster starting at offset is allocated or not. * return via pnum the number of contiguous clusters sharing this allocation. @@ -269,21 +440,28 @@ int64_t block_copy_reset_unallocated(BlockCopyState *s, if (!ret) { bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes); - s->progress_reset_callback(s->progress_opaque); + progress_set_remaining(s->progress, + bdrv_get_dirty_count(s->copy_bitmap) + + s->in_flight_bytes); } *count = bytes; return ret; } -int coroutine_fn block_copy(BlockCopyState *s, - int64_t start, uint64_t bytes, - bool *error_is_read) +/* + * block_copy_dirty_clusters + * + * Copy dirty clusters in @offset/@bytes range. + * Returns 1 if dirty clusters found and successfully copied, 0 if no dirty + * clusters found and -errno on failure. + */ +static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s, + int64_t offset, int64_t bytes, + bool *error_is_read) { int ret = 0; - int64_t end = bytes + start; /* bytes */ - int64_t status_bytes; - BlockCopyInFlightReq req; + bool found_dirty = false; /* * block_copy() user is responsible for keeping source and target in same @@ -292,60 +470,109 @@ int coroutine_fn block_copy(BlockCopyState *s, assert(bdrv_get_aio_context(s->source->bs) == bdrv_get_aio_context(s->target->bs)); - assert(QEMU_IS_ALIGNED(start, s->cluster_size)); - assert(QEMU_IS_ALIGNED(end, s->cluster_size)); - - block_copy_wait_inflight_reqs(s, start, bytes); - block_copy_inflight_req_begin(s, &req, start, end); + assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); + assert(QEMU_IS_ALIGNED(bytes, s->cluster_size)); - while (start < end) { - int64_t next_zero, chunk_end; + while (bytes) { + BlockCopyInFlightReq req; + int64_t next_zero, cur_bytes, status_bytes; - if (!bdrv_dirty_bitmap_get(s->copy_bitmap, start)) { - trace_block_copy_skip(s, start); - start += s->cluster_size; + if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) { + trace_block_copy_skip(s, offset); + offset += s->cluster_size; + bytes -= s->cluster_size; continue; /* already copied */ } - chunk_end = MIN(end, start + s->copy_size); + found_dirty = true; + + cur_bytes = MIN(bytes, s->copy_size); - next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, start, - chunk_end - start); + next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, offset, + cur_bytes); if (next_zero >= 0) { - assert(next_zero > start); /* start is dirty */ - assert(next_zero < chunk_end); /* no need to do MIN() */ - chunk_end = next_zero; + assert(next_zero > offset); /* offset is dirty */ + assert(next_zero < offset + cur_bytes); /* no need to do MIN() */ + cur_bytes = next_zero - offset; } - - if (s->skip_unallocated) { - ret = block_copy_reset_unallocated(s, start, &status_bytes); - if (ret == 0) { - trace_block_copy_skip_range(s, start, status_bytes); - start += status_bytes; - continue; - } - /* Clamp to known allocated region */ - chunk_end = MIN(chunk_end, start + status_bytes); + block_copy_inflight_req_begin(s, &req, offset, cur_bytes); + + ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes); + assert(ret >= 0); /* never fail */ + cur_bytes = MIN(cur_bytes, status_bytes); + block_copy_inflight_req_shrink(s, &req, cur_bytes); + if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) { + block_copy_inflight_req_end(s, &req, 0); + progress_set_remaining(s->progress, + bdrv_get_dirty_count(s->copy_bitmap) + + s->in_flight_bytes); + trace_block_copy_skip_range(s, offset, status_bytes); + offset += status_bytes; + bytes -= status_bytes; + continue; } - trace_block_copy_process(s, start); - - bdrv_reset_dirty_bitmap(s->copy_bitmap, start, chunk_end - start); + trace_block_copy_process(s, offset); - co_get_from_shres(s->mem, chunk_end - start); - ret = block_copy_do_copy(s, start, chunk_end, error_is_read); - co_put_to_shres(s->mem, chunk_end - start); + co_get_from_shres(s->mem, cur_bytes); + ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO, + error_is_read); + co_put_to_shres(s->mem, cur_bytes); + block_copy_inflight_req_end(s, &req, ret); if (ret < 0) { - bdrv_set_dirty_bitmap(s->copy_bitmap, start, chunk_end - start); - break; + return ret; } - s->progress_bytes_callback(chunk_end - start, s->progress_opaque); - start = chunk_end; - ret = 0; + progress_work_done(s->progress, cur_bytes); + s->progress_bytes_callback(cur_bytes, s->progress_opaque); + offset += cur_bytes; + bytes -= cur_bytes; } - block_copy_inflight_req_end(&req); + return found_dirty; +} + +/* + * block_copy + * + * Copy requested region, accordingly to dirty bitmap. + * Collaborate with parallel block_copy requests: if they succeed it will help + * us. If they fail, we will retry not-copied regions. So, if we return error, + * it means that some I/O operation failed in context of _this_ block_copy call, + * not some parallel operation. + */ +int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes, + bool *error_is_read) +{ + int ret; + + do { + ret = block_copy_dirty_clusters(s, offset, bytes, error_is_read); + + if (ret == 0) { + ret = block_copy_wait_one(s, offset, bytes); + } + + /* + * We retry in two cases: + * 1. Some progress done + * Something was copied, which means that there were yield points + * and some new dirty bits may have appeared (due to failed parallel + * block-copy requests). + * 2. We have waited for some intersecting block-copy request + * It may have failed and produced new dirty bits. + */ + } while (ret > 0); return ret; } + +BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s) +{ + return s->copy_bitmap; +} + +void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip) +{ + s->skip_unallocated = skip; +} diff --git a/block/crypto.c b/block/crypto.c index 24823835c1..4425ebeb47 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -30,6 +30,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "qemu/option.h" +#include "qemu/cutils.h" #include "crypto.h" typedef struct BlockCrypto BlockCrypto; @@ -484,6 +485,67 @@ static int64_t block_crypto_getlength(BlockDriverState *bs) } +static BlockMeasureInfo *block_crypto_measure(QemuOpts *opts, + BlockDriverState *in_bs, + Error **errp) +{ + g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL; + Error *local_err = NULL; + BlockMeasureInfo *info; + uint64_t size; + size_t luks_payload_size; + QDict *cryptoopts; + + /* + * Preallocation mode doesn't affect size requirements but we must consume + * the option. + */ + g_free(qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC)); + + size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + + if (in_bs) { + int64_t ssize = bdrv_getlength(in_bs); + + if (ssize < 0) { + error_setg_errno(&local_err, -ssize, + "Unable to get image virtual_size"); + goto err; + } + + size = ssize; + } + + cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL, + &block_crypto_create_opts_luks, true); + qdict_put_str(cryptoopts, "format", "luks"); + create_opts = block_crypto_create_opts_init(cryptoopts, &local_err); + qobject_unref(cryptoopts); + if (!create_opts) { + goto err; + } + + if (!qcrypto_block_calculate_payload_offset(create_opts, NULL, + &luks_payload_size, + &local_err)) { + goto err; + } + + /* + * Unallocated blocks are still encrypted so allocation status makes no + * difference to the file size. + */ + info = g_new(BlockMeasureInfo, 1); + info->fully_allocated = luks_payload_size + size; + info->required = luks_payload_size + size; + return info; + +err: + error_propagate(errp, local_err); + return NULL; +} + + static int block_crypto_probe_luks(const uint8_t *buf, int buf_size, const char *filename) { @@ -596,6 +658,23 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, ret = 0; fail: + /* + * If an error occurred, delete 'filename'. Even if the file existed + * beforehand, it has been truncated and corrupted in the process. + */ + if (ret && bs) { + Error *local_delete_err = NULL; + int r_del = bdrv_co_delete_file(bs, &local_delete_err); + /* + * ENOTSUP will happen if the block driver doesn't support + * the 'bdrv_co_delete_file' interface. This is a predictable + * scenario and shouldn't be reported back to the user. + */ + if ((r_del < 0) && (r_del != -ENOTSUP)) { + error_report_err(local_delete_err); + } + } + bdrv_unref(bs); qapi_free_QCryptoBlockCreateOptions(create_opts); qobject_unref(cryptoopts); @@ -670,6 +749,7 @@ static BlockDriver bdrv_crypto_luks = { .bdrv_co_preadv = block_crypto_co_preadv, .bdrv_co_pwritev = block_crypto_co_pwritev, .bdrv_getlength = block_crypto_getlength, + .bdrv_measure = block_crypto_measure, .bdrv_get_info = block_crypto_get_info_luks, .bdrv_get_specific_info = block_crypto_get_specific_info_luks, diff --git a/block/curl.c b/block/curl.c index f86299378e..6e325901dc 100644 --- a/block/curl.c +++ b/block/curl.c @@ -214,11 +214,35 @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque) { BDRVCURLState *s = opaque; size_t realsize = size * nmemb; - const char *accept_line = "Accept-Ranges: bytes"; + const char *header = (char *)ptr; + const char *end = header + realsize; + const char *accept_ranges = "accept-ranges:"; + const char *bytes = "bytes"; - if (realsize >= strlen(accept_line) - && strncmp((char *)ptr, accept_line, strlen(accept_line)) == 0) { - s->accept_range = true; + if (realsize >= strlen(accept_ranges) + && g_ascii_strncasecmp(header, accept_ranges, + strlen(accept_ranges)) == 0) { + + char *p = strchr(header, ':') + 1; + + /* Skip whitespace between the header name and value. */ + while (p < end && *p && g_ascii_isspace(*p)) { + p++; + } + + if (end - p >= strlen(bytes) + && strncmp(p, bytes, strlen(bytes)) == 0) { + + /* Check that there is nothing but whitespace after the value. */ + p += strlen(bytes); + while (p < end && *p && g_ascii_isspace(*p)) { + p++; + } + + if (p == end || !*p) { + s->accept_range = true; + } + } } return realsize; diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 7039e82520..063793e316 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -860,16 +860,24 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp) return hbitmap_sha256(bitmap->bitmap, errp); } -int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, - uint64_t bytes) +int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset, + int64_t bytes) +{ + return hbitmap_next_dirty(bitmap->bitmap, offset, bytes); +} + +int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset, + int64_t bytes) { return hbitmap_next_zero(bitmap->bitmap, offset, bytes); } bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, - uint64_t *offset, uint64_t *bytes) + int64_t start, int64_t end, int64_t max_dirty_count, + int64_t *dirty_start, int64_t *dirty_count) { - return hbitmap_next_dirty_area(bitmap->bitmap, offset, bytes); + return hbitmap_next_dirty_area(bitmap->bitmap, start, end, max_dirty_count, + dirty_start, dirty_count); } /** diff --git a/block/file-posix.c b/block/file-posix.c index 0f77447a25..9bc3838b2a 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2445,6 +2445,28 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, return raw_co_create(&options, errp); } +static int coroutine_fn raw_co_delete_file(BlockDriverState *bs, + Error **errp) +{ + struct stat st; + int ret; + + if (!(stat(bs->filename, &st) == 0) || !S_ISREG(st.st_mode)) { + error_setg_errno(errp, ENOENT, "%s is not a regular file", + bs->filename); + return -ENOENT; + } + + ret = unlink(bs->filename); + if (ret < 0) { + ret = -errno; + error_setg_errno(errp, -ret, "Error when deleting file %s", + bs->filename); + } + + return ret; +} + /* * Find allocation range in @bs around offset @start. * May change underlying file descriptor's file offset. @@ -3075,6 +3097,7 @@ BlockDriver bdrv_file = { .bdrv_co_block_status = raw_co_block_status, .bdrv_co_invalidate_cache = raw_co_invalidate_cache, .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes, + .bdrv_co_delete_file = raw_co_delete_file, .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, diff --git a/block/io.c b/block/io.c index 7e4cb74cf4..aba67f66b9 100644 --- a/block/io.c +++ b/block/io.c @@ -1399,7 +1399,7 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, if (!(flags & BDRV_REQ_PREFETCH)) { qemu_iovec_from_buf(qiov, qiov_offset + progress, bounce_buffer + skip_bytes, - pnum - skip_bytes); + MIN(pnum - skip_bytes, bytes - progress)); } } else if (!(flags & BDRV_REQ_PREFETCH)) { /* Read directly into the destination */ diff --git a/block/linux-aio.c b/block/linux-aio.c index 91204a25a2..3c0527c2bf 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -121,7 +121,7 @@ struct aio_ring { unsigned incompat_features; unsigned header_length; /* size of aio_ring */ - struct io_event io_events[0]; + struct io_event io_events[]; }; /** diff --git a/block/monitor/Makefile.objs b/block/monitor/Makefile.objs new file mode 100644 index 0000000000..0a74f9a8b5 --- /dev/null +++ b/block/monitor/Makefile.objs @@ -0,0 +1 @@ +common-obj-y += block-hmp-cmds.o diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c new file mode 100644 index 0000000000..4c8c375172 --- /dev/null +++ b/block/monitor/block-hmp-cmds.c @@ -0,0 +1,1013 @@ +/* + * Blockdev HMP commands + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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. + * + * 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 "qemu/osdep.h" +#include "hw/boards.h" +#include "sysemu/block-backend.h" +#include "sysemu/blockdev.h" +#include "qapi/qapi-commands-block.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "qemu/config-file.h" +#include "qemu/option.h" +#include "qemu/sockets.h" +#include "qemu/cutils.h" +#include "sysemu/sysemu.h" +#include "monitor/monitor.h" +#include "monitor/hmp.h" +#include "block/nbd.h" +#include "block/qapi.h" +#include "block/block_int.h" +#include "block/block-hmp-cmds.h" +#include "qemu-io.h" + +static void hmp_drive_add_node(Monitor *mon, const char *optstr) +{ + QemuOpts *opts; + QDict *qdict; + Error *local_err = NULL; + + opts = qemu_opts_parse_noisily(&qemu_drive_opts, optstr, false); + if (!opts) { + return; + } + + qdict = qemu_opts_to_qdict(opts, NULL); + + if (!qdict_get_try_str(qdict, "node-name")) { + qobject_unref(qdict); + error_report("'node-name' needs to be specified"); + goto out; + } + + BlockDriverState *bs = bds_tree_init(qdict, &local_err); + if (!bs) { + error_report_err(local_err); + goto out; + } + + bdrv_set_monitor_owned(bs); +out: + qemu_opts_del(opts); +} + +void hmp_drive_add(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + DriveInfo *dinfo; + QemuOpts *opts; + MachineClass *mc; + const char *optstr = qdict_get_str(qdict, "opts"); + bool node = qdict_get_try_bool(qdict, "node", false); + + if (node) { + hmp_drive_add_node(mon, optstr); + return; + } + + opts = drive_def(optstr); + if (!opts) + return; + + mc = MACHINE_GET_CLASS(current_machine); + dinfo = drive_new(opts, mc->block_default_type, &err); + if (err) { + error_report_err(err); + qemu_opts_del(opts); + goto err; + } + + if (!dinfo) { + return; + } + + switch (dinfo->type) { + case IF_NONE: + monitor_printf(mon, "OK\n"); + break; + default: + monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); + goto err; + } + return; + +err: + if (dinfo) { + BlockBackend *blk = blk_by_legacy_dinfo(dinfo); + monitor_remove_blk(blk); + blk_unref(blk); + } +} + +void hmp_drive_del(Monitor *mon, const QDict *qdict) +{ + const char *id = qdict_get_str(qdict, "id"); + BlockBackend *blk; + BlockDriverState *bs; + AioContext *aio_context; + Error *local_err = NULL; + + bs = bdrv_find_node(id); + if (bs) { + qmp_blockdev_del(id, &local_err); + if (local_err) { + error_report_err(local_err); + } + return; + } + + blk = blk_by_name(id); + if (!blk) { + error_report("Device '%s' not found", id); + return; + } + + if (!blk_legacy_dinfo(blk)) { + error_report("Deleting device added with blockdev-add" + " is not supported"); + return; + } + + aio_context = blk_get_aio_context(blk); + aio_context_acquire(aio_context); + + bs = blk_bs(blk); + if (bs) { + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { + error_report_err(local_err); + aio_context_release(aio_context); + return; + } + + blk_remove_bs(blk); + } + + /* Make the BlockBackend and the attached BlockDriverState anonymous */ + monitor_remove_blk(blk); + + /* + * If this BlockBackend has a device attached to it, its refcount will be + * decremented when the device is removed; otherwise we have to do so here. + */ + if (blk_get_attached_dev(blk)) { + /* Further I/O must not pause the guest */ + blk_set_on_error(blk, BLOCKDEV_ON_ERROR_REPORT, + BLOCKDEV_ON_ERROR_REPORT); + } else { + blk_unref(blk); + } + + aio_context_release(aio_context); +} + +void hmp_commit(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + BlockBackend *blk; + int ret; + + if (!strcmp(device, "all")) { + ret = blk_commit_all(); + } else { + BlockDriverState *bs; + AioContext *aio_context; + + blk = blk_by_name(device); + if (!blk) { + error_report("Device '%s' not found", device); + return; + } + if (!blk_is_available(blk)) { + error_report("Device '%s' has no medium", device); + return; + } + + bs = blk_bs(blk); + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + ret = bdrv_commit(bs); + + aio_context_release(aio_context); + } + if (ret < 0) { + error_report("'commit' error for '%s': %s", device, strerror(-ret)); + } +} + +void hmp_drive_mirror(Monitor *mon, const QDict *qdict) +{ + const char *filename = qdict_get_str(qdict, "target"); + const char *format = qdict_get_try_str(qdict, "format"); + bool reuse = qdict_get_try_bool(qdict, "reuse", false); + bool full = qdict_get_try_bool(qdict, "full", false); + Error *err = NULL; + DriveMirror mirror = { + .device = (char *)qdict_get_str(qdict, "device"), + .target = (char *)filename, + .has_format = !!format, + .format = (char *)format, + .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, + .has_mode = true, + .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS, + .unmap = true, + }; + + if (!filename) { + error_setg(&err, QERR_MISSING_PARAMETER, "target"); + hmp_handle_error(mon, err); + return; + } + qmp_drive_mirror(&mirror, &err); + hmp_handle_error(mon, err); +} + +void hmp_drive_backup(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *filename = qdict_get_str(qdict, "target"); + const char *format = qdict_get_try_str(qdict, "format"); + bool reuse = qdict_get_try_bool(qdict, "reuse", false); + bool full = qdict_get_try_bool(qdict, "full", false); + bool compress = qdict_get_try_bool(qdict, "compress", false); + Error *err = NULL; + DriveBackup backup = { + .device = (char *)device, + .target = (char *)filename, + .has_format = !!format, + .format = (char *)format, + .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, + .has_mode = true, + .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS, + .has_compress = !!compress, + .compress = compress, + }; + + if (!filename) { + error_setg(&err, QERR_MISSING_PARAMETER, "target"); + hmp_handle_error(mon, err); + return; + } + + qmp_drive_backup(&backup, &err); + hmp_handle_error(mon, err); +} + +void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict) +{ + Error *error = NULL; + const char *device = qdict_get_str(qdict, "device"); + int64_t value = qdict_get_int(qdict, "speed"); + + qmp_block_job_set_speed(device, value, &error); + + hmp_handle_error(mon, error); +} + +void hmp_block_job_cancel(Monitor *mon, const QDict *qdict) +{ + Error *error = NULL; + const char *device = qdict_get_str(qdict, "device"); + bool force = qdict_get_try_bool(qdict, "force", false); + + qmp_block_job_cancel(device, true, force, &error); + + hmp_handle_error(mon, error); +} + +void hmp_block_job_pause(Monitor *mon, const QDict *qdict) +{ + Error *error = NULL; + const char *device = qdict_get_str(qdict, "device"); + + qmp_block_job_pause(device, &error); + + hmp_handle_error(mon, error); +} + +void hmp_block_job_resume(Monitor *mon, const QDict *qdict) +{ + Error *error = NULL; + const char *device = qdict_get_str(qdict, "device"); + + qmp_block_job_resume(device, &error); + + hmp_handle_error(mon, error); +} + +void hmp_block_job_complete(Monitor *mon, const QDict *qdict) +{ + Error *error = NULL; + const char *device = qdict_get_str(qdict, "device"); + + qmp_block_job_complete(device, &error); + + hmp_handle_error(mon, error); +} + +void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *filename = qdict_get_try_str(qdict, "snapshot-file"); + const char *format = qdict_get_try_str(qdict, "format"); + bool reuse = qdict_get_try_bool(qdict, "reuse", false); + enum NewImageMode mode; + Error *err = NULL; + + if (!filename) { + /* + * In the future, if 'snapshot-file' is not specified, the snapshot + * will be taken internally. Today it's actually required. + */ + error_setg(&err, QERR_MISSING_PARAMETER, "snapshot-file"); + hmp_handle_error(mon, err); + return; + } + + mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS; + qmp_blockdev_snapshot_sync(true, device, false, NULL, + filename, false, NULL, + !!format, format, + true, mode, &err); + hmp_handle_error(mon, err); +} + +void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *name = qdict_get_str(qdict, "name"); + Error *err = NULL; + + qmp_blockdev_snapshot_internal_sync(device, name, &err); + hmp_handle_error(mon, err); +} + +void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *name = qdict_get_str(qdict, "name"); + const char *id = qdict_get_try_str(qdict, "id"); + Error *err = NULL; + + qmp_blockdev_snapshot_delete_internal_sync(device, !!id, id, + true, name, &err); + hmp_handle_error(mon, err); +} + +void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) +{ + const char *uri = qdict_get_str(qdict, "uri"); + bool writable = qdict_get_try_bool(qdict, "writable", false); + bool all = qdict_get_try_bool(qdict, "all", false); + Error *local_err = NULL; + BlockInfoList *block_list, *info; + SocketAddress *addr; + BlockExportNbd export; + + if (writable && !all) { + error_setg(&local_err, "-w only valid together with -a"); + goto exit; + } + + /* First check if the address is valid and start the server. */ + addr = socket_parse(uri, &local_err); + if (local_err != NULL) { + goto exit; + } + + nbd_server_start(addr, NULL, NULL, &local_err); + qapi_free_SocketAddress(addr); + if (local_err != NULL) { + goto exit; + } + + if (!all) { + return; + } + + /* Then try adding all block devices. If one fails, close all and + * exit. + */ + block_list = qmp_query_block(NULL); + + for (info = block_list; info; info = info->next) { + if (!info->value->has_inserted) { + continue; + } + + export = (BlockExportNbd) { + .device = info->value->device, + .has_writable = true, + .writable = writable, + }; + + qmp_nbd_server_add(&export, &local_err); + + if (local_err != NULL) { + qmp_nbd_server_stop(NULL); + break; + } + } + + qapi_free_BlockInfoList(block_list); + +exit: + hmp_handle_error(mon, local_err); +} + +void hmp_nbd_server_add(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *name = qdict_get_try_str(qdict, "name"); + bool writable = qdict_get_try_bool(qdict, "writable", false); + Error *local_err = NULL; + + BlockExportNbd export = { + .device = (char *) device, + .has_name = !!name, + .name = (char *) name, + .has_writable = true, + .writable = writable, + }; + + qmp_nbd_server_add(&export, &local_err); + hmp_handle_error(mon, local_err); +} + +void hmp_nbd_server_remove(Monitor *mon, const QDict *qdict) +{ + const char *name = qdict_get_str(qdict, "name"); + bool force = qdict_get_try_bool(qdict, "force", false); + Error *err = NULL; + + /* Rely on NBD_SERVER_REMOVE_MODE_SAFE being the default */ + qmp_nbd_server_remove(name, force, NBD_SERVER_REMOVE_MODE_HARD, &err); + hmp_handle_error(mon, err); +} + +void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_nbd_server_stop(&err); + hmp_handle_error(mon, err); +} + +void hmp_block_resize(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + int64_t size = qdict_get_int(qdict, "size"); + Error *err = NULL; + + qmp_block_resize(true, device, false, NULL, size, &err); + hmp_handle_error(mon, err); +} + +void hmp_block_stream(Monitor *mon, const QDict *qdict) +{ + Error *error = NULL; + const char *device = qdict_get_str(qdict, "device"); + const char *base = qdict_get_try_str(qdict, "base"); + int64_t speed = qdict_get_try_int(qdict, "speed", 0); + + qmp_block_stream(true, device, device, base != NULL, base, false, NULL, + false, NULL, qdict_haskey(qdict, "speed"), speed, true, + BLOCKDEV_ON_ERROR_REPORT, false, false, false, false, + &error); + + hmp_handle_error(mon, error); +} + +void hmp_block_passwd(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *password = qdict_get_str(qdict, "password"); + Error *err = NULL; + + qmp_block_passwd(true, device, false, NULL, password, &err); + hmp_handle_error(mon, err); +} + +void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + char *device = (char *) qdict_get_str(qdict, "device"); + BlockIOThrottle throttle = { + .bps = qdict_get_int(qdict, "bps"), + .bps_rd = qdict_get_int(qdict, "bps_rd"), + .bps_wr = qdict_get_int(qdict, "bps_wr"), + .iops = qdict_get_int(qdict, "iops"), + .iops_rd = qdict_get_int(qdict, "iops_rd"), + .iops_wr = qdict_get_int(qdict, "iops_wr"), + }; + + /* + * qmp_block_set_io_throttle has separate parameters for the + * (deprecated) block device name and the qdev ID but the HMP + * version has only one, so we must decide which one to pass. + */ + if (blk_by_name(device)) { + throttle.has_device = true; + throttle.device = device; + } else { + throttle.has_id = true; + throttle.id = device; + } + + qmp_block_set_io_throttle(&throttle, &err); + hmp_handle_error(mon, err); +} + +void hmp_eject(Monitor *mon, const QDict *qdict) +{ + bool force = qdict_get_try_bool(qdict, "force", false); + const char *device = qdict_get_str(qdict, "device"); + Error *err = NULL; + + qmp_eject(true, device, false, NULL, true, force, &err); + hmp_handle_error(mon, err); +} + +void hmp_qemu_io(Monitor *mon, const QDict *qdict) +{ + BlockBackend *blk; + BlockBackend *local_blk = NULL; + bool qdev = qdict_get_try_bool(qdict, "qdev", false); + const char *device = qdict_get_str(qdict, "device"); + const char *command = qdict_get_str(qdict, "command"); + Error *err = NULL; + int ret; + + if (qdev) { + blk = blk_by_qdev_id(device, &err); + if (!blk) { + goto fail; + } + } else { + blk = blk_by_name(device); + if (!blk) { + BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err); + if (bs) { + blk = local_blk = blk_new(bdrv_get_aio_context(bs), + 0, BLK_PERM_ALL); + ret = blk_insert_bs(blk, bs, &err); + if (ret < 0) { + goto fail; + } + } else { + goto fail; + } + } + } + + /* + * Notably absent: Proper permission management. This is sad, but it seems + * almost impossible to achieve without changing the semantics and thereby + * limiting the use cases of the qemu-io HMP command. + * + * In an ideal world we would unconditionally create a new BlockBackend for + * qemuio_command(), but we have commands like 'reopen' and want them to + * take effect on the exact BlockBackend whose name the user passed instead + * of just on a temporary copy of it. + * + * Another problem is that deleting the temporary BlockBackend involves + * draining all requests on it first, but some qemu-iotests cases want to + * issue multiple aio_read/write requests and expect them to complete in + * the background while the monitor has already returned. + * + * This is also what prevents us from saving the original permissions and + * restoring them later: We can't revoke permissions until all requests + * have completed, and we don't know when that is nor can we really let + * anything else run before we have revoken them to avoid race conditions. + * + * What happens now is that command() in qemu-io-cmds.c can extend the + * permissions if necessary for the qemu-io command. And they simply stay + * extended, possibly resulting in a read-only guest device keeping write + * permissions. Ugly, but it appears to be the lesser evil. + */ + qemuio_command(blk, command); + +fail: + blk_unref(local_blk); + hmp_handle_error(mon, err); +} + +static void print_block_info(Monitor *mon, BlockInfo *info, + BlockDeviceInfo *inserted, bool verbose) +{ + ImageInfo *image_info; + + assert(!info || !info->has_inserted || info->inserted == inserted); + + if (info && *info->device) { + monitor_printf(mon, "%s", info->device); + if (inserted && inserted->has_node_name) { + monitor_printf(mon, " (%s)", inserted->node_name); + } + } else { + assert(info || inserted); + monitor_printf(mon, "%s", + inserted && inserted->has_node_name ? inserted->node_name + : info && info->has_qdev ? info->qdev + : "<anonymous>"); + } + + if (inserted) { + monitor_printf(mon, ": %s (%s%s%s)\n", + inserted->file, + inserted->drv, + inserted->ro ? ", read-only" : "", + inserted->encrypted ? ", encrypted" : ""); + } else { + monitor_printf(mon, ": [not inserted]\n"); + } + + if (info) { + if (info->has_qdev) { + monitor_printf(mon, " Attached to: %s\n", info->qdev); + } + if (info->has_io_status && info->io_status != BLOCK_DEVICE_IO_STATUS_OK) { + monitor_printf(mon, " I/O status: %s\n", + BlockDeviceIoStatus_str(info->io_status)); + } + + if (info->removable) { + monitor_printf(mon, " Removable device: %slocked, tray %s\n", + info->locked ? "" : "not ", + info->tray_open ? "open" : "closed"); + } + } + + + if (!inserted) { + return; + } + + monitor_printf(mon, " Cache mode: %s%s%s\n", + inserted->cache->writeback ? "writeback" : "writethrough", + inserted->cache->direct ? ", direct" : "", + inserted->cache->no_flush ? ", ignore flushes" : ""); + + if (inserted->has_backing_file) { + monitor_printf(mon, + " Backing file: %s " + "(chain depth: %" PRId64 ")\n", + inserted->backing_file, + inserted->backing_file_depth); + } + + if (inserted->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF) { + monitor_printf(mon, " Detect zeroes: %s\n", + BlockdevDetectZeroesOptions_str(inserted->detect_zeroes)); + } + + if (inserted->bps || inserted->bps_rd || inserted->bps_wr || + inserted->iops || inserted->iops_rd || inserted->iops_wr) + { + monitor_printf(mon, " I/O throttling: bps=%" PRId64 + " bps_rd=%" PRId64 " bps_wr=%" PRId64 + " bps_max=%" PRId64 + " bps_rd_max=%" PRId64 + " bps_wr_max=%" PRId64 + " iops=%" PRId64 " iops_rd=%" PRId64 + " iops_wr=%" PRId64 + " iops_max=%" PRId64 + " iops_rd_max=%" PRId64 + " iops_wr_max=%" PRId64 + " iops_size=%" PRId64 + " group=%s\n", + inserted->bps, + inserted->bps_rd, + inserted->bps_wr, + inserted->bps_max, + inserted->bps_rd_max, + inserted->bps_wr_max, + inserted->iops, + inserted->iops_rd, + inserted->iops_wr, + inserted->iops_max, + inserted->iops_rd_max, + inserted->iops_wr_max, + inserted->iops_size, + inserted->group); + } + + if (verbose) { + monitor_printf(mon, "\nImages:\n"); + image_info = inserted->image; + while (1) { + bdrv_image_info_dump(image_info); + if (image_info->has_backing_image) { + image_info = image_info->backing_image; + } else { + break; + } + } + } +} + +void hmp_info_block(Monitor *mon, const QDict *qdict) +{ + BlockInfoList *block_list, *info; + BlockDeviceInfoList *blockdev_list, *blockdev; + const char *device = qdict_get_try_str(qdict, "device"); + bool verbose = qdict_get_try_bool(qdict, "verbose", false); + bool nodes = qdict_get_try_bool(qdict, "nodes", false); + bool printed = false; + + /* Print BlockBackend information */ + if (!nodes) { + block_list = qmp_query_block(NULL); + } else { + block_list = NULL; + } + + for (info = block_list; info; info = info->next) { + if (device && strcmp(device, info->value->device)) { + continue; + } + + if (info != block_list) { + monitor_printf(mon, "\n"); + } + + print_block_info(mon, info->value, info->value->has_inserted + ? info->value->inserted : NULL, + verbose); + printed = true; + } + + qapi_free_BlockInfoList(block_list); + + if ((!device && !nodes) || printed) { + return; + } + + /* Print node information */ + blockdev_list = qmp_query_named_block_nodes(false, false, NULL); + for (blockdev = blockdev_list; blockdev; blockdev = blockdev->next) { + assert(blockdev->value->has_node_name); + if (device && strcmp(device, blockdev->value->node_name)) { + continue; + } + + if (blockdev != blockdev_list) { + monitor_printf(mon, "\n"); + } + + print_block_info(mon, NULL, blockdev->value, verbose); + } + qapi_free_BlockDeviceInfoList(blockdev_list); +} + +void hmp_info_blockstats(Monitor *mon, const QDict *qdict) +{ + BlockStatsList *stats_list, *stats; + + stats_list = qmp_query_blockstats(false, false, NULL); + + for (stats = stats_list; stats; stats = stats->next) { + if (!stats->value->has_device) { + continue; + } + + monitor_printf(mon, "%s:", stats->value->device); + monitor_printf(mon, " rd_bytes=%" PRId64 + " wr_bytes=%" PRId64 + " rd_operations=%" PRId64 + " wr_operations=%" PRId64 + " flush_operations=%" PRId64 + " wr_total_time_ns=%" PRId64 + " rd_total_time_ns=%" PRId64 + " flush_total_time_ns=%" PRId64 + " rd_merged=%" PRId64 + " wr_merged=%" PRId64 + " idle_time_ns=%" PRId64 + "\n", + stats->value->stats->rd_bytes, + stats->value->stats->wr_bytes, + stats->value->stats->rd_operations, + stats->value->stats->wr_operations, + stats->value->stats->flush_operations, + stats->value->stats->wr_total_time_ns, + stats->value->stats->rd_total_time_ns, + stats->value->stats->flush_total_time_ns, + stats->value->stats->rd_merged, + stats->value->stats->wr_merged, + stats->value->stats->idle_time_ns); + } + + qapi_free_BlockStatsList(stats_list); +} + +void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) +{ + BlockJobInfoList *list; + + list = qmp_query_block_jobs(&error_abort); + + if (!list) { + monitor_printf(mon, "No active jobs\n"); + return; + } + + while (list) { + if (strcmp(list->value->type, "stream") == 0) { + monitor_printf(mon, "Streaming device %s: Completed %" PRId64 + " of %" PRId64 " bytes, speed limit %" PRId64 + " bytes/s\n", + list->value->device, + list->value->offset, + list->value->len, + list->value->speed); + } else { + monitor_printf(mon, "Type %s, device %s: Completed %" PRId64 + " of %" PRId64 " bytes, speed limit %" PRId64 + " bytes/s\n", + list->value->type, + list->value->device, + list->value->offset, + list->value->len, + list->value->speed); + } + list = list->next; + } + + qapi_free_BlockJobInfoList(list); +} + +void hmp_info_snapshots(Monitor *mon, const QDict *qdict) +{ + BlockDriverState *bs, *bs1; + BdrvNextIterator it1; + QEMUSnapshotInfo *sn_tab, *sn; + bool no_snapshot = true; + int nb_sns, i; + int total; + int *global_snapshots; + AioContext *aio_context; + + typedef struct SnapshotEntry { + QEMUSnapshotInfo sn; + QTAILQ_ENTRY(SnapshotEntry) next; + } SnapshotEntry; + + typedef struct ImageEntry { + const char *imagename; + QTAILQ_ENTRY(ImageEntry) next; + QTAILQ_HEAD(, SnapshotEntry) snapshots; + } ImageEntry; + + QTAILQ_HEAD(, ImageEntry) image_list = + QTAILQ_HEAD_INITIALIZER(image_list); + + ImageEntry *image_entry, *next_ie; + SnapshotEntry *snapshot_entry; + + bs = bdrv_all_find_vmstate_bs(); + if (!bs) { + monitor_printf(mon, "No available block device supports snapshots\n"); + return; + } + aio_context = bdrv_get_aio_context(bs); + + aio_context_acquire(aio_context); + nb_sns = bdrv_snapshot_list(bs, &sn_tab); + aio_context_release(aio_context); + + if (nb_sns < 0) { + monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns); + return; + } + + for (bs1 = bdrv_first(&it1); bs1; bs1 = bdrv_next(&it1)) { + int bs1_nb_sns = 0; + ImageEntry *ie; + SnapshotEntry *se; + AioContext *ctx = bdrv_get_aio_context(bs1); + + aio_context_acquire(ctx); + if (bdrv_can_snapshot(bs1)) { + sn = NULL; + bs1_nb_sns = bdrv_snapshot_list(bs1, &sn); + if (bs1_nb_sns > 0) { + no_snapshot = false; + ie = g_new0(ImageEntry, 1); + ie->imagename = bdrv_get_device_name(bs1); + QTAILQ_INIT(&ie->snapshots); + QTAILQ_INSERT_TAIL(&image_list, ie, next); + for (i = 0; i < bs1_nb_sns; i++) { + se = g_new0(SnapshotEntry, 1); + se->sn = sn[i]; + QTAILQ_INSERT_TAIL(&ie->snapshots, se, next); + } + } + g_free(sn); + } + aio_context_release(ctx); + } + + if (no_snapshot) { + monitor_printf(mon, "There is no snapshot available.\n"); + return; + } + + global_snapshots = g_new0(int, nb_sns); + total = 0; + for (i = 0; i < nb_sns; i++) { + SnapshotEntry *next_sn; + if (bdrv_all_find_snapshot(sn_tab[i].name, &bs1) == 0) { + global_snapshots[total] = i; + total++; + QTAILQ_FOREACH(image_entry, &image_list, next) { + QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots, + next, next_sn) { + if (!strcmp(sn_tab[i].name, snapshot_entry->sn.name)) { + QTAILQ_REMOVE(&image_entry->snapshots, snapshot_entry, + next); + g_free(snapshot_entry); + } + } + } + } + } + monitor_printf(mon, "List of snapshots present on all disks:\n"); + + if (total > 0) { + bdrv_snapshot_dump(NULL); + monitor_printf(mon, "\n"); + for (i = 0; i < total; i++) { + sn = &sn_tab[global_snapshots[i]]; + /* + * The ID is not guaranteed to be the same on all images, so + * overwrite it. + */ + pstrcpy(sn->id_str, sizeof(sn->id_str), "--"); + bdrv_snapshot_dump(sn); + monitor_printf(mon, "\n"); + } + } else { + monitor_printf(mon, "None\n"); + } + + QTAILQ_FOREACH(image_entry, &image_list, next) { + if (QTAILQ_EMPTY(&image_entry->snapshots)) { + continue; + } + monitor_printf(mon, + "\nList of partial (non-loadable) snapshots on '%s':\n", + image_entry->imagename); + bdrv_snapshot_dump(NULL); + monitor_printf(mon, "\n"); + QTAILQ_FOREACH(snapshot_entry, &image_entry->snapshots, next) { + bdrv_snapshot_dump(&snapshot_entry->sn); + monitor_printf(mon, "\n"); + } + } + + QTAILQ_FOREACH_SAFE(image_entry, &image_list, next, next_ie) { + SnapshotEntry *next_sn; + QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots, next, + next_sn) { + g_free(snapshot_entry); + } + g_free(image_entry); + } + g_free(sn_tab); + g_free(global_snapshots); +} diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 8cccc2c9f3..cb06954b4a 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -1288,7 +1288,6 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); const char *bm_name = bdrv_dirty_bitmap_name(bitmap); uint8_t *buf = NULL; - BdrvDirtyBitmapIter *dbi; uint64_t *tb; uint64_t tb_size = size_to_clusters(s, @@ -1307,12 +1306,14 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, return NULL; } - dbi = bdrv_dirty_iter_new(bitmap); buf = g_malloc(s->cluster_size); limit = bytes_covered_by_bitmap_cluster(s, bitmap); assert(DIV_ROUND_UP(bm_size, limit) == tb_size); - while ((offset = bdrv_dirty_iter_next(dbi)) >= 0) { + offset = 0; + while ((offset = bdrv_dirty_bitmap_next_dirty(bitmap, offset, INT64_MAX)) + >= 0) + { uint64_t cluster = offset / limit; uint64_t end, write_size; int64_t off; @@ -1355,23 +1356,17 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, goto fail; } - if (end >= bm_size) { - break; - } - - bdrv_set_dirty_iter(dbi, end); + offset = end; } *bitmap_table_size = tb_size; g_free(buf); - bdrv_dirty_iter_free(dbi); return tb; fail: clear_bitmap_table(bs, tb, tb_size); g_free(buf); - bdrv_dirty_iter_free(dbi); g_free(tb); return NULL; diff --git a/block/qcow2-threads.c b/block/qcow2-threads.c index 77bb578cdf..a68126f291 100644 --- a/block/qcow2-threads.c +++ b/block/qcow2-threads.c @@ -128,12 +128,12 @@ static ssize_t qcow2_compress(void *dest, size_t dest_size, * @src - source buffer, @src_size bytes * * Returns: 0 on success - * -1 on fail + * -EIO on fail */ static ssize_t qcow2_decompress(void *dest, size_t dest_size, const void *src, size_t src_size) { - int ret = 0; + int ret; z_stream strm; memset(&strm, 0, sizeof(strm)); @@ -144,17 +144,19 @@ static ssize_t qcow2_decompress(void *dest, size_t dest_size, ret = inflateInit2(&strm, -12); if (ret != Z_OK) { - return -1; + return -EIO; } ret = inflate(&strm, Z_FINISH); - if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || strm.avail_out != 0) { + if ((ret == Z_STREAM_END || ret == Z_BUF_ERROR) && strm.avail_out == 0) { /* * We approve Z_BUF_ERROR because we need @dest buffer to be filled, but * @src buffer may be processed partly (because in qcow2 we know size of * compressed data with precision of one sector) */ - ret = -1; + ret = 0; + } else { + ret = -EIO; } inflateEnd(&strm); diff --git a/block/qcow2.c b/block/qcow2.c index 3640e8c07d..d44b45633d 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2610,6 +2610,7 @@ static void qcow2_close(BlockDriverState *bs) qcrypto_block_free(s->crypto); s->crypto = NULL; + qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); @@ -4608,60 +4609,6 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) return ret; } -static ssize_t qcow2_measure_crypto_hdr_init_func(QCryptoBlock *block, - size_t headerlen, void *opaque, Error **errp) -{ - size_t *headerlenp = opaque; - - /* Stash away the payload size */ - *headerlenp = headerlen; - return 0; -} - -static ssize_t qcow2_measure_crypto_hdr_write_func(QCryptoBlock *block, - size_t offset, const uint8_t *buf, size_t buflen, - void *opaque, Error **errp) -{ - /* Discard the bytes, we're not actually writing to an image */ - return buflen; -} - -/* Determine the number of bytes for the LUKS payload */ -static bool qcow2_measure_luks_headerlen(QemuOpts *opts, size_t *len, - Error **errp) -{ - QDict *opts_qdict; - QDict *cryptoopts_qdict; - QCryptoBlockCreateOptions *cryptoopts; - QCryptoBlock *crypto; - - /* Extract "encrypt." options into a qdict */ - opts_qdict = qemu_opts_to_qdict(opts, NULL); - qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt."); - qobject_unref(opts_qdict); - - /* Build QCryptoBlockCreateOptions object from qdict */ - qdict_put_str(cryptoopts_qdict, "format", "luks"); - cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp); - qobject_unref(cryptoopts_qdict); - if (!cryptoopts) { - return false; - } - - /* Fake LUKS creation in order to determine the payload size */ - crypto = qcrypto_block_create(cryptoopts, "encrypt.", - qcow2_measure_crypto_hdr_init_func, - qcow2_measure_crypto_hdr_write_func, - len, errp); - qapi_free_QCryptoBlockCreateOptions(cryptoopts); - if (!crypto) { - return false; - } - - qcrypto_block_free(crypto); - return true; -} - static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, Error **errp) { @@ -4712,9 +4659,27 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, g_free(optstr); if (has_luks) { + g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL; + QDict *opts_qdict; + QDict *cryptoopts; size_t headerlen; - if (!qcow2_measure_luks_headerlen(opts, &headerlen, &local_err)) { + opts_qdict = qemu_opts_to_qdict(opts, NULL); + qdict_extract_subqdict(opts_qdict, &cryptoopts, "encrypt."); + qobject_unref(opts_qdict); + + qdict_put_str(cryptoopts, "format", "luks"); + + create_opts = block_crypto_create_opts_init(cryptoopts, errp); + qobject_unref(cryptoopts); + if (!create_opts) { + goto err; + } + + if (!qcrypto_block_calculate_payload_offset(create_opts, + "encrypt.", + &headerlen, + &local_err)) { goto err; } diff --git a/block/trace-events b/block/trace-events index 1a7329b736..29dff8881c 100644 --- a/block/trace-events +++ b/block/trace-events @@ -48,6 +48,7 @@ block_copy_process(void *bcs, int64_t start) "bcs %p start %"PRId64 block_copy_copy_range_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" block_copy_read_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" block_copy_write_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" +block_copy_write_zeroes_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" # ../blockdev.c qmp_block_job_cancel(void *job) "job %p" diff --git a/block/vmdk.c b/block/vmdk.c index 20e909d997..8466051bc9 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -187,7 +187,7 @@ typedef struct VmdkMetaData { typedef struct VmdkGrainMarker { uint64_t lba; uint32_t size; - uint8_t data[0]; + uint8_t data[]; } QEMU_PACKED VmdkGrainMarker; enum { diff --git a/blockdev.c b/blockdev.c index 3e44fa766b..fa8630cb41 100644 --- a/blockdev.c +++ b/blockdev.c @@ -64,9 +64,14 @@ #include "qemu/main-loop.h" #include "qemu/throttle-options.h" -static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = +QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); +void bdrv_set_monitor_owned(BlockDriverState *bs) +{ + QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list); +} + static const char *const if_name[IF_COUNT] = { [IF_NONE] = "none", [IF_IDE] = "ide", @@ -640,7 +645,7 @@ err_no_opts: } /* Takes the ownership of bs_opts */ -static BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp) +BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp) { int bdrv_flags = 0; @@ -1039,41 +1044,6 @@ static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) return bs; } -void hmp_commit(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - BlockBackend *blk; - int ret; - - if (!strcmp(device, "all")) { - ret = blk_commit_all(); - } else { - BlockDriverState *bs; - AioContext *aio_context; - - blk = blk_by_name(device); - if (!blk) { - error_report("Device '%s' not found", device); - return; - } - if (!blk_is_available(blk)) { - error_report("Device '%s' has no medium", device); - return; - } - - bs = blk_bs(blk); - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - - ret = bdrv_commit(bs); - - aio_context_release(aio_context); - } - if (ret < 0) { - error_report("'commit' error for '%s': %s", device, strerror(-ret)); - } -} - static void blockdev_do_action(TransactionAction *action, Error **errp) { TransactionActionList list; @@ -1500,8 +1470,7 @@ static void external_snapshot_prepare(BlkActionState *common, DO_UPCAST(ExternalSnapshotState, common, common); TransactionAction *action = common->action; AioContext *aio_context; - AioContext *old_context; - int ret; + uint64_t perm, shared; /* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar * purpose but a different set of parameters */ @@ -1616,16 +1585,17 @@ static void external_snapshot_prepare(BlkActionState *common, goto out; } - if (bdrv_has_blk(state->new_bs)) { + /* + * Allow attaching a backing file to an overlay that's already in use only + * if the parents don't assume that they are already seeing a valid image. + * (Specifically, allow it as a mirror target, which is write-only access.) + */ + bdrv_get_cumulative_perm(state->new_bs, &perm, &shared); + if (perm & BLK_PERM_CONSISTENT_READ) { error_setg(errp, "The overlay is already in use"); goto out; } - if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, - errp)) { - goto out; - } - if (state->new_bs->backing != NULL) { error_setg(errp, "The overlay already has a backing image"); goto out; @@ -1636,20 +1606,6 @@ static void external_snapshot_prepare(BlkActionState *common, goto out; } - /* Honor bdrv_try_set_aio_context() context acquisition requirements. */ - old_context = bdrv_get_aio_context(state->new_bs); - aio_context_release(aio_context); - aio_context_acquire(old_context); - - ret = bdrv_try_set_aio_context(state->new_bs, aio_context, errp); - - aio_context_release(old_context); - aio_context_acquire(aio_context); - - if (ret < 0) { - goto out; - } - /* This removes our old bs and adds the new bs. This is an operation that * can fail, so we need to do it in .prepare; undoing it for abort is * always possible. */ @@ -2747,66 +2703,6 @@ BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, return ret; } -void hmp_drive_del(Monitor *mon, const QDict *qdict) -{ - const char *id = qdict_get_str(qdict, "id"); - BlockBackend *blk; - BlockDriverState *bs; - AioContext *aio_context; - Error *local_err = NULL; - - bs = bdrv_find_node(id); - if (bs) { - qmp_blockdev_del(id, &local_err); - if (local_err) { - error_report_err(local_err); - } - return; - } - - blk = blk_by_name(id); - if (!blk) { - error_report("Device '%s' not found", id); - return; - } - - if (!blk_legacy_dinfo(blk)) { - error_report("Deleting device added with blockdev-add" - " is not supported"); - return; - } - - aio_context = blk_get_aio_context(blk); - aio_context_acquire(aio_context); - - bs = blk_bs(blk); - if (bs) { - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { - error_report_err(local_err); - aio_context_release(aio_context); - return; - } - - blk_remove_bs(blk); - } - - /* Make the BlockBackend and the attached BlockDriverState anonymous */ - monitor_remove_blk(blk); - - /* If this BlockBackend has a device attached to it, its refcount will be - * decremented when the device is removed; otherwise we have to do so here. - */ - if (blk_get_attached_dev(blk)) { - /* Further I/O must not pause the guest */ - blk_set_on_error(blk, BLOCKDEV_ON_ERROR_REPORT, - BLOCKDEV_ON_ERROR_REPORT); - } else { - blk_unref(blk); - } - - aio_context_release(aio_context); -} - void qmp_block_resize(bool has_device, const char *device, bool has_node_name, const char *node_name, int64_t size, Error **errp) @@ -3814,37 +3710,6 @@ out: aio_context_release(aio_context); } -void hmp_drive_add_node(Monitor *mon, const char *optstr) -{ - QemuOpts *opts; - QDict *qdict; - Error *local_err = NULL; - - opts = qemu_opts_parse_noisily(&qemu_drive_opts, optstr, false); - if (!opts) { - return; - } - - qdict = qemu_opts_to_qdict(opts, NULL); - - if (!qdict_get_try_str(qdict, "node-name")) { - qobject_unref(qdict); - error_report("'node-name' needs to be specified"); - goto out; - } - - BlockDriverState *bs = bds_tree_init(qdict, &local_err); - if (!bs) { - error_report_err(local_err); - goto out; - } - - QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list); - -out: - qemu_opts_del(opts); -} - void qmp_blockdev_add(BlockdevOptions *options, Error **errp) { BlockDriverState *bs; @@ -3874,7 +3739,7 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp) goto fail; } - QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list); + bdrv_set_monitor_owned(bs); fail: visit_free(v); diff --git a/blockjob.c b/blockjob.c index 5d63b1e89d..fc850312c1 100644 --- a/blockjob.c +++ b/blockjob.c @@ -299,8 +299,8 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) info->device = g_strdup(job->job.id); info->busy = atomic_read(&job->job.busy); info->paused = job->job.pause_count > 0; - info->offset = job->job.progress_current; - info->len = job->job.progress_total; + info->offset = job->job.progress.current; + info->len = job->job.progress.total; info->speed = job->speed; info->io_status = job->iostatus; info->ready = job_is_ready(&job->job), @@ -330,8 +330,8 @@ static void block_job_event_cancelled(Notifier *n, void *opaque) qapi_event_send_block_job_cancelled(job_type(&job->job), job->job.id, - job->job.progress_total, - job->job.progress_current, + job->job.progress.total, + job->job.progress.current, job->speed); } @@ -350,8 +350,8 @@ static void block_job_event_completed(Notifier *n, void *opaque) qapi_event_send_block_job_completed(job_type(&job->job), job->job.id, - job->job.progress_total, - job->job.progress_current, + job->job.progress.total, + job->job.progress.current, job->speed, !!msg, msg); @@ -379,8 +379,8 @@ static void block_job_event_ready(Notifier *n, void *opaque) qapi_event_send_block_job_ready(job_type(&job->job), job->job.id, - job->job.progress_total, - job->job.progress_current, + job->job.progress.total, + job->job.progress.current, job->speed); } diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 09e8aed9c7..f8bb1e5459 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -95,7 +95,7 @@ typedef struct TaskState { struct sigqueue *first_free; /* first free siginfo queue entry */ int signal_pending; /* non zero if a signal may be pending */ - uint8_t stack[0]; + uint8_t stack[]; } __attribute__((aligned(16))) TaskState; void init_task_state(TaskState *ts); @@ -303,6 +303,7 @@ libs_qga="" debug_info="yes" stack_protector="" use_containers="yes" +gdb_bin=$(command -v "gdb") if test -e "$source_path/.git" then @@ -405,6 +406,7 @@ EXESUF="" DSOSUF=".so" LDFLAGS_SHARED="-shared" modules="no" +module_upgrades="no" prefix="/usr/local" mandir="\${prefix}/share/man" datadir="\${prefix}/share" @@ -899,7 +901,7 @@ Linux) linux="yes" linux_user="yes" kvm="yes" - QEMU_INCLUDES="-I\$(SRC_PATH)/linux-headers -I$PWD/linux-headers $QEMU_INCLUDES" + QEMU_INCLUDES="-isystem \$(SRC_PATH)/linux-headers -isystem $PWD/linux-headers $QEMU_INCLUDES" supported_os="yes" libudev="yes" ;; @@ -1032,6 +1034,10 @@ for opt do --disable-modules) modules="no" ;; + --disable-module-upgrades) module_upgrades="no" + ;; + --enable-module-upgrades) module_upgrades="yes" + ;; --cpu=*) ;; --target-list=*) target_list="$optarg" @@ -1421,6 +1427,11 @@ for opt do ;; --enable-avx2) avx2_opt="yes" ;; + --disable-avx512f) avx512f_opt="no" + ;; + --enable-avx512f) avx512f_opt="yes" + ;; + --enable-glusterfs) glusterfs="yes" ;; --disable-virtio-blk-data-plane|--enable-virtio-blk-data-plane) @@ -1588,6 +1599,8 @@ for opt do ;; --disable-fuzzing) fuzzing=no ;; + --gdb=*) gdb_bin="$optarg" + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -1773,6 +1786,7 @@ Advanced options (experts only): --enable-plugins enable plugins via shared library loading --disable-containers don't use containers for cross-building + --gdb=GDB-path gdb to use for gdbstub tests [$gdb_bin] Optional features, enabled with --enable-FEATURE and disabled with --disable-FEATURE, default is enabled if available: @@ -1786,6 +1800,7 @@ disabled with --disable-FEATURE, default is enabled if available: guest-agent-msi build guest agent Windows MSI installation package pie Position Independent Executables modules modules support (non-Windows) + module-upgrades try to load modules from alternate paths for upgrades debug-tcg TCG debugging (default is disabled) debug-info debugging information sparse sparse checker @@ -1857,6 +1872,7 @@ disabled with --disable-FEATURE, default is enabled if available: tcmalloc tcmalloc support jemalloc jemalloc support avx2 AVX2 optimization support + avx512f AVX512F optimization support replication replication support opengl opengl support virglrenderer virgl rendering support @@ -2061,6 +2077,11 @@ if test "$modules" = "yes" && test "$mingw32" = "yes" ; then error_exit "Modules are not available for Windows" fi +# module_upgrades is only reasonable if modules are enabled +if test "$modules" = "no" && test "$module_upgrades" = "yes" ; then + error_exit "Can't enable module-upgrades as Modules are not enabled" +fi + # Static linking is not possible with modules or PIE if test "$static" = "yes" ; then if test "$modules" = "yes" ; then @@ -2487,7 +2508,8 @@ fi # zstd check if test "$zstd" != "no" ; then - if $pkg_config --exist libzstd ; then + libzstd_minver="1.4.0" + if $pkg_config --atleast-version=$libzstd_minver libzstd ; then zstd_cflags="$($pkg_config --cflags libzstd)" zstd_libs="$($pkg_config --libs libzstd)" LIBS="$zstd_libs $LIBS" @@ -3361,7 +3383,9 @@ if test "$vnc" = "yes" && test "$vnc_sasl" != "no" ; then int main(void) { sasl_server_init(NULL, "qemu"); return 0; } EOF # Assuming Cyrus-SASL installed in /usr prefix - vnc_sasl_cflags="" + # QEMU defines struct iovec in "qemu/osdep.h", + # we don't want libsasl to redefine it in <sasl/sasl.h>. + vnc_sasl_cflags="-DSTRUCT_IOVEC_DEFINED" vnc_sasl_libs="-lsasl2" if compile_prog "$vnc_sasl_cflags" "$vnc_sasl_libs" ; then vnc_sasl=yes @@ -3843,6 +3867,26 @@ if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then fi fi +# Silence clang warnings triggered by glib < 2.57.2 +cat > $TMPC << EOF +#include <glib.h> +typedef struct Foo { + int i; +} Foo; +static void foo_free(Foo *f) +{ + g_free(f); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC(Foo, foo_free); +int main(void) { return 0; } +EOF +if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then + if cc_has_warning_flag "-Wno-unused-function"; then + glib_cflags="$glib_cflags -Wno-unused-function" + CFLAGS="$CFLAGS -Wno-unused-function" + fi +fi + ######################################### # zlib check @@ -4105,6 +4149,11 @@ if test "$linux_io_uring" != "no" ; then linux_io_uring_cflags=$($pkg_config --cflags liburing) linux_io_uring_libs=$($pkg_config --libs liburing) linux_io_uring=yes + + # io_uring is used in libqemuutil.a where per-file -libs variables are not + # seen by programs linking the archive. It's not ideal, but just add the + # library dependency globally. + LIBS="$linux_io_uring_libs $LIBS" else if test "$linux_io_uring" = "yes" ; then feature_not_found "linux io_uring" "Install liburing devel" @@ -4190,7 +4239,7 @@ fi fdt_required=no for target in $target_list; do case $target in - aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu|riscv*-softmmu) + aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu|riscv*-softmmu|rx-softmmu) fdt_required=yes ;; esac @@ -5580,6 +5629,36 @@ EOF fi fi +########################################## +# avx512f optimization requirement check +# +# There is no point enabling this if cpuid.h is not usable, +# since we won't be able to select the new routines. +# by default, it is turned off. +# if user explicitly want to enable it, check environment + +if test "$cpuid_h" = "yes" && test "$avx512f_opt" = "yes"; then + cat > $TMPC << EOF +#pragma GCC push_options +#pragma GCC target("avx512f") +#include <cpuid.h> +#include <immintrin.h> +static int bar(void *a) { + __m512i x = *(__m512i *)a; + return _mm512_test_epi64_mask(x, x); +} +int main(int argc, char *argv[]) +{ + return bar(argv[0]); +} +EOF + if ! compile_object "" ; then + avx512f_opt="no" + fi +else + avx512f_opt="no" +fi + ######################################## # check if __[u]int128_t is usable. @@ -5718,58 +5797,6 @@ if test "$plugins" = "yes" && fi ######################################## -# See if 16-byte vector operations are supported. -# Even without a vector unit the compiler may expand these. -# There is a bug in old GCC for PPC that crashes here. -# Unfortunately it's the system compiler for Centos 7. - -cat > $TMPC << EOF -typedef unsigned char U1 __attribute__((vector_size(16))); -typedef unsigned short U2 __attribute__((vector_size(16))); -typedef unsigned int U4 __attribute__((vector_size(16))); -typedef unsigned long long U8 __attribute__((vector_size(16))); -typedef signed char S1 __attribute__((vector_size(16))); -typedef signed short S2 __attribute__((vector_size(16))); -typedef signed int S4 __attribute__((vector_size(16))); -typedef signed long long S8 __attribute__((vector_size(16))); -static U1 a1, b1; -static U2 a2, b2; -static U4 a4, b4; -static U8 a8, b8; -static S1 c1; -static S2 c2; -static S4 c4; -static S8 c8; -static int i; -void helper(void *d, void *a, int shift, int i); -void helper(void *d, void *a, int shift, int i) -{ - *(U1 *)(d + i) = *(U1 *)(a + i) << shift; - *(U2 *)(d + i) = *(U2 *)(a + i) << shift; - *(U4 *)(d + i) = *(U4 *)(a + i) << shift; - *(U8 *)(d + i) = *(U8 *)(a + i) << shift; -} -int main(void) -{ - a1 += b1; a2 += b2; a4 += b4; a8 += b8; - a1 -= b1; a2 -= b2; a4 -= b4; a8 -= b8; - a1 *= b1; a2 *= b2; a4 *= b4; a8 *= b8; - a1 &= b1; a2 &= b2; a4 &= b4; a8 &= b8; - a1 |= b1; a2 |= b2; a4 |= b4; a8 |= b8; - a1 ^= b1; a2 ^= b2; a4 ^= b4; a8 ^= b8; - a1 <<= i; a2 <<= i; a4 <<= i; a8 <<= i; - a1 >>= i; a2 >>= i; a4 >>= i; a8 >>= i; - c1 >>= i; c2 >>= i; c4 >>= i; c8 >>= i; - return 0; -} -EOF - -vector16=no -if compile_prog "" "" ; then - vector16=yes -fi - -######################################## # See if __attribute__((alias)) is supported. # This false for Xcode 9, but has been remedied for Xcode 10. # Unfortunately, travis uses Xcode 9 by default. @@ -6596,6 +6623,7 @@ if test "$slirp" != "no" ; then echo "smbd $smbd" fi echo "module support $modules" +echo "alt path mod load $module_upgrades" echo "host CPU $cpu" echo "host big endian $bigendian" echo "target list $target_list" @@ -6723,6 +6751,7 @@ echo "libxml2 $libxml2" echo "tcmalloc support $tcmalloc" echo "jemalloc support $jemalloc" echo "avx2 optimization $avx2_opt" +echo "avx512f optimization $avx512f_opt" echo "replication support $replication" echo "VxHS block device $vxhs" echo "bochs support $bochs" @@ -6740,6 +6769,7 @@ echo "libudev $libudev" echo "default devices $default_devices" echo "plugin support $plugins" echo "fuzzing support $fuzzing" +echo "gdb $gdb_bin" if test "$supported_cpu" = "no"; then echo @@ -6949,6 +6979,9 @@ if test "$modules" = "yes"; then echo "CONFIG_STAMP=_$( (echo $qemu_version; echo $pkgversion; cat $0) | $shacmd - | cut -f1 -d\ )" >> $config_host_mak echo "CONFIG_MODULES=y" >> $config_host_mak fi +if test "$module_upgrades" = "yes"; then + echo "CONFIG_MODULE_UPGRADES=y" >> $config_host_mak +fi if test "$have_x11" = "yes" && test "$need_x11" = "yes"; then echo "CONFIG_X11=y" >> $config_host_mak echo "X11_CFLAGS=$x11_cflags" >> $config_host_mak @@ -7274,6 +7307,10 @@ if test "$avx2_opt" = "yes" ; then echo "CONFIG_AVX2_OPT=y" >> $config_host_mak fi +if test "$avx512f_opt" = "yes" ; then + echo "CONFIG_AVX512F_OPT=y" >> $config_host_mak +fi + if test "$lzo" = "yes" ; then echo "CONFIG_LZO=y" >> $config_host_mak fi @@ -7389,10 +7426,6 @@ if test "$atomic64" = "yes" ; then echo "CONFIG_ATOMIC64=y" >> $config_host_mak fi -if test "$vector16" = "yes" ; then - echo "CONFIG_VECTOR16=y" >> $config_host_mak -fi - if test "$attralias" = "yes" ; then echo "CONFIG_ATTRIBUTE_ALIAS=y" >> $config_host_mak fi @@ -7614,6 +7647,10 @@ if test "$plugins" = "yes" ; then fi fi +if test -n "$gdb_bin" ; then + echo "HAVE_GDB_BIN=$gdb_bin" >> $config_host_mak +fi + if test "$tcg_interpreter" = "yes"; then QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES" elif test "$ARCH" = "sparc64" ; then @@ -7901,6 +7938,12 @@ case "$target_name" in mttcg=yes gdb_xml_files="riscv-64bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-64bit-csr.xml riscv-64bit-virtual.xml" ;; + rx) + TARGET_ARCH=rx + bflt="yes" + target_compiler=$cross_cc_rx + gdb_xml_files="rx-core.xml" + ;; sh4|sh4eb) TARGET_ARCH=sh4 TARGET_SYSTBL_ABI=common @@ -8091,6 +8134,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do riscv*) disas_config "RISCV" ;; + rx) + disas_config "RX" + ;; s390*) disas_config "S390" ;; diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h index 6fc8000e99..f30394fab6 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -286,7 +286,7 @@ typedef struct VuVirtqInflight { uint16_t used_idx; /* Used to track the state of each descriptor in descriptor table */ - VuDescStateSplit desc[0]; + VuDescStateSplit desc[]; } VuVirtqInflight; typedef struct VuVirtqInflightDesc { diff --git a/contrib/vhost-user-gpu/Makefile.objs b/contrib/vhost-user-gpu/Makefile.objs index 6170c919e4..09296091be 100644 --- a/contrib/vhost-user-gpu/Makefile.objs +++ b/contrib/vhost-user-gpu/Makefile.objs @@ -1,7 +1,7 @@ -vhost-user-gpu-obj-y = main.o virgl.o vugbm.o +vhost-user-gpu-obj-y = vhost-user-gpu.o virgl.o vugbm.o -main.o-cflags := $(PIXMAN_CFLAGS) $(GBM_CFLAGS) -main.o-libs := $(PIXMAN_LIBS) +vhost-user-gpu.o-cflags := $(PIXMAN_CFLAGS) $(GBM_CFLAGS) +vhost-user-gpu.o-libs := $(PIXMAN_LIBS) virgl.o-cflags := $(VIRGL_CFLAGS) $(GBM_CFLAGS) virgl.o-libs := $(VIRGL_LIBS) diff --git a/contrib/vhost-user-gpu/main.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index b45d2019b4..b45d2019b4 100644 --- a/contrib/vhost-user-gpu/main.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -1026,9 +1026,9 @@ static int do_vm_stop(RunState state, bool send_stop) int ret = 0; if (runstate_is_running()) { + runstate_set(state); cpu_disable_ticks(); pause_all_vcpus(); - runstate_set(state); vm_state_notify(0, state); if (send_stop) { qapi_event_send_stop(); @@ -1899,6 +1899,10 @@ void resume_all_vcpus(void) { CPUState *cpu; + if (!runstate_is_running()) { + return; + } + qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true); CPU_FOREACH(cpu) { cpu_resume(cpu); diff --git a/crypto/block.c b/crypto/block.c index 325752871c..6f42b32f1e 100644 --- a/crypto/block.c +++ b/crypto/block.c @@ -115,6 +115,42 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, } +static ssize_t qcrypto_block_headerlen_hdr_init_func(QCryptoBlock *block, + size_t headerlen, void *opaque, Error **errp) +{ + size_t *headerlenp = opaque; + + /* Stash away the payload size */ + *headerlenp = headerlen; + return 0; +} + + +static ssize_t qcrypto_block_headerlen_hdr_write_func(QCryptoBlock *block, + size_t offset, const uint8_t *buf, size_t buflen, + void *opaque, Error **errp) +{ + /* Discard the bytes, we're not actually writing to an image */ + return buflen; +} + + +bool +qcrypto_block_calculate_payload_offset(QCryptoBlockCreateOptions *create_opts, + const char *optprefix, + size_t *len, + Error **errp) +{ + /* Fake LUKS creation in order to determine the payload size */ + g_autoptr(QCryptoBlock) crypto = + qcrypto_block_create(create_opts, optprefix, + qcrypto_block_headerlen_hdr_init_func, + qcrypto_block_headerlen_hdr_write_func, + len, errp); + return crypto != NULL; +} + + QCryptoBlockInfo *qcrypto_block_get_info(QCryptoBlock *block, Error **errp) { diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 645e6201bb..36a0e89daa 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -41,3 +41,4 @@ CONFIG_FSL_IMX25=y CONFIG_FSL_IMX7=y CONFIG_FSL_IMX6UL=y CONFIG_SEMIHOSTING=y +CONFIG_ALLWINNER_H3=y diff --git a/default-configs/rx-softmmu.mak b/default-configs/rx-softmmu.mak new file mode 100644 index 0000000000..7c4eb2c1a0 --- /dev/null +++ b/default-configs/rx-softmmu.mak @@ -0,0 +1,2 @@ +# Default configuration for rx-softmmu + diff --git a/device-hotplug.c b/device-hotplug.c deleted file mode 100644 index f01d53774b..0000000000 --- a/device-hotplug.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * QEMU device hotplug helpers - * - * Copyright (c) 2004 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/boards.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" -#include "qapi/qmp/qdict.h" -#include "qapi/error.h" -#include "qemu/config-file.h" -#include "qemu/option.h" -#include "sysemu/sysemu.h" -#include "monitor/monitor.h" -#include "block/block_int.h" - -static DriveInfo *add_init_drive(const char *optstr) -{ - Error *err = NULL; - DriveInfo *dinfo; - QemuOpts *opts; - MachineClass *mc; - - opts = drive_def(optstr); - if (!opts) - return NULL; - - mc = MACHINE_GET_CLASS(current_machine); - dinfo = drive_new(opts, mc->block_default_type, &err); - if (err) { - error_report_err(err); - qemu_opts_del(opts); - return NULL; - } - - return dinfo; -} - -void hmp_drive_add(Monitor *mon, const QDict *qdict) -{ - DriveInfo *dinfo = NULL; - const char *opts = qdict_get_str(qdict, "opts"); - bool node = qdict_get_try_bool(qdict, "node", false); - - if (node) { - hmp_drive_add_node(mon, opts); - return; - } - - dinfo = add_init_drive(opts); - if (!dinfo) { - goto err; - } - - switch (dinfo->type) { - case IF_NONE: - monitor_printf(mon, "OK\n"); - break; - default: - monitor_printf(mon, "Can't hot-add drive to type %d\n", dinfo->type); - goto err; - } - return; - -err: - if (dinfo) { - BlockBackend *blk = blk_by_legacy_dinfo(dinfo); - monitor_remove_blk(blk); - blk_unref(blk); - } -} diff --git a/docs/can.txt b/docs/can.txt index 9fa6ed51c8..11ed8f2d68 100644 --- a/docs/can.txt +++ b/docs/can.txt @@ -13,7 +13,7 @@ controller is implemented. The PCI addon card hardware has been selected as the first CAN interface to implement because such device can be easily connected -to systems with different CPU architectures (x86, PowerPC, ARM, etc.). +to systems with different CPU architectures (x86, PowerPC, Arm, etc.). The project has been initially started in frame of RTEMS GSoC 2013 slot by Jin Yang under our mentoring The initial idea was to provide generic diff --git a/docs/devel/atomics.txt b/docs/devel/atomics.txt index a4db3a4aaa..67bdf82628 100644 --- a/docs/devel/atomics.txt +++ b/docs/devel/atomics.txt @@ -87,7 +87,7 @@ Sequentially consistent loads and stores can be done using: atomic_xchg(ptr, val) for stores However, they are quite expensive on some platforms, notably POWER and -ARM. Therefore, qemu/atomic.h provides two primitives with slightly +Arm. Therefore, qemu/atomic.h provides two primitives with slightly weaker constraints: typeof(*ptr) atomic_mb_read(ptr) diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst index b7bca44704..e5df72b342 100644 --- a/docs/devel/kconfig.rst +++ b/docs/devel/kconfig.rst @@ -8,7 +8,7 @@ time different targets can share large amounts of code. For example, a POWER and an x86 board can run the same code to emulate a PCI network card, even though the boards use different PCI host bridges, and they can run the same code to emulate a SCSI disk while using different -SCSI adapters. ARM, s390 and x86 boards can all present a virtio-blk +SCSI adapters. Arm, s390 and x86 boards can all present a virtio-blk disk to their guests, but with three different virtio guest interfaces. Each QEMU target enables a subset of the boards, devices and buses that diff --git a/docs/devel/loads-stores.rst b/docs/devel/loads-stores.rst index 03aa9e7ff8..0d99eb24c1 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -302,7 +302,7 @@ way QEMU defines the view of memory that a device or CPU has. or bus fabric.) Each CPU has an AddressSpace. Some kinds of CPU have more than -one AddressSpace (for instance ARM guest CPUs have an AddressSpace +one AddressSpace (for instance Arm guest CPUs have an AddressSpace for the Secure world and one for NonSecure if they implement TrustZone). Devices which can do DMA-type operations should generally have an AddressSpace. There is also a "system address space" which typically diff --git a/docs/devel/multi-thread-tcg.txt b/docs/devel/multi-thread-tcg.txt index 782bebc28b..3c85ac0eab 100644 --- a/docs/devel/multi-thread-tcg.txt +++ b/docs/devel/multi-thread-tcg.txt @@ -227,7 +227,7 @@ minimise contention. (Current solution) MMIO access automatically serialises hardware emulation by way of the -BQL. Currently ARM targets serialise all ARM_CP_IO register accesses +BQL. Currently Arm targets serialise all ARM_CP_IO register accesses and also defer the reset/startup of vCPUs to the vCPU context by way of async_run_on_cpu(). @@ -268,7 +268,7 @@ ordered backends this could become a NOP. Aside from explicit standalone memory barrier instructions there are also implicit memory ordering semantics which comes with each guest memory access instruction. For example all x86 load/stores come with -fairly strong guarantees of sequential consistency where as ARM has +fairly strong guarantees of sequential consistency whereas Arm has special variants of load/store instructions that imply acquire/release semantics. @@ -317,7 +317,7 @@ x86 cmpxchg instruction. The second type offer a pair of load/store instructions which offer a guarantee that a region of memory has not been touched between the -load and store instructions. An example of this is ARM's ldrex/strex +load and store instructions. An example of this is Arm's ldrex/strex pair where the strex instruction will return a flag indicating a successful store only if no other CPU has accessed the memory region since the ldrex. @@ -339,7 +339,7 @@ CURRENT OPEN QUESTIONS: The TCG provides a number of atomic helpers (tcg_gen_atomic_*) which can be used directly or combined to emulate other instructions like -ARM's ldrex/strex instructions. While they are susceptible to the ABA +Arm's ldrex/strex instructions. While they are susceptible to the ABA problem so far common guests have not implemented patterns where this may be a problem - typically presenting a locking ABI which assumes cmpxchg like semantics. diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 59d6973e1e..1967adfa92 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -172,7 +172,8 @@ Syntax: ENUM = { 'enum': STRING, 'data': [ ENUM-VALUE, ... ], '*prefix': STRING, - '*if': COND } + '*if': COND, + '*features': FEATURES } ENUM-VALUE = STRING | { 'name': STRING, '*if': COND } @@ -207,6 +208,9 @@ the job satisfactorily. The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Type references and array types === @@ -230,7 +234,9 @@ Syntax: '*features': FEATURES } MEMBERS = { MEMBER, ... } MEMBER = STRING : TYPE-REF - | STRING : { 'type': TYPE-REF, '*if': COND } + | STRING : { 'type': TYPE-REF, + '*if': COND, + '*features': FEATURES } Member 'struct' names the struct type. @@ -279,12 +285,14 @@ below for more on this. Syntax: UNION = { 'union': STRING, 'data': BRANCHES, - '*if': COND } + '*if': COND, + '*features': FEATURES } | { 'union': STRING, 'data': BRANCHES, 'base': ( MEMBERS | STRING ), 'discriminator': STRING, - '*if': COND } + '*if': COND, + '*features': FEATURES } BRANCHES = { BRANCH, ... } BRANCH = STRING : TYPE-REF | STRING : { 'type': TYPE-REF, '*if': COND } @@ -391,15 +399,19 @@ is identical on the wire to: The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Alternate types === Syntax: ALTERNATE = { 'alternate': STRING, 'data': ALTERNATIVES, - '*if': COND } + '*if': COND, + '*features': FEATURES } ALTERNATIVES = { ALTERNATIVE, ... } - ALTERNATIVE = STRING : TYPE-REF + ALTERNATIVE = STRING : STRING | STRING : { 'type': STRING, '*if': COND } Member 'alternate' names the alternate type. @@ -441,6 +453,9 @@ following example objects: The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Commands === @@ -584,6 +599,9 @@ started with --preconfig. The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Events === @@ -595,7 +613,8 @@ Syntax: 'data': STRING, 'boxed': true, ) - '*if': COND } + '*if': COND, + '*features': FEATURES } Member 'event' names the event. This is the event name used in the Client JSON Protocol. @@ -628,6 +647,9 @@ complex type. See section "Code generated for events" for examples. The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. +The optional 'features' member specifies features. See "Features" +below for more on this. + === Features === @@ -642,13 +664,8 @@ that previously resulted in an error). QMP clients may still need to know whether the extension is available. For this purpose, a list of features can be specified for a command or -struct type. This is exposed to the client as a list of strings, -where each string signals that this build of QEMU shows a certain -behaviour. - -Each member of the 'features' array defines a feature. It can either -be { 'name': STRING, '*if': COND }, or STRING, which is shorthand for -{ 'name': STRING }. +struct type. Each list member can either be { 'name': STRING, '*if': +COND }, or STRING, which is shorthand for { 'name': STRING }. The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. @@ -659,6 +676,18 @@ Example: 'data': { 'number': 'int' }, 'features': [ 'allow-negative-numbers' ] } +The feature strings are exposed to clients in introspection, as +explained in section "Client JSON Protocol introspection". + +Intended use is to have each feature string signal that this build of +QEMU shows a certain behaviour. + + +==== Special features ==== + +Feature "deprecated" marks a command, event, or struct member as +deprecated. It is not supported elsewhere so far. + === Naming rules and reserved names === @@ -965,8 +994,9 @@ schema, along with the SchemaInfo type. This text attempts to give an overview how things work. For details you need to consult the QAPI schema. -SchemaInfo objects have common members "name" and "meta-type", and -additional variant members depending on the value of meta-type. +SchemaInfo objects have common members "name", "meta-type", +"features", and additional variant members depending on the value of +meta-type. Each SchemaInfo object describes a wire ABI entity of a certain meta-type: a command, event or one of several kinds of type. @@ -979,6 +1009,9 @@ not. Therefore, the SchemaInfo for types have auto-generated meaningless names. For readability, the examples in this section use meaningful type names instead. +Optional member "features" exposes the entity's feature strings as a +JSON array of strings. + To examine a type, start with a command or event using it, then follow references by name. @@ -988,9 +1021,9 @@ The SchemaInfo for a command has meta-type "command", and variant members "arg-type", "ret-type" and "allow-oob". On the wire, the "arguments" member of a client's "execute" command must conform to the object type named by "arg-type". The "return" member that the server -passes in a success response conforms to the type named by -"ret-type". When "allow-oob" is set, it means the command supports -out-of-band execution. +passes in a success response conforms to the type named by "ret-type". +When "allow-oob" is true, it means the command supports out-of-band +execution. It defaults to false. If the command takes no arguments, "arg-type" names an object type without members. Likewise, if the command returns nothing, "ret-type" @@ -1047,6 +1080,16 @@ Example: the SchemaInfo for MyType from section Struct types { "name": "member2", "type": "int" }, { "name": "member3", "type": "str", "default": null } ] } +"features" exposes the command's feature strings as a JSON array of +strings. + +Example: the SchemaInfo for TestType from section Features: + + { "name": "TestType", "meta-type": "object", + "members": [ + { "name": "number", "type": "int" } ], + "features": ["allow-negative-numbers"] } + "tag" is the name of the common member serving as type tag. "variants" is a JSON array describing the object's variant members. Each element is a JSON object with members "case" (the value of type diff --git a/docs/devel/tcg.rst b/docs/devel/tcg.rst index 4956a30a4e..4ebde44b9d 100644 --- a/docs/devel/tcg.rst +++ b/docs/devel/tcg.rst @@ -83,7 +83,7 @@ memory until the end of the translation block. This is done for internal emulation state that is rarely accessed directly by the program and/or changes very often throughout the execution of a translation block---this includes condition codes on x86, delay slots on SPARC, conditional execution on -ARM, and so on. This state is stored for each target instruction, and +Arm, and so on. This state is stored for each target instruction, and looked up on exceptions. MMU emulation diff --git a/docs/index.html.in b/docs/index.html.in index cc19aad2ec..e9a160384c 100644 --- a/docs/index.html.in +++ b/docs/index.html.in @@ -7,13 +7,13 @@ <body> <h1>QEMU @@VERSION@@ Documentation</h1> <ul> - <li><a href="qemu-qmp-ref.html">QMP Reference Manual</a></li> - <li><a href="qemu-ga-ref.html">Guest Agent Protocol Reference</a></li> - <li><a href="interop/index.html">System Emulation Management and Interoperability Guide</a></li> - <li><a href="specs/index.html">System Emulation Guest Hardware Specifications</a></li> <li><a href="system/index.html">System Emulation User's Guide</a></li> - <li><a href="tools/index.html">Tools Guide</a></li> <li><a href="user/index.html">User Mode Emulation User's Guide</a></li> + <li><a href="tools/index.html">Tools Guide</a></li> + <li><a href="interop/index.html">System Emulation Management and Interoperability Guide</a></li> + <li><a href="specs/index.html">System Emulation Guest Hardware Specifications</a></li> + <li><a href="qemu-qmp-ref.html">QMP Reference Manual</a></li> + <li><a href="qemu-ga-ref.html">Guest Agent Protocol Reference</a></li> </ul> </body> </html> diff --git a/docs/index.rst b/docs/index.rst index 376dab2885..763e3d0426 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,9 +10,9 @@ Welcome to QEMU's documentation! :maxdepth: 2 :caption: Contents: - interop/index - devel/index - specs/index system/index - tools/index user/index + tools/index + interop/index + specs/index + devel/index diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst index 401652397c..3b1b6602c7 100644 --- a/docs/interop/vhost-user.rst +++ b/docs/interop/vhost-user.rst @@ -568,7 +568,7 @@ For split virtqueue, queue region can be implemented as: uint16_t used_idx; /* Used to track the state of each descriptor in descriptor table */ - DescStateSplit desc[0]; + DescStateSplit desc[]; } QueueRegionSplit; To track inflight I/O, the queue region should be processed as follows: @@ -690,7 +690,7 @@ For packed virtqueue, queue region can be implemented as: uint8_t padding[7]; /* Used to track the state of each descriptor fetched from descriptor ring */ - DescStatePacked desc[0]; + DescStatePacked desc[]; } QueueRegionPacked; To track inflight I/O, the queue region should be processed as follows: diff --git a/docs/qemu-option-trace.rst.inc b/docs/qemu-option-trace.rst.inc index 23cfcb4853..7e09773a9c 100644 --- a/docs/qemu-option-trace.rst.inc +++ b/docs/qemu-option-trace.rst.inc @@ -1,7 +1,3 @@ -.. - The contents of this file must be kept in sync with qemu-option-trace.texi - until all the users of the texi file have been converted to rst and - the texi file can be removed. Specify tracing options. diff --git a/docs/replay.txt b/docs/replay.txt index f4619a62a3..70c27edb36 100644 --- a/docs/replay.txt +++ b/docs/replay.txt @@ -19,7 +19,7 @@ Deterministic replay has the following features: the memory, state of the hardware devices, clocks, and screen of the VM. * Writes execution log into the file for later replaying for multiple times on different machines. - * Supports i386, x86_64, and ARM hardware platforms. + * Supports i386, x86_64, and Arm hardware platforms. * Performs deterministic replay of all operations with keyboard and mouse input devices. diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt index 08c00bdf44..8f1ebc66fa 100644 --- a/docs/specs/fw_cfg.txt +++ b/docs/specs/fw_cfg.txt @@ -82,7 +82,7 @@ Selector Register IOport: 0x510 Data Register IOport: 0x511 DMA Address IOport: 0x514 -=== ARM Register Locations === +=== Arm Register Locations === Selector Register address: Base + 8 (2 bytes) Data Register address: Base + 0 (8 bytes) diff --git a/docs/specs/tpm.rst b/docs/specs/tpm.rst index da9eb39ca9..5e61238bc5 100644 --- a/docs/specs/tpm.rst +++ b/docs/specs/tpm.rst @@ -25,7 +25,7 @@ QEMU files related to TPM TIS interface: Both an ISA device and a sysbus device are available. The former is used with pc/q35 machine while the latter can be instantiated in the -ARM virt machine. +Arm virt machine. CRB interface ------------- @@ -331,7 +331,7 @@ In case a pSeries machine is emulated, use the following command line: -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \ -drive file=test.img,format=raw,if=none,id=drive-virtio-disk0 -In case an ARM virt machine is emulated, use the following command line: +In case an Arm virt machine is emulated, use the following command line: .. code-block:: console @@ -346,7 +346,7 @@ In case an ARM virt machine is emulated, use the following command line: -drive if=pflash,format=raw,file=flash0.img,readonly \ -drive if=pflash,format=raw,file=flash1.img - On ARM, ACPI boot with TPM is not yet supported. + On Arm, ACPI boot with TPM is not yet supported. In case SeaBIOS is used as firmware, it should show the TPM menu item after entering the menu with 'ESC'. diff --git a/docs/sphinx/hxtool.py b/docs/sphinx/hxtool.py index 7dd223fe36..fb0649a3d5 100644 --- a/docs/sphinx/hxtool.py +++ b/docs/sphinx/hxtool.py @@ -37,13 +37,11 @@ else: __version__ = '1.0' -# We parse hx files with a state machine which may be in one of three -# states: reading the C code fragment, inside a texi fragment, -# or inside a rST fragment. +# We parse hx files with a state machine which may be in one of two +# states: reading the C code fragment, or inside a rST fragment. class HxState(Enum): CTEXT = 1 - TEXI = 2 - RST = 3 + RST = 2 def serror(file, lnum, errtext): """Raise an exception giving a user-friendly syntax error message""" @@ -110,31 +108,13 @@ class HxtoolDocDirective(Directive): if directive == 'HXCOMM': pass - elif directive == 'STEXI': - if state == HxState.RST: - serror(hxfile, lnum, 'expected ERST, found STEXI') - elif state == HxState.TEXI: - serror(hxfile, lnum, 'expected ETEXI, found STEXI') - else: - state = HxState.TEXI - elif directive == 'ETEXI': - if state == HxState.RST: - serror(hxfile, lnum, 'expected ERST, found ETEXI') - elif state == HxState.CTEXT: - serror(hxfile, lnum, 'expected STEXI, found ETEXI') - else: - state = HxState.CTEXT elif directive == 'SRST': if state == HxState.RST: serror(hxfile, lnum, 'expected ERST, found SRST') - elif state == HxState.TEXI: - serror(hxfile, lnum, 'expected ETEXI, found SRST') else: state = HxState.RST elif directive == 'ERST': - if state == HxState.TEXI: - serror(hxfile, lnum, 'expected ETEXI, found ERST') - elif state == HxState.CTEXT: + if state == HxState.CTEXT: serror(hxfile, lnum, 'expected SRST, found ERST') else: state = HxState.CTEXT diff --git a/docs/arm-cpu-features.rst b/docs/system/arm/cpu-features.rst index fc1623aeca..2d5c06cd01 100644 --- a/docs/arm-cpu-features.rst +++ b/docs/system/arm/cpu-features.rst @@ -1,19 +1,13 @@ +Arm CPU Features ================ -ARM CPU Features -================ - -Examples of probing and using ARM CPU features - -Introduction -============ CPU features are optional features that a CPU of supporting type may choose to implement or not. In QEMU, optional CPU features have corresponding boolean CPU proprieties that, when enabled, indicate that the feature is implemented, and, conversely, when disabled, -indicate that it is not implemented. An example of an ARM CPU feature +indicate that it is not implemented. An example of an Arm CPU feature is the Performance Monitoring Unit (PMU). CPU types such as the -Cortex-A15 and the Cortex-A57, which respectively implement ARM +Cortex-A15 and the Cortex-A57, which respectively implement Arm architecture reference manuals ARMv7-A and ARMv8-A, may both optionally implement PMUs. For example, if a user wants to use a Cortex-A15 without a PMU, then the `-cpu` parameter should contain `pmu=off` on the QEMU diff --git a/docs/system/arm/integratorcp.rst b/docs/system/arm/integratorcp.rst new file mode 100644 index 0000000000..e6f050f602 --- /dev/null +++ b/docs/system/arm/integratorcp.rst @@ -0,0 +1,16 @@ +Integrator/CP (``integratorcp``) +================================ + +The Arm Integrator/CP board is emulated with the following devices: + +- ARM926E, ARM1026E, ARM946E, ARM1136 or Cortex-A8 CPU + +- Two PL011 UARTs + +- SMC 91c111 Ethernet adapter + +- PL110 LCD controller + +- PL050 KMI with PS/2 keyboard and mouse. + +- PL181 MultiMedia Card Interface with SD card. diff --git a/docs/system/arm/musicpal.rst b/docs/system/arm/musicpal.rst new file mode 100644 index 0000000000..9de380edf8 --- /dev/null +++ b/docs/system/arm/musicpal.rst @@ -0,0 +1,19 @@ +Freecom MusicPal (``musicpal``) +=============================== + +The Freecom MusicPal internet radio emulation includes the following +elements: + +- Marvell MV88W8618 Arm core. + +- 32 MB RAM, 256 KB SRAM, 8 MB flash. + +- Up to 2 16550 UARTs + +- MV88W8xx8 Ethernet controller + +- MV88W8618 audio controller, WM8750 CODEC and mixer + +- 128x64 display with brightness control + +- 2 buttons, 2 navigation wheels with button function diff --git a/docs/system/arm/nseries.rst b/docs/system/arm/nseries.rst new file mode 100644 index 0000000000..cd9edf5d88 --- /dev/null +++ b/docs/system/arm/nseries.rst @@ -0,0 +1,33 @@ +Nokia N800 and N810 tablets (``n800``, ``n810``) +================================================ + +Nokia N800 and N810 internet tablets (known also as RX-34 and RX-44 / +48) emulation supports the following elements: + +- Texas Instruments OMAP2420 System-on-chip (ARM1136 core) + +- RAM and non-volatile OneNAND Flash memories + +- Display connected to EPSON remote framebuffer chip and OMAP on-chip + display controller and a LS041y3 MIPI DBI-C controller + +- TI TSC2301 (in N800) and TI TSC2005 (in N810) touchscreen + controllers driven through SPI bus + +- National Semiconductor LM8323-controlled qwerty keyboard driven + through |I2C| bus + +- Secure Digital card connected to OMAP MMC/SD host + +- Three OMAP on-chip UARTs and on-chip STI debugging console + +- Mentor Graphics \"Inventra\" dual-role USB controller embedded in a + TI TUSB6010 chip - only USB host mode is supported + +- TI TMP105 temperature sensor driven through |I2C| bus + +- TI TWL92230C power management companion with an RTC on + |I2C| bus + +- Nokia RETU and TAHVO multi-purpose chips with an RTC, connected + through CBUS diff --git a/docs/system/arm/orangepi.rst b/docs/system/arm/orangepi.rst new file mode 100644 index 0000000000..c41adad488 --- /dev/null +++ b/docs/system/arm/orangepi.rst @@ -0,0 +1,253 @@ +Orange Pi PC (``orangepi-pc``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The Xunlong Orange Pi PC is an Allwinner H3 System on Chip +based embedded computer with mainline support in both U-Boot +and Linux. The board comes with a Quad Core Cortex-A7 @ 1.3GHz, +1GiB RAM, 100Mbit ethernet, USB, SD/MMC, USB, HDMI and +various other I/O. + +Supported devices +""""""""""""""""" + +The Orange Pi PC machine supports the following devices: + + * SMP (Quad Core Cortex-A7) + * Generic Interrupt Controller configuration + * SRAM mappings + * SDRAM controller + * Real Time Clock + * Timer device (re-used from Allwinner A10) + * UART + * SD/MMC storage controller + * EMAC ethernet + * USB 2.0 interfaces + * Clock Control Unit + * System Control module + * Security Identifier device + +Limitations +""""""""""" + +Currently, Orange Pi PC does *not* support the following features: + +- Graphical output via HDMI, GPU and/or the Display Engine +- Audio output +- Hardware Watchdog + +Also see the 'unimplemented' array in the Allwinner H3 SoC module +for a complete list of unimplemented I/O devices: ``./hw/arm/allwinner-h3.c`` + +Boot options +"""""""""""" + +The Orange Pi PC machine can start using the standard -kernel functionality +for loading a Linux kernel or ELF executable. Additionally, the Orange Pi PC +machine can also emulate the BootROM which is present on an actual Allwinner H3 +based SoC, which loads the bootloader from a SD card, specified via the -sd argument +to qemu-system-arm. + +Machine-specific options +"""""""""""""""""""""""" + +The following machine-specific options are supported: + +- allwinner-rtc.base-year=YYYY + + The Allwinner RTC device is automatically created by the Orange Pi PC machine + and uses a default base year value which can be overridden using the 'base-year' property. + The base year is the actual represented year when the RTC year value is zero. + This option can be used in case the target operating system driver uses a different + base year value. The minimum value for the base year is 1900. + +- allwinner-sid.identifier=abcd1122-a000-b000-c000-12345678ffff + + The Security Identifier value can be read by the guest. + For example, U-Boot uses it to determine a unique MAC address. + +The above machine-specific options can be specified in qemu-system-arm +via the '-global' argument, for example: + +.. code-block:: bash + + $ qemu-system-arm -M orangepi-pc -sd mycard.img \ + -global allwinner-rtc.base-year=2000 + +Running mainline Linux +"""""""""""""""""""""" + +Mainline Linux kernels from 4.19 up to latest master are known to work. +To build a Linux mainline kernel that can be booted by the Orange Pi PC machine, +simply configure the kernel using the sunxi_defconfig configuration: + +.. code-block:: bash + + $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make mrproper + $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make sunxi_defconfig + +To be able to use USB storage, you need to manually enable the corresponding +configuration item. Start the kconfig configuration tool: + +.. code-block:: bash + + $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make menuconfig + +Navigate to the following item, enable it and save your configuration: + + Device Drivers > USB support > USB Mass Storage support + +Build the Linux kernel with: + +.. code-block:: bash + + $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- make + +To boot the newly build linux kernel in QEMU with the Orange Pi PC machine, use: + +.. code-block:: bash + + $ qemu-system-arm -M orangepi-pc -nic user -nographic \ + -kernel /path/to/linux/arch/arm/boot/zImage \ + -append 'console=ttyS0,115200' \ + -dtb /path/to/linux/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dtb + +Orange Pi PC images +""""""""""""""""""" + +Note that the mainline kernel does not have a root filesystem. You may provide it +with an official Orange Pi PC image from the official website: + + http://www.orangepi.org/downloadresources/ + +Another possibility is to run an Armbian image for Orange Pi PC which +can be downloaded from: + + https://www.armbian.com/orange-pi-pc/ + +Alternatively, you can also choose to build you own image with buildroot +using the orangepi_pc_defconfig. Also see https://buildroot.org for more information. + +You can choose to attach the selected image either as an SD card or as USB mass storage. +For example, to boot using the Orange Pi PC Debian image on SD card, simply add the -sd +argument and provide the proper root= kernel parameter: + +.. code-block:: bash + + $ qemu-system-arm -M orangepi-pc -nic user -nographic \ + -kernel /path/to/linux/arch/arm/boot/zImage \ + -append 'console=ttyS0,115200 root=/dev/mmcblk0p2' \ + -dtb /path/to/linux/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dtb \ + -sd OrangePi_pc_debian_stretch_server_linux5.3.5_v1.0.img + +To attach the image as an USB mass storage device to the machine, +simply append to the command: + +.. code-block:: bash + + -drive if=none,id=stick,file=myimage.img \ + -device usb-storage,bus=usb-bus.0,drive=stick + +Instead of providing a custom Linux kernel via the -kernel command you may also +choose to let the Orange Pi PC machine load the bootloader from SD card, just like +a real board would do using the BootROM. Simply pass the selected image via the -sd +argument and remove the -kernel, -append, -dbt and -initrd arguments: + +.. code-block:: bash + + $ qemu-system-arm -M orangepi-pc -nic user -nographic \ + -sd Armbian_19.11.3_Orangepipc_buster_current_5.3.9.img + +Note that both the official Orange Pi PC images and Armbian images start +a lot of userland programs via systemd. Depending on the host hardware and OS, +they may be slow to emulate, especially due to emulating the 4 cores. +To help reduce the performance slow down due to emulating the 4 cores, you can +give the following kernel parameters via U-Boot (or via -append): + +.. code-block:: bash + + => setenv extraargs 'systemd.default_timeout_start_sec=9000 loglevel=7 nosmp console=ttyS0,115200' + +Running U-Boot +"""""""""""""" + +U-Boot mainline can be build and configured using the orangepi_pc_defconfig +using similar commands as describe above for Linux. Note that it is recommended +for development/testing to select the following configuration setting in U-Boot: + + Device Tree Control > Provider for DTB for DT Control > Embedded DTB + +To start U-Boot using the Orange Pi PC machine, provide the +u-boot binary to the -kernel argument: + +.. code-block:: bash + + $ qemu-system-arm -M orangepi-pc -nic user -nographic \ + -kernel /path/to/uboot/u-boot -sd disk.img + +Use the following U-boot commands to load and boot a Linux kernel from SD card: + +.. code-block:: bash + + => setenv bootargs console=ttyS0,115200 + => ext2load mmc 0 0x42000000 zImage + => ext2load mmc 0 0x43000000 sun8i-h3-orangepi-pc.dtb + => bootz 0x42000000 - 0x43000000 + +Running NetBSD +"""""""""""""" + +The NetBSD operating system also includes support for Allwinner H3 based boards, +including the Orange Pi PC. NetBSD 9.0 is known to work best for the Orange Pi PC +board and provides a fully working system with serial console, networking and storage. +For the Orange Pi PC machine, get the 'evbarm-earmv7hf' based image from: + + https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/evbarm-earmv7hf/binary/gzimg/armv7.img.gz + +The image requires manually installing U-Boot in the image. Build U-Boot with +the orangepi_pc_defconfig configuration as described in the previous section. +Next, unzip the NetBSD image and write the U-Boot binary including SPL using: + +.. code-block:: bash + + $ gunzip armv7.img.gz + $ dd if=/path/to/u-boot-sunxi-with-spl.bin of=armv7.img bs=1024 seek=8 conv=notrunc + +Finally, before starting the machine the SD image must be extended such +that the NetBSD kernel will not conclude the NetBSD partition is larger than +the emulated SD card: + +.. code-block:: bash + + $ dd if=/dev/zero bs=1M count=64 >> armv7.img + +Start the machine using the following command: + +.. code-block:: bash + + $ qemu-system-arm -M orangepi-pc -nic user -nographic \ + -sd armv7.img -global allwinner-rtc.base-year=2000 + +At the U-Boot stage, interrupt the automatic boot process by pressing a key +and set the following environment variables before booting: + +.. code-block:: bash + + => setenv bootargs root=ld0a + => setenv kernel netbsd-GENERIC.ub + => setenv fdtfile dtb/sun8i-h3-orangepi-pc.dtb + => setenv bootcmd 'fatload mmc 0:1 ${kernel_addr_r} ${kernel}; fatload mmc 0:1 ${fdt_addr_r} ${fdtfile}; fdt addr ${fdt_addr_r}; bootm ${kernel_addr_r} - ${fdt_addr_r}' + +Optionally you may save the environment variables to SD card with 'saveenv'. +To continue booting simply give the 'boot' command and NetBSD boots. + +Orange Pi PC acceptance tests +""""""""""""""""""""""""""""" + +The Orange Pi PC machine has several acceptance tests included. +To run the whole set of tests, build QEMU from source and simply +provide the following command: + +.. code-block:: bash + + $ AVOCADO_ALLOW_LARGE_STORAGE=yes avocado --show=app,console run \ + -t machine:orangepi-pc tests/acceptance/boot_linux_console.py diff --git a/docs/system/arm/palm.rst b/docs/system/arm/palm.rst new file mode 100644 index 0000000000..47ff9b36d4 --- /dev/null +++ b/docs/system/arm/palm.rst @@ -0,0 +1,23 @@ +Palm Tungsten|E PDA (``cheetah``) +================================= + +The Palm Tungsten|E PDA (codename \"Cheetah\") emulation includes the +following elements: + +- Texas Instruments OMAP310 System-on-chip (ARM925T core) + +- ROM and RAM memories (ROM firmware image can be loaded with + -option-rom) + +- On-chip LCD controller + +- On-chip Real Time Clock + +- TI TSC2102i touchscreen controller / analog-digital converter / + Audio CODEC, connected through MicroWire and |I2S| busses + +- GPIO-connected matrix keypad + +- Secure Digital card connected to OMAP MMC/SD host + +- Three on-chip UARTs diff --git a/docs/system/arm/realview.rst b/docs/system/arm/realview.rst new file mode 100644 index 0000000000..65f5be346b --- /dev/null +++ b/docs/system/arm/realview.rst @@ -0,0 +1,34 @@ +Arm Realview boards (``realview-eb``, ``realview-eb-mpcore``, ``realview-pb-a8``, ``realview-pbx-a9``) +====================================================================================================== + +Several variants of the Arm RealView baseboard are emulated, including +the EB, PB-A8 and PBX-A9. Due to interactions with the bootloader, only +certain Linux kernel configurations work out of the box on these boards. + +Kernels for the PB-A8 board should have CONFIG_REALVIEW_HIGH_PHYS_OFFSET +enabled in the kernel, and expect 512M RAM. Kernels for The PBX-A9 board +should have CONFIG_SPARSEMEM enabled, CONFIG_REALVIEW_HIGH_PHYS_OFFSET +disabled and expect 1024M RAM. + +The following devices are emulated: + +- ARM926E, ARM1136, ARM11MPCore, Cortex-A8 or Cortex-A9 MPCore CPU + +- Arm AMBA Generic/Distributed Interrupt Controller + +- Four PL011 UARTs + +- SMC 91c111 or SMSC LAN9118 Ethernet adapter + +- PL110 LCD controller + +- PL050 KMI with PS/2 keyboard and mouse + +- PCI host bridge + +- PCI OHCI USB controller + +- LSI53C895A PCI SCSI Host Bus Adapter with hard disk and CD-ROM + devices + +- PL181 MultiMedia Card Interface with SD card. diff --git a/docs/system/arm/stellaris.rst b/docs/system/arm/stellaris.rst new file mode 100644 index 0000000000..8af4ad79c7 --- /dev/null +++ b/docs/system/arm/stellaris.rst @@ -0,0 +1,26 @@ +Stellaris boards (``lm3s6965evb``, ``lm3s811evb``) +================================================== + +The Luminary Micro Stellaris LM3S811EVB emulation includes the following +devices: + +- Cortex-M3 CPU core. + +- 64k Flash and 8k SRAM. + +- Timers, UARTs, ADC and |I2C| interface. + +- OSRAM Pictiva 96x16 OLED with SSD0303 controller on + |I2C| bus. + +The Luminary Micro Stellaris LM3S6965EVB emulation includes the +following devices: + +- Cortex-M3 CPU core. + +- 256k Flash and 64k SRAM. + +- Timers, UARTs, ADC, |I2C| and SSI interfaces. + +- OSRAM Pictiva 128x64 OLED with SSD0323 controller connected via + SSI. diff --git a/docs/system/arm/sx1.rst b/docs/system/arm/sx1.rst new file mode 100644 index 0000000000..8bce30d4b2 --- /dev/null +++ b/docs/system/arm/sx1.rst @@ -0,0 +1,18 @@ +Siemens SX1 (``sx1``, ``sx1-v1``) +================================= + +The Siemens SX1 models v1 and v2 (default) basic emulation. The +emulation includes the following elements: + +- Texas Instruments OMAP310 System-on-chip (ARM925T core) + +- ROM and RAM memories (ROM firmware image can be loaded with + -pflash) V1 1 Flash of 16MB and 1 Flash of 8MB V2 1 Flash of 32MB + +- On-chip LCD controller + +- On-chip Real Time Clock + +- Secure Digital card connected to OMAP MMC/SD host + +- Three on-chip UARTs diff --git a/docs/system/arm/versatile.rst b/docs/system/arm/versatile.rst new file mode 100644 index 0000000000..51221c30a4 --- /dev/null +++ b/docs/system/arm/versatile.rst @@ -0,0 +1,29 @@ +Arm Versatile boards (``versatileab``, ``versatilepb``) +======================================================= + +The Arm Versatile baseboard is emulated with the following devices: + +- ARM926E, ARM1136 or Cortex-A8 CPU + +- PL190 Vectored Interrupt Controller + +- Four PL011 UARTs + +- SMC 91c111 Ethernet adapter + +- PL110 LCD controller + +- PL050 KMI with PS/2 keyboard and mouse. + +- PCI host bridge. Note the emulated PCI bridge only provides access + to PCI memory space. It does not provide access to PCI IO space. This + means some devices (eg. ne2k_pci NIC) are not usable, and others (eg. + rtl8139 NIC) are only usable when the guest drivers use the memory + mapped control registers. + +- PCI OHCI USB controller. + +- LSI53C895A PCI SCSI Host Bus Adapter with hard disk and CD-ROM + devices. + +- PL181 MultiMedia Card Interface with SD card. diff --git a/docs/system/arm/xscale.rst b/docs/system/arm/xscale.rst new file mode 100644 index 0000000000..89ec93e904 --- /dev/null +++ b/docs/system/arm/xscale.rst @@ -0,0 +1,29 @@ +Sharp XScale-based PDA models (``akita``, ``borzoi``, ``spitz``, ``terrier``) +============================================================================= + +The XScale-based clamshell PDA models (\"Spitz\", \"Akita\", \"Borzoi\" +and \"Terrier\") emulation includes the following peripherals: + +- Intel PXA270 System-on-chip (ARMv5TE core) + +- NAND Flash memory + +- IBM/Hitachi DSCM microdrive in a PXA PCMCIA slot - not in \"Akita\" + +- On-chip OHCI USB controller + +- On-chip LCD controller + +- On-chip Real Time Clock + +- TI ADS7846 touchscreen controller on SSP bus + +- Maxim MAX1111 analog-digital converter on |I2C| bus + +- GPIO-connected keyboard controller and LEDs + +- Secure Digital card connected to PXA MMC/SD host + +- Three on-chip UARTs + +- WM8750 audio CODEC on |I2C| and |I2S| busses diff --git a/docs/system/cpu-models-x86.rst.inc b/docs/system/cpu-models-x86.rst.inc index cbad930c70..9a2327828e 100644 --- a/docs/system/cpu-models-x86.rst.inc +++ b/docs/system/cpu-models-x86.rst.inc @@ -49,10 +49,15 @@ mixture of host CPU models between machines, if live migration compatibility is required, use the newest CPU model that is compatible across all desired hosts. -``Skylake-Server``, ``Skylake-Server-IBRS`` +``Cascadelake-Server``, ``Cascadelake-Server-noTSX`` + Intel Xeon Processor (Cascade Lake, 2019), with "stepping" levels 6 + or 7 only. (The Cascade Lake Xeon processor with *stepping 5 is + vulnerable to MDS variants*.) + +``Skylake-Server``, ``Skylake-Server-IBRS``, ``Skylake-Server-IBRS-noTSX`` Intel Xeon Processor (Skylake, 2016) -``Skylake-Client``, ``Skylake-Client-IBRS`` +``Skylake-Client``, ``Skylake-Client-IBRS``, ``Skylake-Client-noTSX-IBRS}`` Intel Core Processor (Skylake, 2015) ``Broadwell``, ``Broadwell-IBRS``, ``Broadwell-noTSX``, ``Broadwell-noTSX-IBRS`` @@ -148,6 +153,54 @@ features are included if using "Host passthrough" or "Host model". Requires the host CPU microcode to support this feature before it can be used for guest CPUs. +``mds-no`` + Recommended to inform the guest OS that the host is *not* vulnerable + to any of the MDS variants ([MFBDS] CVE-2018-12130, [MLPDS] + CVE-2018-12127, [MSBDS] CVE-2018-12126). + + This is an MSR (Model-Specific Register) feature rather than a CPUID feature, + so it will not appear in the Linux ``/proc/cpuinfo`` in the host or + guest. Instead, the host kernel uses it to populate the MDS + vulnerability file in ``sysfs``. + + So it should only be enabled for VMs if the host reports @code{Not + affected} in the ``/sys/devices/system/cpu/vulnerabilities/mds`` file. + +``taa-no`` + Recommended to inform that the guest that the host is ``not`` + vulnerable to CVE-2019-11135, TSX Asynchronous Abort (TAA). + + This too is an MSR feature, so it does not show up in the Linux + ``/proc/cpuinfo`` in the host or guest. + + It should only be enabled for VMs if the host reports ``Not affected`` + in the ``/sys/devices/system/cpu/vulnerabilities/tsx_async_abort`` + file. + +``tsx-ctrl`` + Recommended to inform the guest that it can disable the Intel TSX + (Transactional Synchronization Extensions) feature; or, if the + processor is vulnerable, use the Intel VERW instruction (a + processor-level instruction that performs checks on memory access) as + a mitigation for the TAA vulnerability. (For details, refer to + Intel's `deep dive into MDS + <https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-microarchitectural-data-sampling>`_.) + + Expose this to the guest OS if and only if: (a) the host has TSX + enabled; *and* (b) the guest has ``rtm`` CPU flag enabled. + + By disabling TSX, KVM-based guests can avoid paying the price of + mitigating TSX-based attacks. + + Note that ``tsx-ctrl`` too is an MSR feature, so it does not show + up in the Linux ``/proc/cpuinfo`` in the host or guest. + + To validate that Intel TSX is indeed disabled for the guest, there are + two ways: (a) check for the *absence* of ``rtm`` in the guest's + ``/proc/cpuinfo``; or (b) the + ``/sys/devices/system/cpu/vulnerabilities/tsx_async_abort`` file in + the guest should report ``Mitigation: TSX disabled``. + Preferred CPU models for AMD x86 hosts ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst index 6c1d9034d9..c633fe2bef 100644 --- a/docs/system/deprecated.rst +++ b/docs/system/deprecated.rst @@ -180,27 +180,67 @@ QEMU Machine Protocol (QMP) commands Use ``blockdev-change-medium`` or ``change-vnc-password`` instead. +``blockdev-open-tray``, ``blockdev-close-tray`` argument ``device`` (since 2.8.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use argument ``id`` instead. + +``eject`` argument ``device`` (since 2.8.0) +''''''''''''''''''''''''''''''''''''''''''' + +Use argument ``id`` instead. + +``blockdev-change-medium`` argument ``device`` (since 2.8.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use argument ``id`` instead. + +``block_set_io_throttle`` argument ``device`` (since 2.8.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use argument ``id`` instead. + ``migrate_set_downtime`` and ``migrate_set_speed`` (since 2.8.0) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Use ``migrate-set-parameters`` instead. +``query-named-block-nodes`` result ``encryption_key_missing`` (since 2.10.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Always false. + +``query-block`` result ``inserted.encryption_key_missing`` (since 2.10.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Always false. + +``blockdev-add`` empty string argument ``backing`` (since 2.10.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use argument value ``null`` instead. + ``migrate-set-cache-size`` and ``query-migrate-cache-size`` (since 2.11.0) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Use ``migrate-set-parameters`` and ``query-migrate-parameters`` instead. +``block-commit`` arguments ``base`` and ``top`` (since 3.1.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use arguments ``base-node`` and ``top-node`` instead. + ``object-add`` option ``props`` (since 5.0) ''''''''''''''''''''''''''''''''''''''''''' Specify the properties for the object as top-level arguments instead. -``query-block`` result field ``dirty-bitmaps[i].status`` (since 4.0) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``query-named-block-nodes`` and ``query-block`` result dirty-bitmaps[i].status (since 4.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' The ``status`` field of the ``BlockDirtyInfo`` structure, returned by -the query-block command is deprecated. Two new boolean fields, -``recording`` and ``busy`` effectively replace it. +these commands is deprecated. Two new boolean fields, ``recording`` and +``busy`` effectively replace it. ``query-block`` result field ``dirty-bitmaps`` (Since 4.2) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -248,12 +288,6 @@ the 'wait' field, which is only applicable to sockets in server mode Human Monitor Protocol (HMP) commands ------------------------------------- -The ``hub_id`` parameter of ``hostfwd_add`` / ``hostfwd_remove`` (since 3.1) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -The ``[hub_id name]`` parameter tuple of the 'hostfwd_add' and -'hostfwd_remove' HMP commands has been replaced by ``netdev_id``. - ``cpu-add`` (since 4.0) ''''''''''''''''''''''' @@ -295,6 +329,13 @@ The RISC-V no MMU cpus have been depcreated. The two CPUs: ``rv32imacu-nommu`` a ``rv64imacu-nommu`` should no longer be used. Instead the MMU status can be specified via the CPU ``mmu`` option when using the ``rv32`` or ``rv64`` CPUs. +``compat`` property of server class POWER CPUs (since 5.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``compat`` property used to set backwards compatibility modes for +the processor has been deprecated. The ``max-cpu-compat`` property of +the ``pseries`` machine type should be used instead. + System emulator devices ----------------------- @@ -430,6 +471,15 @@ QEMU Machine Protocol (QMP) commands The "autoload" parameter has been ignored since 2.12.0. All bitmaps are automatically loaded from qcow2 images. +Human Monitor Protocol (HMP) commands +------------------------------------- + +The ``hub_id`` parameter of ``hostfwd_add`` / ``hostfwd_remove`` (removed in 5.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``[hub_id name]`` parameter tuple of the 'hostfwd_add' and +'hostfwd_remove' HMP commands has been replaced by ``netdev_id``. + Related binaries ---------------- diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index d2a3b44ce8..324e2af1cb 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -1,217 +1,88 @@ .. _ARM-System-emulator: -ARM System emulator +Arm System emulator ------------------- -Use the executable ``qemu-system-arm`` to simulate a ARM machine. The -ARM Integrator/CP board is emulated with the following devices: - -- ARM926E, ARM1026E, ARM946E, ARM1136 or Cortex-A8 CPU - -- Two PL011 UARTs - -- SMC 91c111 Ethernet adapter - -- PL110 LCD controller - -- PL050 KMI with PS/2 keyboard and mouse. - -- PL181 MultiMedia Card Interface with SD card. - -The ARM Versatile baseboard is emulated with the following devices: - -- ARM926E, ARM1136 or Cortex-A8 CPU - -- PL190 Vectored Interrupt Controller - -- Four PL011 UARTs - -- SMC 91c111 Ethernet adapter - -- PL110 LCD controller - -- PL050 KMI with PS/2 keyboard and mouse. - -- PCI host bridge. Note the emulated PCI bridge only provides access - to PCI memory space. It does not provide access to PCI IO space. This - means some devices (eg. ne2k_pci NIC) are not usable, and others (eg. - rtl8139 NIC) are only usable when the guest drivers use the memory - mapped control registers. - -- PCI OHCI USB controller. - -- LSI53C895A PCI SCSI Host Bus Adapter with hard disk and CD-ROM - devices. - -- PL181 MultiMedia Card Interface with SD card. - -Several variants of the ARM RealView baseboard are emulated, including -the EB, PB-A8 and PBX-A9. Due to interactions with the bootloader, only -certain Linux kernel configurations work out of the box on these boards. - -Kernels for the PB-A8 board should have CONFIG_REALVIEW_HIGH_PHYS_OFFSET -enabled in the kernel, and expect 512M RAM. Kernels for The PBX-A9 board -should have CONFIG_SPARSEMEM enabled, CONFIG_REALVIEW_HIGH_PHYS_OFFSET -disabled and expect 1024M RAM. - -The following devices are emulated: - -- ARM926E, ARM1136, ARM11MPCore, Cortex-A8 or Cortex-A9 MPCore CPU - -- ARM AMBA Generic/Distributed Interrupt Controller - -- Four PL011 UARTs - -- SMC 91c111 or SMSC LAN9118 Ethernet adapter - -- PL110 LCD controller - -- PL050 KMI with PS/2 keyboard and mouse - -- PCI host bridge - -- PCI OHCI USB controller - -- LSI53C895A PCI SCSI Host Bus Adapter with hard disk and CD-ROM - devices - -- PL181 MultiMedia Card Interface with SD card. - -The XScale-based clamshell PDA models (\"Spitz\", \"Akita\", \"Borzoi\" -and \"Terrier\") emulation includes the following peripherals: - -- Intel PXA270 System-on-chip (ARM V5TE core) - -- NAND Flash memory - -- IBM/Hitachi DSCM microdrive in a PXA PCMCIA slot - not in \"Akita\" - -- On-chip OHCI USB controller - -- On-chip LCD controller - -- On-chip Real Time Clock - -- TI ADS7846 touchscreen controller on SSP bus - -- Maxim MAX1111 analog-digital converter on |I2C| bus - -- GPIO-connected keyboard controller and LEDs - -- Secure Digital card connected to PXA MMC/SD host - -- Three on-chip UARTs - -- WM8750 audio CODEC on |I2C| and |I2S| busses - -The Palm Tungsten|E PDA (codename \"Cheetah\") emulation includes the -following elements: - -- Texas Instruments OMAP310 System-on-chip (ARM 925T core) - -- ROM and RAM memories (ROM firmware image can be loaded with - -option-rom) - -- On-chip LCD controller - -- On-chip Real Time Clock - -- TI TSC2102i touchscreen controller / analog-digital converter / - Audio CODEC, connected through MicroWire and |I2S| busses - -- GPIO-connected matrix keypad - -- Secure Digital card connected to OMAP MMC/SD host - -- Three on-chip UARTs - -Nokia N800 and N810 internet tablets (known also as RX-34 and RX-44 / -48) emulation supports the following elements: - -- Texas Instruments OMAP2420 System-on-chip (ARM 1136 core) - -- RAM and non-volatile OneNAND Flash memories - -- Display connected to EPSON remote framebuffer chip and OMAP on-chip - display controller and a LS041y3 MIPI DBI-C controller - -- TI TSC2301 (in N800) and TI TSC2005 (in N810) touchscreen - controllers driven through SPI bus - -- National Semiconductor LM8323-controlled qwerty keyboard driven - through |I2C| bus - -- Secure Digital card connected to OMAP MMC/SD host - -- Three OMAP on-chip UARTs and on-chip STI debugging console - -- Mentor Graphics \"Inventra\" dual-role USB controller embedded in a - TI TUSB6010 chip - only USB host mode is supported - -- TI TMP105 temperature sensor driven through |I2C| bus - -- TI TWL92230C power management companion with an RTC on - |I2C| bus - -- Nokia RETU and TAHVO multi-purpose chips with an RTC, connected - through CBUS - -The Luminary Micro Stellaris LM3S811EVB emulation includes the following -devices: - -- Cortex-M3 CPU core. - -- 64k Flash and 8k SRAM. - -- Timers, UARTs, ADC and |I2C| interface. - -- OSRAM Pictiva 96x16 OLED with SSD0303 controller on - |I2C| bus. - -The Luminary Micro Stellaris LM3S6965EVB emulation includes the -following devices: - -- Cortex-M3 CPU core. - -- 256k Flash and 64k SRAM. - -- Timers, UARTs, ADC, |I2C| and SSI interfaces. - -- OSRAM Pictiva 128x64 OLED with SSD0323 controller connected via - SSI. - -The Freecom MusicPal internet radio emulation includes the following -elements: - -- Marvell MV88W8618 ARM core. - -- 32 MB RAM, 256 KB SRAM, 8 MB flash. - -- Up to 2 16550 UARTs - -- MV88W8xx8 Ethernet controller - -- MV88W8618 audio controller, WM8750 CODEC and mixer - -- 128x64 display with brightness control - -- 2 buttons, 2 navigation wheels with button function - -The Siemens SX1 models v1 and v2 (default) basic emulation. The -emulation includes the following elements: - -- Texas Instruments OMAP310 System-on-chip (ARM 925T core) - -- ROM and RAM memories (ROM firmware image can be loaded with - -pflash) V1 1 Flash of 16MB and 1 Flash of 8MB V2 1 Flash of 32MB - -- On-chip LCD controller - -- On-chip Real Time Clock - -- Secure Digital card connected to OMAP MMC/SD host - -- Three on-chip UARTs - -A Linux 2.6 test image is available on the QEMU web site. More -information is available in the QEMU mailing-list archive. +QEMU can emulate both 32-bit and 64-bit Arm CPUs. Use the +``qemu-system-aarch64`` executable to simulate a 64-bit Arm machine. +You can use either ``qemu-system-arm`` or ``qemu-system-aarch64`` +to simulate a 32-bit Arm machine: in general, command lines that +work for ``qemu-system-arm`` will behave the same when used with +``qemu-system-aarch64``. + +QEMU has generally good support for Arm guests. It has support for +nearly fifty different machines. The reason we support so many is that +Arm hardware is much more widely varying than x86 hardware. Arm CPUs +are generally built into "system-on-chip" (SoC) designs created by +many different companies with different devices, and these SoCs are +then built into machines which can vary still further even if they use +the same SoC. Even with fifty boards QEMU does not cover more than a +small fraction of the Arm hardware ecosystem. + +The situation for 64-bit Arm is fairly similar, except that we don't +implement so many different machines. + +As well as the more common "A-profile" CPUs (which have MMUs and will +run Linux) QEMU also supports "M-profile" CPUs such as the Cortex-M0, +Cortex-M4 and Cortex-M33 (which are microcontrollers used in very +embedded boards). For most boards the CPU type is fixed (matching what +the hardware has), so typically you don't need to specify the CPU type +by hand, except for special cases like the ``virt`` board. + +Choosing a board model +====================== + +For QEMU's Arm system emulation, you must specify which board +model you want to use with the ``-M`` or ``--machine`` option; +there is no default. + +Because Arm systems differ so much and in fundamental ways, typically +operating system or firmware images intended to run on one machine +will not run at all on any other. This is often surprising for new +users who are used to the x86 world where every system looks like a +standard PC. (Once the kernel has booted, most userspace software +cares much less about the detail of the hardware.) + +If you already have a system image or a kernel that works on hardware +and you want to boot with QEMU, check whether QEMU lists that machine +in its ``-machine help`` output. If it is listed, then you can probably +use that board model. If it is not listed, then unfortunately your image +will almost certainly not boot on QEMU. (You might be able to +extract the filesystem and use that with a different kernel which +boots on a system that QEMU does emulate.) + +If you don't care about reproducing the idiosyncrasies of a particular +bit of hardware, such as small amount of RAM, no PCI or other hard +disk, etc., and just want to run Linux, the best option is to use the +``virt`` board. This is a platform which doesn't correspond to any +real hardware and is designed for use in virtual machines. You'll +need to compile Linux with a suitable configuration for running on +the ``virt`` board. ``virt`` supports PCI, virtio, recent CPUs and +large amounts of RAM. It also supports 64-bit CPUs. + +Board-specific documentation +============================ + +Unfortunately many of the Arm boards QEMU supports are currently +undocumented; you can get a complete list by running +``qemu-system-aarch64 --machine help``. + +.. toctree:: + :maxdepth: 1 + + arm/integratorcp + arm/versatile + arm/realview + arm/xscale + arm/palm + arm/nseries + arm/stellaris + arm/musicpal + arm/sx1 + arm/orangepi + +Arm CPU features +================ + +.. toctree:: + arm/cpu-features diff --git a/docs/user/main.rst b/docs/user/main.rst index ca69f7727d..bd99b0fdbe 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -35,7 +35,7 @@ QEMU user space emulation has the following notable features: On Linux, QEMU can emulate the ``clone`` syscall and create a real host thread (with a separate virtual CPU) for each emulated thread. Note that not all targets currently emulate atomic operations - correctly. x86 and ARM use a global lock in order to preserve their + correctly. x86 and Arm use a global lock in order to preserve their semantics. QEMU was conceived so that ultimately it can emulate itself. Although it @@ -173,11 +173,11 @@ Other binaries user mode (Alpha) ``qemu-alpha`` TODO. -user mode (ARM) +user mode (Arm) ``qemu-armeb`` TODO. -user mode (ARM) -``qemu-arm`` is also capable of running ARM \"Angel\" semihosted ELF +user mode (Arm) +``qemu-arm`` is also capable of running Arm \"Angel\" semihosted ELF binaries (as implemented by the arm-elf and arm-eabi Newlib/GDB configurations), and arm-uclinux bFLT format binaries. @@ -1315,7 +1315,7 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, unsigned client) { DirtyMemoryBlocks *blocks; - unsigned long end, page; + unsigned long end, page, start_page; bool dirty = false; RAMBlock *ramblock; uint64_t mr_offset, mr_size; @@ -1325,7 +1325,8 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, } end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; - page = start >> TARGET_PAGE_BITS; + start_page = start >> TARGET_PAGE_BITS; + page = start_page; WITH_RCU_READ_LOCK_GUARD() { blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); @@ -1345,8 +1346,8 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, page += num; } - mr_offset = (ram_addr_t)(page << TARGET_PAGE_BITS) - ramblock->offset; - mr_size = (end - page) << TARGET_PAGE_BITS; + mr_offset = (ram_addr_t)(start_page << TARGET_PAGE_BITS) - ramblock->offset; + mr_size = (end - start_page) << TARGET_PAGE_BITS; memory_region_clear_dirty_bitmap(ramblock->mr, mr_offset, mr_size); } diff --git a/gdb-xml/rx-core.xml b/gdb-xml/rx-core.xml new file mode 100644 index 0000000000..b5aa9ac4a8 --- /dev/null +++ b/gdb-xml/rx-core.xml @@ -0,0 +1,70 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2019 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> + +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.rx.core"> + <reg name="r0" bitsize="32" type="data_ptr"/> + <reg name="r1" bitsize="32" type="uint32"/> + <reg name="r2" bitsize="32" type="uint32"/> + <reg name="r3" bitsize="32" type="uint32"/> + <reg name="r4" bitsize="32" type="uint32"/> + <reg name="r5" bitsize="32" type="uint32"/> + <reg name="r6" bitsize="32" type="uint32"/> + <reg name="r7" bitsize="32" type="uint32"/> + <reg name="r8" bitsize="32" type="uint32"/> + <reg name="r9" bitsize="32" type="uint32"/> + <reg name="r10" bitsize="32" type="uint32"/> + <reg name="r11" bitsize="32" type="uint32"/> + <reg name="r12" bitsize="32" type="uint32"/> + <reg name="r13" bitsize="32" type="uint32"/> + <reg name="r14" bitsize="32" type="uint32"/> + <reg name="r15" bitsize="32" type="uint32"/> + + <flags id="psw_flags" size="4"> + <field name="C" start="0" end="0"/> + <field name="Z" start="1" end="1"/> + <field name="S" start="2" end="2"/> + <field name="O" start="3" end="3"/> + <field name="I" start="16" end="16"/> + <field name="U" start="17" end="17"/> + <field name="PM" start="20" end="20"/> + <field name="IPL" start="24" end="27"/> + </flags> + + <flags id="fpsw_flags" size="4"> + <field name="RM" start="0" end="1"/> + <field name="CV" start="2" end="2"/> + <field name="CO" start="3" end="3"/> + <field name="CZ" start="4" end="4"/> + <field name="CU" start="5" end="5"/> + <field name="CX" start="6" end="6"/> + <field name="CE" start="7" end="7"/> + <field name="DN" start="8" end="8"/> + <field name="EV" start="10" end="10"/> + <field name="EO" start="11" end="11"/> + <field name="EZ" start="12" end="12"/> + <field name="EU" start="13" end="13"/> + <field name="EX" start="14" end="14"/> + <field name="FV" start="26" end="26"/> + <field name="FO" start="27" end="27"/> + <field name="FZ" start="28" end="28"/> + <field name="FU" start="29" end="29"/> + <field name="FX" start="30" end="30"/> + <field name="FS" start="31" end="31"/> + </flags> + + <reg name="usp" bitsize="32" type="data_ptr"/> + <reg name="isp" bitsize="32" type="data_ptr"/> + <reg name="psw" bitsize="32" type="psw_flags"/> + <reg name="pc" bitsize="32" type="code_ptr"/> + <reg name="intb" bitsize="32" type="data_ptr"/> + <reg name="bpsw" bitsize="32" type="psw_flags"/> + <reg name="bpc" bitsize="32" type="code_ptr"/> + <reg name="fintv" bitsize="32" type="code_ptr"/> + <reg name="fpsw" bitsize="32" type="fpsw_flags"/> + <reg name="acc" bitsize="64" type="uint64"/> +</feature> @@ -319,8 +319,8 @@ static int gdb_signal_to_target (int sig) typedef struct GDBRegisterState { int base_reg; int num_regs; - gdb_reg_cb get_reg; - gdb_reg_cb set_reg; + gdb_get_reg_cb get_reg; + gdb_set_reg_cb set_reg; const char *xml; struct GDBRegisterState *next; } GDBRegisterState; @@ -342,6 +342,7 @@ enum RSState { RS_CHKSUM2, }; typedef struct GDBState { + bool init; /* have we been initialised? */ CPUState *c_cpu; /* current CPU for step/continue ops */ CPUState *g_cpu; /* current CPU for other ops */ CPUState *query_cpu; /* for q{f|s}ThreadInfo */ @@ -350,8 +351,7 @@ typedef struct GDBState { int line_buf_index; int line_sum; /* running checksum */ int line_csum; /* checksum at the end of the packet */ - uint8_t last_packet[MAX_PACKET_LENGTH + 4]; - int last_packet_len; + GByteArray *last_packet; int signal; #ifdef CONFIG_USER_ONLY int fd; @@ -365,6 +365,8 @@ typedef struct GDBState { int process_num; char syscall_buf[256]; gdb_syscall_complete_cb current_syscall_cb; + GString *str_buf; + GByteArray *mem_buf; } GDBState; /* By default use no IRQs and no timers while single stepping so as to @@ -372,7 +374,26 @@ typedef struct GDBState { */ static int sstep_flags = SSTEP_ENABLE|SSTEP_NOIRQ|SSTEP_NOTIMER; -static GDBState *gdbserver_state; +static GDBState gdbserver_state; + +static void init_gdbserver_state(void) +{ + g_assert(!gdbserver_state.init); + memset(&gdbserver_state, 0, sizeof(GDBState)); + gdbserver_state.init = true; + gdbserver_state.str_buf = g_string_new(NULL); + gdbserver_state.mem_buf = g_byte_array_sized_new(MAX_PACKET_LENGTH); + gdbserver_state.last_packet = g_byte_array_sized_new(MAX_PACKET_LENGTH + 4); +} + +#ifndef CONFIG_USER_ONLY +static void reset_gdbserver_state(void) +{ + g_free(gdbserver_state.processes); + gdbserver_state.processes = NULL; + gdbserver_state.process_num = 0; +} +#endif bool gdb_has_xml; @@ -380,21 +401,21 @@ bool gdb_has_xml; /* XXX: This is not thread safe. Do we care? */ static int gdbserver_fd = -1; -static int get_char(GDBState *s) +static int get_char(void) { uint8_t ch; int ret; for(;;) { - ret = qemu_recv(s->fd, &ch, 1, 0); + ret = qemu_recv(gdbserver_state.fd, &ch, 1, 0); if (ret < 0) { if (errno == ECONNRESET) - s->fd = -1; + gdbserver_state.fd = -1; if (errno != EINTR) return -1; } else if (ret == 0) { - close(s->fd); - s->fd = -1; + close(gdbserver_state.fd); + gdbserver_state.fd = -1; return -1; } else { break; @@ -425,18 +446,18 @@ int use_gdb_syscalls(void) /* -semihosting-config target=auto */ /* On the first call check if gdb is connected and remember. */ if (gdb_syscall_mode == GDB_SYS_UNKNOWN) { - gdb_syscall_mode = (gdbserver_state ? GDB_SYS_ENABLED - : GDB_SYS_DISABLED); + gdb_syscall_mode = gdbserver_state.init ? + GDB_SYS_ENABLED : GDB_SYS_DISABLED; } return gdb_syscall_mode == GDB_SYS_ENABLED; } /* Resume execution. */ -static inline void gdb_continue(GDBState *s) +static inline void gdb_continue(void) { #ifdef CONFIG_USER_ONLY - s->running_state = 1; + gdbserver_state.running_state = 1; trace_gdbstub_op_continue(); #else if (!runstate_needs_reset()) { @@ -450,7 +471,7 @@ static inline void gdb_continue(GDBState *s) * Resume execution, per CPU actions. For user-mode emulation it's * equivalent to gdb_continue. */ -static int gdb_continue_partial(GDBState *s, char *newstates) +static int gdb_continue_partial(char *newstates) { CPUState *cpu; int res = 0; @@ -465,7 +486,7 @@ static int gdb_continue_partial(GDBState *s, char *newstates) cpu_single_step(cpu, sstep_flags); } } - s->running_state = 1; + gdbserver_state.running_state = 1; #else int flag = 0; @@ -503,13 +524,13 @@ static int gdb_continue_partial(GDBState *s, char *newstates) return res; } -static void put_buffer(GDBState *s, const uint8_t *buf, int len) +static void put_buffer(const uint8_t *buf, int len) { #ifdef CONFIG_USER_ONLY int ret; while (len > 0) { - ret = send(s->fd, buf, len, 0); + ret = send(gdbserver_state.fd, buf, len, 0); if (ret < 0) { if (errno != EINTR) return; @@ -521,7 +542,7 @@ static void put_buffer(GDBState *s, const uint8_t *buf, int len) #else /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(&s->chr, buf, len); + qemu_chr_fe_write_all(&gdbserver_state.chr, buf, len); #endif } @@ -546,25 +567,24 @@ static inline int tohex(int v) } /* writes 2*len+1 bytes in buf */ -static void memtohex(char *buf, const uint8_t *mem, int len) +static void memtohex(GString *buf, const uint8_t *mem, int len) { int i, c; - char *q; - q = buf; for(i = 0; i < len; i++) { c = mem[i]; - *q++ = tohex(c >> 4); - *q++ = tohex(c & 0xf); + g_string_append_c(buf, tohex(c >> 4)); + g_string_append_c(buf, tohex(c & 0xf)); } - *q = '\0'; + g_string_append_c(buf, '\0'); } -static void hextomem(uint8_t *mem, const char *buf, int len) +static void hextomem(GByteArray *mem, const char *buf, int len) { int i; for(i = 0; i < len; i++) { - mem[i] = (fromhex(buf[0]) << 4) | fromhex(buf[1]); + guint8 byte = fromhex(buf[0]) << 4 | fromhex(buf[1]); + g_byte_array_append(mem, &byte, 1); buf += 2; } } @@ -603,33 +623,35 @@ static void hexdump(const char *buf, int len, } /* return -1 if error, 0 if OK */ -static int put_packet_binary(GDBState *s, const char *buf, int len, bool dump) +static int put_packet_binary(const char *buf, int len, bool dump) { int csum, i; - uint8_t *p; + uint8_t footer[3]; if (dump && trace_event_get_state_backends(TRACE_GDBSTUB_IO_BINARYREPLY)) { hexdump(buf, len, trace_gdbstub_io_binaryreply); } for(;;) { - p = s->last_packet; - *(p++) = '$'; - memcpy(p, buf, len); - p += len; + g_byte_array_set_size(gdbserver_state.last_packet, 0); + g_byte_array_append(gdbserver_state.last_packet, + (const uint8_t *) "$", 1); + g_byte_array_append(gdbserver_state.last_packet, + (const uint8_t *) buf, len); csum = 0; for(i = 0; i < len; i++) { csum += buf[i]; } - *(p++) = '#'; - *(p++) = tohex((csum >> 4) & 0xf); - *(p++) = tohex((csum) & 0xf); + footer[0] = '#'; + footer[1] = tohex((csum >> 4) & 0xf); + footer[2] = tohex((csum) & 0xf); + g_byte_array_append(gdbserver_state.last_packet, footer, 3); - s->last_packet_len = p - s->last_packet; - put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len); + put_buffer(gdbserver_state.last_packet->data, + gdbserver_state.last_packet->len); #ifdef CONFIG_USER_ONLY - i = get_char(s); + i = get_char(); if (i < 0) return -1; if (i == '+') @@ -642,65 +664,69 @@ static int put_packet_binary(GDBState *s, const char *buf, int len, bool dump) } /* return -1 if error, 0 if OK */ -static int put_packet(GDBState *s, const char *buf) +static int put_packet(const char *buf) { trace_gdbstub_io_reply(buf); - return put_packet_binary(s, buf, strlen(buf), false); + return put_packet_binary(buf, strlen(buf), false); +} + +static void put_strbuf(void) +{ + put_packet(gdbserver_state.str_buf->str); } /* Encode data using the encoding for 'x' packets. */ -static int memtox(char *buf, const char *mem, int len) +static void memtox(GString *buf, const char *mem, int len) { - char *p = buf; char c; while (len--) { c = *(mem++); switch (c) { case '#': case '$': case '*': case '}': - *(p++) = '}'; - *(p++) = c ^ 0x20; + g_string_append_c(buf, '}'); + g_string_append_c(buf, c ^ 0x20); break; default: - *(p++) = c; + g_string_append_c(buf, c); break; } } - return p - buf; } -static uint32_t gdb_get_cpu_pid(const GDBState *s, CPUState *cpu) +static uint32_t gdb_get_cpu_pid(CPUState *cpu) { /* TODO: In user mode, we should use the task state PID */ if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) { /* Return the default process' PID */ - return s->processes[s->process_num - 1].pid; + int index = gdbserver_state.process_num - 1; + return gdbserver_state.processes[index].pid; } return cpu->cluster_index + 1; } -static GDBProcess *gdb_get_process(const GDBState *s, uint32_t pid) +static GDBProcess *gdb_get_process(uint32_t pid) { int i; if (!pid) { /* 0 means any process, we take the first one */ - return &s->processes[0]; + return &gdbserver_state.processes[0]; } - for (i = 0; i < s->process_num; i++) { - if (s->processes[i].pid == pid) { - return &s->processes[i]; + for (i = 0; i < gdbserver_state.process_num; i++) { + if (gdbserver_state.processes[i].pid == pid) { + return &gdbserver_state.processes[i]; } } return NULL; } -static GDBProcess *gdb_get_cpu_process(const GDBState *s, CPUState *cpu) +static GDBProcess *gdb_get_cpu_process(CPUState *cpu) { - return gdb_get_process(s, gdb_get_cpu_pid(s, cpu)); + return gdb_get_process(gdb_get_cpu_pid(cpu)); } static CPUState *find_cpu(uint32_t thread_id) @@ -716,13 +742,12 @@ static CPUState *find_cpu(uint32_t thread_id) return NULL; } -static CPUState *get_first_cpu_in_process(const GDBState *s, - GDBProcess *process) +static CPUState *get_first_cpu_in_process(GDBProcess *process) { CPUState *cpu; CPU_FOREACH(cpu) { - if (gdb_get_cpu_pid(s, cpu) == process->pid) { + if (gdb_get_cpu_pid(cpu) == process->pid) { return cpu; } } @@ -730,13 +755,13 @@ static CPUState *get_first_cpu_in_process(const GDBState *s, return NULL; } -static CPUState *gdb_next_cpu_in_process(const GDBState *s, CPUState *cpu) +static CPUState *gdb_next_cpu_in_process(CPUState *cpu) { - uint32_t pid = gdb_get_cpu_pid(s, cpu); + uint32_t pid = gdb_get_cpu_pid(cpu); cpu = CPU_NEXT(cpu); while (cpu) { - if (gdb_get_cpu_pid(s, cpu) == pid) { + if (gdb_get_cpu_pid(cpu) == pid) { break; } @@ -747,12 +772,12 @@ static CPUState *gdb_next_cpu_in_process(const GDBState *s, CPUState *cpu) } /* Return the cpu following @cpu, while ignoring unattached processes. */ -static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu) +static CPUState *gdb_next_attached_cpu(CPUState *cpu) { cpu = CPU_NEXT(cpu); while (cpu) { - if (gdb_get_cpu_process(s, cpu)->attached) { + if (gdb_get_cpu_process(cpu)->attached) { break; } @@ -763,29 +788,29 @@ static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu) } /* Return the first attached cpu */ -static CPUState *gdb_first_attached_cpu(const GDBState *s) +static CPUState *gdb_first_attached_cpu(void) { CPUState *cpu = first_cpu; - GDBProcess *process = gdb_get_cpu_process(s, cpu); + GDBProcess *process = gdb_get_cpu_process(cpu); if (!process->attached) { - return gdb_next_attached_cpu(s, cpu); + return gdb_next_attached_cpu(cpu); } return cpu; } -static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) +static CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid) { GDBProcess *process; CPUState *cpu; if (!pid && !tid) { /* 0 means any process/thread, we take the first attached one */ - return gdb_first_attached_cpu(s); + return gdb_first_attached_cpu(); } else if (pid && !tid) { /* any thread in a specific process */ - process = gdb_get_process(s, pid); + process = gdb_get_process(pid); if (process == NULL) { return NULL; @@ -795,7 +820,7 @@ static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) return NULL; } - return get_first_cpu_in_process(s, process); + return get_first_cpu_in_process(process); } else { /* a specific thread */ cpu = find_cpu(tid); @@ -804,7 +829,7 @@ static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) return NULL; } - process = gdb_get_cpu_process(s, cpu); + process = gdb_get_cpu_process(cpu); if (pid && process->pid != pid) { return NULL; @@ -818,13 +843,13 @@ static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) } } -static const char *get_feature_xml(const GDBState *s, const char *p, - const char **newp, GDBProcess *process) +static const char *get_feature_xml(const char *p, const char **newp, + GDBProcess *process) { size_t len; int i; const char *name; - CPUState *cpu = get_first_cpu_in_process(s, process); + CPUState *cpu = get_first_cpu_in_process(process); CPUClass *cc = CPU_GET_CLASS(cpu); len = 0; @@ -881,19 +906,19 @@ static const char *get_feature_xml(const GDBState *s, const char *p, return name ? xml_builtin[i][1] : NULL; } -static int gdb_read_register(CPUState *cpu, uint8_t *mem_buf, int reg) +static int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) { CPUClass *cc = CPU_GET_CLASS(cpu); CPUArchState *env = cpu->env_ptr; GDBRegisterState *r; if (reg < cc->gdb_num_core_regs) { - return cc->gdb_read_register(cpu, mem_buf, reg); + return cc->gdb_read_register(cpu, buf, reg); } for (r = cpu->gdb_regs; r; r = r->next) { if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) { - return r->get_reg(env, mem_buf, reg - r->base_reg); + return r->get_reg(env, buf, reg - r->base_reg); } } return 0; @@ -924,7 +949,7 @@ static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) */ void gdb_register_coprocessor(CPUState *cpu, - gdb_reg_cb get_reg, gdb_reg_cb set_reg, + gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, int num_regs, const char *xml, int g_pos) { GDBRegisterState *s; @@ -984,7 +1009,7 @@ static int gdb_breakpoint_insert(int type, target_ulong addr, target_ulong len) int err = 0; if (kvm_enabled()) { - return kvm_insert_breakpoint(gdbserver_state->c_cpu, addr, len, type); + return kvm_insert_breakpoint(gdbserver_state.c_cpu, addr, len, type); } switch (type) { @@ -1021,7 +1046,7 @@ static int gdb_breakpoint_remove(int type, target_ulong addr, target_ulong len) int err = 0; if (kvm_enabled()) { - return kvm_remove_breakpoint(gdbserver_state->c_cpu, addr, len, type); + return kvm_remove_breakpoint(gdbserver_state.c_cpu, addr, len, type); } switch (type) { @@ -1059,13 +1084,13 @@ static inline void gdb_cpu_breakpoint_remove_all(CPUState *cpu) #endif } -static void gdb_process_breakpoint_remove_all(const GDBState *s, GDBProcess *p) +static void gdb_process_breakpoint_remove_all(GDBProcess *p) { - CPUState *cpu = get_first_cpu_in_process(s, p); + CPUState *cpu = get_first_cpu_in_process(p); while (cpu) { gdb_cpu_breakpoint_remove_all(cpu); - cpu = gdb_next_cpu_in_process(s, cpu); + cpu = gdb_next_cpu_in_process(cpu); } } @@ -1074,7 +1099,7 @@ static void gdb_breakpoint_remove_all(void) CPUState *cpu; if (kvm_enabled()) { - kvm_remove_all_breakpoints(gdbserver_state->c_cpu); + kvm_remove_all_breakpoints(gdbserver_state.c_cpu); return; } @@ -1083,25 +1108,22 @@ static void gdb_breakpoint_remove_all(void) } } -static void gdb_set_cpu_pc(GDBState *s, target_ulong pc) +static void gdb_set_cpu_pc(target_ulong pc) { - CPUState *cpu = s->c_cpu; + CPUState *cpu = gdbserver_state.c_cpu; cpu_synchronize_state(cpu); cpu_set_pc(cpu, pc); } -static char *gdb_fmt_thread_id(const GDBState *s, CPUState *cpu, - char *buf, size_t buf_size) +static void gdb_append_thread_id(CPUState *cpu, GString *buf) { - if (s->multiprocess) { - snprintf(buf, buf_size, "p%02x.%02x", - gdb_get_cpu_pid(s, cpu), cpu_gdb_index(cpu)); + if (gdbserver_state.multiprocess) { + g_string_append_printf(buf, "p%02x.%02x", + gdb_get_cpu_pid(cpu), cpu_gdb_index(cpu)); } else { - snprintf(buf, buf_size, "%02x", cpu_gdb_index(cpu)); + g_string_append_printf(buf, "%02x", cpu_gdb_index(cpu)); } - - return buf; } typedef enum GDBThreadIdKind { @@ -1163,7 +1185,7 @@ static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, * returns -ENOTSUP if a command is unsupported, -EINVAL or -ERANGE if there is * a format error, 0 on success. */ -static int gdb_handle_vcont(GDBState *s, const char *p) +static int gdb_handle_vcont(const char *p) { int res, signal = 0; char cur_action; @@ -1238,36 +1260,36 @@ static int gdb_handle_vcont(GDBState *s, const char *p) goto out; case GDB_ALL_PROCESSES: - cpu = gdb_first_attached_cpu(s); + cpu = gdb_first_attached_cpu(); while (cpu) { if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; } - cpu = gdb_next_attached_cpu(s, cpu); + cpu = gdb_next_attached_cpu(cpu); } break; case GDB_ALL_THREADS: - process = gdb_get_process(s, pid); + process = gdb_get_process(pid); if (!process->attached) { res = -EINVAL; goto out; } - cpu = get_first_cpu_in_process(s, process); + cpu = get_first_cpu_in_process(process); while (cpu) { if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; } - cpu = gdb_next_cpu_in_process(s, cpu); + cpu = gdb_next_cpu_in_process(cpu); } break; case GDB_ONE_THREAD: - cpu = gdb_get_cpu(s, pid, tid); + cpu = gdb_get_cpu(pid, tid); /* invalid CPU/thread specified */ if (!cpu) { @@ -1282,8 +1304,8 @@ static int gdb_handle_vcont(GDBState *s, const char *p) break; } } - s->signal = signal; - gdb_continue_partial(s, newstates); + gdbserver_state.signal = signal; + gdb_continue_partial(newstates); out: g_free(newstates); @@ -1392,11 +1414,8 @@ static int cmd_parse_params(const char *data, const char *schema, } typedef struct GdbCmdContext { - GDBState *s; GdbCmdVariant *params; int num_params; - uint8_t mem_buf[MAX_PACKET_LENGTH]; - char str_buf[MAX_PACKET_LENGTH + 1]; } GdbCmdContext; typedef void (*GdbCmdHandler)(GdbCmdContext *gdb_ctx, void *user_ctx); @@ -1436,7 +1455,7 @@ static inline int startswith(const char *string, const char *pattern) return !strncmp(string, pattern, strlen(pattern)); } -static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, +static int process_string_cmd(void *user_ctx, const char *data, const GdbCmdParseEntry *cmds, int num_cmds) { int i, schema_len, max_num_params = 0; @@ -1473,7 +1492,6 @@ static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, return -1; } - gdb_ctx.s = s; cmd->handler(&gdb_ctx, user_ctx); return 0; } @@ -1481,53 +1499,54 @@ static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, return -1; } -static void run_cmd_parser(GDBState *s, const char *data, - const GdbCmdParseEntry *cmd) +static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) { if (!data) { return; } + g_string_set_size(gdbserver_state.str_buf, 0); + g_byte_array_set_size(gdbserver_state.mem_buf, 0); + /* In case there was an error during the command parsing we must * send a NULL packet to indicate the command is not supported */ - if (process_string_cmd(s, NULL, data, cmd, 1)) { - put_packet(s, ""); + if (process_string_cmd(NULL, data, cmd, 1)) { + put_packet(""); } } static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx) { GDBProcess *process; - GDBState *s = gdb_ctx->s; uint32_t pid = 1; - if (s->multiprocess) { + if (gdbserver_state.multiprocess) { if (!gdb_ctx->num_params) { - put_packet(s, "E22"); + put_packet("E22"); return; } pid = gdb_ctx->params[0].val_ul; } - process = gdb_get_process(s, pid); - gdb_process_breakpoint_remove_all(s, process); + process = gdb_get_process(pid); + gdb_process_breakpoint_remove_all(process); process->attached = false; - if (pid == gdb_get_cpu_pid(s, s->c_cpu)) { - s->c_cpu = gdb_first_attached_cpu(s); + if (pid == gdb_get_cpu_pid(gdbserver_state.c_cpu)) { + gdbserver_state.c_cpu = gdb_first_attached_cpu(); } - if (pid == gdb_get_cpu_pid(s, s->g_cpu)) { - s->g_cpu = gdb_first_attached_cpu(s); + if (pid == gdb_get_cpu_pid(gdbserver_state.g_cpu)) { + gdbserver_state.g_cpu = gdb_first_attached_cpu(); } - if (!s->c_cpu) { + if (!gdbserver_state.c_cpu) { /* No more process attached */ gdb_syscall_mode = GDB_SYS_DISABLED; - gdb_continue(s); + gdb_continue(); } - put_packet(s, "OK"); + put_packet("OK"); } static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1535,33 +1554,33 @@ static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx) CPUState *cpu; if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } if (gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - cpu = gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[0].thread_id.pid, + cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid, gdb_ctx->params[0].thread_id.tid); if (!cpu) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_continue(GdbCmdContext *gdb_ctx, void *user_ctx) { if (gdb_ctx->num_params) { - gdb_set_cpu_pc(gdb_ctx->s, gdb_ctx->params[0].val_ull); + gdb_set_cpu_pc(gdb_ctx->params[0].val_ull); } - gdb_ctx->s->signal = 0; - gdb_continue(gdb_ctx->s); + gdbserver_state.signal = 0; + gdb_continue(); } static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1576,11 +1595,11 @@ static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) signal = gdb_ctx->params[0].val_ul; } - gdb_ctx->s->signal = gdb_signal_to_target(signal); - if (gdb_ctx->s->signal == -1) { - gdb_ctx->s->signal = 0; + gdbserver_state.signal = gdb_signal_to_target(signal); + if (gdbserver_state.signal == -1) { + gdbserver_state.signal = 0; } - gdb_continue(gdb_ctx->s); + gdb_continue(); } static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1588,24 +1607,24 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) CPUState *cpu; if (gdb_ctx->num_params != 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } if (gdb_ctx->params[1].thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } if (gdb_ctx->params[1].thread_id.kind != GDB_ONE_THREAD) { - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); return; } - cpu = gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[1].thread_id.pid, + cpu = gdb_get_cpu(gdb_ctx->params[1].thread_id.pid, gdb_ctx->params[1].thread_id.tid); if (!cpu) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -1615,15 +1634,15 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) */ switch (gdb_ctx->params[0].opcode) { case 'c': - gdb_ctx->s->c_cpu = cpu; - put_packet(gdb_ctx->s, "OK"); + gdbserver_state.c_cpu = cpu; + put_packet("OK"); break; case 'g': - gdb_ctx->s->g_cpu = cpu; - put_packet(gdb_ctx->s, "OK"); + gdbserver_state.g_cpu = cpu; + put_packet("OK"); break; default: - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); break; } } @@ -1633,7 +1652,7 @@ static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx) int res; if (gdb_ctx->num_params != 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -1641,14 +1660,14 @@ static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_ctx->params[1].val_ull, gdb_ctx->params[2].val_ull); if (res >= 0) { - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); return; } else if (res == -ENOSYS) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); } static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1656,7 +1675,7 @@ static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) int res; if (gdb_ctx->num_params != 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -1664,14 +1683,14 @@ static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_ctx->params[1].val_ull, gdb_ctx->params[2].val_ull); if (res >= 0) { - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); return; } else if (res == -ENOSYS) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); } /* @@ -1690,20 +1709,20 @@ static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx) int reg_size; if (!gdb_has_xml) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } if (gdb_ctx->num_params != 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } reg_size = strlen(gdb_ctx->params[1].data) / 2; - hextomem(gdb_ctx->mem_buf, gdb_ctx->params[1].data, reg_size); - gdb_write_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf, + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[1].data, reg_size); + gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data, gdb_ctx->params[0].val_ull); - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1711,73 +1730,79 @@ static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx) int reg_size; if (!gdb_has_xml) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E14"); + put_packet("E14"); return; } - reg_size = gdb_read_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf, + reg_size = gdb_read_register(gdbserver_state.g_cpu, + gdbserver_state.mem_buf, gdb_ctx->params[0].val_ull); if (!reg_size) { - put_packet(gdb_ctx->s, "E14"); + put_packet("E14"); return; + } else { + g_byte_array_set_size(gdbserver_state.mem_buf, reg_size); } - memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, reg_size); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, reg_size); + put_strbuf(); } static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx) { if (gdb_ctx->num_params != 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } /* hextomem() reads 2*len bytes */ if (gdb_ctx->params[1].val_ull > strlen(gdb_ctx->params[2].data) / 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - hextomem(gdb_ctx->mem_buf, gdb_ctx->params[2].data, + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[2].data, gdb_ctx->params[1].val_ull); - if (target_memory_rw_debug(gdb_ctx->s->g_cpu, gdb_ctx->params[0].val_ull, - gdb_ctx->mem_buf, - gdb_ctx->params[1].val_ull, true)) { - put_packet(gdb_ctx->s, "E14"); + if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull, + gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len, true)) { + put_packet("E14"); return; } - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx) { if (gdb_ctx->num_params != 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } /* memtohex() doubles the required space */ if (gdb_ctx->params[1].val_ull > MAX_PACKET_LENGTH / 2) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - if (target_memory_rw_debug(gdb_ctx->s->g_cpu, gdb_ctx->params[0].val_ull, - gdb_ctx->mem_buf, - gdb_ctx->params[1].val_ull, false)) { - put_packet(gdb_ctx->s, "E14"); + g_byte_array_set_size(gdbserver_state.mem_buf, gdb_ctx->params[1].val_ull); + + if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull, + gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len, false)) { + put_packet("E14"); return; } - memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, gdb_ctx->params[1].val_ull); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len); + put_strbuf(); } static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1790,37 +1815,40 @@ static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - cpu_synchronize_state(gdb_ctx->s->g_cpu); - registers = gdb_ctx->mem_buf; + cpu_synchronize_state(gdbserver_state.g_cpu); len = strlen(gdb_ctx->params[0].data) / 2; - hextomem(registers, gdb_ctx->params[0].data, len); - for (addr = 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs && len > 0; + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len); + registers = gdbserver_state.mem_buf->data; + for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0; addr++) { - reg_size = gdb_write_register(gdb_ctx->s->g_cpu, registers, addr); + reg_size = gdb_write_register(gdbserver_state.g_cpu, registers, addr); len -= reg_size; registers += reg_size; } - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) { target_ulong addr, len; - cpu_synchronize_state(gdb_ctx->s->g_cpu); + cpu_synchronize_state(gdbserver_state.g_cpu); + g_byte_array_set_size(gdbserver_state.mem_buf, 0); len = 0; - for (addr = 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs; addr++) { - len += gdb_read_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf + len, + for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs; addr++) { + len += gdb_read_register(gdbserver_state.g_cpu, + gdbserver_state.mem_buf, addr); } + g_assert(len == gdbserver_state.mem_buf->len); - memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len); + put_strbuf(); } static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) { - if (gdb_ctx->num_params >= 1 && gdb_ctx->s->current_syscall_cb) { + if (gdb_ctx->num_params >= 1 && gdbserver_state.current_syscall_cb) { target_ulong ret, err; ret = (target_ulong)gdb_ctx->params[0].val_ull; @@ -1829,31 +1857,31 @@ static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) } else { err = 0; } - gdb_ctx->s->current_syscall_cb(gdb_ctx->s->c_cpu, ret, err); - gdb_ctx->s->current_syscall_cb = NULL; + gdbserver_state.current_syscall_cb(gdbserver_state.c_cpu, ret, err); + gdbserver_state.current_syscall_cb = NULL; } if (gdb_ctx->num_params >= 3 && gdb_ctx->params[2].opcode == (uint8_t)'C') { - put_packet(gdb_ctx->s, "T02"); + put_packet("T02"); return; } - gdb_continue(gdb_ctx->s); + gdb_continue(); } static void handle_step(GdbCmdContext *gdb_ctx, void *user_ctx) { if (gdb_ctx->num_params) { - gdb_set_cpu_pc(gdb_ctx->s, (target_ulong)gdb_ctx->params[0].val_ull); + gdb_set_cpu_pc((target_ulong)gdb_ctx->params[0].val_ull); } - cpu_single_step(gdb_ctx->s->c_cpu, sstep_flags); - gdb_continue(gdb_ctx->s); + cpu_single_step(gdbserver_state.c_cpu, sstep_flags); + gdb_continue(); } static void handle_v_cont_query(GdbCmdContext *gdb_ctx, void *user_ctx) { - put_packet(gdb_ctx->s, "vCont;c;C;s;S"); + put_packet("vCont;c;C;s;S"); } static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1864,11 +1892,11 @@ static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - res = gdb_handle_vcont(gdb_ctx->s, gdb_ctx->params[0].data); + res = gdb_handle_vcont(gdb_ctx->params[0].data); if ((res == -EINVAL) || (res == -ERANGE)) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); } else if (res) { - put_packet(gdb_ctx->s, ""); + put_packet(""); } } @@ -1876,38 +1904,37 @@ static void handle_v_attach(GdbCmdContext *gdb_ctx, void *user_ctx) { GDBProcess *process; CPUState *cpu; - char thread_id[16]; - pstrcpy(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "E22"); + g_string_assign(gdbserver_state.str_buf, "E22"); if (!gdb_ctx->num_params) { goto cleanup; } - process = gdb_get_process(gdb_ctx->s, gdb_ctx->params[0].val_ul); + process = gdb_get_process(gdb_ctx->params[0].val_ul); if (!process) { goto cleanup; } - cpu = get_first_cpu_in_process(gdb_ctx->s, process); + cpu = get_first_cpu_in_process(process); if (!cpu) { goto cleanup; } process->attached = true; - gdb_ctx->s->g_cpu = cpu; - gdb_ctx->s->c_cpu = cpu; + gdbserver_state.g_cpu = cpu; + gdbserver_state.c_cpu = cpu; - gdb_fmt_thread_id(gdb_ctx->s, cpu, thread_id, sizeof(thread_id)); - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "T%02xthread:%s;", - GDB_SIGNAL_TRAP, thread_id); + g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); + gdb_append_thread_id(cpu, gdbserver_state.str_buf); + g_string_append_c(gdbserver_state.str_buf, ';'); cleanup: - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + put_strbuf(); } static void handle_v_kill(GdbCmdContext *gdb_ctx, void *user_ctx) { /* Kill the target */ - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); error_report("QEMU: Terminated via GDBstub"); exit(0); } @@ -1944,19 +1971,18 @@ static void handle_v_commands(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_v_commands_table, ARRAY_SIZE(gdb_v_commands_table))) { - put_packet(gdb_ctx->s, ""); + put_packet(""); } } static void handle_query_qemu_sstepbits(GdbCmdContext *gdb_ctx, void *user_ctx) { - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), - "ENABLE=%x,NOIRQ=%x,NOTIMER=%x", SSTEP_ENABLE, - SSTEP_NOIRQ, SSTEP_NOTIMER); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_printf(gdbserver_state.str_buf, "ENABLE=%x,NOIRQ=%x,NOTIMER=%x", + SSTEP_ENABLE, SSTEP_NOIRQ, SSTEP_NOTIMER); + put_strbuf(); } static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -1966,68 +1992,63 @@ static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) } sstep_flags = gdb_ctx->params[0].val_ul; - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } static void handle_query_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) { - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "0x%x", sstep_flags); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_printf(gdbserver_state.str_buf, "0x%x", sstep_flags); + put_strbuf(); } static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, void *user_ctx) { CPUState *cpu; GDBProcess *process; - char thread_id[16]; /* * "Current thread" remains vague in the spec, so always return * the first thread of the current process (gdb returns the * first thread). */ - process = gdb_get_cpu_process(gdb_ctx->s, gdb_ctx->s->g_cpu); - cpu = get_first_cpu_in_process(gdb_ctx->s, process); - gdb_fmt_thread_id(gdb_ctx->s, cpu, thread_id, sizeof(thread_id)); - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "QC%s", thread_id); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + process = gdb_get_cpu_process(gdbserver_state.g_cpu); + cpu = get_first_cpu_in_process(process); + g_string_assign(gdbserver_state.str_buf, "QC"); + gdb_append_thread_id(cpu, gdbserver_state.str_buf); + put_strbuf(); } static void handle_query_threads(GdbCmdContext *gdb_ctx, void *user_ctx) { - char thread_id[16]; - - if (!gdb_ctx->s->query_cpu) { - put_packet(gdb_ctx->s, "l"); + if (!gdbserver_state.query_cpu) { + put_packet("l"); return; } - gdb_fmt_thread_id(gdb_ctx->s, gdb_ctx->s->query_cpu, thread_id, - sizeof(thread_id)); - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "m%s", thread_id); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); - gdb_ctx->s->query_cpu = - gdb_next_attached_cpu(gdb_ctx->s, gdb_ctx->s->query_cpu); + g_string_assign(gdbserver_state.str_buf, "m"); + gdb_append_thread_id(gdbserver_state.query_cpu, gdbserver_state.str_buf); + put_strbuf(); + gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); } static void handle_query_first_threads(GdbCmdContext *gdb_ctx, void *user_ctx) { - gdb_ctx->s->query_cpu = gdb_first_attached_cpu(gdb_ctx->s); + gdbserver_state.query_cpu = gdb_first_attached_cpu(); handle_query_threads(gdb_ctx, user_ctx); } static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx) { + g_autoptr(GString) rs = g_string_new(NULL); CPUState *cpu; - int len; if (!gdb_ctx->num_params || gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - cpu = gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[0].thread_id.pid, + cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid, gdb_ctx->params[0].thread_id.tid); if (!cpu) { return; @@ -2035,24 +2056,21 @@ static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx) cpu_synchronize_state(cpu); - if (gdb_ctx->s->multiprocess && (gdb_ctx->s->process_num > 1)) { + if (gdbserver_state.multiprocess && (gdbserver_state.process_num > 1)) { /* Print the CPU model and name in multiprocess mode */ ObjectClass *oc = object_get_class(OBJECT(cpu)); const char *cpu_model = object_class_get_name(oc); - char *cpu_name = object_get_canonical_path_component(OBJECT(cpu)); - len = snprintf((char *)gdb_ctx->mem_buf, sizeof(gdb_ctx->str_buf) / 2, - "%s %s [%s]", cpu_model, cpu_name, - cpu->halted ? "halted " : "running"); - g_free(cpu_name); + g_autofree char *cpu_name; + cpu_name = object_get_canonical_path_component(OBJECT(cpu)); + g_string_printf(rs, "%s %s [%s]", cpu_model, cpu_name, + cpu->halted ? "halted " : "running"); } else { - /* memtohex() doubles the required space */ - len = snprintf((char *)gdb_ctx->mem_buf, sizeof(gdb_ctx->str_buf) / 2, - "CPU#%d [%s]", cpu->cpu_index, + g_string_printf(rs, "CPU#%d [%s]", cpu->cpu_index, cpu->halted ? "halted " : "running"); } - trace_gdbstub_op_extra_info((char *)gdb_ctx->mem_buf); - memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + trace_gdbstub_op_extra_info(rs->str); + memtohex(gdbserver_state.str_buf, (uint8_t *)rs->str, rs->len); + put_strbuf(); } #ifdef CONFIG_USER_ONLY @@ -2060,37 +2078,40 @@ static void handle_query_offsets(GdbCmdContext *gdb_ctx, void *user_ctx) { TaskState *ts; - ts = gdb_ctx->s->c_cpu->opaque; - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), - "Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx - ";Bss=" TARGET_ABI_FMT_lx, - ts->info->code_offset, - ts->info->data_offset, - ts->info->data_offset); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + ts = gdbserver_state.c_cpu->opaque; + g_string_printf(gdbserver_state.str_buf, + "Text=" TARGET_ABI_FMT_lx + ";Data=" TARGET_ABI_FMT_lx + ";Bss=" TARGET_ABI_FMT_lx, + ts->info->code_offset, + ts->info->data_offset, + ts->info->data_offset); + put_strbuf(); } #else static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx) { + const guint8 zero = 0; int len; if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } len = strlen(gdb_ctx->params[0].data); if (len % 2) { - put_packet(gdb_ctx->s, "E01"); + put_packet("E01"); return; } + g_assert(gdbserver_state.mem_buf->len == 0); len = len / 2; - hextomem(gdb_ctx->mem_buf, gdb_ctx->params[0].data, len); - gdb_ctx->mem_buf[len++] = 0; - qemu_chr_be_write(gdb_ctx->s->mon_chr, gdb_ctx->mem_buf, len); - put_packet(gdb_ctx->s, "OK"); - + hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len); + g_byte_array_append(gdbserver_state.mem_buf, &zero, 1); + qemu_chr_be_write(gdbserver_state.mon_chr, gdbserver_state.mem_buf->data, + gdbserver_state.mem_buf->len); + put_packet("OK"); } #endif @@ -2098,21 +2119,19 @@ static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx) { CPUClass *cc; - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "PacketSize=%x", - MAX_PACKET_LENGTH); + g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH); cc = CPU_GET_CLASS(first_cpu); if (cc->gdb_core_xml_file) { - pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), - ";qXfer:features:read+"); + g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); } if (gdb_ctx->num_params && strstr(gdb_ctx->params[0].data, "multiprocess+")) { - gdb_ctx->s->multiprocess = true; + gdbserver_state.multiprocess = true; } - pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";multiprocess+"); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+"); + put_strbuf(); } static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -2124,22 +2143,22 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) const char *p; if (gdb_ctx->num_params < 3) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } - process = gdb_get_cpu_process(gdb_ctx->s, gdb_ctx->s->g_cpu); - cc = CPU_GET_CLASS(gdb_ctx->s->g_cpu); + process = gdb_get_cpu_process(gdbserver_state.g_cpu); + cc = CPU_GET_CLASS(gdbserver_state.g_cpu); if (!cc->gdb_core_xml_file) { - put_packet(gdb_ctx->s, ""); + put_packet(""); return; } gdb_has_xml = true; p = gdb_ctx->params[0].data; - xml = get_feature_xml(gdb_ctx->s, p, &p, process); + xml = get_feature_xml(p, &p, process); if (!xml) { - put_packet(gdb_ctx->s, "E00"); + put_packet("E00"); return; } @@ -2147,7 +2166,7 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) len = gdb_ctx->params[2].val_ul; total_len = strlen(xml); if (addr > total_len) { - put_packet(gdb_ctx->s, "E00"); + put_packet("E00"); return; } @@ -2156,42 +2175,43 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx) } if (len < total_len - addr) { - gdb_ctx->str_buf[0] = 'm'; - len = memtox(gdb_ctx->str_buf + 1, xml + addr, len); + g_string_assign(gdbserver_state.str_buf, "m"); + memtox(gdbserver_state.str_buf, xml + addr, len); } else { - gdb_ctx->str_buf[0] = 'l'; - len = memtox(gdb_ctx->str_buf + 1, xml + addr, total_len - addr); + g_string_assign(gdbserver_state.str_buf, "l"); + memtox(gdbserver_state.str_buf, xml + addr, total_len - addr); } - put_packet_binary(gdb_ctx->s, gdb_ctx->str_buf, len + 1, true); + put_packet_binary(gdbserver_state.str_buf->str, + gdbserver_state.str_buf->len, true); } static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) { - put_packet(gdb_ctx->s, GDB_ATTACHED); + put_packet(GDB_ATTACHED); } static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx) { - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "sstepbits;sstep"); + g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep"); #ifndef CONFIG_USER_ONLY - pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";PhyMemMode"); + g_string_append(gdbserver_state.str_buf, ";PhyMemMode"); #endif - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + put_strbuf(); } #ifndef CONFIG_USER_ONLY static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) { - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "%d", phy_memory_mode); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode); + put_strbuf(); } static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) { if (!gdb_ctx->num_params) { - put_packet(gdb_ctx->s, "E22"); + put_packet("E22"); return; } @@ -2200,7 +2220,7 @@ static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) } else { phy_memory_mode = 1; } - put_packet(gdb_ctx->s, "OK"); + put_packet("OK"); } #endif @@ -2316,16 +2336,16 @@ static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - if (!process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (!process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_gen_query_table, ARRAY_SIZE(gdb_gen_query_table))) { - put_packet(gdb_ctx->s, ""); + put_packet(""); } } @@ -2335,28 +2355,25 @@ static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx) return; } - if (!process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (!process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_gen_query_set_common_table, ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + if (process_string_cmd(NULL, gdb_ctx->params[0].data, gdb_gen_set_table, ARRAY_SIZE(gdb_gen_set_table))) { - put_packet(gdb_ctx->s, ""); + put_packet(""); } } static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) { - char thread_id[16]; - - gdb_fmt_thread_id(gdb_ctx->s, gdb_ctx->s->c_cpu, thread_id, - sizeof(thread_id)); - snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "T%02xthread:%s;", - GDB_SIGNAL_TRAP, thread_id); - put_packet(gdb_ctx->s, gdb_ctx->str_buf); + g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP); + gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf); + g_string_append_c(gdbserver_state.str_buf, ';'); + put_strbuf(); /* * Remove all the breakpoints when this query is issued, * because gdb is doing an initial connect and the state @@ -2365,7 +2382,7 @@ static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_breakpoint_remove_all(); } -static int gdb_handle_packet(GDBState *s, const char *line_buf) +static int gdb_handle_packet(const char *line_buf) { const GdbCmdParseEntry *cmd_parser = NULL; @@ -2373,7 +2390,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) switch (line_buf[0]) { case '!': - put_packet(s, "OK"); + put_packet("OK"); break; case '?': { @@ -2588,12 +2605,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) break; default: /* put empty packet */ - put_packet(s, ""); + put_packet(""); break; } if (cmd_parser) { - run_cmd_parser(s, line_buf, cmd_parser); + run_cmd_parser(line_buf, cmd_parser); } return RS_IDLE; @@ -2601,7 +2618,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) void gdb_set_stop_cpu(CPUState *cpu) { - GDBProcess *p = gdb_get_cpu_process(gdbserver_state, cpu); + GDBProcess *p = gdb_get_cpu_process(cpu); if (!p->attached) { /* @@ -2611,26 +2628,25 @@ void gdb_set_stop_cpu(CPUState *cpu) return; } - gdbserver_state->c_cpu = cpu; - gdbserver_state->g_cpu = cpu; + gdbserver_state.c_cpu = cpu; + gdbserver_state.g_cpu = cpu; } #ifndef CONFIG_USER_ONLY static void gdb_vm_state_change(void *opaque, int running, RunState state) { - GDBState *s = gdbserver_state; - CPUState *cpu = s->c_cpu; - char buf[256]; - char thread_id[16]; + CPUState *cpu = gdbserver_state.c_cpu; + g_autoptr(GString) buf = g_string_new(NULL); + g_autoptr(GString) tid = g_string_new(NULL); const char *type; int ret; - if (running || s->state == RS_INACTIVE) { + if (running || gdbserver_state.state == RS_INACTIVE) { return; } /* Is there a GDB syscall waiting to be sent? */ - if (s->current_syscall_cb) { - put_packet(s, s->syscall_buf); + if (gdbserver_state.current_syscall_cb) { + put_packet(gdbserver_state.syscall_buf); return; } @@ -2639,7 +2655,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) return; } - gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id)); + gdb_append_thread_id(cpu, tid); switch (state) { case RUN_STATE_DEBUG: @@ -2657,10 +2673,9 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) } trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu), (target_ulong)cpu->watchpoint_hit->vaddr); - snprintf(buf, sizeof(buf), - "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";", - GDB_SIGNAL_TRAP, thread_id, type, - (target_ulong)cpu->watchpoint_hit->vaddr); + g_string_printf(buf, "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";", + GDB_SIGNAL_TRAP, tid->str, type, + (target_ulong)cpu->watchpoint_hit->vaddr); cpu->watchpoint_hit = NULL; goto send_packet; } else { @@ -2701,10 +2716,10 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) break; } gdb_set_stop_cpu(cpu); - snprintf(buf, sizeof(buf), "T%02xthread:%s;", ret, thread_id); + g_string_printf(buf, "T%02xthread:%s;", ret, tid->str); send_packet: - put_packet(s, buf); + put_packet(buf->str); /* disable single step if it was enabled */ cpu_single_step(cpu, 0); @@ -2722,17 +2737,17 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) char *p_end; target_ulong addr; uint64_t i64; - GDBState *s; - s = gdbserver_state; - if (!s) + if (!gdbserver_state.init) { return; - s->current_syscall_cb = cb; + } + + gdbserver_state.current_syscall_cb = cb; #ifndef CONFIG_USER_ONLY vm_stop(RUN_STATE_DEBUG); #endif - p = s->syscall_buf; - p_end = &s->syscall_buf[sizeof(s->syscall_buf)]; + p = &gdbserver_state.syscall_buf[0]; + p_end = &gdbserver_state.syscall_buf[sizeof(gdbserver_state.syscall_buf)]; *(p++) = 'F'; while (*fmt) { if (*fmt == '%') { @@ -2765,14 +2780,14 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) } *p = 0; #ifdef CONFIG_USER_ONLY - put_packet(s, s->syscall_buf); + put_packet(gdbserver_state.syscall_buf); /* Return control to gdb for it to process the syscall request. * Since the protocol requires that gdb hands control back to us * using a "here are the results" F packet, we don't need to check * gdb_handlesig's return value (which is the signal to deliver if * execution was resumed via a continue packet). */ - gdb_handlesig(s->c_cpu, 0); + gdb_handlesig(gdbserver_state.c_cpu, 0); #else /* In this case wait to send the syscall packet until notification that the CPU has stopped. This must be done because if the packet is sent @@ -2780,7 +2795,7 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) is still in the running state, which can cause packets to be dropped and state transition 'T' packets to be sent while the syscall is still being processed. */ - qemu_cpu_kick(s->c_cpu); + qemu_cpu_kick(gdbserver_state.c_cpu); #endif } @@ -2793,25 +2808,27 @@ void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...) va_end(va); } -static void gdb_read_byte(GDBState *s, uint8_t ch) +static void gdb_read_byte(uint8_t ch) { uint8_t reply; #ifndef CONFIG_USER_ONLY - if (s->last_packet_len) { + if (gdbserver_state.last_packet->len) { /* Waiting for a response to the last packet. If we see the start of a new command then abandon the previous response. */ if (ch == '-') { trace_gdbstub_err_got_nack(); - put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len); + put_buffer(gdbserver_state.last_packet->data, + gdbserver_state.last_packet->len); } else if (ch == '+') { trace_gdbstub_io_got_ack(); } else { trace_gdbstub_io_got_unexpected(ch); } - if (ch == '+' || ch == '$') - s->last_packet_len = 0; + if (ch == '+' || ch == '$') { + g_byte_array_set_size(gdbserver_state.last_packet, 0); + } if (ch != '$') return; } @@ -2822,13 +2839,13 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) } else #endif { - switch(s->state) { + switch(gdbserver_state.state) { case RS_IDLE: if (ch == '$') { /* start of command packet */ - s->line_buf_index = 0; - s->line_sum = 0; - s->state = RS_GETLINE; + gdbserver_state.line_buf_index = 0; + gdbserver_state.line_sum = 0; + gdbserver_state.state = RS_GETLINE; } else { trace_gdbstub_err_garbage(ch); } @@ -2836,37 +2853,37 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) case RS_GETLINE: if (ch == '}') { /* start escape sequence */ - s->state = RS_GETLINE_ESC; - s->line_sum += ch; + gdbserver_state.state = RS_GETLINE_ESC; + gdbserver_state.line_sum += ch; } else if (ch == '*') { /* start run length encoding sequence */ - s->state = RS_GETLINE_RLE; - s->line_sum += ch; + gdbserver_state.state = RS_GETLINE_RLE; + gdbserver_state.line_sum += ch; } else if (ch == '#') { /* end of command, start of checksum*/ - s->state = RS_CHKSUM1; - } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { + gdbserver_state.state = RS_CHKSUM1; + } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { trace_gdbstub_err_overrun(); - s->state = RS_IDLE; + gdbserver_state.state = RS_IDLE; } else { /* unescaped command character */ - s->line_buf[s->line_buf_index++] = ch; - s->line_sum += ch; + gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch; + gdbserver_state.line_sum += ch; } break; case RS_GETLINE_ESC: if (ch == '#') { /* unexpected end of command in escape sequence */ - s->state = RS_CHKSUM1; - } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { + gdbserver_state.state = RS_CHKSUM1; + } else if (gdbserver_state.line_buf_index >= sizeof(gdbserver_state.line_buf) - 1) { /* command buffer overrun */ trace_gdbstub_err_overrun(); - s->state = RS_IDLE; + gdbserver_state.state = RS_IDLE; } else { /* parse escaped character and leave escape state */ - s->line_buf[s->line_buf_index++] = ch ^ 0x20; - s->line_sum += ch; - s->state = RS_GETLINE; + gdbserver_state.line_buf[gdbserver_state.line_buf_index++] = ch ^ 0x20; + gdbserver_state.line_sum += ch; + gdbserver_state.state = RS_GETLINE; } break; case RS_GETLINE_RLE: @@ -2877,25 +2894,25 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) if (ch < ' ' || ch == '#' || ch == '$' || ch > 126) { /* invalid RLE count encoding */ trace_gdbstub_err_invalid_repeat(ch); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; } else { /* decode repeat length */ int repeat = ch - ' ' + 3; - if (s->line_buf_index + repeat >= sizeof(s->line_buf) - 1) { + if (gdbserver_state.line_buf_index + repeat >= sizeof(gdbserver_state.line_buf) - 1) { /* that many repeats would overrun the command buffer */ trace_gdbstub_err_overrun(); - s->state = RS_IDLE; - } else if (s->line_buf_index < 1) { + gdbserver_state.state = RS_IDLE; + } else if (gdbserver_state.line_buf_index < 1) { /* got a repeat but we have nothing to repeat */ trace_gdbstub_err_invalid_rle(); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; } else { /* repeat the last character */ - memset(s->line_buf + s->line_buf_index, - s->line_buf[s->line_buf_index - 1], repeat); - s->line_buf_index += repeat; - s->line_sum += ch; - s->state = RS_GETLINE; + memset(gdbserver_state.line_buf + gdbserver_state.line_buf_index, + gdbserver_state.line_buf[gdbserver_state.line_buf_index - 1], repeat); + gdbserver_state.line_buf_index += repeat; + gdbserver_state.line_sum += ch; + gdbserver_state.state = RS_GETLINE; } } break; @@ -2903,33 +2920,33 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) /* get high hex digit of checksum */ if (!isxdigit(ch)) { trace_gdbstub_err_checksum_invalid(ch); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; break; } - s->line_buf[s->line_buf_index] = '\0'; - s->line_csum = fromhex(ch) << 4; - s->state = RS_CHKSUM2; + gdbserver_state.line_buf[gdbserver_state.line_buf_index] = '\0'; + gdbserver_state.line_csum = fromhex(ch) << 4; + gdbserver_state.state = RS_CHKSUM2; break; case RS_CHKSUM2: /* get low hex digit of checksum */ if (!isxdigit(ch)) { trace_gdbstub_err_checksum_invalid(ch); - s->state = RS_GETLINE; + gdbserver_state.state = RS_GETLINE; break; } - s->line_csum |= fromhex(ch); + gdbserver_state.line_csum |= fromhex(ch); - if (s->line_csum != (s->line_sum & 0xff)) { - trace_gdbstub_err_checksum_incorrect(s->line_sum, s->line_csum); + if (gdbserver_state.line_csum != (gdbserver_state.line_sum & 0xff)) { + trace_gdbstub_err_checksum_incorrect(gdbserver_state.line_sum, gdbserver_state.line_csum); /* send NAK reply */ reply = '-'; - put_buffer(s, &reply, 1); - s->state = RS_IDLE; + put_buffer(&reply, 1); + gdbserver_state.state = RS_IDLE; } else { /* send ACK reply */ reply = '+'; - put_buffer(s, &reply, 1); - s->state = gdb_handle_packet(s, s->line_buf); + put_buffer(&reply, 1); + gdbserver_state.state = gdb_handle_packet(gdbserver_state.line_buf); } break; default: @@ -2941,15 +2958,13 @@ static void gdb_read_byte(GDBState *s, uint8_t ch) /* Tell the remote gdb that the process has exited. */ void gdb_exit(CPUArchState *env, int code) { - GDBState *s; char buf[4]; - s = gdbserver_state; - if (!s) { + if (!gdbserver_state.init) { return; } #ifdef CONFIG_USER_ONLY - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return; } #endif @@ -2957,10 +2972,10 @@ void gdb_exit(CPUArchState *env, int code) trace_gdbstub_op_exiting((uint8_t)code); snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); - put_packet(s, buf); + put_packet(buf); #ifndef CONFIG_USER_ONLY - qemu_chr_fe_deinit(&s->chr, true); + qemu_chr_fe_deinit(&gdbserver_state.chr, true); #endif } @@ -2974,7 +2989,7 @@ static void create_default_process(GDBState *s) GDBProcess *process; int max_pid = 0; - if (s->process_num) { + if (gdbserver_state.process_num) { max_pid = s->processes[s->process_num - 1].pid; } @@ -2993,12 +3008,10 @@ static void create_default_process(GDBState *s) int gdb_handlesig(CPUState *cpu, int sig) { - GDBState *s; char buf[256]; int n; - s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return sig; } @@ -3008,58 +3021,55 @@ gdb_handlesig(CPUState *cpu, int sig) if (sig != 0) { snprintf(buf, sizeof(buf), "S%02x", target_signal_to_gdb(sig)); - put_packet(s, buf); + put_packet(buf); } /* put_packet() might have detected that the peer terminated the connection. */ - if (s->fd < 0) { + if (gdbserver_state.fd < 0) { return sig; } sig = 0; - s->state = RS_IDLE; - s->running_state = 0; - while (s->running_state == 0) { - n = read(s->fd, buf, 256); + gdbserver_state.state = RS_IDLE; + gdbserver_state.running_state = 0; + while (gdbserver_state.running_state == 0) { + n = read(gdbserver_state.fd, buf, 256); if (n > 0) { int i; for (i = 0; i < n; i++) { - gdb_read_byte(s, buf[i]); + gdb_read_byte(buf[i]); } } else { /* XXX: Connection closed. Should probably wait for another connection before continuing. */ if (n == 0) { - close(s->fd); + close(gdbserver_state.fd); } - s->fd = -1; + gdbserver_state.fd = -1; return sig; } } - sig = s->signal; - s->signal = 0; + sig = gdbserver_state.signal; + gdbserver_state.signal = 0; return sig; } /* Tell the remote gdb that the process has exited due to SIG. */ void gdb_signalled(CPUArchState *env, int sig) { - GDBState *s; char buf[4]; - s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return; } snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb(sig)); - put_packet(s, buf); + put_packet(buf); } static bool gdb_accept(void) { - GDBState *s; struct sockaddr_in sockaddr; socklen_t len; int fd; @@ -3083,15 +3093,13 @@ static bool gdb_accept(void) return false; } - s = g_malloc0(sizeof(GDBState)); - create_default_process(s); - s->processes[0].attached = true; - s->c_cpu = gdb_first_attached_cpu(s); - s->g_cpu = s->c_cpu; - s->fd = fd; + init_gdbserver_state(); + create_default_process(&gdbserver_state); + gdbserver_state.processes[0].attached = true; + gdbserver_state.c_cpu = gdb_first_attached_cpu(); + gdbserver_state.g_cpu = gdbserver_state.c_cpu; + gdbserver_state.fd = fd; gdb_has_xml = false; - - gdbserver_state = s; return true; } @@ -3144,13 +3152,11 @@ int gdbserver_start(int port) /* Disable gdb stub for child processes. */ void gdbserver_fork(CPUState *cpu) { - GDBState *s = gdbserver_state; - - if (gdbserver_fd < 0 || s->fd < 0) { + if (gdbserver_fd < 0 || gdbserver_state.fd < 0) { return; } - close(s->fd); - s->fd = -1; + close(gdbserver_state.fd); + gdbserver_state.fd = -1; cpu_breakpoint_remove_all(cpu, BP_GDB); cpu_watchpoint_remove_all(cpu, BP_GDB); } @@ -3167,7 +3173,7 @@ static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) int i; for (i = 0; i < size; i++) { - gdb_read_byte(gdbserver_state, buf[i]); + gdb_read_byte(buf[i]); } } @@ -3183,7 +3189,7 @@ static void gdb_chr_event(void *opaque, QEMUChrEvent event) s->processes[i].attached = !i; } - s->c_cpu = gdb_first_attached_cpu(s); + s->c_cpu = gdb_first_attached_cpu(); s->g_cpu = s->c_cpu; vm_stop(RUN_STATE_PAUSED); @@ -3194,32 +3200,11 @@ static void gdb_chr_event(void *opaque, QEMUChrEvent event) } } -static void gdb_monitor_output(GDBState *s, const char *msg, int len) -{ - char buf[MAX_PACKET_LENGTH]; - - buf[0] = 'O'; - if (len > (MAX_PACKET_LENGTH/2) - 1) - len = (MAX_PACKET_LENGTH/2) - 1; - memtohex(buf + 1, (uint8_t *)msg, len); - put_packet(s, buf); -} - static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len) { - const char *p = (const char *)buf; - int max_sz; - - max_sz = (sizeof(gdbserver_state->last_packet) - 2) / 2; - for (;;) { - if (len <= max_sz) { - gdb_monitor_output(gdbserver_state, p, len); - break; - } - gdb_monitor_output(gdbserver_state, p, max_sz); - p += max_sz; - len -= max_sz; - } + g_autoptr(GString) hex_buf = g_string_new("O"); + memtohex(hex_buf, buf, len); + put_packet(hex_buf->str); return len; } @@ -3300,26 +3285,18 @@ static void create_processes(GDBState *s) { object_child_foreach(object_get_root(), find_cpu_clusters, s); - if (s->processes) { + if (gdbserver_state.processes) { /* Sort by PID */ - qsort(s->processes, s->process_num, sizeof(s->processes[0]), pid_order); + qsort(gdbserver_state.processes, gdbserver_state.process_num, sizeof(gdbserver_state.processes[0]), pid_order); } create_default_process(s); } -static void cleanup_processes(GDBState *s) -{ - g_free(s->processes); - s->process_num = 0; - s->processes = NULL; -} - int gdbserver_start(const char *device) { trace_gdbstub_op_start(device); - GDBState *s; char gdbstub_device_name[128]; Chardev *chr = NULL; Chardev *mon_chr; @@ -3357,10 +3334,8 @@ int gdbserver_start(const char *device) return -1; } - s = gdbserver_state; - if (!s) { - s = g_malloc0(sizeof(GDBState)); - gdbserver_state = s; + if (!gdbserver_state.init) { + init_gdbserver_state(); qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); @@ -3369,31 +3344,30 @@ int gdbserver_start(const char *device) NULL, NULL, &error_abort); monitor_init_hmp(mon_chr, false, &error_abort); } else { - qemu_chr_fe_deinit(&s->chr, true); - mon_chr = s->mon_chr; - cleanup_processes(s); - memset(s, 0, sizeof(GDBState)); - s->mon_chr = mon_chr; + qemu_chr_fe_deinit(&gdbserver_state.chr, true); + mon_chr = gdbserver_state.mon_chr; + reset_gdbserver_state(); } - create_processes(s); + create_processes(&gdbserver_state); if (chr) { - qemu_chr_fe_init(&s->chr, chr, &error_abort); - qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive, - gdb_chr_event, NULL, s, NULL, true); + qemu_chr_fe_init(&gdbserver_state.chr, chr, &error_abort); + qemu_chr_fe_set_handlers(&gdbserver_state.chr, gdb_chr_can_receive, + gdb_chr_receive, gdb_chr_event, + NULL, &gdbserver_state, NULL, true); } - s->state = chr ? RS_IDLE : RS_INACTIVE; - s->mon_chr = mon_chr; - s->current_syscall_cb = NULL; + gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE; + gdbserver_state.mon_chr = mon_chr; + gdbserver_state.current_syscall_cb = NULL; return 0; } void gdbserver_cleanup(void) { - if (gdbserver_state) { - put_packet(gdbserver_state, "W00"); + if (gdbserver_state.init) { + put_packet("W00"); } } diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 499d6d54b0..ca5198438d 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -1,9 +1,9 @@ -HXCOMM Use DEFHEADING() to define headings in both help text and texi -HXCOMM Text between STEXI and ETEXI are copied to texi version and -HXCOMM discarded from C version +HXCOMM Use DEFHEADING() to define headings in both help text and rST. +HXCOMM Text between SRST and ERST is copied to the rST version and +HXCOMM discarded from C version. HXCOMM DEF(command, args, callback, arg_string, help) is used to construct HXCOMM monitor info commands -HXCOMM HXCOMM can be used for comments, discarded from both texi and C +HXCOMM HXCOMM can be used for comments, discarded from both rST and C. HXCOMM HXCOMM In this file, generally SRST fragments should have two extra HXCOMM spaces of indent, so that the documentation list item for "info foo" diff --git a/hmp-commands.hx b/hmp-commands.hx index eb3d1605fd..7f0f3974ad 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1,9 +1,9 @@ -HXCOMM Use DEFHEADING() to define headings in both help text and texi -HXCOMM Text between STEXI and ETEXI are copied to texi version and -HXCOMM discarded from C version +HXCOMM Use DEFHEADING() to define headings in both help text and rST. +HXCOMM Text between SRST and ERST is copied to the rST version and +HXCOMM discarded from C version. HXCOMM DEF(command, args, callback, arg_string, help) is used to construct HXCOMM monitor commands -HXCOMM HXCOMM can be used for comments, discarded from both texi and C +HXCOMM HXCOMM can be used for comments, discarded from both rST and C. { @@ -1369,8 +1369,8 @@ ERST #ifdef CONFIG_SLIRP { .name = "hostfwd_add", - .args_type = "arg1:s,arg2:s?,arg3:s?", - .params = "[hub_id name]|[netdev_id] [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport", + .args_type = "arg1:s,arg2:s?", + .params = "[netdev_id] [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport", .help = "redirect TCP or UDP connections from host to guest (requires -net user)", .cmd = hmp_hostfwd_add, }, @@ -1383,8 +1383,8 @@ ERST #ifdef CONFIG_SLIRP { .name = "hostfwd_remove", - .args_type = "arg1:s,arg2:s?,arg3:s?", - .params = "[hub_id name]|[netdev_id] [tcp|udp]:[hostaddr]:hostport", + .args_type = "arg1:s,arg2:s?", + .params = "[netdev_id] [tcp|udp]:[hostaddr]:hostport", .help = "remove host-to-guest TCP or UDP redirection", .cmd = hmp_hostfwd_remove, }, diff --git a/hw/9pfs/9p-proxy.c b/hw/9pfs/9p-proxy.c index 8136e1342d..6f598a0f11 100644 --- a/hw/9pfs/9p-proxy.c +++ b/hw/9pfs/9p-proxy.c @@ -1139,10 +1139,10 @@ static int proxy_parse_opts(QemuOpts *opts, FsDriverEntry *fs, Error **errp) } if (socket) { fs->path = g_strdup(socket); - fs->export_flags = V9FS_PROXY_SOCK_NAME; + fs->export_flags |= V9FS_PROXY_SOCK_NAME; } else { fs->path = g_strdup(sock_fd); - fs->export_flags = V9FS_PROXY_SOCK_FD; + fs->export_flags |= V9FS_PROXY_SOCK_FD; } return 0; } diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 4e74284b65..336cacea41 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -357,81 +357,6 @@ static void ich9_pm_set_cpu_hotplug_legacy(Object *obj, bool value, s->pm.cpu_hotplug_legacy = value; } -static void ich9_pm_get_disable_s3(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ICH9LPCPMRegs *pm = opaque; - uint8_t value = pm->disable_s3; - - visit_type_uint8(v, name, &value, errp); -} - -static void ich9_pm_set_disable_s3(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ICH9LPCPMRegs *pm = opaque; - Error *local_err = NULL; - uint8_t value; - - visit_type_uint8(v, name, &value, &local_err); - if (local_err) { - goto out; - } - pm->disable_s3 = value; -out: - error_propagate(errp, local_err); -} - -static void ich9_pm_get_disable_s4(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ICH9LPCPMRegs *pm = opaque; - uint8_t value = pm->disable_s4; - - visit_type_uint8(v, name, &value, errp); -} - -static void ich9_pm_set_disable_s4(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ICH9LPCPMRegs *pm = opaque; - Error *local_err = NULL; - uint8_t value; - - visit_type_uint8(v, name, &value, &local_err); - if (local_err) { - goto out; - } - pm->disable_s4 = value; -out: - error_propagate(errp, local_err); -} - -static void ich9_pm_get_s4_val(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ICH9LPCPMRegs *pm = opaque; - uint8_t value = pm->s4_val; - - visit_type_uint8(v, name, &value, errp); -} - -static void ich9_pm_set_s4_val(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ICH9LPCPMRegs *pm = opaque; - Error *local_err = NULL; - uint8_t value; - - visit_type_uint8(v, name, &value, &local_err); - if (local_err) { - goto out; - } - pm->s4_val = value; -out: - error_propagate(errp, local_err); -} - static bool ich9_pm_get_enable_tco(Object *obj, Error **errp) { ICH9LPCState *s = ICH9_LPC_DEVICE(obj); @@ -454,12 +379,12 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) pm->s4_val = 2; object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE, - &pm->pm_io_base, errp); + &pm->pm_io_base, OBJ_PROP_FLAG_READ, errp); object_property_add(obj, ACPI_PM_PROP_GPE0_BLK, "uint32", ich9_pm_get_gpe0_blk, NULL, NULL, pm, NULL); object_property_add_uint32_ptr(obj, ACPI_PM_PROP_GPE0_BLK_LEN, - &gpe0_len, errp); + &gpe0_len, OBJ_PROP_FLAG_READ, errp); object_property_add_bool(obj, "memory-hotplug-support", ich9_pm_get_memory_hotplug_support, ich9_pm_set_memory_hotplug_support, @@ -468,18 +393,14 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) ich9_pm_get_cpu_hotplug_legacy, ich9_pm_set_cpu_hotplug_legacy, NULL); - object_property_add(obj, ACPI_PM_PROP_S3_DISABLED, "uint8", - ich9_pm_get_disable_s3, - ich9_pm_set_disable_s3, - NULL, pm, NULL); - object_property_add(obj, ACPI_PM_PROP_S4_DISABLED, "uint8", - ich9_pm_get_disable_s4, - ich9_pm_set_disable_s4, - NULL, pm, NULL); - object_property_add(obj, ACPI_PM_PROP_S4_VAL, "uint8", - ich9_pm_get_s4_val, - ich9_pm_set_s4_val, - NULL, pm, NULL); + object_property_add_uint8_ptr(obj, ACPI_PM_PROP_S3_DISABLED, + &pm->disable_s3, OBJ_PROP_FLAG_READWRITE, + NULL); + object_property_add_uint8_ptr(obj, ACPI_PM_PROP_S4_DISABLED, + &pm->disable_s4, OBJ_PROP_FLAG_READWRITE, + NULL); + object_property_add_uint8_ptr(obj, ACPI_PM_PROP_S4_VAL, + &pm->s4_val, OBJ_PROP_FLAG_READWRITE, NULL); object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED, ich9_pm_get_enable_tco, ich9_pm_set_enable_tco, diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 5219dd0e2e..eb6a37b14e 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -485,7 +485,7 @@ struct NvdimmFuncGetLabelDataOut { /* the size of buffer filled by QEMU. */ uint32_t len; uint32_t func_ret_status; /* return status code. */ - uint8_t out_buf[0]; /* the data got via Get Namesapce Label function. */ + uint8_t out_buf[]; /* the data got via Get Namesapce Label function. */ } QEMU_PACKED; typedef struct NvdimmFuncGetLabelDataOut NvdimmFuncGetLabelDataOut; QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataOut) > NVDIMM_DSM_MEMORY_SIZE); @@ -493,7 +493,7 @@ QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncGetLabelDataOut) > NVDIMM_DSM_MEMORY_SIZE); struct NvdimmFuncSetLabelDataIn { uint32_t offset; /* the offset in the namespace label data area. */ uint32_t length; /* the size of data is to be written via the function. */ - uint8_t in_buf[0]; /* the data written to label data area. */ + uint8_t in_buf[]; /* the data written to label data area. */ } QEMU_PACKED; typedef struct NvdimmFuncSetLabelDataIn NvdimmFuncSetLabelDataIn; QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncSetLabelDataIn) + @@ -510,7 +510,7 @@ struct NvdimmFuncReadFITOut { /* the size of buffer filled by QEMU. */ uint32_t len; uint32_t func_ret_status; /* return status code. */ - uint8_t fit[0]; /* the FIT data. */ + uint8_t fit[]; /* the FIT data. */ } QEMU_PACKED; typedef struct NvdimmFuncReadFITOut NvdimmFuncReadFITOut; QEMU_BUILD_BUG_ON(sizeof(NvdimmFuncReadFITOut) > NVDIMM_DSM_MEMORY_SIZE); diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 8413348a33..4dcef372bf 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -80,7 +80,8 @@ static void *acpi_set_bsel(PCIBus *bus, void *opaque) *bus_bsel = (*bsel_alloc)++; object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, - bus_bsel, &error_abort); + bus_bsel, OBJ_PROP_FLAG_READ, + &error_abort); } return bsel_alloc; @@ -373,9 +374,9 @@ void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, memory_region_add_subregion(address_space_io, s->io_base, &s->io); object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_BASE_PROP, &s->io_base, - &error_abort); + OBJ_PROP_FLAG_READ, &error_abort); object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_LEN_PROP, &s->io_len, - &error_abort); + OBJ_PROP_FLAG_READ, &error_abort); } const VMStateDescription vmstate_acpi_pcihp_pci_status = { diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index b84dbba2c3..964d6f5990 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -444,17 +444,17 @@ static void piix4_pm_add_propeties(PIIX4PMState *s) static const uint16_t sci_int = 9; object_property_add_uint8_ptr(OBJECT(s), ACPI_PM_PROP_ACPI_ENABLE_CMD, - &acpi_enable_cmd, NULL); + &acpi_enable_cmd, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint8_ptr(OBJECT(s), ACPI_PM_PROP_ACPI_DISABLE_CMD, - &acpi_disable_cmd, NULL); + &acpi_disable_cmd, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_GPE0_BLK, - &gpe0_blk, NULL); + &gpe0_blk, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_GPE0_BLK_LEN, - &gpe0_blk_len, NULL); + &gpe0_blk_len, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint16_ptr(OBJECT(s), ACPI_PM_PROP_SCI_INT, - &sci_int, NULL); + &sci_int, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_PM_IO_BASE, - &s->io_base, NULL); + &s->io_base, OBJ_PROP_FLAG_READ, NULL); } static void piix4_pm_realize(PCIDevice *dev, Error **errp) diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index e5350a287f..f7751b18f6 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -15,7 +15,7 @@ #include "qemu/error-report.h" #include "sysemu/sysemu.h" #include "hw/rtc/mc146818rtc.h" -#include "hw/ide.h" +#include "hw/ide/pci.h" #include "hw/timer/i8254.h" #include "hw/isa/superio.h" #include "hw/dma/i8257.h" @@ -57,6 +57,7 @@ static void clipper_init(MachineState *machine) const char *initrd_filename = machine->initrd_filename; AlphaCPU *cpus[4]; PCIBus *pci_bus; + PCIDevice *pci_dev; ISABus *isa_bus; qemu_irq rtc_irq; long size, i; @@ -99,12 +100,8 @@ static void clipper_init(MachineState *machine) isa_create_simple(isa_bus, TYPE_SMC37C669_SUPERIO); /* IDE disk setup. */ - { - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - ide_drive_get(hd, ARRAY_SIZE(hd)); - - pci_cmd646_ide_init(pci_bus, hd, 0); - } + pci_dev = pci_create_simple(pci_bus, -1, "cmd646-ide"); + pci_ide_create_devs(pci_dev); /* Load PALcode. Given that this is not "real" cpu palcode, but one explicitly written for the emulation, we might as diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index bc54fd61f9..188419dc1e 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -297,6 +297,18 @@ config ALLWINNER_A10 select SERIAL select UNIMP +config ALLWINNER_H3 + bool + select ALLWINNER_A10_PIT + select ALLWINNER_SUN8I_EMAC + select SERIAL + select ARM_TIMER + select ARM_GIC + select UNIMP + select USB_OHCI + select USB_EHCI_SYSBUS + select SD + config RASPI bool select FRAMEBUFFER @@ -361,6 +373,7 @@ config FSL_IMX6 select IMX select IMX_FEC select IMX_I2C + select IMX_USBPHY select SDHCI config ASPEED_SOC diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 336f6dd374..534a6a119e 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -35,6 +35,7 @@ obj-$(CONFIG_DIGIC) += digic.o obj-$(CONFIG_OMAP) += omap1.o omap2.o obj-$(CONFIG_STRONGARM) += strongarm.o obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o +obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3.o orangepi.o obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o obj-$(CONFIG_STM32F405_SOC) += stm32f405_soc.o diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index 2ae9c15311..62a67a3e1a 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -27,6 +27,7 @@ #include "hw/boards.h" #include "hw/usb/hcd-ohci.h" +#define AW_A10_MMC0_BASE 0x01c0f000 #define AW_A10_PIC_REG_BASE 0x01c20400 #define AW_A10_PIT_REG_BASE 0x01c20c00 #define AW_A10_UART0_REG_BASE 0x01c28000 @@ -34,6 +35,7 @@ #define AW_A10_EHCI_BASE 0x01c14000 #define AW_A10_OHCI_BASE 0x01c14400 #define AW_A10_SATA_BASE 0x01c18000 +#define AW_A10_RTC_BASE 0x01c20d00 static void aw_a10_init(Object *obj) { @@ -64,6 +66,12 @@ static void aw_a10_init(Object *obj) sizeof(s->ohci[i]), TYPE_SYSBUS_OHCI); } } + + sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0), + TYPE_AW_SDHOST_SUN4I); + + sysbus_init_child_obj(obj, "rtc", &s->rtc, sizeof(s->rtc), + TYPE_AW_RTC_SUN4I); } static void aw_a10_realize(DeviceState *dev, Error **errp) @@ -164,6 +172,17 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(dev, 64 + i)); } } + + /* SD/MMC */ + qdev_init_nofail(DEVICE(&s->mmc0)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc0), 0, AW_A10_MMC0_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc0), 0, qdev_get_gpio_in(dev, 32)); + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->mmc0), + "sd-bus", &error_abort); + + /* RTC */ + qdev_init_nofail(DEVICE(&s->rtc)); + sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->rtc), 0, AW_A10_RTC_BASE, 10); } static void aw_a10_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c new file mode 100644 index 0000000000..9e4ce36093 --- /dev/null +++ b/hw/arm/allwinner-h3.c @@ -0,0 +1,465 @@ +/* + * Allwinner H3 System on Chip emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "hw/qdev-core.h" +#include "cpu.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "hw/misc/unimp.h" +#include "hw/usb/hcd-ehci.h" +#include "hw/loader.h" +#include "sysemu/sysemu.h" +#include "hw/arm/allwinner-h3.h" + +/* Memory map */ +const hwaddr allwinner_h3_memmap[] = { + [AW_H3_SRAM_A1] = 0x00000000, + [AW_H3_SRAM_A2] = 0x00044000, + [AW_H3_SRAM_C] = 0x00010000, + [AW_H3_SYSCTRL] = 0x01c00000, + [AW_H3_MMC0] = 0x01c0f000, + [AW_H3_SID] = 0x01c14000, + [AW_H3_EHCI0] = 0x01c1a000, + [AW_H3_OHCI0] = 0x01c1a400, + [AW_H3_EHCI1] = 0x01c1b000, + [AW_H3_OHCI1] = 0x01c1b400, + [AW_H3_EHCI2] = 0x01c1c000, + [AW_H3_OHCI2] = 0x01c1c400, + [AW_H3_EHCI3] = 0x01c1d000, + [AW_H3_OHCI3] = 0x01c1d400, + [AW_H3_CCU] = 0x01c20000, + [AW_H3_PIT] = 0x01c20c00, + [AW_H3_UART0] = 0x01c28000, + [AW_H3_UART1] = 0x01c28400, + [AW_H3_UART2] = 0x01c28800, + [AW_H3_UART3] = 0x01c28c00, + [AW_H3_EMAC] = 0x01c30000, + [AW_H3_DRAMCOM] = 0x01c62000, + [AW_H3_DRAMCTL] = 0x01c63000, + [AW_H3_DRAMPHY] = 0x01c65000, + [AW_H3_GIC_DIST] = 0x01c81000, + [AW_H3_GIC_CPU] = 0x01c82000, + [AW_H3_GIC_HYP] = 0x01c84000, + [AW_H3_GIC_VCPU] = 0x01c86000, + [AW_H3_RTC] = 0x01f00000, + [AW_H3_CPUCFG] = 0x01f01c00, + [AW_H3_SDRAM] = 0x40000000 +}; + +/* List of unimplemented devices */ +struct AwH3Unimplemented { + const char *device_name; + hwaddr base; + hwaddr size; +} unimplemented[] = { + { "d-engine", 0x01000000, 4 * MiB }, + { "d-inter", 0x01400000, 128 * KiB }, + { "dma", 0x01c02000, 4 * KiB }, + { "nfdc", 0x01c03000, 4 * KiB }, + { "ts", 0x01c06000, 4 * KiB }, + { "keymem", 0x01c0b000, 4 * KiB }, + { "lcd0", 0x01c0c000, 4 * KiB }, + { "lcd1", 0x01c0d000, 4 * KiB }, + { "ve", 0x01c0e000, 4 * KiB }, + { "mmc1", 0x01c10000, 4 * KiB }, + { "mmc2", 0x01c11000, 4 * KiB }, + { "crypto", 0x01c15000, 4 * KiB }, + { "msgbox", 0x01c17000, 4 * KiB }, + { "spinlock", 0x01c18000, 4 * KiB }, + { "usb0-otg", 0x01c19000, 4 * KiB }, + { "usb0-phy", 0x01c1a000, 4 * KiB }, + { "usb1-phy", 0x01c1b000, 4 * KiB }, + { "usb2-phy", 0x01c1c000, 4 * KiB }, + { "usb3-phy", 0x01c1d000, 4 * KiB }, + { "smc", 0x01c1e000, 4 * KiB }, + { "pio", 0x01c20800, 1 * KiB }, + { "owa", 0x01c21000, 1 * KiB }, + { "pwm", 0x01c21400, 1 * KiB }, + { "keyadc", 0x01c21800, 1 * KiB }, + { "pcm0", 0x01c22000, 1 * KiB }, + { "pcm1", 0x01c22400, 1 * KiB }, + { "pcm2", 0x01c22800, 1 * KiB }, + { "audio", 0x01c22c00, 2 * KiB }, + { "smta", 0x01c23400, 1 * KiB }, + { "ths", 0x01c25000, 1 * KiB }, + { "uart0", 0x01c28000, 1 * KiB }, + { "uart1", 0x01c28400, 1 * KiB }, + { "uart2", 0x01c28800, 1 * KiB }, + { "uart3", 0x01c28c00, 1 * KiB }, + { "twi0", 0x01c2ac00, 1 * KiB }, + { "twi1", 0x01c2b000, 1 * KiB }, + { "twi2", 0x01c2b400, 1 * KiB }, + { "scr", 0x01c2c400, 1 * KiB }, + { "gpu", 0x01c40000, 64 * KiB }, + { "hstmr", 0x01c60000, 4 * KiB }, + { "spi0", 0x01c68000, 4 * KiB }, + { "spi1", 0x01c69000, 4 * KiB }, + { "csi", 0x01cb0000, 320 * KiB }, + { "tve", 0x01e00000, 64 * KiB }, + { "hdmi", 0x01ee0000, 128 * KiB }, + { "r_timer", 0x01f00800, 1 * KiB }, + { "r_intc", 0x01f00c00, 1 * KiB }, + { "r_wdog", 0x01f01000, 1 * KiB }, + { "r_prcm", 0x01f01400, 1 * KiB }, + { "r_twd", 0x01f01800, 1 * KiB }, + { "r_cir-rx", 0x01f02000, 1 * KiB }, + { "r_twi", 0x01f02400, 1 * KiB }, + { "r_uart", 0x01f02800, 1 * KiB }, + { "r_pio", 0x01f02c00, 1 * KiB }, + { "r_pwm", 0x01f03800, 1 * KiB }, + { "core-dbg", 0x3f500000, 128 * KiB }, + { "tsgen-ro", 0x3f506000, 4 * KiB }, + { "tsgen-ctl", 0x3f507000, 4 * KiB }, + { "ddr-mem", 0x40000000, 2 * GiB }, + { "n-brom", 0xffff0000, 32 * KiB }, + { "s-brom", 0xffff0000, 64 * KiB } +}; + +/* Per Processor Interrupts */ +enum { + AW_H3_GIC_PPI_MAINT = 9, + AW_H3_GIC_PPI_HYPTIMER = 10, + AW_H3_GIC_PPI_VIRTTIMER = 11, + AW_H3_GIC_PPI_SECTIMER = 13, + AW_H3_GIC_PPI_PHYSTIMER = 14 +}; + +/* Shared Processor Interrupts */ +enum { + AW_H3_GIC_SPI_UART0 = 0, + AW_H3_GIC_SPI_UART1 = 1, + AW_H3_GIC_SPI_UART2 = 2, + AW_H3_GIC_SPI_UART3 = 3, + AW_H3_GIC_SPI_TIMER0 = 18, + AW_H3_GIC_SPI_TIMER1 = 19, + AW_H3_GIC_SPI_MMC0 = 60, + AW_H3_GIC_SPI_EHCI0 = 72, + AW_H3_GIC_SPI_OHCI0 = 73, + AW_H3_GIC_SPI_EHCI1 = 74, + AW_H3_GIC_SPI_OHCI1 = 75, + AW_H3_GIC_SPI_EHCI2 = 76, + AW_H3_GIC_SPI_OHCI2 = 77, + AW_H3_GIC_SPI_EHCI3 = 78, + AW_H3_GIC_SPI_OHCI3 = 79, + AW_H3_GIC_SPI_EMAC = 82 +}; + +/* Allwinner H3 general constants */ +enum { + AW_H3_GIC_NUM_SPI = 128 +}; + +void allwinner_h3_bootrom_setup(AwH3State *s, BlockBackend *blk) +{ + const int64_t rom_size = 32 * KiB; + g_autofree uint8_t *buffer = g_new0(uint8_t, rom_size); + + if (blk_pread(blk, 8 * KiB, buffer, rom_size) < 0) { + error_setg(&error_fatal, "%s: failed to read BlockBackend data", + __func__); + return; + } + + rom_add_blob("allwinner-h3.bootrom", buffer, rom_size, + rom_size, s->memmap[AW_H3_SRAM_A1], + NULL, NULL, NULL, NULL, false); +} + +static void allwinner_h3_init(Object *obj) +{ + AwH3State *s = AW_H3(obj); + + s->memmap = allwinner_h3_memmap; + + for (int i = 0; i < AW_H3_NUM_CPUS; i++) { + object_initialize_child(obj, "cpu[*]", &s->cpus[i], sizeof(s->cpus[i]), + ARM_CPU_TYPE_NAME("cortex-a7"), + &error_abort, NULL); + } + + sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), + TYPE_ARM_GIC); + + sysbus_init_child_obj(obj, "timer", &s->timer, sizeof(s->timer), + TYPE_AW_A10_PIT); + object_property_add_alias(obj, "clk0-freq", OBJECT(&s->timer), + "clk0-freq", &error_abort); + object_property_add_alias(obj, "clk1-freq", OBJECT(&s->timer), + "clk1-freq", &error_abort); + + sysbus_init_child_obj(obj, "ccu", &s->ccu, sizeof(s->ccu), + TYPE_AW_H3_CCU); + + sysbus_init_child_obj(obj, "sysctrl", &s->sysctrl, sizeof(s->sysctrl), + TYPE_AW_H3_SYSCTRL); + + sysbus_init_child_obj(obj, "cpucfg", &s->cpucfg, sizeof(s->cpucfg), + TYPE_AW_CPUCFG); + + sysbus_init_child_obj(obj, "sid", &s->sid, sizeof(s->sid), + TYPE_AW_SID); + object_property_add_alias(obj, "identifier", OBJECT(&s->sid), + "identifier", &error_abort); + + sysbus_init_child_obj(obj, "mmc0", &s->mmc0, sizeof(s->mmc0), + TYPE_AW_SDHOST_SUN5I); + + sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac), + TYPE_AW_SUN8I_EMAC); + + sysbus_init_child_obj(obj, "dramc", &s->dramc, sizeof(s->dramc), + TYPE_AW_H3_DRAMC); + object_property_add_alias(obj, "ram-addr", OBJECT(&s->dramc), + "ram-addr", &error_abort); + object_property_add_alias(obj, "ram-size", OBJECT(&s->dramc), + "ram-size", &error_abort); + + sysbus_init_child_obj(obj, "rtc", &s->rtc, sizeof(s->rtc), + TYPE_AW_RTC_SUN6I); +} + +static void allwinner_h3_realize(DeviceState *dev, Error **errp) +{ + AwH3State *s = AW_H3(dev); + unsigned i; + + /* CPUs */ + for (i = 0; i < AW_H3_NUM_CPUS; i++) { + + /* Provide Power State Coordination Interface */ + qdev_prop_set_int32(DEVICE(&s->cpus[i]), "psci-conduit", + QEMU_PSCI_CONDUIT_HVC); + + /* Disable secondary CPUs */ + qdev_prop_set_bit(DEVICE(&s->cpus[i]), "start-powered-off", + i > 0); + + /* All exception levels required */ + qdev_prop_set_bit(DEVICE(&s->cpus[i]), "has_el3", true); + qdev_prop_set_bit(DEVICE(&s->cpus[i]), "has_el2", true); + + /* Mark realized */ + qdev_init_nofail(DEVICE(&s->cpus[i])); + } + + /* Generic Interrupt Controller */ + qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", AW_H3_GIC_NUM_SPI + + GIC_INTERNAL); + qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2); + qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", AW_H3_NUM_CPUS); + qdev_prop_set_bit(DEVICE(&s->gic), "has-security-extensions", false); + qdev_prop_set_bit(DEVICE(&s->gic), "has-virtualization-extensions", true); + qdev_init_nofail(DEVICE(&s->gic)); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 0, s->memmap[AW_H3_GIC_DIST]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 1, s->memmap[AW_H3_GIC_CPU]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 2, s->memmap[AW_H3_GIC_HYP]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gic), 3, s->memmap[AW_H3_GIC_VCPU]); + + /* + * Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, + * and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs. + */ + for (i = 0; i < AW_H3_NUM_CPUS; i++) { + DeviceState *cpudev = DEVICE(&s->cpus[i]); + int ppibase = AW_H3_GIC_NUM_SPI + i * GIC_INTERNAL + GIC_NR_SGIS; + int irq; + /* + * Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs used for this board. + */ + const int timer_irq[] = { + [GTIMER_PHYS] = AW_H3_GIC_PPI_PHYSTIMER, + [GTIMER_VIRT] = AW_H3_GIC_PPI_VIRTTIMER, + [GTIMER_HYP] = AW_H3_GIC_PPI_HYPTIMER, + [GTIMER_SEC] = AW_H3_GIC_PPI_SECTIMER, + }; + + /* Connect CPU timer outputs to GIC PPI inputs */ + for (irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { + qdev_connect_gpio_out(cpudev, irq, + qdev_get_gpio_in(DEVICE(&s->gic), + ppibase + timer_irq[irq])); + } + + /* Connect GIC outputs to CPU interrupt inputs */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + AW_H3_NUM_CPUS, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (2 * AW_H3_NUM_CPUS), + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (3 * AW_H3_NUM_CPUS), + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + + /* GIC maintenance signal */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + (4 * AW_H3_NUM_CPUS), + qdev_get_gpio_in(DEVICE(&s->gic), + ppibase + AW_H3_GIC_PPI_MAINT)); + } + + /* Timer */ + qdev_init_nofail(DEVICE(&s->timer)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer), 0, s->memmap[AW_H3_PIT]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_TIMER0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 1, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_TIMER1)); + + /* SRAM */ + memory_region_init_ram(&s->sram_a1, OBJECT(dev), "sram A1", + 64 * KiB, &error_abort); + memory_region_init_ram(&s->sram_a2, OBJECT(dev), "sram A2", + 32 * KiB, &error_abort); + memory_region_init_ram(&s->sram_c, OBJECT(dev), "sram C", + 44 * KiB, &error_abort); + memory_region_add_subregion(get_system_memory(), s->memmap[AW_H3_SRAM_A1], + &s->sram_a1); + memory_region_add_subregion(get_system_memory(), s->memmap[AW_H3_SRAM_A2], + &s->sram_a2); + memory_region_add_subregion(get_system_memory(), s->memmap[AW_H3_SRAM_C], + &s->sram_c); + + /* Clock Control Unit */ + qdev_init_nofail(DEVICE(&s->ccu)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, s->memmap[AW_H3_CCU]); + + /* System Control */ + qdev_init_nofail(DEVICE(&s->sysctrl)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctrl), 0, s->memmap[AW_H3_SYSCTRL]); + + /* CPU Configuration */ + qdev_init_nofail(DEVICE(&s->cpucfg)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->cpucfg), 0, s->memmap[AW_H3_CPUCFG]); + + /* Security Identifier */ + qdev_init_nofail(DEVICE(&s->sid)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sid), 0, s->memmap[AW_H3_SID]); + + /* SD/MMC */ + qdev_init_nofail(DEVICE(&s->mmc0)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->mmc0), 0, s->memmap[AW_H3_MMC0]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->mmc0), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_MMC0)); + + object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->mmc0), + "sd-bus", &error_abort); + + /* EMAC */ + if (nd_table[0].used) { + qemu_check_nic_model(&nd_table[0], TYPE_AW_SUN8I_EMAC); + qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]); + } + qdev_init_nofail(DEVICE(&s->emac)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->emac), 0, s->memmap[AW_H3_EMAC]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->emac), 0, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_EMAC)); + + /* Universal Serial Bus */ + sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI0], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_EHCI0)); + sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI1], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_EHCI1)); + sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI2], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_EHCI2)); + sysbus_create_simple(TYPE_AW_H3_EHCI, s->memmap[AW_H3_EHCI3], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_EHCI3)); + + sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI0], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_OHCI0)); + sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI1], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_OHCI1)); + sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI2], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_OHCI2)); + sysbus_create_simple("sysbus-ohci", s->memmap[AW_H3_OHCI3], + qdev_get_gpio_in(DEVICE(&s->gic), + AW_H3_GIC_SPI_OHCI3)); + + /* UART0. For future clocktree API: All UARTS are connected to APB2_CLK. */ + serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART0], 2, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART0), + 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); + /* UART1 */ + serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART1], 2, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART1), + 115200, serial_hd(1), DEVICE_NATIVE_ENDIAN); + /* UART2 */ + serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART2], 2, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART2), + 115200, serial_hd(2), DEVICE_NATIVE_ENDIAN); + /* UART3 */ + serial_mm_init(get_system_memory(), s->memmap[AW_H3_UART3], 2, + qdev_get_gpio_in(DEVICE(&s->gic), AW_H3_GIC_SPI_UART3), + 115200, serial_hd(3), DEVICE_NATIVE_ENDIAN); + + /* DRAMC */ + qdev_init_nofail(DEVICE(&s->dramc)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 0, s->memmap[AW_H3_DRAMCOM]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 1, s->memmap[AW_H3_DRAMCTL]); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dramc), 2, s->memmap[AW_H3_DRAMPHY]); + + /* RTC */ + qdev_init_nofail(DEVICE(&s->rtc)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, s->memmap[AW_H3_RTC]); + + /* Unimplemented devices */ + for (i = 0; i < ARRAY_SIZE(unimplemented); i++) { + create_unimplemented_device(unimplemented[i].device_name, + unimplemented[i].base, + unimplemented[i].size); + } +} + +static void allwinner_h3_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = allwinner_h3_realize; + /* Reason: uses serial_hd() in realize function */ + dc->user_creatable = false; +} + +static const TypeInfo allwinner_h3_type_info = { + .name = TYPE_AW_H3, + .parent = TYPE_DEVICE, + .instance_size = sizeof(AwH3State), + .instance_init = allwinner_h3_init, + .class_init = allwinner_h3_class_init, +}; + +static void allwinner_h3_register_types(void) +{ + type_register_static(&allwinner_h3_type_info); +} + +type_init(allwinner_h3_register_types) diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index 871b1beef4..0b8ba44976 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -22,6 +22,7 @@ #include "sysemu/sysemu.h" #include "hw/sysbus.h" #include "hw/boards.h" +#include "hw/qdev-properties.h" #include "hw/arm/allwinner-a10.h" static struct arm_boot_info cubieboard_binfo = { @@ -33,6 +34,10 @@ static void cubieboard_init(MachineState *machine) { AwA10State *a10; Error *err = NULL; + DriveInfo *di; + BlockBackend *blk; + BusState *bus; + DeviceState *carddev; /* BIOS is not supported by this board */ if (bios_name) { @@ -54,6 +59,9 @@ static void cubieboard_init(MachineState *machine) } a10 = AW_A10(object_new(TYPE_AW_A10)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(a10), + &error_abort); + object_unref(OBJECT(a10)); object_property_set_int(OBJECT(&a10->emac), 1, "phy-addr", &err); if (err != NULL) { @@ -79,6 +87,16 @@ static void cubieboard_init(MachineState *machine) exit(1); } + /* Retrieve SD bus */ + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + bus = qdev_get_child_bus(DEVICE(a10), "sd-bus"); + + /* Plug in SD card */ + carddev = qdev_create(bus, TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal); + memory_region_add_subregion(get_system_memory(), AW_A10_SDRAM_BASE, machine->ram); diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index 59a27bdd68..1f7253ef6f 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -305,23 +305,21 @@ static void exynos4210_realize(DeviceState *socdev, Error **errp) /*** Memory ***/ /* Chip-ID and OMR */ - memory_region_init_io(&s->chipid_mem, NULL, &exynos4210_chipid_and_omr_ops, - NULL, "exynos4210.chipid", sizeof(chipid_and_omr)); + memory_region_init_io(&s->chipid_mem, OBJECT(socdev), + &exynos4210_chipid_and_omr_ops, NULL, + "exynos4210.chipid", sizeof(chipid_and_omr)); memory_region_add_subregion(system_mem, EXYNOS4210_CHIPID_ADDR, &s->chipid_mem); /* Internal ROM */ - memory_region_init_ram(&s->irom_mem, NULL, "exynos4210.irom", + memory_region_init_rom(&s->irom_mem, OBJECT(socdev), "exynos4210.irom", EXYNOS4210_IROM_SIZE, &error_fatal); - memory_region_set_readonly(&s->irom_mem, true); memory_region_add_subregion(system_mem, EXYNOS4210_IROM_BASE_ADDR, &s->irom_mem); /* mirror of iROM */ - memory_region_init_alias(&s->irom_alias_mem, NULL, "exynos4210.irom_alias", - &s->irom_mem, - 0, + memory_region_init_alias(&s->irom_alias_mem, OBJECT(socdev), + "exynos4210.irom_alias", &s->irom_mem, 0, EXYNOS4210_IROM_SIZE); - memory_region_set_readonly(&s->irom_alias_mem, true); memory_region_add_subregion(system_mem, EXYNOS4210_IROM_MIRROR_BASE_ADDR, &s->irom_alias_mem); diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index da3471b395..6f1a82ce3d 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -31,6 +31,8 @@ #include "hw/qdev-properties.h" #include "chardev/char.h" +#define IMX25_ESDHC_CAPABILITIES 0x07e20000 + static void fsl_imx25_init(Object *obj) { FslIMX25State *s = FSL_IMX25(obj); @@ -74,6 +76,17 @@ static void fsl_imx25_init(Object *obj) sysbus_init_child_obj(obj, "gpio[*]", &s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); } + + for (i = 0; i < FSL_IMX25_NUM_ESDHCS; i++) { + sysbus_init_child_obj(obj, "sdhc[*]", &s->esdhc[i], sizeof(s->esdhc[i]), + TYPE_IMX_USDHC); + } + + for (i = 0; i < FSL_IMX25_NUM_USBS; i++) { + sysbus_init_child_obj(obj, "usb[*]", &s->usb[i], sizeof(s->usb[i]), + TYPE_CHIPIDEA); + } + } static void fsl_imx25_realize(DeviceState *dev, Error **errp) @@ -246,17 +259,60 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) gpio_table[i].irq)); } + /* Initialize all SDHC */ + for (i = 0; i < FSL_IMX25_NUM_ESDHCS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } esdhc_table[FSL_IMX25_NUM_ESDHCS] = { + { FSL_IMX25_ESDHC1_ADDR, FSL_IMX25_ESDHC1_IRQ }, + { FSL_IMX25_ESDHC2_ADDR, FSL_IMX25_ESDHC2_IRQ }, + }; + + object_property_set_uint(OBJECT(&s->esdhc[i]), 2, "sd-spec-version", + &err); + object_property_set_uint(OBJECT(&s->esdhc[i]), IMX25_ESDHC_CAPABILITIES, + "capareg", &err); + object_property_set_bool(OBJECT(&s->esdhc[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->esdhc[i]), 0, esdhc_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->esdhc[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + esdhc_table[i].irq)); + } + + /* USB */ + for (i = 0; i < FSL_IMX25_NUM_USBS; i++) { + static const struct { + hwaddr addr; + unsigned int irq; + } usb_table[FSL_IMX25_NUM_USBS] = { + { FSL_IMX25_USB1_ADDR, FSL_IMX25_USB1_IRQ }, + { FSL_IMX25_USB2_ADDR, FSL_IMX25_USB2_IRQ }, + }; + + object_property_set_bool(OBJECT(&s->usb[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, usb_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, + qdev_get_gpio_in(DEVICE(&s->avic), + usb_table[i].irq)); + } + /* initialize 2 x 16 KB ROM */ - memory_region_init_rom(&s->rom[0], NULL, - "imx25.rom0", FSL_IMX25_ROM0_SIZE, &err); + memory_region_init_rom(&s->rom[0], OBJECT(dev), "imx25.rom0", + FSL_IMX25_ROM0_SIZE, &err); if (err) { error_propagate(errp, err); return; } memory_region_add_subregion(get_system_memory(), FSL_IMX25_ROM0_ADDR, &s->rom[0]); - memory_region_init_rom(&s->rom[1], NULL, - "imx25.rom1", FSL_IMX25_ROM1_SIZE, &err); + memory_region_init_rom(&s->rom[1], OBJECT(dev), "imx25.rom1", + FSL_IMX25_ROM1_SIZE, &err); if (err) { error_propagate(errp, err); return; @@ -275,7 +331,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) &s->iram); /* internal RAM (128 KB) is aliased over 128 MB - 128 KB */ - memory_region_init_alias(&s->iram_alias, NULL, "imx25.iram_alias", + memory_region_init_alias(&s->iram_alias, OBJECT(dev), "imx25.iram_alias", &s->iram, 0, FSL_IMX25_IRAM_ALIAS_SIZE); memory_region_add_subregion(get_system_memory(), FSL_IMX25_IRAM_ALIAS_ADDR, &s->iram_alias); diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 55e90d104b..8472d2e911 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -206,7 +206,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) } /* On a real system, the first 16k is a `secure boot rom' */ - memory_region_init_rom(&s->secure_rom, NULL, "imx31.secure_rom", + memory_region_init_rom(&s->secure_rom, OBJECT(dev), "imx31.secure_rom", FSL_IMX31_SECURE_ROM_SIZE, &err); if (err) { error_propagate(errp, err); @@ -216,7 +216,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) &s->secure_rom); /* There is also a 16k ROM */ - memory_region_init_rom(&s->rom, NULL, "imx31.rom", + memory_region_init_rom(&s->rom, OBJECT(dev), "imx31.rom", FSL_IMX31_ROM_SIZE, &err); if (err) { error_propagate(errp, err); @@ -236,7 +236,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) &s->iram); /* internal RAM (16 KB) is aliased over 256 MB - 16 KB */ - memory_region_init_alias(&s->iram_alias, NULL, "imx31.iram_alias", + memory_region_init_alias(&s->iram_alias, OBJECT(dev), "imx31.iram_alias", &s->iram, 0, FSL_IMX31_IRAM_ALIAS_SIZE); memory_region_add_subregion(get_system_memory(), FSL_IMX31_IRAM_ALIAS_ADDR, &s->iram_alias); diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index ecc62855f2..13f1bf23a6 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -22,6 +22,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx6.h" +#include "hw/usb/imx-usb-phy.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "sysemu/sysemu.h" @@ -86,6 +87,17 @@ static void fsl_imx6_init(Object *obj) TYPE_IMX_USDHC); } + for (i = 0; i < FSL_IMX6_NUM_USB_PHYS; i++) { + snprintf(name, NAME_SIZE, "usbphy%d", i); + sysbus_init_child_obj(obj, name, &s->usbphy[i], sizeof(s->usbphy[i]), + TYPE_IMX_USBPHY); + } + for (i = 0; i < FSL_IMX6_NUM_USBS; i++) { + snprintf(name, NAME_SIZE, "usb%d", i); + sysbus_init_child_obj(obj, name, &s->usb[i], sizeof(s->usb[i]), + TYPE_CHIPIDEA); + } + for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) { snprintf(name, NAME_SIZE, "spi%d", i + 1); sysbus_init_child_obj(obj, name, &s->spi[i], sizeof(s->spi[i]), @@ -349,6 +361,30 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) esdhc_table[i].irq)); } + /* USB */ + for (i = 0; i < FSL_IMX6_NUM_USB_PHYS; i++) { + object_property_set_bool(OBJECT(&s->usbphy[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usbphy[i]), 0, + FSL_IMX6_USBPHY1_ADDR + i * 0x1000); + } + for (i = 0; i < FSL_IMX6_NUM_USBS; i++) { + static const int FSL_IMX6_USBn_IRQ[] = { + FSL_IMX6_USB_OTG_IRQ, + FSL_IMX6_USB_HOST1_IRQ, + FSL_IMX6_USB_HOST2_IRQ, + FSL_IMX6_USB_HOST3_IRQ, + }; + + object_property_set_bool(OBJECT(&s->usb[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, + FSL_IMX6_USBOH3_USB_ADDR + i * 0x200); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + FSL_IMX6_USBn_IRQ[i])); + } + /* Initialize all ECSPI */ for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) { static const struct { @@ -405,7 +441,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) } /* ROM memory */ - memory_region_init_rom(&s->rom, NULL, "imx6.rom", + memory_region_init_rom(&s->rom, OBJECT(dev), "imx6.rom", FSL_IMX6_ROM_SIZE, &err); if (err) { error_propagate(errp, err); @@ -415,7 +451,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) &s->rom); /* CAAM memory */ - memory_region_init_rom(&s->caam, NULL, "imx6.caam", + memory_region_init_rom(&s->caam, OBJECT(dev), "imx6.caam", FSL_IMX6_CAAM_MEM_SIZE, &err); if (err) { error_propagate(errp, err); @@ -435,7 +471,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) &s->ocram); /* internal OCRAM (256 KB) is aliased over 1 MB */ - memory_region_init_alias(&s->ocram_alias, NULL, "imx6.ocram_alias", + memory_region_init_alias(&s->ocram_alias, OBJECT(dev), "imx6.ocram_alias", &s->ocram, 0, FSL_IMX6_OCRAM_ALIAS_SIZE); memory_region_add_subregion(get_system_memory(), FSL_IMX6_OCRAM_ALIAS_ADDR, &s->ocram_alias); diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index c405b68d1d..56dfd7cecc 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -20,6 +20,7 @@ #include "qapi/error.h" #include "hw/arm/fsl-imx6ul.h" #include "hw/misc/unimp.h" +#include "hw/usb/imx-usb-phy.h" #include "hw/boards.h" #include "sysemu/sysemu.h" #include "qemu/error-report.h" @@ -133,6 +134,18 @@ static void fsl_imx6ul_init(Object *obj) TYPE_IMX_ENET); } + /* USB */ + for (i = 0; i < FSL_IMX6UL_NUM_USB_PHYS; i++) { + snprintf(name, NAME_SIZE, "usbphy%d", i); + sysbus_init_child_obj(obj, name, &s->usbphy[i], sizeof(s->usbphy[i]), + TYPE_IMX_USBPHY); + } + for (i = 0; i < FSL_IMX6UL_NUM_USBS; i++) { + snprintf(name, NAME_SIZE, "usb%d", i); + sysbus_init_child_obj(obj, name, &s->usb[i], sizeof(s->usb[i]), + TYPE_CHIPIDEA); + } + /* * SDHCI */ @@ -456,6 +469,28 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_ENETn_TIMER_IRQ[i])); } + /* USB */ + for (i = 0; i < FSL_IMX6UL_NUM_USB_PHYS; i++) { + object_property_set_bool(OBJECT(&s->usbphy[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usbphy[i]), 0, + FSL_IMX6UL_USBPHY1_ADDR + i * 0x1000); + } + + for (i = 0; i < FSL_IMX6UL_NUM_USBS; i++) { + static const int FSL_IMX6UL_USBn_IRQ[] = { + FSL_IMX6UL_USB1_IRQ, + FSL_IMX6UL_USB2_IRQ, + }; + object_property_set_bool(OBJECT(&s->usb[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, + FSL_IMX6UL_USBO2_USB_ADDR + i * 0x200); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX6UL_USBn_IRQ[i])); + } + /* * USDHC */ @@ -517,6 +552,20 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) create_unimplemented_device("sdma", FSL_IMX6UL_SDMA_ADDR, 0x4000); /* + * PWM + */ + create_unimplemented_device("pwm1", FSL_IMX6UL_PWM1_ADDR, 0x4000); + create_unimplemented_device("pwm2", FSL_IMX6UL_PWM2_ADDR, 0x4000); + create_unimplemented_device("pwm3", FSL_IMX6UL_PWM3_ADDR, 0x4000); + create_unimplemented_device("pwm4", FSL_IMX6UL_PWM4_ADDR, 0x4000); + + /* + * CAN + */ + create_unimplemented_device("can1", FSL_IMX6UL_CAN1_ADDR, 0x4000); + create_unimplemented_device("can2", FSL_IMX6UL_CAN2_ADDR, 0x4000); + + /* * APHB_DMA */ create_unimplemented_device("aphb_dma", FSL_IMX6UL_APBH_DMA_ADDR, @@ -543,7 +592,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) /* * ROM memory */ - memory_region_init_rom(&s->rom, NULL, "imx6ul.rom", + memory_region_init_rom(&s->rom, OBJECT(dev), "imx6ul.rom", FSL_IMX6UL_ROM_SIZE, &error_abort); memory_region_add_subregion(get_system_memory(), FSL_IMX6UL_ROM_ADDR, &s->rom); @@ -551,7 +600,7 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) /* * CAAM memory */ - memory_region_init_rom(&s->caam, NULL, "imx6ul.caam", + memory_region_init_rom(&s->caam, OBJECT(dev), "imx6ul.caam", FSL_IMX6UL_CAAM_MEM_SIZE, &error_abort); memory_region_add_subregion(get_system_memory(), FSL_IMX6UL_CAAM_MEM_ADDR, &s->caam); @@ -568,8 +617,9 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) /* * internal OCRAM (128 KB) is aliased over 512 KB */ - memory_region_init_alias(&s->ocram_alias, NULL, "imx6ul.ocram_alias", - &s->ocram, 0, FSL_IMX6UL_OCRAM_ALIAS_SIZE); + memory_region_init_alias(&s->ocram_alias, OBJECT(dev), + "imx6ul.ocram_alias", &s->ocram, 0, + FSL_IMX6UL_OCRAM_ALIAS_SIZE); memory_region_add_subregion(get_system_memory(), FSL_IMX6UL_OCRAM_ALIAS_ADDR, &s->ocram_alias); } diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c index 26713d9a7e..b3ca82bafa 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "cpu.h" +#include "hw/qdev-properties.h" #include "hw/arm/fsl-imx25.h" #include "hw/boards.h" #include "qemu/error-report.h" @@ -120,6 +121,21 @@ static void imx25_pdk_init(MachineState *machine) imx25_pdk_binfo.board_id = 1771, imx25_pdk_binfo.nb_cpus = 1; + for (i = 0; i < FSL_IMX25_NUM_ESDHCS; i++) { + BusState *bus; + DeviceState *carddev; + DriveInfo *di; + BlockBackend *blk; + + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + bus = qdev_get_child_bus(DEVICE(&s->soc.esdhc[i]), "sd-bus"); + carddev = qdev_create(bus, TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, + "realized", &error_fatal); + } + /* * We test explicitly for qtest here as it is not done (yet?) in * arm_load_kernel(). Without this the "make check" command would diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c index 1042017086..6bc643651b 100644 --- a/hw/arm/mainstone.c +++ b/hw/arm/mainstone.c @@ -124,9 +124,8 @@ static void mainstone_common_init(MemoryRegion *address_space_mem, /* Setup CPU & memory */ mpu = pxa270_init(address_space_mem, mainstone_binfo.ram_size, machine->cpu_type); - memory_region_init_ram(rom, NULL, "mainstone.rom", MAINSTONE_ROM, + memory_region_init_rom(rom, NULL, "mainstone.rom", MAINSTONE_ROM, &error_fatal); - memory_region_set_readonly(rom, true); memory_region_add_subregion(address_space_mem, 0, rom); /* There are two 32MiB flash devices on the board */ diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index 8f84692e64..588d643b8d 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -96,7 +96,7 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) MemoryRegion *nvm_alias = g_new(MemoryRegion, 1); MemoryRegion *sram = g_new(MemoryRegion, 1); - memory_region_init_rom(nvm, NULL, "MSF2.eNVM", s->envm_size, + memory_region_init_rom(nvm, OBJECT(dev_soc), "MSF2.eNVM", s->envm_size, &error_fatal); /* * On power-on, the eNVM region 0x60000000 is automatically @@ -104,8 +104,8 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) * start address (0x0). We do not support remapping other eNVM, * eSRAM and DDR regions by guest(via Sysreg) currently. */ - memory_region_init_alias(nvm_alias, NULL, "MSF2.eNVM", - nvm, 0, s->envm_size); + memory_region_init_alias(nvm_alias, OBJECT(dev_soc), "MSF2.eNVM", nvm, 0, + s->envm_size); memory_region_add_subregion(system_memory, ENVM_BASE_ADDRESS, nvm); memory_region_add_subregion(system_memory, 0, nvm_alias); diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index 4817a76ae0..57eff63f0d 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -165,7 +165,7 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) } /* STUB Peripherals */ - memory_region_init_io(&s->clock, NULL, &clock_ops, NULL, + memory_region_init_io(&s->clock, OBJECT(dev_soc), &clock_ops, NULL, "nrf51_soc.clock", 0x1000); memory_region_add_subregion_overlap(&s->container, NRF51_IOMEM_BASE, &s->clock, -1); diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index de5ff447dc..57829b3744 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -131,9 +131,8 @@ static void sx1_init(MachineState *machine, const int version) mpu = omap310_mpu_init(machine->ram, machine->cpu_type); /* External Flash (EMIFS) */ - memory_region_init_ram(flash, NULL, "omap_sx1.flash0-0", flash_size, + memory_region_init_rom(flash, NULL, "omap_sx1.flash0-0", flash_size, &error_fatal); - memory_region_set_readonly(flash, true); memory_region_add_subregion(address_space, OMAP_CS0_BASE, flash); memory_region_init_io(&cs[0], NULL, &static_ops, &cs0val, @@ -167,9 +166,8 @@ static void sx1_init(MachineState *machine, const int version) if ((version == 1) && (dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) { MemoryRegion *flash_1 = g_new(MemoryRegion, 1); - memory_region_init_ram(flash_1, NULL, "omap_sx1.flash1-0", + memory_region_init_rom(flash_1, NULL, "omap_sx1.flash1-0", flash1_size, &error_fatal); - memory_region_set_readonly(flash_1, true); memory_region_add_subregion(address_space, OMAP_CS1_BASE, flash_1); memory_region_init_io(&cs[1], NULL, &static_ops, &cs1val, diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c new file mode 100644 index 0000000000..181f5badab --- /dev/null +++ b/hw/arm/orangepi.c @@ -0,0 +1,130 @@ +/* + * Orange Pi emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "exec/address-spaces.h" +#include "qapi/error.h" +#include "cpu.h" +#include "hw/sysbus.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "hw/arm/allwinner-h3.h" +#include "sysemu/sysemu.h" + +static struct arm_boot_info orangepi_binfo = { + .nb_cpus = AW_H3_NUM_CPUS, +}; + +static void orangepi_init(MachineState *machine) +{ + AwH3State *h3; + DriveInfo *di; + BlockBackend *blk; + BusState *bus; + DeviceState *carddev; + + /* BIOS is not supported by this board */ + if (bios_name) { + error_report("BIOS not supported for this machine"); + exit(1); + } + + /* This board has fixed size RAM */ + if (machine->ram_size != 1 * GiB) { + error_report("This machine can only be used with 1GiB of RAM"); + exit(1); + } + + /* Only allow Cortex-A7 for this board */ + if (strcmp(machine->cpu_type, ARM_CPU_TYPE_NAME("cortex-a7")) != 0) { + error_report("This board can only be used with cortex-a7 CPU"); + exit(1); + } + + h3 = AW_H3(object_new(TYPE_AW_H3)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(h3), + &error_abort); + object_unref(OBJECT(h3)); + + /* Setup timer properties */ + object_property_set_int(OBJECT(h3), 32768, "clk0-freq", + &error_abort); + object_property_set_int(OBJECT(h3), 24 * 1000 * 1000, "clk1-freq", + &error_abort); + + /* Setup SID properties. Currently using a default fixed SID identifier. */ + if (qemu_uuid_is_null(&h3->sid.identifier)) { + qdev_prop_set_string(DEVICE(h3), "identifier", + "02c00081-1111-2222-3333-000044556677"); + } else if (ldl_be_p(&h3->sid.identifier.data[0]) != 0x02c00081) { + warn_report("Security Identifier value does not include H3 prefix"); + } + + /* Setup EMAC properties */ + object_property_set_int(OBJECT(&h3->emac), 1, "phy-addr", &error_abort); + + /* DRAMC */ + object_property_set_uint(OBJECT(h3), h3->memmap[AW_H3_SDRAM], + "ram-addr", &error_abort); + object_property_set_int(OBJECT(h3), machine->ram_size / MiB, "ram-size", + &error_abort); + + /* Mark H3 object realized */ + object_property_set_bool(OBJECT(h3), true, "realized", &error_abort); + + /* Retrieve SD bus */ + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + bus = qdev_get_child_bus(DEVICE(h3), "sd-bus"); + + /* Plug in SD card */ + carddev = qdev_create(bus, TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal); + + /* SDRAM */ + memory_region_add_subregion(get_system_memory(), h3->memmap[AW_H3_SDRAM], + machine->ram); + + /* Load target kernel or start using BootROM */ + if (!machine->kernel_filename && blk_is_available(blk)) { + /* Use Boot ROM to copy data from SD card to SRAM */ + allwinner_h3_bootrom_setup(h3, blk); + } + orangepi_binfo.loader_start = h3->memmap[AW_H3_SDRAM]; + orangepi_binfo.ram_size = machine->ram_size; + arm_load_kernel(ARM_CPU(first_cpu), machine, &orangepi_binfo); +} + +static void orangepi_machine_init(MachineClass *mc) +{ + mc->desc = "Orange Pi PC"; + mc->init = orangepi_init; + mc->block_default_type = IF_SD; + mc->units_per_default_bus = 1; + mc->min_cpus = AW_H3_NUM_CPUS; + mc->max_cpus = AW_H3_NUM_CPUS; + mc->default_cpus = AW_H3_NUM_CPUS; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"); + mc->default_ram_size = 1 * GiB; + mc->default_ram_id = "orangepi.ram"; +} + +DEFINE_MACHINE("orangepi-pc", orangepi_machine_init) diff --git a/hw/arm/palm.c b/hw/arm/palm.c index 99554bda19..97ca105d29 100644 --- a/hw/arm/palm.c +++ b/hw/arm/palm.c @@ -213,9 +213,8 @@ static void palmte_init(MachineState *machine) mpu = omap310_mpu_init(machine->ram, machine->cpu_type); /* External Flash (EMIFS) */ - memory_region_init_ram(flash, NULL, "palmte.flash", flash_size, + memory_region_init_rom(flash, NULL, "palmte.flash", flash_size, &error_fatal); - memory_region_set_readonly(flash, true); memory_region_add_subregion(address_space_mem, OMAP_CS0_BASE, flash); memory_region_init_io(&cs[0], NULL, &static_ops, &cs0val, "palmte-cs0", diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 56a36202d7..336c9bad4a 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -2290,9 +2290,6 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); } - sysbus_create_simple("sysbus-ohci", 0x4c000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); - s->pcmcia[0] = pxa2xx_pcmcia_init(address_space, 0x20000000); s->pcmcia[1] = pxa2xx_pcmcia_init(address_space, 0x30000000); diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index cbfa6934cf..c28d9b5ed7 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -929,8 +929,7 @@ static void spitz_common_init(MachineState *machine, sl_flash_register(mpu, (model == spitz) ? FLASH_128M : FLASH_1024M); - memory_region_init_ram(rom, NULL, "spitz.rom", SPITZ_ROM, &error_fatal); - memory_region_set_readonly(rom, true); + memory_region_init_rom(rom, NULL, "spitz.rom", SPITZ_ROM, &error_fatal); memory_region_add_subregion(address_space_mem, 0, rom); /* Setup peripherals */ diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 221a78674e..d136ba1a92 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1300,9 +1300,8 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) sram_size = ((board->dc0 >> 18) + 1) * 1024; /* Flash programming is done via the SCU, so pretend it is ROM. */ - memory_region_init_ram(flash, NULL, "stellaris.flash", flash_size, + memory_region_init_rom(flash, NULL, "stellaris.flash", flash_size, &error_fatal); - memory_region_set_readonly(flash, true); memory_region_add_subregion(system_memory, 0, flash); memory_region_init_ram(sram, NULL, "stellaris.sram", sram_size, diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index 627fd446f5..118c342559 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -93,13 +93,10 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) MemoryRegion *flash = g_new(MemoryRegion, 1); MemoryRegion *flash_alias = g_new(MemoryRegion, 1); - memory_region_init_ram(flash, NULL, "STM32F205.flash", FLASH_SIZE, - &error_fatal); - memory_region_init_alias(flash_alias, NULL, "STM32F205.flash.alias", - flash, 0, FLASH_SIZE); - - memory_region_set_readonly(flash, true); - memory_region_set_readonly(flash_alias, true); + memory_region_init_rom(flash, OBJECT(dev_soc), "STM32F205.flash", + FLASH_SIZE, &error_fatal); + memory_region_init_alias(flash_alias, OBJECT(dev_soc), + "STM32F205.flash.alias", flash, 0, FLASH_SIZE); memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, flash); memory_region_add_subregion(system_memory, 0, flash_alias); diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c index 9bcad97853..4f10ce6176 100644 --- a/hw/arm/stm32f405_soc.c +++ b/hw/arm/stm32f405_soc.c @@ -95,17 +95,15 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp) Error *err = NULL; int i; - memory_region_init_ram(&s->flash, NULL, "STM32F405.flash", FLASH_SIZE, - &err); + memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32F405.flash", + FLASH_SIZE, &err); if (err != NULL) { error_propagate(errp, err); return; } - memory_region_init_alias(&s->flash_alias, NULL, "STM32F405.flash.alias", - &s->flash, 0, FLASH_SIZE); - - memory_region_set_readonly(&s->flash, true); - memory_region_set_readonly(&s->flash_alias, true); + memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc), + "STM32F405.flash.alias", &s->flash, 0, + FLASH_SIZE); memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash); memory_region_add_subregion(system_memory, 0, &s->flash_alias); diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index 4d95a1f3e2..5dee2d76c6 100644 --- a/hw/arm/tosa.c +++ b/hw/arm/tosa.c @@ -226,8 +226,7 @@ static void tosa_init(MachineState *machine) mpu = pxa255_init(address_space_mem, tosa_binfo.ram_size); - memory_region_init_ram(rom, NULL, "tosa.rom", TOSA_ROM, &error_fatal); - memory_region_set_readonly(rom, true); + memory_region_init_rom(rom, NULL, "tosa.rom", TOSA_ROM, &error_fatal); memory_region_add_subregion(address_space_mem, 0, rom); tmio = tc6393xb_init(address_space_mem, 0x10000000, diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 32d865a488..94f93dda54 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -299,7 +299,7 @@ static void fdt_add_timer_nodes(const VirtMachineState *vms) irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI; } - if (vms->gic_version == 2) { + if (vms->gic_version == VIRT_GIC_VERSION_2) { irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vms->smp_cpus) - 1); @@ -440,7 +440,7 @@ static void fdt_add_gic_node(VirtMachineState *vms) qemu_fdt_setprop_cell(vms->fdt, nodename, "#address-cells", 0x2); qemu_fdt_setprop_cell(vms->fdt, nodename, "#size-cells", 0x2); qemu_fdt_setprop(vms->fdt, nodename, "ranges", NULL, 0); - if (vms->gic_version == 3) { + if (vms->gic_version == VIRT_GIC_VERSION_3) { int nb_redist_regions = virt_gicv3_redist_region_count(vms); qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", @@ -519,7 +519,7 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms) } } - if (vms->gic_version == 2) { + if (vms->gic_version == VIRT_GIC_VERSION_2) { irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START, GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vms->smp_cpus) - 1); @@ -1470,7 +1470,7 @@ static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx) * purposes are to make TCG consistent (with 64-bit KVM hosts) * and to improve SGI efficiency. */ - if (vms->gic_version == 3) { + if (vms->gic_version == VIRT_GIC_VERSION_3) { clustersz = GICV3_TARGETLIST_BITS; } else { clustersz = GIC_TARGETLIST_BITS; @@ -1535,6 +1535,105 @@ static void virt_set_memmap(VirtMachineState *vms) } } +/* + * finalize_gic_version - Determines the final gic_version + * according to the gic-version property + * + * Default GIC type is v2 + */ +static void finalize_gic_version(VirtMachineState *vms) +{ + unsigned int max_cpus = MACHINE(vms)->smp.max_cpus; + + if (kvm_enabled()) { + int probe_bitmap; + + if (!kvm_irqchip_in_kernel()) { + switch (vms->gic_version) { + case VIRT_GIC_VERSION_HOST: + warn_report( + "gic-version=host not relevant with kernel-irqchip=off " + "as only userspace GICv2 is supported. Using v2 ..."); + return; + case VIRT_GIC_VERSION_MAX: + case VIRT_GIC_VERSION_NOSEL: + vms->gic_version = VIRT_GIC_VERSION_2; + return; + case VIRT_GIC_VERSION_2: + return; + case VIRT_GIC_VERSION_3: + error_report( + "gic-version=3 is not supported with kernel-irqchip=off"); + exit(1); + } + } + + probe_bitmap = kvm_arm_vgic_probe(); + if (!probe_bitmap) { + error_report("Unable to determine GIC version supported by host"); + exit(1); + } + + switch (vms->gic_version) { + case VIRT_GIC_VERSION_HOST: + case VIRT_GIC_VERSION_MAX: + if (probe_bitmap & KVM_ARM_VGIC_V3) { + vms->gic_version = VIRT_GIC_VERSION_3; + } else { + vms->gic_version = VIRT_GIC_VERSION_2; + } + return; + case VIRT_GIC_VERSION_NOSEL: + if ((probe_bitmap & KVM_ARM_VGIC_V2) && max_cpus <= GIC_NCPU) { + vms->gic_version = VIRT_GIC_VERSION_2; + } else if (probe_bitmap & KVM_ARM_VGIC_V3) { + /* + * in case the host does not support v2 in-kernel emulation or + * the end-user requested more than 8 VCPUs we now default + * to v3. In any case defaulting to v2 would be broken. + */ + vms->gic_version = VIRT_GIC_VERSION_3; + } else if (max_cpus > GIC_NCPU) { + error_report("host only supports in-kernel GICv2 emulation " + "but more than 8 vcpus are requested"); + exit(1); + } + break; + case VIRT_GIC_VERSION_2: + case VIRT_GIC_VERSION_3: + break; + } + + /* Check chosen version is effectively supported by the host */ + if (vms->gic_version == VIRT_GIC_VERSION_2 && + !(probe_bitmap & KVM_ARM_VGIC_V2)) { + error_report("host does not support in-kernel GICv2 emulation"); + exit(1); + } else if (vms->gic_version == VIRT_GIC_VERSION_3 && + !(probe_bitmap & KVM_ARM_VGIC_V3)) { + error_report("host does not support in-kernel GICv3 emulation"); + exit(1); + } + return; + } + + /* TCG mode */ + switch (vms->gic_version) { + case VIRT_GIC_VERSION_NOSEL: + vms->gic_version = VIRT_GIC_VERSION_2; + break; + case VIRT_GIC_VERSION_MAX: + vms->gic_version = VIRT_GIC_VERSION_3; + break; + case VIRT_GIC_VERSION_HOST: + error_report("gic-version=host requires KVM"); + exit(1); + case VIRT_GIC_VERSION_2: + case VIRT_GIC_VERSION_3: + break; + } +} + static void machvirt_init(MachineState *machine) { VirtMachineState *vms = VIRT_MACHINE(machine); @@ -1561,25 +1660,7 @@ static void machvirt_init(MachineState *machine) /* We can probe only here because during property set * KVM is not available yet */ - if (vms->gic_version <= 0) { - /* "host" or "max" */ - if (!kvm_enabled()) { - if (vms->gic_version == 0) { - error_report("gic-version=host requires KVM"); - exit(1); - } else { - /* "max": currently means 3 for TCG */ - vms->gic_version = 3; - } - } else { - vms->gic_version = kvm_arm_vgic_probe(); - if (!vms->gic_version) { - error_report( - "Unable to determine GIC version supported by host"); - exit(1); - } - } - } + finalize_gic_version(vms); if (!cpu_type_valid(machine->cpu_type)) { error_report("mach-virt: CPU type %s not supported", machine->cpu_type); @@ -1628,7 +1709,7 @@ static void machvirt_init(MachineState *machine) /* The maximum number of CPUs depends on the GIC version, or on how * many redistributors we can fit into the memory map. */ - if (vms->gic_version == 3) { + if (vms->gic_version == VIRT_GIC_VERSION_3) { virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; virt_max_cpus += @@ -1856,7 +1937,7 @@ static void virt_set_its(Object *obj, bool value, Error **errp) static char *virt_get_gic_version(Object *obj, Error **errp) { VirtMachineState *vms = VIRT_MACHINE(obj); - const char *val = vms->gic_version == 3 ? "3" : "2"; + const char *val = vms->gic_version == VIRT_GIC_VERSION_3 ? "3" : "2"; return g_strdup(val); } @@ -1866,13 +1947,13 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp) VirtMachineState *vms = VIRT_MACHINE(obj); if (!strcmp(value, "3")) { - vms->gic_version = 3; + vms->gic_version = VIRT_GIC_VERSION_3; } else if (!strcmp(value, "2")) { - vms->gic_version = 2; + vms->gic_version = VIRT_GIC_VERSION_2; } else if (!strcmp(value, "host")) { - vms->gic_version = 0; /* Will probe later */ + vms->gic_version = VIRT_GIC_VERSION_HOST; /* Will probe later */ } else if (!strcmp(value, "max")) { - vms->gic_version = -1; /* Will probe later */ + vms->gic_version = VIRT_GIC_VERSION_MAX; /* Will probe later */ } else { error_setg(errp, "Invalid gic-version value"); error_append_hint(errp, "Valid values are 3, 2, host, max.\n"); @@ -2140,13 +2221,13 @@ static void virt_instance_init(Object *obj) "Set on/off to enable/disable using " "physical address space above 32 bits", NULL); - /* Default GIC type is v2 */ - vms->gic_version = 2; + vms->gic_version = VIRT_GIC_VERSION_NOSEL; object_property_add_str(obj, "gic-version", virt_get_gic_version, virt_set_gic_version, NULL); object_property_set_description(obj, "gic-version", "Set GIC version. " - "Valid values are 2, 3 and host", NULL); + "Valid values are 2, 3, host and max", + NULL); vms->highmem_ecam = !vmc->no_highmem_ecam; diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index cab0160ae9..49f1c8d0de 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -318,9 +318,9 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) ddr_low_size = XLNX_ZYNQMP_MAX_LOW_RAM_SIZE; ddr_high_size = ram_size - XLNX_ZYNQMP_MAX_LOW_RAM_SIZE; - memory_region_init_alias(&s->ddr_ram_high, NULL, - "ddr-ram-high", s->ddr_ram, - ddr_low_size, ddr_high_size); + memory_region_init_alias(&s->ddr_ram_high, OBJECT(dev), + "ddr-ram-high", s->ddr_ram, ddr_low_size, + ddr_high_size); memory_region_add_subregion(get_system_memory(), XLNX_ZYNQMP_HIGH_RAM_START, &s->ddr_ram_high); @@ -330,9 +330,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) ddr_low_size = ram_size; } - memory_region_init_alias(&s->ddr_ram_low, NULL, - "ddr-ram-low", s->ddr_ram, - 0, ddr_low_size); + memory_region_init_alias(&s->ddr_ram_low, OBJECT(dev), "ddr-ram-low", + s->ddr_ram, 0, ddr_low_size); memory_region_add_subregion(get_system_memory(), 0, &s->ddr_ram_low); /* Create the four OCM banks */ diff --git a/hw/audio/fmopl.c b/hw/audio/fmopl.c index 173a7521f2..356d4dfbca 100644 --- a/hw/audio/fmopl.c +++ b/hw/audio/fmopl.c @@ -186,7 +186,7 @@ static int32_t *VIB_TABLE; /* envelope output curve table */ /* attack + decay + OFF */ -static int32_t ENV_CURVE[2*EG_ENT+1]; +static int32_t *ENV_CURVE; /* multiple table */ #define ML 2 @@ -1090,6 +1090,7 @@ FM_OPL *OPLCreate(int clock, int rate) OPL->clock = clock; OPL->rate = rate; OPL->max_ch = max_ch; + ENV_CURVE = g_new(int32_t, 2 * EG_ENT + 1); /* init grobal tables */ OPL_initialize(OPL); /* reset chip */ @@ -1127,6 +1128,7 @@ void OPLDestroy(FM_OPL *OPL) #endif OPL_UnLockTable(); free(OPL); + g_free(ENV_CURVE); } /* ---------- Option handlers ---------- */ diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 1bcc3e5cf8..e8d18b7c58 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -181,7 +181,9 @@ struct IntelHDAState { IntelHDAStream st[8]; /* state */ + MemoryRegion container; MemoryRegion mmio; + MemoryRegion alias; uint32_t rirb_count; int64_t wall_base_ns; @@ -670,12 +672,6 @@ static const struct IntelHDAReg regtab[] = { .offset = offsetof(IntelHDAState, wall_clk), .rhandler = intel_hda_get_wall_clk, }, - [ ICH6_REG_WALLCLK + 0x2000 ] = { - .name = "WALLCLK(alias)", - .size = 4, - .offset = offsetof(IntelHDAState, wall_clk), - .rhandler = intel_hda_get_wall_clk, - }, /* dma engine */ [ ICH6_REG_CORBLBASE ] = { @@ -837,12 +833,6 @@ static const struct IntelHDAReg regtab[] = { .size = 4, \ .offset = offsetof(IntelHDAState, st[_i].lpib), \ }, \ - [ ST_REG(_i, ICH6_REG_SD_LPIB) + 0x2000 ] = { \ - .stream = _i, \ - .name = _t stringify(_i) " LPIB(alias)", \ - .size = 4, \ - .offset = offsetof(IntelHDAState, st[_i].lpib), \ - }, \ [ ST_REG(_i, ICH6_REG_SD_CBL) ] = { \ .stream = _i, \ .name = _t stringify(_i) " CBL", \ @@ -1125,9 +1115,15 @@ static void intel_hda_realize(PCIDevice *pci, Error **errp) error_free(err); } + memory_region_init(&d->container, OBJECT(d), + "intel-hda-container", 0x4000); memory_region_init_io(&d->mmio, OBJECT(d), &intel_hda_mmio_ops, d, - "intel-hda", 0x4000); - pci_register_bar(&d->pci, 0, 0, &d->mmio); + "intel-hda", 0x2000); + memory_region_add_subregion(&d->container, 0x0000, &d->mmio); + memory_region_init_alias(&d->alias, OBJECT(d), "intel-hda-alias", + &d->mmio, 0, 0x2000); + memory_region_add_subregion(&d->container, 0x2000, &d->alias); + pci_register_bar(&d->pci, 0, 0, &d->container); hda_codec_bus_init(DEVICE(pci), &d->codecs, sizeof(d->codecs), intel_hda_response, intel_hda_xfer); diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 61f2fb8f8f..8227088441 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -32,17 +32,7 @@ #include "qemu/module.h" #include "qemu/error-report.h" #include "qapi/error.h" - -#ifndef M25P80_ERR_DEBUG -#define M25P80_ERR_DEBUG 0 -#endif - -#define DB_PRINT_L(level, ...) do { \ - if (M25P80_ERR_DEBUG > (level)) { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ - } \ -} while (0) +#include "trace.h" /* Fields for FlashPartInfo->flags */ @@ -574,7 +564,8 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd) abort(); } - DB_PRINT_L(0, "offset = %#x, len = %d\n", offset, len); + trace_m25p80_flash_erase(s, offset, len); + if ((s->pi->flags & capa_to_assert) != capa_to_assert) { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: %d erase size not supported by" " device\n", len); @@ -607,8 +598,7 @@ void flash_write8(Flash *s, uint32_t addr, uint8_t data) } if ((prev ^ data) & data) { - DB_PRINT_L(1, "programming zero to one! addr=%" PRIx32 " %" PRIx8 - " -> %" PRIx8 "\n", addr, prev, data); + trace_m25p80_programming_zero_to_one(s, addr, prev, data); } if (s->pi->flags & EEPROM) { @@ -662,6 +652,9 @@ static void complete_collecting_data(Flash *s) s->state = STATE_IDLE; + trace_m25p80_complete_collecting(s, s->cmd_in_progress, n, s->ear, + s->cur_addr); + switch (s->cmd_in_progress) { case DPP: case QPP: @@ -825,7 +818,7 @@ static void reset_memory(Flash *s) break; } - DB_PRINT_L(0, "Reset done.\n"); + trace_m25p80_reset_done(s); } static void decode_fast_read_cmd(Flash *s) @@ -941,9 +934,10 @@ static void decode_qio_read_cmd(Flash *s) static void decode_new_cmd(Flash *s, uint32_t value) { - s->cmd_in_progress = value; int i; - DB_PRINT_L(0, "decoded new command:%x\n", value); + + s->cmd_in_progress = value; + trace_m25p80_command_decoded(s, value); if (value != RESET_MEMORY) { s->reset_enable = false; @@ -1042,12 +1036,15 @@ static void decode_new_cmd(Flash *s, uint32_t value) break; case JEDEC_READ: - DB_PRINT_L(0, "populated jedec code\n"); + trace_m25p80_populated_jedec(s); for (i = 0; i < s->pi->id_len; i++) { s->data[i] = s->pi->id[i]; } + for (; i < SPI_NOR_MAX_ID_LEN; i++) { + s->data[i] = 0; + } - s->len = s->pi->id_len; + s->len = SPI_NOR_MAX_ID_LEN; s->pos = 0; s->state = STATE_READING_DATA; break; @@ -1063,7 +1060,7 @@ static void decode_new_cmd(Flash *s, uint32_t value) case BULK_ERASE_60: case BULK_ERASE: if (s->write_enable) { - DB_PRINT_L(0, "chip erase\n"); + trace_m25p80_chip_erase(s); flash_erase(s, 0, BULK_ERASE); } else { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: chip erase with write " @@ -1164,6 +1161,11 @@ static void decode_new_cmd(Flash *s, uint32_t value) s->quad_enable = false; break; default: + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + s->data_read_loop = true; + s->data[0] = 0; qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value); break; } @@ -1184,7 +1186,7 @@ static int m25p80_cs(SSISlave *ss, bool select) s->data_read_loop = false; } - DB_PRINT_L(0, "%sselect\n", select ? "de" : ""); + trace_m25p80_select(s, select ? "de" : ""); return 0; } @@ -1194,19 +1196,20 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) Flash *s = M25P80(ss); uint32_t r = 0; + trace_m25p80_transfer(s, s->state, s->len, s->needed_bytes, s->pos, + s->cur_addr, (uint8_t)tx); + switch (s->state) { case STATE_PAGE_PROGRAM: - DB_PRINT_L(1, "page program cur_addr=%#" PRIx32 " data=%" PRIx8 "\n", - s->cur_addr, (uint8_t)tx); + trace_m25p80_page_program(s, s->cur_addr, (uint8_t)tx); flash_write8(s, s->cur_addr, (uint8_t)tx); s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; case STATE_READ: r = s->storage[s->cur_addr]; - DB_PRINT_L(1, "READ 0x%" PRIx32 "=%" PRIx8 "\n", s->cur_addr, - (uint8_t)r); + trace_m25p80_read_byte(s, s->cur_addr, (uint8_t)r); s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; @@ -1244,6 +1247,7 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) } r = s->data[s->pos]; + trace_m25p80_read_data(s, s->pos, (uint8_t)r); s->pos++; if (s->pos == s->len) { s->pos = 0; @@ -1281,7 +1285,7 @@ static void m25p80_realize(SSISlave *ss, Error **errp) return; } - DB_PRINT_L(0, "Binding to IF_MTD drive\n"); + trace_m25p80_binding(s); s->storage = blk_blockalign(s->blk, s->size); if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { @@ -1289,7 +1293,7 @@ static void m25p80_realize(SSISlave *ss, Error **errp) return; } } else { - DB_PRINT_L(0, "No BDRV - binding to RAM\n"); + trace_m25p80_binding_no_bdrv(s); s->storage = blk_blockalign(NULL, s->size); memset(s->storage, 0xFF, s->size); } diff --git a/hw/block/trace-events b/hw/block/trace-events index c03e80c2c9..f78939fa9d 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -134,3 +134,19 @@ xen_block_blockdev_add(char *str) "%s" xen_block_blockdev_del(const char *node_name) "%s" xen_block_device_create(unsigned int number) "%u" xen_block_device_destroy(unsigned int number) "%u" + +# m25p80.c +m25p80_flash_erase(void *s, int offset, uint32_t len) "[%p] offset = 0x%"PRIx32", len = %u" +m25p80_programming_zero_to_one(void *s, uint32_t addr, uint8_t prev, uint8_t data) "[%p] programming zero to one! addr=0x%"PRIx32" 0x%"PRIx8" -> 0x%"PRIx8 +m25p80_reset_done(void *s) "[%p] Reset done." +m25p80_command_decoded(void *s, uint32_t cmd) "[%p] new command:0x%"PRIx32 +m25p80_complete_collecting(void *s, uint32_t cmd, int n, uint8_t ear, uint32_t cur_addr) "[%p] decode cmd: 0x%"PRIx32" len %d ear 0x%"PRIx8" addr 0x%"PRIx32 +m25p80_populated_jedec(void *s) "[%p] populated jedec code" +m25p80_chip_erase(void *s) "[%p] chip erase" +m25p80_select(void *s, const char *what) "[%p] %sselect" +m25p80_page_program(void *s, uint32_t addr, uint8_t tx) "[%p] page program cur_addr=0x%"PRIx32" data=0x%"PRIx8 +m25p80_transfer(void *s, uint8_t state, uint32_t len, uint8_t needed, uint32_t pos, uint32_t cur_addr, uint8_t t) "[%p] Transfer state 0x%"PRIx8" len 0x%"PRIx32" needed 0x%"PRIx8" pos 0x%"PRIx32" addr 0x%"PRIx32" tx 0x%"PRIx8 +m25p80_read_byte(void *s, uint32_t addr, uint8_t v) "[%p] Read byte 0x%"PRIx32"=0x%"PRIx8 +m25p80_read_data(void *s, uint32_t pos, uint8_t v) "[%p] Read data 0x%"PRIx32"=0x%"PRIx8 +m25p80_binding(void *s) "[%p] Binding to IF_MTD drive" +m25p80_binding_no_bdrv(void *s) "[%p] No BDRV - binding to RAM" diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index 3885464513..07bb32e22b 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -998,29 +998,27 @@ static void xen_block_device_destroy(XenBackendInstance *backend, XenBlockVdev *vdev = &blockdev->props.vdev; XenBlockDrive *drive = blockdev->drive; XenBlockIOThread *iothread = blockdev->iothread; + Error *local_err = NULL; trace_xen_block_device_destroy(vdev->number); object_unparent(OBJECT(xendev)); if (iothread) { - Error *local_err = NULL; - xen_block_iothread_destroy(iothread, &local_err); if (local_err) { error_propagate_prepend(errp, local_err, - "failed to destroy iothread: "); + "failed to destroy iothread: "); return; } } if (drive) { - Error *local_err = NULL; - xen_block_drive_destroy(drive, &local_err); if (local_err) { error_propagate_prepend(errp, local_err, - "failed to destroy drive: "); + "failed to destroy drive: "); + return; } } } diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index c420dc066e..2b5f37b6a2 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -31,7 +31,7 @@ typedef struct OprtnsCommand { EventBufferHeader header; MDMSU message_unit; - char data[0]; + char data[]; } QEMU_PACKED OprtnsCommand; /* max size for line-mode data in 4K SCCB page */ diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index 1fa124dab9..5c7664905e 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -25,7 +25,7 @@ typedef struct ASCIIConsoleData { EventBufferHeader ebh; - char data[0]; + char data[]; } QEMU_PACKED ASCIIConsoleData; /* max size for ASCII data in 4K SCCB page */ diff --git a/hw/char/serial.c b/hw/char/serial.c index 9298881af9..2ab8b69e03 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -997,7 +997,7 @@ static void serial_io_realize(DeviceState *dev, Error **errp) return; } - memory_region_init_io(&s->io, NULL, &serial_io_ops, s, "serial", 8); + memory_region_init_io(&s->io, OBJECT(dev), &serial_io_ops, s, "serial", 8); sysbus_init_mmio(SYS_BUS_DEVICE(sio), &s->io); sysbus_init_irq(SYS_BUS_DEVICE(sio), &s->irq); } @@ -1106,8 +1106,9 @@ static void serial_mm_realize(DeviceState *dev, Error **errp) return; } - memory_region_init_io(&s->io, NULL, &serial_mm_ops[smm->endianness], smm, - "serial", 8 << smm->regshift); + memory_region_init_io(&s->io, OBJECT(dev), + &serial_mm_ops[smm->endianness], smm, "serial", + 8 << smm->regshift); sysbus_init_mmio(SYS_BUS_DEVICE(smm), &s->io); sysbus_init_irq(SYS_BUS_DEVICE(smm), &smm->serial.irq); } diff --git a/hw/core/cpu.c b/hw/core/cpu.c index fe65ca62ac..786a1bec8a 100644 --- a/hw/core/cpu.c +++ b/hw/core/cpu.c @@ -177,7 +177,7 @@ static int cpu_common_write_elf64_note(WriteCoreDumpFunction f, } -static int cpu_common_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg) +static int cpu_common_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) { return 0; } @@ -239,27 +239,16 @@ void cpu_dump_statistics(CPUState *cpu, int flags) } } -void cpu_class_set_parent_reset(CPUClass *cc, - void (*child_reset)(CPUState *cpu), - void (**parent_reset)(CPUState *cpu)) -{ - *parent_reset = cc->reset; - cc->reset = child_reset; -} - void cpu_reset(CPUState *cpu) { - CPUClass *klass = CPU_GET_CLASS(cpu); - - if (klass->reset != NULL) { - (*klass->reset)(cpu); - } + device_cold_reset(DEVICE(cpu)); trace_guest_cpu_reset(cpu); } -static void cpu_common_reset(CPUState *cpu) +static void cpu_common_reset(DeviceState *dev) { + CPUState *cpu = CPU(dev); CPUClass *cc = CPU_GET_CLASS(cpu); if (qemu_loglevel_mask(CPU_LOG_RESET)) { @@ -419,7 +408,6 @@ static void cpu_class_init(ObjectClass *klass, void *data) CPUClass *k = CPU_CLASS(klass); k->parse_features = cpu_common_parse_features; - k->reset = cpu_common_reset; k->get_arch_id = cpu_common_get_arch_id; k->has_work = cpu_common_has_work; k->get_paging_enabled = cpu_common_get_paging_enabled; @@ -440,6 +428,7 @@ static void cpu_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_CPU, dc->categories); dc->realize = cpu_common_realizefn; dc->unrealize = cpu_common_unrealizefn; + dc->reset = cpu_common_reset; device_class_set_props(dc, cpu_common_props); /* * Reason: CPUs still need special care by board code: wiring up diff --git a/hw/core/loader.c b/hw/core/loader.c index d1b78f60cd..eeef6da9a1 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -1119,19 +1119,26 @@ static void rom_reset(void *unused) { Rom *rom; - /* - * We don't need to fill in the RAM with ROM data because we'll fill - * the data in during the next incoming migration in all cases. Note - * that some of those RAMs can actually be modified by the guest on ARM - * so this is probably the only right thing to do here. - */ - if (runstate_check(RUN_STATE_INMIGRATE)) - return; - QTAILQ_FOREACH(rom, &roms, next) { if (rom->fw_file) { continue; } + /* + * We don't need to fill in the RAM with ROM data because we'll fill + * the data in during the next incoming migration in all cases. Note + * that some of those RAMs can actually be modified by the guest. + */ + if (runstate_check(RUN_STATE_INMIGRATE)) { + if (rom->data && rom->isrom) { + /* + * Free it so that a rom_reset after migration doesn't + * overwrite a potentially modified 'rom'. + */ + rom_free_data(rom); + } + continue; + } + if (rom->data == NULL) { continue; } diff --git a/hw/core/machine.c b/hw/core/machine.c index 9e8c06036f..de0c425605 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -53,7 +53,7 @@ GlobalProperty hw_compat_4_0[] = { { "secondary-vga", "edid", "false" }, { "bochs-display", "edid", "false" }, { "virtio-vga", "edid", "false" }, - { "virtio-gpu", "edid", "false" }, + { "virtio-gpu-device", "edid", "false" }, { "virtio-device", "use-started", "false" }, { "virtio-balloon-device", "qemu-4-0-config-size", "true" }, { "pl031", "migrate-tick-offset", "false" }, @@ -425,6 +425,14 @@ static void machine_set_memory_encryption(Object *obj, const char *value, g_free(ms->memory_encryption); ms->memory_encryption = g_strdup(value); + + /* + * With memory encryption, the host can't see the real contents of RAM, + * so there's no point in it trying to merge areas. + */ + if (value) { + machine_set_mem_merge(obj, false, errp); + } } static bool machine_get_nvdimm(Object *obj, Error **errp) @@ -749,6 +757,7 @@ static void smp_parse(MachineState *ms, QemuOpts *opts) ms->smp.cpus = cpus; ms->smp.cores = cores; ms->smp.threads = threads; + ms->smp.sockets = sockets; } if (ms->smp.cpus > 1) { diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c index 22c5f76dd0..d494e5cec1 100644 --- a/hw/core/platform-bus.c +++ b/hw/core/platform-bus.c @@ -187,7 +187,8 @@ static void platform_bus_realize(DeviceState *dev, Error **errp) d = SYS_BUS_DEVICE(dev); pbus = PLATFORM_BUS_DEVICE(dev); - memory_region_init(&pbus->mmio, NULL, "platform bus", pbus->mmio_size); + memory_region_init(&pbus->mmio, OBJECT(dev), "platform bus", + pbus->mmio_size); sysbus_init_mmio(d, &pbus->mmio); pbus->used_irqs = bitmap_new(pbus->num_irqs); diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index 62085f9fc0..70eb619ef4 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -284,8 +284,8 @@ static void bochs_display_realize(PCIDevice *dev, Error **errp) memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s, "qemu extended regs", PCI_VGA_QEXT_SIZE); - memory_region_init(&s->mmio, obj, "bochs-display-mmio", - PCI_VGA_MMIO_SIZE); + memory_region_init_io(&s->mmio, obj, &unassigned_io_ops, NULL, + "bochs-display-mmio", PCI_VGA_MMIO_SIZE); memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe); memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext); diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 4fb67c6b1c..a1ede10394 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -287,9 +287,8 @@ static void cg3_initfn(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); CG3State *s = CG3(obj); - memory_region_init_ram_nomigrate(&s->rom, obj, "cg3.prom", FCODE_MAX_ROM_SIZE, - &error_fatal); - memory_region_set_readonly(&s->rom, true); + memory_region_init_rom_nomigrate(&s->rom, obj, "cg3.prom", + FCODE_MAX_ROM_SIZE, &error_fatal); sysbus_init_mmio(sbd, &s->rom); memory_region_init_io(&s->reg, obj, &cg3_reg_ops, s, "cg3.reg", diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index 55185c95c6..adcba96e34 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -477,7 +477,8 @@ static void g364fb_init(DeviceState *dev, G364State *s) s->con = graphic_console_init(dev, 0, &g364fb_ops, s); - memory_region_init_io(&s->mem_ctrl, NULL, &g364fb_ctrl_ops, s, "ctrl", 0x180000); + memory_region_init_io(&s->mem_ctrl, OBJECT(dev), &g364fb_ctrl_ops, s, + "ctrl", 0x180000); memory_region_init_ram_ptr(&s->mem_vram, NULL, "vram", s->vram_size, s->vram); vmstate_register_ram(&s->mem_vram, dev); diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 8bff16d535..b68faff4bb 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -362,8 +362,8 @@ static void macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) return; } - memory_region_init_io(&s->mem_ctrl, NULL, &macfb_ctrl_ops, s, "macfb-ctrl", - 0x1000); + memory_region_init_io(&s->mem_ctrl, OBJECT(dev), &macfb_ctrl_ops, s, + "macfb-ctrl", 0x1000); memory_region_init_ram_nomigrate(&s->mem_vram, OBJECT(s), "macfb-vram", MACFB_VRAM_SIZE, errp); diff --git a/hw/display/tcx.c b/hw/display/tcx.c index ca458f94fe..76de16e8ea 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -755,9 +755,8 @@ static void tcx_initfn(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); TCXState *s = TCX(obj); - memory_region_init_ram_nomigrate(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE, - &error_fatal); - memory_region_set_readonly(&s->rom, true); + memory_region_init_rom_nomigrate(&s->rom, obj, "tcx.prom", + FCODE_MAX_ROM_SIZE, &error_fatal); sysbus_init_mmio(sbd, &s->rom); /* 2/STIP : Stippler */ diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index b346324673..6b9db86e36 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -249,8 +249,8 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) /* mmio bar for vga register access */ if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_MMIO)) { - memory_region_init(&d->mmio, NULL, "vga.mmio", - PCI_VGA_MMIO_SIZE); + memory_region_init_io(&d->mmio, OBJECT(dev), &unassigned_io_ops, NULL, + "vga.mmio", PCI_VGA_MMIO_SIZE); if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { qext = true; @@ -285,8 +285,8 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); /* mmio bar */ - memory_region_init(&d->mmio, OBJECT(dev), "vga.mmio", - PCI_VGA_MMIO_SIZE); + memory_region_init_io(&d->mmio, OBJECT(dev), &unassigned_io_ops, NULL, + "vga.mmio", PCI_VGA_MMIO_SIZE); if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { qext = true; diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c index bad8debae4..ef15c06d77 100644 --- a/hw/dma/i8257.c +++ b/hw/dma/i8257.c @@ -553,7 +553,7 @@ static void i8257_realize(DeviceState *dev, Error **errp) I8257State *d = I8257(dev); int i; - memory_region_init_io(&d->channel_io, NULL, &channel_io_ops, d, + memory_region_init_io(&d->channel_io, OBJECT(dev), &channel_io_ops, d, "dma-chan", 8 << d->dshift); memory_region_add_subregion(isa_address_space_io(isa), d->base, &d->channel_io); diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index 21e2c360ac..7434d274aa 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -679,9 +679,9 @@ static void rc4030_realize(DeviceState *dev, Error **errp) s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, rc4030_periodic_timer, s); - memory_region_init_io(&s->iomem_chipset, NULL, &rc4030_ops, s, + memory_region_init_io(&s->iomem_chipset, o, &rc4030_ops, s, "rc4030.chipset", 0x300); - memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s, + memory_region_init_io(&s->iomem_jazzio, o, &jazzio_ops, s, "rc4030.jazzio", 0x00001000); memory_region_init_iommu(&s->dma_mr, sizeof(s->dma_mr), diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index c3e41581b6..3a430057f5 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -80,7 +80,7 @@ struct dma_s { } *memmap; int memmap_size; - struct soc_dma_ch_s ch[0]; + struct soc_dma_ch_s ch[]; }; static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes) diff --git a/hw/hppa/hppa_sys.h b/hw/hppa/hppa_sys.h index 4d08501464..0b18271cc9 100644 --- a/hw/hppa/hppa_sys.h +++ b/hw/hppa/hppa_sys.h @@ -5,7 +5,6 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" -#include "hw/ide.h" #include "hw/boards.h" #include "hw/intc/i8259.h" diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 9175f4b790..00dd9f58d6 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -13,7 +13,6 @@ #include "sysemu/reset.h" #include "sysemu/sysemu.h" #include "hw/rtc/mc146818rtc.h" -#include "hw/ide.h" #include "hw/timer/i8254.h" #include "hw/char/serial.h" #include "hw/net/lasi_82596.h" diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 204b6841ec..df7ad254ac 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3094,6 +3094,12 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, uint16_t mask, source_id; uint8_t bus, bus_max, bus_min; + if (index >= iommu->intr_size) { + error_report_once("%s: index too large: ind=0x%x", + __func__, index); + return -VTD_FR_IR_INDEX_OVER; + } + addr = iommu->intr_root + index * sizeof(*entry); if (dma_memory_read(&address_space_memory, addr, entry, sizeof(*entry))) { diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 362eb2a180..98ee763f68 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -781,6 +781,7 @@ void pc_smp_parse(MachineState *ms, QemuOpts *opts) ms->smp.cpus = cpus; ms->smp.cores = cores; ms->smp.threads = threads; + ms->smp.sockets = sockets; x86ms->smp_dies = dies; } @@ -1505,7 +1506,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, int idx; CPUState *cs; CPUArchId *cpu_slot; - X86CPUTopoInfo topo; + X86CPUTopoIDs topo_ids; X86CPU *cpu = X86_CPU(dev); CPUX86State *env = &cpu->env; MachineState *ms = MACHINE(hotplug_dev); @@ -1513,6 +1514,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, X86MachineState *x86ms = X86_MACHINE(pcms); unsigned int smp_cores = ms->smp.cores; unsigned int smp_threads = ms->smp.threads; + X86CPUTopoInfo topo_info; if(!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { error_setg(errp, "Invalid CPU type, expected cpu type: '%s'", @@ -1520,7 +1522,10 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, return; } + init_topo_info(&topo_info, x86ms); + env->nr_dies = x86ms->smp_dies; + env->nr_nodes = topo_info.nodes_per_pkg; /* * If APIC ID is not set, @@ -1571,24 +1576,22 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, return; } - topo.pkg_id = cpu->socket_id; - topo.die_id = cpu->die_id; - topo.core_id = cpu->core_id; - topo.smt_id = cpu->thread_id; - cpu->apic_id = apicid_from_topo_ids(x86ms->smp_dies, smp_cores, - smp_threads, &topo); + topo_ids.pkg_id = cpu->socket_id; + topo_ids.die_id = cpu->die_id; + topo_ids.core_id = cpu->core_id; + topo_ids.smt_id = cpu->thread_id; + cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids); } cpu_slot = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, &idx); if (!cpu_slot) { MachineState *ms = MACHINE(pcms); - x86_topo_ids_from_apicid(cpu->apic_id, x86ms->smp_dies, - smp_cores, smp_threads, &topo); + x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); error_setg(errp, "Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with" " APIC ID %" PRIu32 ", valid index range 0:%d", - topo.pkg_id, topo.die_id, topo.core_id, topo.smt_id, + topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id, cpu->apic_id, ms->possible_cpus->len - 1); return; } @@ -1605,35 +1608,37 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn() * once -smp refactoring is complete and there will be CPU private * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */ - x86_topo_ids_from_apicid(cpu->apic_id, x86ms->smp_dies, - smp_cores, smp_threads, &topo); - if (cpu->socket_id != -1 && cpu->socket_id != topo.pkg_id) { + x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); + if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) { error_setg(errp, "property socket-id: %u doesn't match set apic-id:" - " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, topo.pkg_id); + " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, + topo_ids.pkg_id); return; } - cpu->socket_id = topo.pkg_id; + cpu->socket_id = topo_ids.pkg_id; - if (cpu->die_id != -1 && cpu->die_id != topo.die_id) { + if (cpu->die_id != -1 && cpu->die_id != topo_ids.die_id) { error_setg(errp, "property die-id: %u doesn't match set apic-id:" - " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo.die_id); + " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo_ids.die_id); return; } - cpu->die_id = topo.die_id; + cpu->die_id = topo_ids.die_id; - if (cpu->core_id != -1 && cpu->core_id != topo.core_id) { + if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) { error_setg(errp, "property core-id: %u doesn't match set apic-id:" - " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, topo.core_id); + " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, + topo_ids.core_id); return; } - cpu->core_id = topo.core_id; + cpu->core_id = topo_ids.core_id; - if (cpu->thread_id != -1 && cpu->thread_id != topo.smt_id) { + if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.smt_id) { error_setg(errp, "property thread-id: %u doesn't match set apic-id:" - " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id, topo.smt_id); + " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id, + topo_ids.smt_id); return; } - cpu->thread_id = topo.smt_id; + cpu->thread_id = topo_ids.smt_id; if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) && !kvm_hv_vpindex_settable()) { diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index e2d98243bc..e6756216f9 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -38,7 +38,7 @@ #include "hw/pci/pci_ids.h" #include "hw/usb.h" #include "net/net.h" -#include "hw/ide.h" +#include "hw/ide/pci.h" #include "hw/irq.h" #include "sysemu/kvm.h" #include "hw/kvm/clock.h" @@ -85,7 +85,6 @@ static void pc_init1(MachineState *machine, int piix3_devfn = -1; qemu_irq smi_irq; GSIState *gsi_state; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; BusState *idebus[MAX_IDE_BUS]; ISADevice *rtc_state; MemoryRegion *ram_memory; @@ -239,21 +238,22 @@ static void pc_init1(MachineState *machine, pc_nic_init(pcmc, isa_bus, pci_bus); - ide_drive_get(hd, ARRAY_SIZE(hd)); if (pcmc->pci_enabled) { PCIDevice *dev; - if (xen_enabled()) { - dev = pci_piix3_xen_ide_init(pci_bus, hd, piix3_devfn + 1); - } else { - dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); - } + + dev = pci_create_simple(pci_bus, piix3_devfn + 1, + xen_enabled() ? "piix3-ide-xen" : "piix3-ide"); + pci_ide_create_devs(dev); idebus[0] = qdev_get_child_bus(&dev->qdev, "ide.0"); idebus[1] = qdev_get_child_bus(&dev->qdev, "ide.1"); pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state); } #ifdef CONFIG_IDE_ISA -else { + else { + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; int i; + + ide_drive_get(hd, ARRAY_SIZE(hd)); for (i = 0; i < MAX_IDE_BUS; i++) { ISADevice *dev; char busname[] = "ide.0"; diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 7f38e6ba8b..87b73fe33c 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -57,6 +57,17 @@ /* Physical Address of PVH entry point read from kernel ELF NOTE */ static size_t pvh_start_addr; +inline void init_topo_info(X86CPUTopoInfo *topo_info, + const X86MachineState *x86ms) +{ + MachineState *ms = MACHINE(x86ms); + + topo_info->nodes_per_pkg = ms->numa_state->num_nodes / ms->smp.sockets; + topo_info->dies_per_pkg = x86ms->smp_dies; + topo_info->cores_per_die = ms->smp.cores; + topo_info->threads_per_core = ms->smp.threads; +} + /* * Calculates initial APIC ID for a specific CPU index * @@ -68,13 +79,14 @@ static size_t pvh_start_addr; uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms, unsigned int cpu_index) { - MachineState *ms = MACHINE(x86ms); X86MachineClass *x86mc = X86_MACHINE_GET_CLASS(x86ms); + X86CPUTopoInfo topo_info; uint32_t correct_id; static bool warned; - correct_id = x86_apicid_from_cpu_idx(x86ms->smp_dies, ms->smp.cores, - ms->smp.threads, cpu_index); + init_topo_info(&topo_info, x86ms); + + correct_id = x86_apicid_from_cpu_idx(&topo_info, cpu_index); if (x86mc->compat_apic_id_mode) { if (cpu_index != correct_id && !warned && !qtest_enabled()) { error_report("APIC IDs set in compatibility mode, " @@ -92,13 +104,9 @@ void x86_cpu_new(X86MachineState *x86ms, int64_t apic_id, Error **errp) { Object *cpu = NULL; Error *local_err = NULL; - CPUX86State *env = NULL; cpu = object_new(MACHINE(x86ms)->cpu_type); - env = &X86_CPU(cpu)->env; - env->nr_dies = x86ms->smp_dies; - object_property_set_uint(cpu, apic_id, "apic-id", &local_err); object_property_set_bool(cpu, true, "realized", &local_err); @@ -143,21 +151,24 @@ x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) { - X86CPUTopoInfo topo; + X86CPUTopoIDs topo_ids; X86MachineState *x86ms = X86_MACHINE(ms); + X86CPUTopoInfo topo_info; + + init_topo_info(&topo_info, x86ms); assert(idx < ms->possible_cpus->len); x86_topo_ids_from_apicid(ms->possible_cpus->cpus[idx].arch_id, - x86ms->smp_dies, ms->smp.cores, - ms->smp.threads, &topo); - return topo.pkg_id % ms->numa_state->num_nodes; + &topo_info, &topo_ids); + return topo_ids.pkg_id % ms->numa_state->num_nodes; } const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) { X86MachineState *x86ms = X86_MACHINE(ms); - int i; unsigned int max_cpus = ms->smp.max_cpus; + X86CPUTopoInfo topo_info; + int i; if (ms->possible_cpus) { /* @@ -171,26 +182,28 @@ const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) + sizeof(CPUArchId) * max_cpus); ms->possible_cpus->len = max_cpus; + + init_topo_info(&topo_info, x86ms); + for (i = 0; i < ms->possible_cpus->len; i++) { - X86CPUTopoInfo topo; + X86CPUTopoIDs topo_ids; ms->possible_cpus->cpus[i].type = ms->cpu_type; ms->possible_cpus->cpus[i].vcpus_count = 1; ms->possible_cpus->cpus[i].arch_id = x86_cpu_apic_id_from_index(x86ms, i); x86_topo_ids_from_apicid(ms->possible_cpus->cpus[i].arch_id, - x86ms->smp_dies, ms->smp.cores, - ms->smp.threads, &topo); + &topo_info, &topo_ids); ms->possible_cpus->cpus[i].props.has_socket_id = true; - ms->possible_cpus->cpus[i].props.socket_id = topo.pkg_id; + ms->possible_cpus->cpus[i].props.socket_id = topo_ids.pkg_id; if (x86ms->smp_dies > 1) { ms->possible_cpus->cpus[i].props.has_die_id = true; - ms->possible_cpus->cpus[i].props.die_id = topo.die_id; + ms->possible_cpus->cpus[i].props.die_id = topo_ids.die_id; } ms->possible_cpus->cpus[i].props.has_core_id = true; - ms->possible_cpus->cpus[i].props.core_id = topo.core_id; + ms->possible_cpus->cpus[i].props.core_id = topo_ids.core_id; ms->possible_cpus->cpus[i].props.has_thread_id = true; - ms->possible_cpus->cpus[i].props.thread_id = topo.smt_id; + ms->possible_cpus->cpus[i].props.thread_id = topo_ids.smt_id; } return ms->possible_cpus; } @@ -328,7 +341,7 @@ struct setup_data { uint64_t next; uint32_t type; uint32_t len; - uint8_t data[0]; + uint8_t data[]; } __attribute__((packed)); diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h index 73424516da..bab0459774 100644 --- a/hw/ide/ahci_internal.h +++ b/hw/ide/ahci_internal.h @@ -27,6 +27,7 @@ #include "hw/ide/ahci.h" #include "hw/ide/internal.h" #include "hw/sysbus.h" +#include "hw/pci/pci.h" #define AHCI_MEM_BAR_SIZE 0x1000 #define AHCI_MAX_PORTS 32 diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 335c060673..699f25824d 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -207,9 +207,9 @@ static void cmd646_set_irq(void *opaque, int channel, int level) cmd646_update_irq(pd); } -static void cmd646_reset(void *opaque) +static void cmd646_reset(DeviceState *dev) { - PCIIDEState *d = opaque; + PCIIDEState *d = PCI_IDE(dev); unsigned int i; for (i = 0; i < 2; i++) { @@ -301,9 +301,6 @@ static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) ide_register_restart_cb(&d->bus[i]); } g_free(irq); - - vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d); - qemu_register_reset(cmd646_reset, d); } static void pci_cmd646_ide_exitfn(PCIDevice *dev) @@ -317,18 +314,6 @@ static void pci_cmd646_ide_exitfn(PCIDevice *dev) } } -void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, - int secondary_ide_enabled) -{ - PCIDevice *dev; - - dev = pci_create(bus, -1, "cmd646-ide"); - qdev_prop_set_uint32(&dev->qdev, "secondary", secondary_ide_enabled); - qdev_init_nofail(&dev->qdev); - - pci_ide_create_devs(dev, hd_table); -} - static Property cmd646_ide_properties[] = { DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0), DEFINE_PROP_END_OF_LIST(), @@ -339,6 +324,8 @@ static void cmd646_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + dc->reset = cmd646_reset; + dc->vmsd = &vmstate_ide_pci; k->realize = pci_cmd646_ide_realize; k->exit = pci_cmd646_ide_exitfn; k->vendor_id = PCI_VENDOR_ID_CMD; diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 1a6a287e76..97347f07f1 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -476,17 +476,20 @@ const VMStateDescription vmstate_ide_pci = { } }; -void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table) +/* hd_table must contain 4 block drivers */ +void pci_ide_create_devs(PCIDevice *dev) { PCIIDEState *d = PCI_IDE(dev); + DriveInfo *hd_table[2 * MAX_IDE_DEVS]; static const int bus[4] = { 0, 0, 1, 1 }; static const int unit[4] = { 0, 1, 0, 1 }; int i; + ide_drive_get(hd_table, ARRAY_SIZE(hd_table)); for (i = 0; i < 4; i++) { - if (hd_table[i] == NULL) - continue; - ide_create_drive(d->bus+bus[i], unit[i], hd_table[i]); + if (hd_table[i]) { + ide_create_drive(d->bus + bus[i], unit[i], hd_table[i]); + } } } diff --git a/hw/ide/piix.c b/hw/ide/piix.c index bc575b4d70..3b2de4c312 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -197,15 +197,6 @@ int pci_piix3_xen_ide_unplug(DeviceState *dev, bool aux) return 0; } -PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix3-ide-xen"); - pci_ide_create_devs(dev, hd_table); - return dev; -} - static void pci_piix_ide_exitfn(PCIDevice *dev) { PCIIDEState *d = PCI_IDE(dev); @@ -217,28 +208,7 @@ static void pci_piix_ide_exitfn(PCIDevice *dev) } } -/* hd_table must contain 4 block drivers */ /* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */ -PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix3-ide"); - pci_ide_create_devs(dev, hd_table); - return dev; -} - -/* hd_table must contain 4 block drivers */ -/* NOTE: for the PIIX4, the IRQs and IOports are hardcoded */ -PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix4-ide"); - pci_ide_create_devs(dev, hd_table); - return dev; -} - static void piix3_ide_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -266,6 +236,7 @@ static const TypeInfo piix3_ide_xen_info = { .class_init = piix3_ide_class_init, }; +/* NOTE: for the PIIX4, the IRQs and IOports are hardcoded */ static void piix4_ide_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ide/via.c b/hw/ide/via.c index 096de8dba0..8de4945cc1 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -113,10 +113,7 @@ static void via_ide_set_irq(void *opaque, int n, int level) } level = (d->config[0x70] & 0x80) || (d->config[0x78] & 0x80); - n = pci_get_byte(d->config + PCI_INTERRUPT_LINE); - if (n) { - qemu_set_irq(isa_get_irq(NULL, n), level); - } + qemu_set_irq(isa_get_irq(NULL, 14 + n), level); } static void via_ide_reset(DeviceState *dev) @@ -167,9 +164,10 @@ static void via_ide_realize(PCIDevice *dev, Error **errp) uint8_t *pci_conf = dev->config; int i; - pci_config_set_prog_interface(pci_conf, 0x8f); /* native PCI ATA mode */ + pci_config_set_prog_interface(pci_conf, 0x8a); /* legacy mode */ pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0); - dev->wmask[PCI_INTERRUPT_LINE] = 0xf; + dev->wmask[PCI_INTERRUPT_LINE] = 0; + dev->wmask[PCI_CLASS_PROG] = 5; memory_region_init_io(&d->data_bar[0], OBJECT(d), &pci_ide_data_le_ops, &d->bus[0], "via-ide0-data", 8); @@ -190,8 +188,6 @@ static void via_ide_realize(PCIDevice *dev, Error **errp) bmdma_setup_bar(d); pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); - vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_pci, d); - for (i = 0; i < 2; i++) { ide_bus_new(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2); ide_init2(&d->bus[i], qemu_allocate_irq(via_ide_set_irq, d, i)); @@ -213,20 +209,13 @@ static void via_ide_exitfn(PCIDevice *dev) } } -void via_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -{ - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "via-ide"); - pci_ide_create_devs(dev, hd_table); -} - static void via_ide_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); dc->reset = via_ide_reset; + dc->vmsd = &vmstate_ide_pci; k->realize = via_ide_realize; k->exit = via_ide_exitfn; k->vendor_id = PCI_VENDOR_ID_VIA; diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index a62587eb3f..1ad35e5529 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -2593,6 +2593,12 @@ static void armv7m_nvic_reset(DeviceState *dev) s->itns[i] = true; } } + + /* + * We updated state that affects the CPU's MMUidx and thus its hflags; + * and we can't guarantee that we run before the CPU reset function. + */ + arm_rebuild_hflags(&s->cpu->env); } static void nvic_systick_trigger(void *opaque, int n, int level) diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 20c8155557..6608d7220a 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -677,8 +677,8 @@ static void spapr_xive_dt(SpaprInterruptController *intc, uint32_t nr_servers, uint64_t timas[2 * 2]; /* Interrupt number ranges for the IPIs */ uint32_t lisn_ranges[] = { - cpu_to_be32(0), - cpu_to_be32(nr_servers), + cpu_to_be32(SPAPR_IRQ_IPI), + cpu_to_be32(SPAPR_IRQ_IPI + nr_servers), }; /* * EQ size - the sizes of pages supported by the system 4K, 64K, diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index cb79616ced..fbc3165d03 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -625,36 +625,21 @@ static const MemoryRegionOps ich9_rst_cnt_ops = { .endianness = DEVICE_LITTLE_ENDIAN }; -static void ich9_lpc_get_sci_int(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static void ich9_lpc_initfn(Object *obj) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(obj); - uint32_t value = lpc->sci_gsi; - - visit_type_uint32(v, name, &value, errp); -} -static void ich9_lpc_add_properties(ICH9LPCState *lpc) -{ static const uint8_t acpi_enable_cmd = ICH9_APM_ACPI_ENABLE; static const uint8_t acpi_disable_cmd = ICH9_APM_ACPI_DISABLE; - object_property_add(OBJECT(lpc), ACPI_PM_PROP_SCI_INT, "uint32", - ich9_lpc_get_sci_int, - NULL, NULL, NULL, NULL); + object_property_add_uint8_ptr(obj, ACPI_PM_PROP_SCI_INT, + &lpc->sci_gsi, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_ENABLE_CMD, - &acpi_enable_cmd, NULL); + &acpi_enable_cmd, OBJ_PROP_FLAG_READ, NULL); object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_DISABLE_CMD, - &acpi_disable_cmd, NULL); - - ich9_pm_add_properties(OBJECT(lpc), &lpc->pm, NULL); -} - -static void ich9_lpc_initfn(Object *obj) -{ - ICH9LPCState *lpc = ICH9_LPC_DEVICE(obj); + &acpi_disable_cmd, OBJ_PROP_FLAG_READ, NULL); - ich9_lpc_add_properties(lpc); + ich9_pm_add_properties(obj, &lpc->pm, NULL); } static void ich9_lpc_realize(PCIDevice *d, Error **errp) diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c index 7edec5e149..9a10fb9b3c 100644 --- a/hw/isa/piix4.c +++ b/hw/isa/piix4.c @@ -34,7 +34,7 @@ #include "hw/dma/i8257.h" #include "hw/timer/i8254.h" #include "hw/rtc/mc146818rtc.h" -#include "hw/ide.h" +#include "hw/ide/pci.h" #include "migration/vmstate.h" #include "sysemu/reset.h" #include "sysemu/runstate.h" @@ -240,28 +240,25 @@ static void piix4_register_types(void) type_init(piix4_register_types) -DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, - I2CBus **smbus, size_t ide_buses) +DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus) { - size_t ide_drives = ide_buses * MAX_IDE_DEVS; - DriveInfo **hd; PCIDevice *pci; DeviceState *dev; + int devfn = PCI_DEVFN(10, 0); - pci = pci_create_simple_multifunction(pci_bus, PCI_DEVFN(10, 0), - true, TYPE_PIIX4_PCI_DEVICE); + pci = pci_create_simple_multifunction(pci_bus, devfn, true, + TYPE_PIIX4_PCI_DEVICE); dev = DEVICE(pci); if (isa_bus) { *isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0")); } - hd = g_new(DriveInfo *, ide_drives); - ide_drive_get(hd, ide_drives); - pci_piix4_ide_init(pci_bus, hd, pci->devfn + 1); - g_free(hd); - pci_create_simple(pci_bus, pci->devfn + 2, "piix4-usb-uhci"); + pci = pci_create_simple(pci_bus, devfn + 1, "piix4-ide"); + pci_ide_create_devs(pci); + + pci_create_simple(pci_bus, devfn + 2, "piix4-usb-uhci"); if (smbus) { - *smbus = piix4_pm_init(pci_bus, pci->devfn + 3, 0x1100, + *smbus = piix4_pm_init(pci_bus, devfn + 3, 0x1100, isa_get_irq(NULL, 9), NULL, 0, NULL); } diff --git a/hw/m68k/bootinfo.h b/hw/m68k/bootinfo.h index 5f8ded2686..c954270aad 100644 --- a/hw/m68k/bootinfo.h +++ b/hw/m68k/bootinfo.h @@ -14,7 +14,7 @@ struct bi_record { uint16_t tag; /* tag ID */ uint16_t size; /* size of record */ - uint32_t data[0]; /* data */ + uint32_t data[]; /* data */ }; /* machine independent tags */ diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index c5699f6f3e..81749e7ec6 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -399,13 +399,12 @@ static void q800_init(MachineState *machine) uint8_t *ptr; /* allocate and load BIOS */ rom = g_malloc(sizeof(*rom)); - memory_region_init_ram(rom, NULL, "m68k_mac.rom", MACROM_SIZE, + memory_region_init_rom(rom, NULL, "m68k_mac.rom", MACROM_SIZE, &error_abort); if (bios_name == NULL) { bios_name = MACROM_FILENAME; } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - memory_region_set_readonly(rom, true); memory_region_add_subregion(get_system_memory(), MACROM_ADDR, rom); /* Load MacROM binary */ diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index 4727b1d3a4..5040afd581 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -36,7 +36,7 @@ #include "audio/audio.h" #include "qemu/log.h" #include "hw/loader.h" -#include "hw/ide.h" +#include "hw/ide/pci.h" #include "elf.h" #include "hw/isa/vt82c686.h" #include "hw/rtc/mc146818rtc.h" @@ -238,7 +238,7 @@ static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc, { qemu_irq *i8259; ISABus *isa_bus; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + PCIDevice *dev; isa_bus = vt82c686b_isa_init(pci_bus, PCI_DEVFN(slot, 0)); if (!isa_bus) { @@ -256,8 +256,8 @@ static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc, /* Super I/O */ isa_create_simple(isa_bus, TYPE_VT82C686B_SUPERIO); - ide_drive_get(hd, ARRAY_SIZE(hd)); - via_ide_init(pci_bus, hd, PCI_DEVFN(slot, 1)); + dev = pci_create_simple(pci_bus, PCI_DEVFN(slot, 1), "via-ide"); + pci_ide_create_devs(dev); pci_create_simple(pci_bus, PCI_DEVFN(slot, 2), "vt82c686b-usb-uhci"); pci_create_simple(pci_bus, PCI_DEVFN(slot, 3), "vt82c686b-usb-uhci"); diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index d380f73d7b..e4c4de1b4e 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -1403,7 +1403,7 @@ void mips_malta_init(MachineState *machine) pci_bus = gt64120_register(s->i8259); /* Southbridge */ - dev = piix4_create(pci_bus, &isa_bus, &smbus, MAX_IDE_BUS); + dev = piix4_create(pci_bus, &isa_bus, &smbus); /* Interrupt controller */ qdev_connect_gpio_out_named(dev, "intr", 0, i8259_irq); diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index ad8b75e286..3487013a4a 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -26,6 +26,7 @@ #include "qemu/log.h" #include "hw/mips/bios.h" #include "hw/ide.h" +#include "hw/ide/internal.h" #include "hw/loader.h" #include "elf.h" #include "hw/rtc/mc146818rtc.h" diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index da993f45b7..68aae2eabb 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -28,6 +28,11 @@ common-obj-$(CONFIG_MACIO) += macio/ common-obj-$(CONFIG_IVSHMEM_DEVICE) += ivshmem.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-ccu.o +obj-$(CONFIG_ALLWINNER_H3) += allwinner-cpucfg.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-dramc.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-h3-sysctrl.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-sid.o common-obj-$(CONFIG_REALVIEW) += arm_sysctl.o common-obj-$(CONFIG_NSERIES) += cbus.o common-obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o diff --git a/hw/misc/allwinner-cpucfg.c b/hw/misc/allwinner-cpucfg.c new file mode 100644 index 0000000000..bbd33a7dac --- /dev/null +++ b/hw/misc/allwinner-cpucfg.c @@ -0,0 +1,282 @@ +/* + * Allwinner CPU Configuration Module emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include "qemu/timer.h" +#include "hw/core/cpu.h" +#include "target/arm/arm-powerctl.h" +#include "target/arm/cpu.h" +#include "hw/misc/allwinner-cpucfg.h" +#include "trace.h" + +/* CPUCFG register offsets */ +enum { + REG_CPUS_RST_CTRL = 0x0000, /* CPUs Reset Control */ + REG_CPU0_RST_CTRL = 0x0040, /* CPU#0 Reset Control */ + REG_CPU0_CTRL = 0x0044, /* CPU#0 Control */ + REG_CPU0_STATUS = 0x0048, /* CPU#0 Status */ + REG_CPU1_RST_CTRL = 0x0080, /* CPU#1 Reset Control */ + REG_CPU1_CTRL = 0x0084, /* CPU#1 Control */ + REG_CPU1_STATUS = 0x0088, /* CPU#1 Status */ + REG_CPU2_RST_CTRL = 0x00C0, /* CPU#2 Reset Control */ + REG_CPU2_CTRL = 0x00C4, /* CPU#2 Control */ + REG_CPU2_STATUS = 0x00C8, /* CPU#2 Status */ + REG_CPU3_RST_CTRL = 0x0100, /* CPU#3 Reset Control */ + REG_CPU3_CTRL = 0x0104, /* CPU#3 Control */ + REG_CPU3_STATUS = 0x0108, /* CPU#3 Status */ + REG_CPU_SYS_RST = 0x0140, /* CPU System Reset */ + REG_CLK_GATING = 0x0144, /* CPU Clock Gating */ + REG_GEN_CTRL = 0x0184, /* General Control */ + REG_SUPER_STANDBY = 0x01A0, /* Super Standby Flag */ + REG_ENTRY_ADDR = 0x01A4, /* Reset Entry Address */ + REG_DBG_EXTERN = 0x01E4, /* Debug External */ + REG_CNT64_CTRL = 0x0280, /* 64-bit Counter Control */ + REG_CNT64_LOW = 0x0284, /* 64-bit Counter Low */ + REG_CNT64_HIGH = 0x0288, /* 64-bit Counter High */ +}; + +/* CPUCFG register flags */ +enum { + CPUX_RESET_RELEASED = ((1 << 1) | (1 << 0)), + CPUX_STATUS_SMP = (1 << 0), + CPU_SYS_RESET_RELEASED = (1 << 0), + CLK_GATING_ENABLE = ((1 << 8) | 0xF), +}; + +/* CPUCFG register reset values */ +enum { + REG_CLK_GATING_RST = 0x0000010F, + REG_GEN_CTRL_RST = 0x00000020, + REG_SUPER_STANDBY_RST = 0x0, + REG_CNT64_CTRL_RST = 0x0, +}; + +/* CPUCFG constants */ +enum { + CPU_EXCEPTION_LEVEL_ON_RESET = 3, /* EL3 */ +}; + +static void allwinner_cpucfg_cpu_reset(AwCpuCfgState *s, uint8_t cpu_id) +{ + int ret; + + trace_allwinner_cpucfg_cpu_reset(cpu_id, s->entry_addr); + + ARMCPU *target_cpu = ARM_CPU(arm_get_cpu_by_id(cpu_id)); + if (!target_cpu) { + /* + * Called with a bogus value for cpu_id. Guest error will + * already have been logged, we can simply return here. + */ + return; + } + bool target_aa64 = arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64); + + ret = arm_set_cpu_on(cpu_id, s->entry_addr, 0, + CPU_EXCEPTION_LEVEL_ON_RESET, target_aa64); + if (ret != QEMU_ARM_POWERCTL_RET_SUCCESS) { + error_report("%s: failed to bring up CPU %d: err %d", + __func__, cpu_id, ret); + return; + } +} + +static uint64_t allwinner_cpucfg_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwCpuCfgState *s = AW_CPUCFG(opaque); + uint64_t val = 0; + + switch (offset) { + case REG_CPUS_RST_CTRL: /* CPUs Reset Control */ + case REG_CPU_SYS_RST: /* CPU System Reset */ + val = CPU_SYS_RESET_RELEASED; + break; + case REG_CPU0_RST_CTRL: /* CPU#0 Reset Control */ + case REG_CPU1_RST_CTRL: /* CPU#1 Reset Control */ + case REG_CPU2_RST_CTRL: /* CPU#2 Reset Control */ + case REG_CPU3_RST_CTRL: /* CPU#3 Reset Control */ + val = CPUX_RESET_RELEASED; + break; + case REG_CPU0_CTRL: /* CPU#0 Control */ + case REG_CPU1_CTRL: /* CPU#1 Control */ + case REG_CPU2_CTRL: /* CPU#2 Control */ + case REG_CPU3_CTRL: /* CPU#3 Control */ + val = 0; + break; + case REG_CPU0_STATUS: /* CPU#0 Status */ + case REG_CPU1_STATUS: /* CPU#1 Status */ + case REG_CPU2_STATUS: /* CPU#2 Status */ + case REG_CPU3_STATUS: /* CPU#3 Status */ + val = CPUX_STATUS_SMP; + break; + case REG_CLK_GATING: /* CPU Clock Gating */ + val = CLK_GATING_ENABLE; + break; + case REG_GEN_CTRL: /* General Control */ + val = s->gen_ctrl; + break; + case REG_SUPER_STANDBY: /* Super Standby Flag */ + val = s->super_standby; + break; + case REG_ENTRY_ADDR: /* Reset Entry Address */ + val = s->entry_addr; + break; + case REG_DBG_EXTERN: /* Debug External */ + case REG_CNT64_CTRL: /* 64-bit Counter Control */ + case REG_CNT64_LOW: /* 64-bit Counter Low */ + case REG_CNT64_HIGH: /* 64-bit Counter High */ + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register at 0x%04x\n", + __func__, (uint32_t)offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + } + + trace_allwinner_cpucfg_read(offset, val, size); + + return val; +} + +static void allwinner_cpucfg_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwCpuCfgState *s = AW_CPUCFG(opaque); + + trace_allwinner_cpucfg_write(offset, val, size); + + switch (offset) { + case REG_CPUS_RST_CTRL: /* CPUs Reset Control */ + case REG_CPU_SYS_RST: /* CPU System Reset */ + break; + case REG_CPU0_RST_CTRL: /* CPU#0 Reset Control */ + case REG_CPU1_RST_CTRL: /* CPU#1 Reset Control */ + case REG_CPU2_RST_CTRL: /* CPU#2 Reset Control */ + case REG_CPU3_RST_CTRL: /* CPU#3 Reset Control */ + if (val) { + allwinner_cpucfg_cpu_reset(s, (offset - REG_CPU0_RST_CTRL) >> 6); + } + break; + case REG_CPU0_CTRL: /* CPU#0 Control */ + case REG_CPU1_CTRL: /* CPU#1 Control */ + case REG_CPU2_CTRL: /* CPU#2 Control */ + case REG_CPU3_CTRL: /* CPU#3 Control */ + case REG_CPU0_STATUS: /* CPU#0 Status */ + case REG_CPU1_STATUS: /* CPU#1 Status */ + case REG_CPU2_STATUS: /* CPU#2 Status */ + case REG_CPU3_STATUS: /* CPU#3 Status */ + case REG_CLK_GATING: /* CPU Clock Gating */ + break; + case REG_GEN_CTRL: /* General Control */ + s->gen_ctrl = val; + break; + case REG_SUPER_STANDBY: /* Super Standby Flag */ + s->super_standby = val; + break; + case REG_ENTRY_ADDR: /* Reset Entry Address */ + s->entry_addr = val; + break; + case REG_DBG_EXTERN: /* Debug External */ + case REG_CNT64_CTRL: /* 64-bit Counter Control */ + case REG_CNT64_LOW: /* 64-bit Counter Low */ + case REG_CNT64_HIGH: /* 64-bit Counter High */ + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register at 0x%04x\n", + __func__, (uint32_t)offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + } +} + +static const MemoryRegionOps allwinner_cpucfg_ops = { + .read = allwinner_cpucfg_read, + .write = allwinner_cpucfg_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_cpucfg_reset(DeviceState *dev) +{ + AwCpuCfgState *s = AW_CPUCFG(dev); + + /* Set default values for registers */ + s->gen_ctrl = REG_GEN_CTRL_RST; + s->super_standby = REG_SUPER_STANDBY_RST; + s->entry_addr = 0; +} + +static void allwinner_cpucfg_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwCpuCfgState *s = AW_CPUCFG(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_cpucfg_ops, s, + TYPE_AW_CPUCFG, 1 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_cpucfg_vmstate = { + .name = "allwinner-cpucfg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(gen_ctrl, AwCpuCfgState), + VMSTATE_UINT32(super_standby, AwCpuCfgState), + VMSTATE_UINT32(entry_addr, AwCpuCfgState), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_cpucfg_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_cpucfg_reset; + dc->vmsd = &allwinner_cpucfg_vmstate; +} + +static const TypeInfo allwinner_cpucfg_info = { + .name = TYPE_AW_CPUCFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_cpucfg_init, + .instance_size = sizeof(AwCpuCfgState), + .class_init = allwinner_cpucfg_class_init, +}; + +static void allwinner_cpucfg_register(void) +{ + type_register_static(&allwinner_cpucfg_info); +} + +type_init(allwinner_cpucfg_register) diff --git a/hw/misc/allwinner-h3-ccu.c b/hw/misc/allwinner-h3-ccu.c new file mode 100644 index 0000000000..18d1074545 --- /dev/null +++ b/hw/misc/allwinner-h3-ccu.c @@ -0,0 +1,242 @@ +/* + * Allwinner H3 Clock Control Unit emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/misc/allwinner-h3-ccu.h" + +/* CCU register offsets */ +enum { + REG_PLL_CPUX = 0x0000, /* PLL CPUX Control */ + REG_PLL_AUDIO = 0x0008, /* PLL Audio Control */ + REG_PLL_VIDEO = 0x0010, /* PLL Video Control */ + REG_PLL_VE = 0x0018, /* PLL VE Control */ + REG_PLL_DDR = 0x0020, /* PLL DDR Control */ + REG_PLL_PERIPH0 = 0x0028, /* PLL Peripherals 0 Control */ + REG_PLL_GPU = 0x0038, /* PLL GPU Control */ + REG_PLL_PERIPH1 = 0x0044, /* PLL Peripherals 1 Control */ + REG_PLL_DE = 0x0048, /* PLL Display Engine Control */ + REG_CPUX_AXI = 0x0050, /* CPUX/AXI Configuration */ + REG_APB1 = 0x0054, /* ARM Peripheral Bus 1 Config */ + REG_APB2 = 0x0058, /* ARM Peripheral Bus 2 Config */ + REG_DRAM_CFG = 0x00F4, /* DRAM Configuration */ + REG_MBUS = 0x00FC, /* MBUS Reset */ + REG_PLL_TIME0 = 0x0200, /* PLL Stable Time 0 */ + REG_PLL_TIME1 = 0x0204, /* PLL Stable Time 1 */ + REG_PLL_CPUX_BIAS = 0x0220, /* PLL CPUX Bias */ + REG_PLL_AUDIO_BIAS = 0x0224, /* PLL Audio Bias */ + REG_PLL_VIDEO_BIAS = 0x0228, /* PLL Video Bias */ + REG_PLL_VE_BIAS = 0x022C, /* PLL VE Bias */ + REG_PLL_DDR_BIAS = 0x0230, /* PLL DDR Bias */ + REG_PLL_PERIPH0_BIAS = 0x0234, /* PLL Peripherals 0 Bias */ + REG_PLL_GPU_BIAS = 0x023C, /* PLL GPU Bias */ + REG_PLL_PERIPH1_BIAS = 0x0244, /* PLL Peripherals 1 Bias */ + REG_PLL_DE_BIAS = 0x0248, /* PLL Display Engine Bias */ + REG_PLL_CPUX_TUNING = 0x0250, /* PLL CPUX Tuning */ + REG_PLL_DDR_TUNING = 0x0260, /* PLL DDR Tuning */ +}; + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* CCU register flags */ +enum { + REG_DRAM_CFG_UPDATE = (1 << 16), +}; + +enum { + REG_PLL_ENABLE = (1 << 31), + REG_PLL_LOCK = (1 << 28), +}; + + +/* CCU register reset values */ +enum { + REG_PLL_CPUX_RST = 0x00001000, + REG_PLL_AUDIO_RST = 0x00035514, + REG_PLL_VIDEO_RST = 0x03006207, + REG_PLL_VE_RST = 0x03006207, + REG_PLL_DDR_RST = 0x00001000, + REG_PLL_PERIPH0_RST = 0x00041811, + REG_PLL_GPU_RST = 0x03006207, + REG_PLL_PERIPH1_RST = 0x00041811, + REG_PLL_DE_RST = 0x03006207, + REG_CPUX_AXI_RST = 0x00010000, + REG_APB1_RST = 0x00001010, + REG_APB2_RST = 0x01000000, + REG_DRAM_CFG_RST = 0x00000000, + REG_MBUS_RST = 0x80000000, + REG_PLL_TIME0_RST = 0x000000FF, + REG_PLL_TIME1_RST = 0x000000FF, + REG_PLL_CPUX_BIAS_RST = 0x08100200, + REG_PLL_AUDIO_BIAS_RST = 0x10100000, + REG_PLL_VIDEO_BIAS_RST = 0x10100000, + REG_PLL_VE_BIAS_RST = 0x10100000, + REG_PLL_DDR_BIAS_RST = 0x81104000, + REG_PLL_PERIPH0_BIAS_RST = 0x10100010, + REG_PLL_GPU_BIAS_RST = 0x10100000, + REG_PLL_PERIPH1_BIAS_RST = 0x10100010, + REG_PLL_DE_BIAS_RST = 0x10100000, + REG_PLL_CPUX_TUNING_RST = 0x0A101000, + REG_PLL_DDR_TUNING_RST = 0x14880000, +}; + +static uint64_t allwinner_h3_ccu_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3ClockCtlState *s = AW_H3_CCU(opaque); + const uint32_t idx = REG_INDEX(offset); + + switch (offset) { + case 0x308 ... AW_H3_CCU_IOSIZE: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + return s->regs[idx]; +} + +static void allwinner_h3_ccu_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3ClockCtlState *s = AW_H3_CCU(opaque); + const uint32_t idx = REG_INDEX(offset); + + switch (offset) { + case REG_DRAM_CFG: /* DRAM Configuration */ + val &= ~REG_DRAM_CFG_UPDATE; + break; + case REG_PLL_CPUX: /* PLL CPUX Control */ + case REG_PLL_AUDIO: /* PLL Audio Control */ + case REG_PLL_VIDEO: /* PLL Video Control */ + case REG_PLL_VE: /* PLL VE Control */ + case REG_PLL_DDR: /* PLL DDR Control */ + case REG_PLL_PERIPH0: /* PLL Peripherals 0 Control */ + case REG_PLL_GPU: /* PLL GPU Control */ + case REG_PLL_PERIPH1: /* PLL Peripherals 1 Control */ + case REG_PLL_DE: /* PLL Display Engine Control */ + if (val & REG_PLL_ENABLE) { + val |= REG_PLL_LOCK; + } + break; + case 0x308 ... AW_H3_CCU_IOSIZE: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + } + + s->regs[idx] = (uint32_t) val; +} + +static const MemoryRegionOps allwinner_h3_ccu_ops = { + .read = allwinner_h3_ccu_read, + .write = allwinner_h3_ccu_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_h3_ccu_reset(DeviceState *dev) +{ + AwH3ClockCtlState *s = AW_H3_CCU(dev); + + /* Set default values for registers */ + s->regs[REG_INDEX(REG_PLL_CPUX)] = REG_PLL_CPUX_RST; + s->regs[REG_INDEX(REG_PLL_AUDIO)] = REG_PLL_AUDIO_RST; + s->regs[REG_INDEX(REG_PLL_VIDEO)] = REG_PLL_VIDEO_RST; + s->regs[REG_INDEX(REG_PLL_VE)] = REG_PLL_VE_RST; + s->regs[REG_INDEX(REG_PLL_DDR)] = REG_PLL_DDR_RST; + s->regs[REG_INDEX(REG_PLL_PERIPH0)] = REG_PLL_PERIPH0_RST; + s->regs[REG_INDEX(REG_PLL_GPU)] = REG_PLL_GPU_RST; + s->regs[REG_INDEX(REG_PLL_PERIPH1)] = REG_PLL_PERIPH1_RST; + s->regs[REG_INDEX(REG_PLL_DE)] = REG_PLL_DE_RST; + s->regs[REG_INDEX(REG_CPUX_AXI)] = REG_CPUX_AXI_RST; + s->regs[REG_INDEX(REG_APB1)] = REG_APB1_RST; + s->regs[REG_INDEX(REG_APB2)] = REG_APB2_RST; + s->regs[REG_INDEX(REG_DRAM_CFG)] = REG_DRAM_CFG_RST; + s->regs[REG_INDEX(REG_MBUS)] = REG_MBUS_RST; + s->regs[REG_INDEX(REG_PLL_TIME0)] = REG_PLL_TIME0_RST; + s->regs[REG_INDEX(REG_PLL_TIME1)] = REG_PLL_TIME1_RST; + s->regs[REG_INDEX(REG_PLL_CPUX_BIAS)] = REG_PLL_CPUX_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_AUDIO_BIAS)] = REG_PLL_AUDIO_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_VIDEO_BIAS)] = REG_PLL_VIDEO_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_VE_BIAS)] = REG_PLL_VE_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_DDR_BIAS)] = REG_PLL_DDR_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_PERIPH0_BIAS)] = REG_PLL_PERIPH0_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_GPU_BIAS)] = REG_PLL_GPU_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_PERIPH1_BIAS)] = REG_PLL_PERIPH1_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_DE_BIAS)] = REG_PLL_DE_BIAS_RST; + s->regs[REG_INDEX(REG_PLL_CPUX_TUNING)] = REG_PLL_CPUX_TUNING_RST; + s->regs[REG_INDEX(REG_PLL_DDR_TUNING)] = REG_PLL_DDR_TUNING_RST; +} + +static void allwinner_h3_ccu_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwH3ClockCtlState *s = AW_H3_CCU(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_ccu_ops, s, + TYPE_AW_H3_CCU, AW_H3_CCU_IOSIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_h3_ccu_vmstate = { + .name = "allwinner-h3-ccu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AwH3ClockCtlState, AW_H3_CCU_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_h3_ccu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_h3_ccu_reset; + dc->vmsd = &allwinner_h3_ccu_vmstate; +} + +static const TypeInfo allwinner_h3_ccu_info = { + .name = TYPE_AW_H3_CCU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_h3_ccu_init, + .instance_size = sizeof(AwH3ClockCtlState), + .class_init = allwinner_h3_ccu_class_init, +}; + +static void allwinner_h3_ccu_register(void) +{ + type_register_static(&allwinner_h3_ccu_info); +} + +type_init(allwinner_h3_ccu_register) diff --git a/hw/misc/allwinner-h3-dramc.c b/hw/misc/allwinner-h3-dramc.c new file mode 100644 index 0000000000..2b5260260e --- /dev/null +++ b/hw/misc/allwinner-h3-dramc.c @@ -0,0 +1,358 @@ +/* + * Allwinner H3 SDRAM Controller emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/error-report.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "exec/address-spaces.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "hw/misc/allwinner-h3-dramc.h" +#include "trace.h" + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* DRAMCOM register offsets */ +enum { + REG_DRAMCOM_CR = 0x0000, /* Control Register */ +}; + +/* DRAMCTL register offsets */ +enum { + REG_DRAMCTL_PIR = 0x0000, /* PHY Initialization Register */ + REG_DRAMCTL_PGSR = 0x0010, /* PHY General Status Register */ + REG_DRAMCTL_STATR = 0x0018, /* Status Register */ +}; + +/* DRAMCTL register flags */ +enum { + REG_DRAMCTL_PGSR_INITDONE = (1 << 0), +}; + +enum { + REG_DRAMCTL_STATR_ACTIVE = (1 << 0), +}; + +static void allwinner_h3_dramc_map_rows(AwH3DramCtlState *s, uint8_t row_bits, + uint8_t bank_bits, uint16_t page_size) +{ + /* + * This function simulates row addressing behavior when bootloader + * software attempts to detect the amount of available SDRAM. In U-Boot + * the controller is configured with the widest row addressing available. + * Then a pattern is written to RAM at an offset on the row boundary size. + * If the value read back equals the value read back from the + * start of RAM, the bootloader knows the amount of row bits. + * + * This function inserts a mirrored memory region when the configured row + * bits are not matching the actual emulated memory, to simulate the + * same behavior on hardware as expected by the bootloader. + */ + uint8_t row_bits_actual = 0; + + /* Calculate the actual row bits using the ram_size property */ + for (uint8_t i = 8; i < 12; i++) { + if (1 << i == s->ram_size) { + row_bits_actual = i + 3; + break; + } + } + + if (s->ram_size == (1 << (row_bits - 3))) { + /* When row bits is the expected value, remove the mirror */ + memory_region_set_enabled(&s->row_mirror_alias, false); + trace_allwinner_h3_dramc_rowmirror_disable(); + + } else if (row_bits_actual) { + /* Row bits not matching ram_size, install the rows mirror */ + hwaddr row_mirror = s->ram_addr + ((1 << (row_bits_actual + + bank_bits)) * page_size); + + memory_region_set_enabled(&s->row_mirror_alias, true); + memory_region_set_address(&s->row_mirror_alias, row_mirror); + + trace_allwinner_h3_dramc_rowmirror_enable(row_mirror); + } +} + +static uint64_t allwinner_h3_dramcom_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3DramCtlState *s = AW_H3_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_DRAMCOM_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_h3_dramcom_read(offset, s->dramcom[idx], size); + + return s->dramcom[idx]; +} + +static void allwinner_h3_dramcom_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3DramCtlState *s = AW_H3_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + trace_allwinner_h3_dramcom_write(offset, val, size); + + if (idx >= AW_H3_DRAMCOM_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + switch (offset) { + case REG_DRAMCOM_CR: /* Control Register */ + allwinner_h3_dramc_map_rows(s, ((val >> 4) & 0xf) + 1, + ((val >> 2) & 0x1) + 2, + 1 << (((val >> 8) & 0xf) + 3)); + break; + default: + break; + }; + + s->dramcom[idx] = (uint32_t) val; +} + +static uint64_t allwinner_h3_dramctl_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3DramCtlState *s = AW_H3_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_DRAMCTL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_h3_dramctl_read(offset, s->dramctl[idx], size); + + return s->dramctl[idx]; +} + +static void allwinner_h3_dramctl_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3DramCtlState *s = AW_H3_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + trace_allwinner_h3_dramctl_write(offset, val, size); + + if (idx >= AW_H3_DRAMCTL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + switch (offset) { + case REG_DRAMCTL_PIR: /* PHY Initialization Register */ + s->dramctl[REG_INDEX(REG_DRAMCTL_PGSR)] |= REG_DRAMCTL_PGSR_INITDONE; + s->dramctl[REG_INDEX(REG_DRAMCTL_STATR)] |= REG_DRAMCTL_STATR_ACTIVE; + break; + default: + break; + } + + s->dramctl[idx] = (uint32_t) val; +} + +static uint64_t allwinner_h3_dramphy_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3DramCtlState *s = AW_H3_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_DRAMPHY_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_h3_dramphy_read(offset, s->dramphy[idx], size); + + return s->dramphy[idx]; +} + +static void allwinner_h3_dramphy_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3DramCtlState *s = AW_H3_DRAMC(opaque); + const uint32_t idx = REG_INDEX(offset); + + trace_allwinner_h3_dramphy_write(offset, val, size); + + if (idx >= AW_H3_DRAMPHY_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + s->dramphy[idx] = (uint32_t) val; +} + +static const MemoryRegionOps allwinner_h3_dramcom_ops = { + .read = allwinner_h3_dramcom_read, + .write = allwinner_h3_dramcom_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static const MemoryRegionOps allwinner_h3_dramctl_ops = { + .read = allwinner_h3_dramctl_read, + .write = allwinner_h3_dramctl_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static const MemoryRegionOps allwinner_h3_dramphy_ops = { + .read = allwinner_h3_dramphy_read, + .write = allwinner_h3_dramphy_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_h3_dramc_reset(DeviceState *dev) +{ + AwH3DramCtlState *s = AW_H3_DRAMC(dev); + + /* Set default values for registers */ + memset(&s->dramcom, 0, sizeof(s->dramcom)); + memset(&s->dramctl, 0, sizeof(s->dramctl)); + memset(&s->dramphy, 0, sizeof(s->dramphy)); +} + +static void allwinner_h3_dramc_realize(DeviceState *dev, Error **errp) +{ + AwH3DramCtlState *s = AW_H3_DRAMC(dev); + + /* Only power of 2 RAM sizes from 256MiB up to 2048MiB are supported */ + for (uint8_t i = 8; i < 13; i++) { + if (1 << i == s->ram_size) { + break; + } else if (i == 12) { + error_report("%s: ram-size %u MiB is not supported", + __func__, s->ram_size); + exit(1); + } + } + + /* Setup row mirror mappings */ + memory_region_init_ram(&s->row_mirror, OBJECT(s), + "allwinner-h3-dramc.row-mirror", + 4 * KiB, &error_abort); + memory_region_add_subregion_overlap(get_system_memory(), s->ram_addr, + &s->row_mirror, 10); + + memory_region_init_alias(&s->row_mirror_alias, OBJECT(s), + "allwinner-h3-dramc.row-mirror-alias", + &s->row_mirror, 0, 4 * KiB); + memory_region_add_subregion_overlap(get_system_memory(), + s->ram_addr + 1 * MiB, + &s->row_mirror_alias, 10); + memory_region_set_enabled(&s->row_mirror_alias, false); +} + +static void allwinner_h3_dramc_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwH3DramCtlState *s = AW_H3_DRAMC(obj); + + /* DRAMCOM registers */ + memory_region_init_io(&s->dramcom_iomem, OBJECT(s), + &allwinner_h3_dramcom_ops, s, + TYPE_AW_H3_DRAMC, 4 * KiB); + sysbus_init_mmio(sbd, &s->dramcom_iomem); + + /* DRAMCTL registers */ + memory_region_init_io(&s->dramctl_iomem, OBJECT(s), + &allwinner_h3_dramctl_ops, s, + TYPE_AW_H3_DRAMC, 4 * KiB); + sysbus_init_mmio(sbd, &s->dramctl_iomem); + + /* DRAMPHY registers */ + memory_region_init_io(&s->dramphy_iomem, OBJECT(s), + &allwinner_h3_dramphy_ops, s, + TYPE_AW_H3_DRAMC, 4 * KiB); + sysbus_init_mmio(sbd, &s->dramphy_iomem); +} + +static Property allwinner_h3_dramc_properties[] = { + DEFINE_PROP_UINT64("ram-addr", AwH3DramCtlState, ram_addr, 0x0), + DEFINE_PROP_UINT32("ram-size", AwH3DramCtlState, ram_size, 256 * MiB), + DEFINE_PROP_END_OF_LIST() +}; + +static const VMStateDescription allwinner_h3_dramc_vmstate = { + .name = "allwinner-h3-dramc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(dramcom, AwH3DramCtlState, AW_H3_DRAMCOM_REGS_NUM), + VMSTATE_UINT32_ARRAY(dramctl, AwH3DramCtlState, AW_H3_DRAMCTL_REGS_NUM), + VMSTATE_UINT32_ARRAY(dramphy, AwH3DramCtlState, AW_H3_DRAMPHY_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_h3_dramc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_h3_dramc_reset; + dc->vmsd = &allwinner_h3_dramc_vmstate; + dc->realize = allwinner_h3_dramc_realize; + device_class_set_props(dc, allwinner_h3_dramc_properties); +} + +static const TypeInfo allwinner_h3_dramc_info = { + .name = TYPE_AW_H3_DRAMC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_h3_dramc_init, + .instance_size = sizeof(AwH3DramCtlState), + .class_init = allwinner_h3_dramc_class_init, +}; + +static void allwinner_h3_dramc_register(void) +{ + type_register_static(&allwinner_h3_dramc_info); +} + +type_init(allwinner_h3_dramc_register) diff --git a/hw/misc/allwinner-h3-sysctrl.c b/hw/misc/allwinner-h3-sysctrl.c new file mode 100644 index 0000000000..1d07efa880 --- /dev/null +++ b/hw/misc/allwinner-h3-sysctrl.c @@ -0,0 +1,140 @@ +/* + * Allwinner H3 System Control emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/misc/allwinner-h3-sysctrl.h" + +/* System Control register offsets */ +enum { + REG_VER = 0x24, /* Version */ + REG_EMAC_PHY_CLK = 0x30, /* EMAC PHY Clock */ +}; + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + +/* System Control register reset values */ +enum { + REG_VER_RST = 0x0, + REG_EMAC_PHY_CLK_RST = 0x58000, +}; + +static uint64_t allwinner_h3_sysctrl_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwH3SysCtrlState *s = AW_H3_SYSCTRL(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_SYSCTRL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + return s->regs[idx]; +} + +static void allwinner_h3_sysctrl_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwH3SysCtrlState *s = AW_H3_SYSCTRL(opaque); + const uint32_t idx = REG_INDEX(offset); + + if (idx >= AW_H3_SYSCTRL_REGS_NUM) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + switch (offset) { + case REG_VER: /* Version */ + break; + default: + s->regs[idx] = (uint32_t) val; + break; + } +} + +static const MemoryRegionOps allwinner_h3_sysctrl_ops = { + .read = allwinner_h3_sysctrl_read, + .write = allwinner_h3_sysctrl_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_h3_sysctrl_reset(DeviceState *dev) +{ + AwH3SysCtrlState *s = AW_H3_SYSCTRL(dev); + + /* Set default values for registers */ + s->regs[REG_INDEX(REG_VER)] = REG_VER_RST; + s->regs[REG_INDEX(REG_EMAC_PHY_CLK)] = REG_EMAC_PHY_CLK_RST; +} + +static void allwinner_h3_sysctrl_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwH3SysCtrlState *s = AW_H3_SYSCTRL(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_h3_sysctrl_ops, s, + TYPE_AW_H3_SYSCTRL, 4 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_h3_sysctrl_vmstate = { + .name = "allwinner-h3-sysctrl", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AwH3SysCtrlState, AW_H3_SYSCTRL_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_h3_sysctrl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_h3_sysctrl_reset; + dc->vmsd = &allwinner_h3_sysctrl_vmstate; +} + +static const TypeInfo allwinner_h3_sysctrl_info = { + .name = TYPE_AW_H3_SYSCTRL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_h3_sysctrl_init, + .instance_size = sizeof(AwH3SysCtrlState), + .class_init = allwinner_h3_sysctrl_class_init, +}; + +static void allwinner_h3_sysctrl_register(void) +{ + type_register_static(&allwinner_h3_sysctrl_info); +} + +type_init(allwinner_h3_sysctrl_register) diff --git a/hw/misc/allwinner-sid.c b/hw/misc/allwinner-sid.c new file mode 100644 index 0000000000..196380c33a --- /dev/null +++ b/hw/misc/allwinner-sid.c @@ -0,0 +1,168 @@ +/* + * Allwinner Security ID emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/guest-random.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" +#include "hw/misc/allwinner-sid.h" +#include "trace.h" + +/* SID register offsets */ +enum { + REG_PRCTL = 0x40, /* Control */ + REG_RDKEY = 0x60, /* Read Key */ +}; + +/* SID register flags */ +enum { + REG_PRCTL_WRITE = 0x0002, /* Unknown write flag */ + REG_PRCTL_OP_LOCK = 0xAC00, /* Lock operation */ +}; + +static uint64_t allwinner_sid_read(void *opaque, hwaddr offset, + unsigned size) +{ + const AwSidState *s = AW_SID(opaque); + uint64_t val = 0; + + switch (offset) { + case REG_PRCTL: /* Control */ + val = s->control; + break; + case REG_RDKEY: /* Read Key */ + val = s->rdkey; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + trace_allwinner_sid_read(offset, val, size); + + return val; +} + +static void allwinner_sid_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwSidState *s = AW_SID(opaque); + + trace_allwinner_sid_write(offset, val, size); + + switch (offset) { + case REG_PRCTL: /* Control */ + s->control = val; + + if ((s->control & REG_PRCTL_OP_LOCK) && + (s->control & REG_PRCTL_WRITE)) { + uint32_t id = s->control >> 16; + + if (id <= sizeof(QemuUUID) - sizeof(s->rdkey)) { + s->rdkey = ldl_be_p(&s->identifier.data[id]); + } + } + s->control &= ~REG_PRCTL_WRITE; + break; + case REG_RDKEY: /* Read Key */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + break; + } +} + +static const MemoryRegionOps allwinner_sid_ops = { + .read = allwinner_sid_read, + .write = allwinner_sid_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_sid_reset(DeviceState *dev) +{ + AwSidState *s = AW_SID(dev); + + /* Set default values for registers */ + s->control = 0; + s->rdkey = 0; +} + +static void allwinner_sid_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwSidState *s = AW_SID(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_sid_ops, s, + TYPE_AW_SID, 1 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static Property allwinner_sid_properties[] = { + DEFINE_PROP_UUID_NODEFAULT("identifier", AwSidState, identifier), + DEFINE_PROP_END_OF_LIST() +}; + +static const VMStateDescription allwinner_sid_vmstate = { + .name = "allwinner-sid", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(control, AwSidState), + VMSTATE_UINT32(rdkey, AwSidState), + VMSTATE_UINT8_ARRAY_V(identifier.data, AwSidState, sizeof(QemuUUID), 1), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_sid_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_sid_reset; + dc->vmsd = &allwinner_sid_vmstate; + device_class_set_props(dc, allwinner_sid_properties); +} + +static const TypeInfo allwinner_sid_info = { + .name = TYPE_AW_SID, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_sid_init, + .instance_size = sizeof(AwSidState), + .class_init = allwinner_sid_class_init, +}; + +static void allwinner_sid_register(void) +{ + type_register_static(&allwinner_sid_info); +} + +type_init(allwinner_sid_register) diff --git a/hw/misc/edu.c b/hw/misc/edu.c index d5e2bdbb57..ff10f5b794 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -396,21 +396,14 @@ static void pci_edu_uninit(PCIDevice *pdev) msi_uninit(pdev); } -static void edu_obj_uint64(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint64_t *val = opaque; - - visit_type_uint64(v, name, val, errp); -} - static void edu_instance_init(Object *obj) { EduState *edu = EDU(obj); edu->dma_mask = (1UL << 28) - 1; - object_property_add(obj, "dma_mask", "uint64", edu_obj_uint64, - edu_obj_uint64, NULL, &edu->dma_mask, NULL); + object_property_add_uint64_ptr(obj, "dma_mask", + &edu->dma_mask, OBJ_PROP_FLAG_READWRITE, + NULL); } static void edu_class_init(ObjectClass *class, void *data) diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 1a0fad74e1..a8dc9b377d 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -832,7 +832,6 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) IVShmemState *s = IVSHMEM_COMMON(dev); Error *err = NULL; uint8_t *pci_conf; - Error *local_err = NULL; /* IRQFD requires MSI */ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && @@ -899,9 +898,9 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) if (!ivshmem_is_master(s)) { error_setg(&s->migration_blocker, "Migration is disabled when using feature 'peer mode' in device 'ivshmem'"); - migrate_add_blocker(s->migration_blocker, &local_err); - if (local_err) { - error_propagate(errp, local_err); + migrate_add_blocker(s->migration_blocker, &err); + if (err) { + error_propagate(errp, err); error_free(s->migration_blocker); return; } diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index b7d0012794..81343301b1 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -30,6 +30,7 @@ #include "hw/qdev-properties.h" #include "sysemu/block-backend.h" #include "trace.h" +#include "qemu/log.h" /* * VIAs: There are two in every machine, @@ -381,8 +382,10 @@ static void via2_irq_request(void *opaque, int irq, int level) static void pram_update(MacVIAState *m) { if (m->blk) { - blk_pwrite(m->blk, 0, m->mos6522_via1.PRAM, - sizeof(m->mos6522_via1.PRAM), 0); + if (blk_pwrite(m->blk, 0, m->mos6522_via1.PRAM, + sizeof(m->mos6522_via1.PRAM), 0) < 0) { + qemu_log("pram_update: cannot write to file\n"); + } } } diff --git a/hw/misc/omap_l4.c b/hw/misc/omap_l4.c index 61b6df564a..54aeaecd69 100644 --- a/hw/misc/omap_l4.c +++ b/hw/misc/omap_l4.c @@ -24,7 +24,7 @@ struct omap_l4_s { MemoryRegion *address_space; hwaddr base; int ta_num; - struct omap_target_agent_s ta[0]; + struct omap_target_agent_s ta[]; }; struct omap_l4_s *omap_l4_init(MemoryRegion *address_space, diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 7f0f5dff3a..a5862b2bed 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -1,5 +1,24 @@ # See docs/devel/tracing.txt for syntax documentation. +# allwinner-cpucfg.c +allwinner_cpucfg_cpu_reset(uint8_t cpu_id, uint32_t reset_addr) "id %u, reset_addr 0x%" PRIu32 +allwinner_cpucfg_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_cpucfg_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 + +# allwinner-h3-dramc.c +allwinner_h3_dramc_rowmirror_disable(void) "Disable row mirror" +allwinner_h3_dramc_rowmirror_enable(uint64_t addr) "Enable row mirror: addr 0x%" PRIx64 +allwinner_h3_dramcom_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_h3_dramcom_write(uint64_t offset, uint64_t data, unsigned size) "Write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_h3_dramctl_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_h3_dramctl_write(uint64_t offset, uint64_t data, unsigned size) "Write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_h3_dramphy_read(uint64_t offset, uint64_t data, unsigned size) "Read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_h3_dramphy_write(uint64_t offset, uint64_t data, unsigned size) "write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 + +# allwinner-sid.c +allwinner_sid_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_sid_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 + # eccmemctl.c ecc_mem_writel_mer(uint32_t val) "Write memory enable 0x%08x" ecc_mem_writel_mdr(uint32_t val) "Write memory delay 0x%08x" diff --git a/hw/net/Kconfig b/hw/net/Kconfig index 54411d3dcc..e43c96dae0 100644 --- a/hw/net/Kconfig +++ b/hw/net/Kconfig @@ -79,6 +79,9 @@ config MIPSNET config ALLWINNER_EMAC bool +config ALLWINNER_SUN8I_EMAC + bool + config IMX_FEC bool diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 991c46c773..af4d194866 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -23,6 +23,7 @@ common-obj-$(CONFIG_XGMAC) += xgmac.o common-obj-$(CONFIG_MIPSNET) += mipsnet.o common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o +common-obj-$(CONFIG_ALLWINNER_SUN8I_EMAC) += allwinner-sun8i-emac.o common-obj-$(CONFIG_IMX_FEC) += imx_fec.o common-obj-$(CONFIG_CADENCE) += cadence_gem.o diff --git a/hw/net/allwinner-sun8i-emac.c b/hw/net/allwinner-sun8i-emac.c new file mode 100644 index 0000000000..3fc5e34640 --- /dev/null +++ b/hw/net/allwinner-sun8i-emac.c @@ -0,0 +1,871 @@ +/* + * Allwinner Sun8i Ethernet MAC emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "net/net.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "qemu/log.h" +#include "trace.h" +#include "net/checksum.h" +#include "qemu/module.h" +#include "exec/cpu-common.h" +#include "hw/net/allwinner-sun8i-emac.h" + +/* EMAC register offsets */ +enum { + REG_BASIC_CTL_0 = 0x0000, /* Basic Control 0 */ + REG_BASIC_CTL_1 = 0x0004, /* Basic Control 1 */ + REG_INT_STA = 0x0008, /* Interrupt Status */ + REG_INT_EN = 0x000C, /* Interrupt Enable */ + REG_TX_CTL_0 = 0x0010, /* Transmit Control 0 */ + REG_TX_CTL_1 = 0x0014, /* Transmit Control 1 */ + REG_TX_FLOW_CTL = 0x001C, /* Transmit Flow Control */ + REG_TX_DMA_DESC_LIST = 0x0020, /* Transmit Descriptor List Address */ + REG_RX_CTL_0 = 0x0024, /* Receive Control 0 */ + REG_RX_CTL_1 = 0x0028, /* Receive Control 1 */ + REG_RX_DMA_DESC_LIST = 0x0034, /* Receive Descriptor List Address */ + REG_FRM_FLT = 0x0038, /* Receive Frame Filter */ + REG_RX_HASH_0 = 0x0040, /* Receive Hash Table 0 */ + REG_RX_HASH_1 = 0x0044, /* Receive Hash Table 1 */ + REG_MII_CMD = 0x0048, /* Management Interface Command */ + REG_MII_DATA = 0x004C, /* Management Interface Data */ + REG_ADDR_HIGH = 0x0050, /* MAC Address High */ + REG_ADDR_LOW = 0x0054, /* MAC Address Low */ + REG_TX_DMA_STA = 0x00B0, /* Transmit DMA Status */ + REG_TX_CUR_DESC = 0x00B4, /* Transmit Current Descriptor */ + REG_TX_CUR_BUF = 0x00B8, /* Transmit Current Buffer */ + REG_RX_DMA_STA = 0x00C0, /* Receive DMA Status */ + REG_RX_CUR_DESC = 0x00C4, /* Receive Current Descriptor */ + REG_RX_CUR_BUF = 0x00C8, /* Receive Current Buffer */ + REG_RGMII_STA = 0x00D0, /* RGMII Status */ +}; + +/* EMAC register flags */ +enum { + BASIC_CTL0_100Mbps = (0b11 << 2), + BASIC_CTL0_FD = (1 << 0), + BASIC_CTL1_SOFTRST = (1 << 0), +}; + +enum { + INT_STA_RGMII_LINK = (1 << 16), + INT_STA_RX_EARLY = (1 << 13), + INT_STA_RX_OVERFLOW = (1 << 12), + INT_STA_RX_TIMEOUT = (1 << 11), + INT_STA_RX_DMA_STOP = (1 << 10), + INT_STA_RX_BUF_UA = (1 << 9), + INT_STA_RX = (1 << 8), + INT_STA_TX_EARLY = (1 << 5), + INT_STA_TX_UNDERFLOW = (1 << 4), + INT_STA_TX_TIMEOUT = (1 << 3), + INT_STA_TX_BUF_UA = (1 << 2), + INT_STA_TX_DMA_STOP = (1 << 1), + INT_STA_TX = (1 << 0), +}; + +enum { + INT_EN_RX_EARLY = (1 << 13), + INT_EN_RX_OVERFLOW = (1 << 12), + INT_EN_RX_TIMEOUT = (1 << 11), + INT_EN_RX_DMA_STOP = (1 << 10), + INT_EN_RX_BUF_UA = (1 << 9), + INT_EN_RX = (1 << 8), + INT_EN_TX_EARLY = (1 << 5), + INT_EN_TX_UNDERFLOW = (1 << 4), + INT_EN_TX_TIMEOUT = (1 << 3), + INT_EN_TX_BUF_UA = (1 << 2), + INT_EN_TX_DMA_STOP = (1 << 1), + INT_EN_TX = (1 << 0), +}; + +enum { + TX_CTL0_TX_EN = (1 << 31), + TX_CTL1_TX_DMA_START = (1 << 31), + TX_CTL1_TX_DMA_EN = (1 << 30), + TX_CTL1_TX_FLUSH = (1 << 0), +}; + +enum { + RX_CTL0_RX_EN = (1 << 31), + RX_CTL0_STRIP_FCS = (1 << 28), + RX_CTL0_CRC_IPV4 = (1 << 27), +}; + +enum { + RX_CTL1_RX_DMA_START = (1 << 31), + RX_CTL1_RX_DMA_EN = (1 << 30), + RX_CTL1_RX_MD = (1 << 1), +}; + +enum { + RX_FRM_FLT_DIS_ADDR = (1 << 31), +}; + +enum { + MII_CMD_PHY_ADDR_SHIFT = (12), + MII_CMD_PHY_ADDR_MASK = (0xf000), + MII_CMD_PHY_REG_SHIFT = (4), + MII_CMD_PHY_REG_MASK = (0xf0), + MII_CMD_PHY_RW = (1 << 1), + MII_CMD_PHY_BUSY = (1 << 0), +}; + +enum { + TX_DMA_STA_STOP = (0b000), + TX_DMA_STA_RUN_FETCH = (0b001), + TX_DMA_STA_WAIT_STA = (0b010), +}; + +enum { + RX_DMA_STA_STOP = (0b000), + RX_DMA_STA_RUN_FETCH = (0b001), + RX_DMA_STA_WAIT_FRM = (0b011), +}; + +/* EMAC register reset values */ +enum { + REG_BASIC_CTL_1_RST = 0x08000000, +}; + +/* EMAC constants */ +enum { + AW_SUN8I_EMAC_MIN_PKT_SZ = 64 +}; + +/* Transmit/receive frame descriptor */ +typedef struct FrameDescriptor { + uint32_t status; + uint32_t status2; + uint32_t addr; + uint32_t next; +} FrameDescriptor; + +/* Frame descriptor flags */ +enum { + DESC_STATUS_CTL = (1 << 31), + DESC_STATUS2_BUF_SIZE_MASK = (0x7ff), +}; + +/* Transmit frame descriptor flags */ +enum { + TX_DESC_STATUS_LENGTH_ERR = (1 << 14), + TX_DESC_STATUS2_FIRST_DESC = (1 << 29), + TX_DESC_STATUS2_LAST_DESC = (1 << 30), + TX_DESC_STATUS2_CHECKSUM_MASK = (0x3 << 27), +}; + +/* Receive frame descriptor flags */ +enum { + RX_DESC_STATUS_FIRST_DESC = (1 << 9), + RX_DESC_STATUS_LAST_DESC = (1 << 8), + RX_DESC_STATUS_FRM_LEN_MASK = (0x3fff0000), + RX_DESC_STATUS_FRM_LEN_SHIFT = (16), + RX_DESC_STATUS_NO_BUF = (1 << 14), + RX_DESC_STATUS_HEADER_ERR = (1 << 7), + RX_DESC_STATUS_LENGTH_ERR = (1 << 4), + RX_DESC_STATUS_CRC_ERR = (1 << 1), + RX_DESC_STATUS_PAYLOAD_ERR = (1 << 0), + RX_DESC_STATUS2_RX_INT_CTL = (1 << 31), +}; + +/* MII register offsets */ +enum { + MII_REG_CR = (0x0), /* Control */ + MII_REG_ST = (0x1), /* Status */ + MII_REG_ID_HIGH = (0x2), /* Identifier High */ + MII_REG_ID_LOW = (0x3), /* Identifier Low */ + MII_REG_ADV = (0x4), /* Advertised abilities */ + MII_REG_LPA = (0x5), /* Link partner abilities */ +}; + +/* MII register flags */ +enum { + MII_REG_CR_RESET = (1 << 15), + MII_REG_CR_POWERDOWN = (1 << 11), + MII_REG_CR_10Mbit = (0), + MII_REG_CR_100Mbit = (1 << 13), + MII_REG_CR_1000Mbit = (1 << 6), + MII_REG_CR_AUTO_NEG = (1 << 12), + MII_REG_CR_AUTO_NEG_RESTART = (1 << 9), + MII_REG_CR_FULLDUPLEX = (1 << 8), +}; + +enum { + MII_REG_ST_100BASE_T4 = (1 << 15), + MII_REG_ST_100BASE_X_FD = (1 << 14), + MII_REG_ST_100BASE_X_HD = (1 << 13), + MII_REG_ST_10_FD = (1 << 12), + MII_REG_ST_10_HD = (1 << 11), + MII_REG_ST_100BASE_T2_FD = (1 << 10), + MII_REG_ST_100BASE_T2_HD = (1 << 9), + MII_REG_ST_AUTONEG_COMPLETE = (1 << 5), + MII_REG_ST_AUTONEG_AVAIL = (1 << 3), + MII_REG_ST_LINK_UP = (1 << 2), +}; + +enum { + MII_REG_LPA_10_HD = (1 << 5), + MII_REG_LPA_10_FD = (1 << 6), + MII_REG_LPA_100_HD = (1 << 7), + MII_REG_LPA_100_FD = (1 << 8), + MII_REG_LPA_PAUSE = (1 << 10), + MII_REG_LPA_ASYMPAUSE = (1 << 11), +}; + +/* MII constants */ +enum { + MII_PHY_ID_HIGH = 0x0044, + MII_PHY_ID_LOW = 0x1400, +}; + +static void allwinner_sun8i_emac_mii_set_link(AwSun8iEmacState *s, + bool link_active) +{ + if (link_active) { + s->mii_st |= MII_REG_ST_LINK_UP; + } else { + s->mii_st &= ~MII_REG_ST_LINK_UP; + } +} + +static void allwinner_sun8i_emac_mii_reset(AwSun8iEmacState *s, + bool link_active) +{ + s->mii_cr = MII_REG_CR_100Mbit | MII_REG_CR_AUTO_NEG | + MII_REG_CR_FULLDUPLEX; + s->mii_st = MII_REG_ST_100BASE_T4 | MII_REG_ST_100BASE_X_FD | + MII_REG_ST_100BASE_X_HD | MII_REG_ST_10_FD | MII_REG_ST_10_HD | + MII_REG_ST_100BASE_T2_FD | MII_REG_ST_100BASE_T2_HD | + MII_REG_ST_AUTONEG_COMPLETE | MII_REG_ST_AUTONEG_AVAIL; + s->mii_adv = 0; + + allwinner_sun8i_emac_mii_set_link(s, link_active); +} + +static void allwinner_sun8i_emac_mii_cmd(AwSun8iEmacState *s) +{ + uint8_t addr, reg; + + addr = (s->mii_cmd & MII_CMD_PHY_ADDR_MASK) >> MII_CMD_PHY_ADDR_SHIFT; + reg = (s->mii_cmd & MII_CMD_PHY_REG_MASK) >> MII_CMD_PHY_REG_SHIFT; + + if (addr != s->mii_phy_addr) { + return; + } + + /* Read or write a PHY register? */ + if (s->mii_cmd & MII_CMD_PHY_RW) { + trace_allwinner_sun8i_emac_mii_write_reg(reg, s->mii_data); + + switch (reg) { + case MII_REG_CR: + if (s->mii_data & MII_REG_CR_RESET) { + allwinner_sun8i_emac_mii_reset(s, s->mii_st & + MII_REG_ST_LINK_UP); + } else { + s->mii_cr = s->mii_data & ~(MII_REG_CR_RESET | + MII_REG_CR_AUTO_NEG_RESTART); + } + break; + case MII_REG_ADV: + s->mii_adv = s->mii_data; + break; + case MII_REG_ID_HIGH: + case MII_REG_ID_LOW: + case MII_REG_LPA: + break; + default: + qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to " + "unknown MII register 0x%x\n", reg); + break; + } + } else { + switch (reg) { + case MII_REG_CR: + s->mii_data = s->mii_cr; + break; + case MII_REG_ST: + s->mii_data = s->mii_st; + break; + case MII_REG_ID_HIGH: + s->mii_data = MII_PHY_ID_HIGH; + break; + case MII_REG_ID_LOW: + s->mii_data = MII_PHY_ID_LOW; + break; + case MII_REG_ADV: + s->mii_data = s->mii_adv; + break; + case MII_REG_LPA: + s->mii_data = MII_REG_LPA_10_HD | MII_REG_LPA_10_FD | + MII_REG_LPA_100_HD | MII_REG_LPA_100_FD | + MII_REG_LPA_PAUSE | MII_REG_LPA_ASYMPAUSE; + break; + default: + qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to " + "unknown MII register 0x%x\n", reg); + s->mii_data = 0; + break; + } + + trace_allwinner_sun8i_emac_mii_read_reg(reg, s->mii_data); + } +} + +static void allwinner_sun8i_emac_update_irq(AwSun8iEmacState *s) +{ + qemu_set_irq(s->irq, (s->int_sta & s->int_en) != 0); +} + +static uint32_t allwinner_sun8i_emac_next_desc(FrameDescriptor *desc, + size_t min_size) +{ + uint32_t paddr = desc->next; + + cpu_physical_memory_read(paddr, desc, sizeof(*desc)); + + if ((desc->status & DESC_STATUS_CTL) && + (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) { + return paddr; + } else { + return 0; + } +} + +static uint32_t allwinner_sun8i_emac_get_desc(FrameDescriptor *desc, + uint32_t start_addr, + size_t min_size) +{ + uint32_t desc_addr = start_addr; + + /* Note that the list is a cycle. Last entry points back to the head. */ + while (desc_addr != 0) { + cpu_physical_memory_read(desc_addr, desc, sizeof(*desc)); + + if ((desc->status & DESC_STATUS_CTL) && + (desc->status2 & DESC_STATUS2_BUF_SIZE_MASK) >= min_size) { + return desc_addr; + } else if (desc->next == start_addr) { + break; + } else { + desc_addr = desc->next; + } + } + + return 0; +} + +static uint32_t allwinner_sun8i_emac_rx_desc(AwSun8iEmacState *s, + FrameDescriptor *desc, + size_t min_size) +{ + return allwinner_sun8i_emac_get_desc(desc, s->rx_desc_curr, min_size); +} + +static uint32_t allwinner_sun8i_emac_tx_desc(AwSun8iEmacState *s, + FrameDescriptor *desc, + size_t min_size) +{ + return allwinner_sun8i_emac_get_desc(desc, s->tx_desc_head, min_size); +} + +static void allwinner_sun8i_emac_flush_desc(FrameDescriptor *desc, + uint32_t phys_addr) +{ + cpu_physical_memory_write(phys_addr, desc, sizeof(*desc)); +} + +static int allwinner_sun8i_emac_can_receive(NetClientState *nc) +{ + AwSun8iEmacState *s = qemu_get_nic_opaque(nc); + FrameDescriptor desc; + + return (s->rx_ctl0 & RX_CTL0_RX_EN) && + (allwinner_sun8i_emac_rx_desc(s, &desc, 0) != 0); +} + +static ssize_t allwinner_sun8i_emac_receive(NetClientState *nc, + const uint8_t *buf, + size_t size) +{ + AwSun8iEmacState *s = qemu_get_nic_opaque(nc); + FrameDescriptor desc; + size_t bytes_left = size; + size_t desc_bytes = 0; + size_t pad_fcs_size = 4; + size_t padding = 0; + + if (!(s->rx_ctl0 & RX_CTL0_RX_EN)) { + return -1; + } + + s->rx_desc_curr = allwinner_sun8i_emac_rx_desc(s, &desc, + AW_SUN8I_EMAC_MIN_PKT_SZ); + if (!s->rx_desc_curr) { + s->int_sta |= INT_STA_RX_BUF_UA; + } + + /* Keep filling RX descriptors until the whole frame is written */ + while (s->rx_desc_curr && bytes_left > 0) { + desc.status &= ~DESC_STATUS_CTL; + desc.status &= ~RX_DESC_STATUS_FRM_LEN_MASK; + + if (bytes_left == size) { + desc.status |= RX_DESC_STATUS_FIRST_DESC; + } + + if ((desc.status2 & DESC_STATUS2_BUF_SIZE_MASK) < + (bytes_left + pad_fcs_size)) { + desc_bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK; + desc.status |= desc_bytes << RX_DESC_STATUS_FRM_LEN_SHIFT; + } else { + padding = pad_fcs_size; + if (bytes_left < AW_SUN8I_EMAC_MIN_PKT_SZ) { + padding += (AW_SUN8I_EMAC_MIN_PKT_SZ - bytes_left); + } + + desc_bytes = (bytes_left); + desc.status |= RX_DESC_STATUS_LAST_DESC; + desc.status |= (bytes_left + padding) + << RX_DESC_STATUS_FRM_LEN_SHIFT; + } + + cpu_physical_memory_write(desc.addr, buf, desc_bytes); + allwinner_sun8i_emac_flush_desc(&desc, s->rx_desc_curr); + trace_allwinner_sun8i_emac_receive(s->rx_desc_curr, desc.addr, + desc_bytes); + + /* Check if frame needs to raise the receive interrupt */ + if (!(desc.status2 & RX_DESC_STATUS2_RX_INT_CTL)) { + s->int_sta |= INT_STA_RX; + } + + /* Increment variables */ + buf += desc_bytes; + bytes_left -= desc_bytes; + + /* Move to the next descriptor */ + s->rx_desc_curr = allwinner_sun8i_emac_next_desc(&desc, 64); + if (!s->rx_desc_curr) { + /* Not enough buffer space available */ + s->int_sta |= INT_STA_RX_BUF_UA; + s->rx_desc_curr = s->rx_desc_head; + break; + } + } + + /* Report receive DMA is finished */ + s->rx_ctl1 &= ~RX_CTL1_RX_DMA_START; + allwinner_sun8i_emac_update_irq(s); + + return size; +} + +static void allwinner_sun8i_emac_transmit(AwSun8iEmacState *s) +{ + NetClientState *nc = qemu_get_queue(s->nic); + FrameDescriptor desc; + size_t bytes = 0; + size_t packet_bytes = 0; + size_t transmitted = 0; + static uint8_t packet_buf[2048]; + + s->tx_desc_curr = allwinner_sun8i_emac_tx_desc(s, &desc, 0); + + /* Read all transmit descriptors */ + while (s->tx_desc_curr != 0) { + + /* Read from physical memory into packet buffer */ + bytes = desc.status2 & DESC_STATUS2_BUF_SIZE_MASK; + if (bytes + packet_bytes > sizeof(packet_buf)) { + desc.status |= TX_DESC_STATUS_LENGTH_ERR; + break; + } + cpu_physical_memory_read(desc.addr, packet_buf + packet_bytes, bytes); + packet_bytes += bytes; + desc.status &= ~DESC_STATUS_CTL; + allwinner_sun8i_emac_flush_desc(&desc, s->tx_desc_curr); + + /* After the last descriptor, send the packet */ + if (desc.status2 & TX_DESC_STATUS2_LAST_DESC) { + if (desc.status2 & TX_DESC_STATUS2_CHECKSUM_MASK) { + net_checksum_calculate(packet_buf, packet_bytes); + } + + qemu_send_packet(nc, packet_buf, packet_bytes); + trace_allwinner_sun8i_emac_transmit(s->tx_desc_curr, desc.addr, + bytes); + + packet_bytes = 0; + transmitted++; + } + s->tx_desc_curr = allwinner_sun8i_emac_next_desc(&desc, 0); + } + + /* Raise transmit completed interrupt */ + if (transmitted > 0) { + s->int_sta |= INT_STA_TX; + s->tx_ctl1 &= ~TX_CTL1_TX_DMA_START; + allwinner_sun8i_emac_update_irq(s); + } +} + +static void allwinner_sun8i_emac_reset(DeviceState *dev) +{ + AwSun8iEmacState *s = AW_SUN8I_EMAC(dev); + NetClientState *nc = qemu_get_queue(s->nic); + + trace_allwinner_sun8i_emac_reset(); + + s->mii_cmd = 0; + s->mii_data = 0; + s->basic_ctl0 = 0; + s->basic_ctl1 = REG_BASIC_CTL_1_RST; + s->int_en = 0; + s->int_sta = 0; + s->frm_flt = 0; + s->rx_ctl0 = 0; + s->rx_ctl1 = RX_CTL1_RX_MD; + s->rx_desc_head = 0; + s->rx_desc_curr = 0; + s->tx_ctl0 = 0; + s->tx_ctl1 = 0; + s->tx_desc_head = 0; + s->tx_desc_curr = 0; + s->tx_flowctl = 0; + + allwinner_sun8i_emac_mii_reset(s, !nc->link_down); +} + +static uint64_t allwinner_sun8i_emac_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwSun8iEmacState *s = AW_SUN8I_EMAC(opaque); + uint64_t value = 0; + FrameDescriptor desc; + + switch (offset) { + case REG_BASIC_CTL_0: /* Basic Control 0 */ + value = s->basic_ctl0; + break; + case REG_BASIC_CTL_1: /* Basic Control 1 */ + value = s->basic_ctl1; + break; + case REG_INT_STA: /* Interrupt Status */ + value = s->int_sta; + break; + case REG_INT_EN: /* Interupt Enable */ + value = s->int_en; + break; + case REG_TX_CTL_0: /* Transmit Control 0 */ + value = s->tx_ctl0; + break; + case REG_TX_CTL_1: /* Transmit Control 1 */ + value = s->tx_ctl1; + break; + case REG_TX_FLOW_CTL: /* Transmit Flow Control */ + value = s->tx_flowctl; + break; + case REG_TX_DMA_DESC_LIST: /* Transmit Descriptor List Address */ + value = s->tx_desc_head; + break; + case REG_RX_CTL_0: /* Receive Control 0 */ + value = s->rx_ctl0; + break; + case REG_RX_CTL_1: /* Receive Control 1 */ + value = s->rx_ctl1; + break; + case REG_RX_DMA_DESC_LIST: /* Receive Descriptor List Address */ + value = s->rx_desc_head; + break; + case REG_FRM_FLT: /* Receive Frame Filter */ + value = s->frm_flt; + break; + case REG_RX_HASH_0: /* Receive Hash Table 0 */ + case REG_RX_HASH_1: /* Receive Hash Table 1 */ + break; + case REG_MII_CMD: /* Management Interface Command */ + value = s->mii_cmd; + break; + case REG_MII_DATA: /* Management Interface Data */ + value = s->mii_data; + break; + case REG_ADDR_HIGH: /* MAC Address High */ + value = *(((uint32_t *) (s->conf.macaddr.a)) + 1); + break; + case REG_ADDR_LOW: /* MAC Address Low */ + value = *(uint32_t *) (s->conf.macaddr.a); + break; + case REG_TX_DMA_STA: /* Transmit DMA Status */ + break; + case REG_TX_CUR_DESC: /* Transmit Current Descriptor */ + value = s->tx_desc_curr; + break; + case REG_TX_CUR_BUF: /* Transmit Current Buffer */ + if (s->tx_desc_curr != 0) { + cpu_physical_memory_read(s->tx_desc_curr, &desc, sizeof(desc)); + value = desc.addr; + } else { + value = 0; + } + break; + case REG_RX_DMA_STA: /* Receive DMA Status */ + break; + case REG_RX_CUR_DESC: /* Receive Current Descriptor */ + value = s->rx_desc_curr; + break; + case REG_RX_CUR_BUF: /* Receive Current Buffer */ + if (s->rx_desc_curr != 0) { + cpu_physical_memory_read(s->rx_desc_curr, &desc, sizeof(desc)); + value = desc.addr; + } else { + value = 0; + } + break; + case REG_RGMII_STA: /* RGMII Status */ + break; + default: + qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: read access to unknown " + "EMAC register 0x" TARGET_FMT_plx "\n", + offset); + } + + trace_allwinner_sun8i_emac_read(offset, value); + return value; +} + +static void allwinner_sun8i_emac_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + AwSun8iEmacState *s = AW_SUN8I_EMAC(opaque); + NetClientState *nc = qemu_get_queue(s->nic); + + trace_allwinner_sun8i_emac_write(offset, value); + + switch (offset) { + case REG_BASIC_CTL_0: /* Basic Control 0 */ + s->basic_ctl0 = value; + break; + case REG_BASIC_CTL_1: /* Basic Control 1 */ + if (value & BASIC_CTL1_SOFTRST) { + allwinner_sun8i_emac_reset(DEVICE(s)); + value &= ~BASIC_CTL1_SOFTRST; + } + s->basic_ctl1 = value; + if (allwinner_sun8i_emac_can_receive(nc)) { + qemu_flush_queued_packets(nc); + } + break; + case REG_INT_STA: /* Interrupt Status */ + s->int_sta &= ~value; + allwinner_sun8i_emac_update_irq(s); + break; + case REG_INT_EN: /* Interrupt Enable */ + s->int_en = value; + allwinner_sun8i_emac_update_irq(s); + break; + case REG_TX_CTL_0: /* Transmit Control 0 */ + s->tx_ctl0 = value; + break; + case REG_TX_CTL_1: /* Transmit Control 1 */ + s->tx_ctl1 = value; + if (value & TX_CTL1_TX_DMA_EN) { + allwinner_sun8i_emac_transmit(s); + } + break; + case REG_TX_FLOW_CTL: /* Transmit Flow Control */ + s->tx_flowctl = value; + break; + case REG_TX_DMA_DESC_LIST: /* Transmit Descriptor List Address */ + s->tx_desc_head = value; + s->tx_desc_curr = value; + break; + case REG_RX_CTL_0: /* Receive Control 0 */ + s->rx_ctl0 = value; + break; + case REG_RX_CTL_1: /* Receive Control 1 */ + s->rx_ctl1 = value | RX_CTL1_RX_MD; + if ((value & RX_CTL1_RX_DMA_EN) && + allwinner_sun8i_emac_can_receive(nc)) { + qemu_flush_queued_packets(nc); + } + break; + case REG_RX_DMA_DESC_LIST: /* Receive Descriptor List Address */ + s->rx_desc_head = value; + s->rx_desc_curr = value; + break; + case REG_FRM_FLT: /* Receive Frame Filter */ + s->frm_flt = value; + break; + case REG_RX_HASH_0: /* Receive Hash Table 0 */ + case REG_RX_HASH_1: /* Receive Hash Table 1 */ + break; + case REG_MII_CMD: /* Management Interface Command */ + s->mii_cmd = value & ~MII_CMD_PHY_BUSY; + allwinner_sun8i_emac_mii_cmd(s); + break; + case REG_MII_DATA: /* Management Interface Data */ + s->mii_data = value; + break; + case REG_ADDR_HIGH: /* MAC Address High */ + s->conf.macaddr.a[4] = (value & 0xff); + s->conf.macaddr.a[5] = (value & 0xff00) >> 8; + break; + case REG_ADDR_LOW: /* MAC Address Low */ + s->conf.macaddr.a[0] = (value & 0xff); + s->conf.macaddr.a[1] = (value & 0xff00) >> 8; + s->conf.macaddr.a[2] = (value & 0xff0000) >> 16; + s->conf.macaddr.a[3] = (value & 0xff000000) >> 24; + break; + case REG_TX_DMA_STA: /* Transmit DMA Status */ + case REG_TX_CUR_DESC: /* Transmit Current Descriptor */ + case REG_TX_CUR_BUF: /* Transmit Current Buffer */ + case REG_RX_DMA_STA: /* Receive DMA Status */ + case REG_RX_CUR_DESC: /* Receive Current Descriptor */ + case REG_RX_CUR_BUF: /* Receive Current Buffer */ + case REG_RGMII_STA: /* RGMII Status */ + break; + default: + qemu_log_mask(LOG_UNIMP, "allwinner-h3-emac: write access to unknown " + "EMAC register 0x" TARGET_FMT_plx "\n", + offset); + } +} + +static void allwinner_sun8i_emac_set_link(NetClientState *nc) +{ + AwSun8iEmacState *s = qemu_get_nic_opaque(nc); + + trace_allwinner_sun8i_emac_set_link(!nc->link_down); + allwinner_sun8i_emac_mii_set_link(s, !nc->link_down); +} + +static const MemoryRegionOps allwinner_sun8i_emac_mem_ops = { + .read = allwinner_sun8i_emac_read, + .write = allwinner_sun8i_emac_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static NetClientInfo net_allwinner_sun8i_emac_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .can_receive = allwinner_sun8i_emac_can_receive, + .receive = allwinner_sun8i_emac_receive, + .link_status_changed = allwinner_sun8i_emac_set_link, +}; + +static void allwinner_sun8i_emac_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwSun8iEmacState *s = AW_SUN8I_EMAC(obj); + + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_sun8i_emac_mem_ops, + s, TYPE_AW_SUN8I_EMAC, 64 * KiB); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); +} + +static void allwinner_sun8i_emac_realize(DeviceState *dev, Error **errp) +{ + AwSun8iEmacState *s = AW_SUN8I_EMAC(dev); + + qemu_macaddr_default_if_unset(&s->conf.macaddr); + s->nic = qemu_new_nic(&net_allwinner_sun8i_emac_info, &s->conf, + object_get_typename(OBJECT(dev)), dev->id, s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); +} + +static Property allwinner_sun8i_emac_properties[] = { + DEFINE_NIC_PROPERTIES(AwSun8iEmacState, conf), + DEFINE_PROP_UINT8("phy-addr", AwSun8iEmacState, mii_phy_addr, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static int allwinner_sun8i_emac_post_load(void *opaque, int version_id) +{ + AwSun8iEmacState *s = opaque; + + allwinner_sun8i_emac_set_link(qemu_get_queue(s->nic)); + + return 0; +} + +static const VMStateDescription vmstate_aw_emac = { + .name = "allwinner-sun8i-emac", + .version_id = 1, + .minimum_version_id = 1, + .post_load = allwinner_sun8i_emac_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(mii_phy_addr, AwSun8iEmacState), + VMSTATE_UINT32(mii_cmd, AwSun8iEmacState), + VMSTATE_UINT32(mii_data, AwSun8iEmacState), + VMSTATE_UINT32(mii_cr, AwSun8iEmacState), + VMSTATE_UINT32(mii_st, AwSun8iEmacState), + VMSTATE_UINT32(mii_adv, AwSun8iEmacState), + VMSTATE_UINT32(basic_ctl0, AwSun8iEmacState), + VMSTATE_UINT32(basic_ctl1, AwSun8iEmacState), + VMSTATE_UINT32(int_en, AwSun8iEmacState), + VMSTATE_UINT32(int_sta, AwSun8iEmacState), + VMSTATE_UINT32(frm_flt, AwSun8iEmacState), + VMSTATE_UINT32(rx_ctl0, AwSun8iEmacState), + VMSTATE_UINT32(rx_ctl1, AwSun8iEmacState), + VMSTATE_UINT32(rx_desc_head, AwSun8iEmacState), + VMSTATE_UINT32(rx_desc_curr, AwSun8iEmacState), + VMSTATE_UINT32(tx_ctl0, AwSun8iEmacState), + VMSTATE_UINT32(tx_ctl1, AwSun8iEmacState), + VMSTATE_UINT32(tx_desc_head, AwSun8iEmacState), + VMSTATE_UINT32(tx_desc_curr, AwSun8iEmacState), + VMSTATE_UINT32(tx_flowctl, AwSun8iEmacState), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_sun8i_emac_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = allwinner_sun8i_emac_realize; + dc->reset = allwinner_sun8i_emac_reset; + dc->vmsd = &vmstate_aw_emac; + device_class_set_props(dc, allwinner_sun8i_emac_properties); +} + +static const TypeInfo allwinner_sun8i_emac_info = { + .name = TYPE_AW_SUN8I_EMAC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AwSun8iEmacState), + .instance_init = allwinner_sun8i_emac_init, + .class_init = allwinner_sun8i_emac_class_init, +}; + +static void allwinner_sun8i_emac_register_types(void) +{ + type_register_static(&allwinner_sun8i_emac_info); +} + +type_init(allwinner_sun8i_emac_register_types) diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 81fc13ee9f..1563c11b9e 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -986,13 +986,12 @@ static void dp8393x_realize(DeviceState *dev, Error **errp) s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); - memory_region_init_ram(&s->prom, OBJECT(dev), - "dp8393x-prom", SONIC_PROM_SIZE, &local_err); + memory_region_init_rom(&s->prom, OBJECT(dev), "dp8393x-prom", + SONIC_PROM_SIZE, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - memory_region_set_readonly(&s->prom, true); prom = memory_region_get_ram_ptr(&s->prom); checksum = 0; for (i = 0; i < 6; i++) { diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 6a124a154a..5c145a8197 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -855,13 +855,15 @@ static void imx_enet_write(IMXFECState *s, uint32_t index, uint32_t value) break; case ENET_TGSR: /* implement clear timer flag */ - value = value & 0x0000000f; + s->regs[index] &= ~(value & 0x0000000f); /* all bits W1C */ break; case ENET_TCSR0: case ENET_TCSR1: case ENET_TCSR2: case ENET_TCSR3: - value = value & 0x000000fd; + s->regs[index] &= ~(value & 0x00000080); /* W1C bits */ + s->regs[index] &= ~0x0000007d; /* writable fields */ + s->regs[index] |= (value & 0x0000007d); break; case ENET_TCCR0: case ENET_TCCR1: diff --git a/hw/net/trace-events b/hw/net/trace-events index a1da98a643..e18f883cfd 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -1,5 +1,15 @@ # See docs/devel/tracing.txt for syntax documentation. +# allwinner-sun8i-emac.c +allwinner_sun8i_emac_mii_write_reg(uint32_t reg, uint32_t value) "MII write: reg=0x%" PRIx32 " value=0x%" PRIx32 +allwinner_sun8i_emac_mii_read_reg(uint32_t reg, uint32_t value) "MII read: reg=0x%" PRIx32 " value=0x%" PRIx32 +allwinner_sun8i_emac_receive(uint32_t desc, uint32_t paddr, uint32_t bytes) "RX packet: desc=0x%" PRIx32 " paddr=0x%" PRIx32 " bytes=%" PRIu32 +allwinner_sun8i_emac_transmit(uint32_t desc, uint32_t paddr, uint32_t bytes) "TX packet: desc=0x%" PRIx32 " paddr=0x%" PRIx32 " bytes=%" PRIu32 +allwinner_sun8i_emac_reset(void) "HW reset" +allwinner_sun8i_emac_set_link(bool active) "Set link: active=%u" +allwinner_sun8i_emac_read(uint64_t offset, uint64_t val) "MMIO read: offset=0x%" PRIx64 " value=0x%" PRIx64 +allwinner_sun8i_emac_write(uint64_t offset, uint64_t val) "MMIO write: offset=0x%" PRIx64 " value=0x%" PRIx64 + # etraxfs_eth.c mdio_phy_read(int regnum, uint16_t value) "read phy_reg:%d value:0x%04x" mdio_phy_write(int regnum, uint16_t value) "write phy_reg:%d value:0x%04x" diff --git a/hw/nvram/eeprom93xx.c b/hw/nvram/eeprom93xx.c index 07f09549ed..ca6f591c84 100644 --- a/hw/nvram/eeprom93xx.c +++ b/hw/nvram/eeprom93xx.c @@ -86,7 +86,7 @@ struct _eeprom_t { uint8_t addrbits; uint16_t size; uint16_t data; - uint16_t contents[0]; + uint16_t contents[]; }; /* Code for saving and restoring of EEPROM state. */ diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c index 1aff72bec6..1a02e9a670 100644 --- a/hw/pci-host/prep.c +++ b/hw/pci-host/prep.c @@ -325,9 +325,8 @@ static void raven_realize(PCIDevice *d, Error **errp) d->config[0x0D] = 0x10; // latency_timer d->config[0x34] = 0x00; // capabilities_pointer - memory_region_init_ram_nomigrate(&s->bios, OBJECT(s), "bios", BIOS_SIZE, - &error_fatal); - memory_region_set_readonly(&s->bios, true); + memory_region_init_rom_nomigrate(&s->bios, OBJECT(s), "bios", BIOS_SIZE, + &error_fatal); memory_region_add_subregion(get_system_memory(), (uint32_t)(-BIOS_SIZE), &s->bios); if (s->bios_name) { diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 993f467668..2bbc90b28f 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -166,14 +166,6 @@ static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, visit_type_uint64(v, name, &value, errp); } -static void q35_host_get_mmcfg_size(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - PCIExpressHost *e = PCIE_HOST_BRIDGE(obj); - - visit_type_uint64(v, name, &e->size, errp); -} - /* * NOTE: setting defaults for the mch.* fields in this table * doesn't work, because mch is a separate QOM object that is @@ -214,6 +206,7 @@ static void q35_host_initfn(Object *obj) { Q35PCIHost *s = Q35_HOST_DEVICE(obj); PCIHostState *phb = PCI_HOST_BRIDGE(obj); + PCIExpressHost *pehb = PCIE_HOST_BRIDGE(obj); memory_region_init_io(&phb->conf_mem, obj, &pci_host_conf_le_ops, phb, "pci-conf-idx", 4); @@ -243,9 +236,8 @@ static void q35_host_initfn(Object *obj) q35_host_get_pci_hole64_end, NULL, NULL, NULL, NULL); - object_property_add(obj, PCIE_HOST_MCFG_SIZE, "uint64", - q35_host_get_mmcfg_size, - NULL, NULL, NULL, NULL); + object_property_add_uint64_ptr(obj, PCIE_HOST_MCFG_SIZE, + &pehb->size, OBJ_PROP_FLAG_READ, NULL); object_property_add_link(obj, MCH_HOST_PROP_RAM_MEM, TYPE_MEMORY_REGION, (Object **) &s->mch.ram_memory, diff --git a/hw/pci/pci.c b/hw/pci/pci.c index e1ed6677e1..b5bc842fac 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -302,8 +302,11 @@ static void pci_do_device_reset(PCIDevice *dev) pci_word_test_and_clear_mask(dev->config + PCI_STATUS, pci_get_word(dev->wmask + PCI_STATUS) | pci_get_word(dev->w1cmask + PCI_STATUS)); + /* Some devices make bits of PCI_INTERRUPT_LINE read only */ + pci_byte_test_and_clear_mask(dev->config + PCI_INTERRUPT_LINE, + pci_get_word(dev->wmask + PCI_INTERRUPT_LINE) | + pci_get_word(dev->w1cmask + PCI_INTERRUPT_LINE)); dev->config[PCI_CACHE_LINE_SIZE] = 0x0; - dev->config[PCI_INTERRUPT_LINE] = 0x0; for (r = 0; r < PCI_NUM_REGIONS; ++r) { PCIIORegion *region = &dev->io_regions[r]; if (!region->size) { diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index b8189bf7a4..428cf63578 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -62,7 +62,6 @@ #include "hw/char/escc.h" #include "hw/misc/macio/macio.h" #include "hw/ppc/openpic.h" -#include "hw/ide.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" #include "elf.h" @@ -155,13 +154,12 @@ static void ppc_core99_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), 0, machine->ram); /* allocate and load BIOS */ - memory_region_init_ram(bios, NULL, "ppc_core99.bios", BIOS_SIZE, + memory_region_init_rom(bios, NULL, "ppc_core99.bios", BIOS_SIZE, &error_fatal); if (bios_name == NULL) bios_name = PROM_FILENAME; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - memory_region_set_readonly(bios, true); memory_region_add_subregion(get_system_memory(), PROM_ADDR, bios); /* Load OpenBIOS (ELF) */ diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 440c406eb4..101bdc5c4d 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -41,7 +41,6 @@ #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" #include "hw/misc/macio/macio.h" -#include "hw/ide.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" #include "elf.h" @@ -129,13 +128,12 @@ static void ppc_heathrow_init(MachineState *machine) memory_region_add_subregion(sysmem, 0, machine->ram); /* allocate and load BIOS */ - memory_region_init_ram(bios, NULL, "ppc_heathrow.bios", BIOS_SIZE, + memory_region_init_rom(bios, NULL, "ppc_heathrow.bios", BIOS_SIZE, &error_fatal); if (bios_name == NULL) bios_name = PROM_FILENAME; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - memory_region_set_readonly(bios, true); memory_region_add_subregion(sysmem, PROM_ADDR, bios); /* Load OpenBIOS (ELF) */ diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index f150deca34..b5ffa48dac 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -829,7 +829,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) bool hostboot_mode = !!pnv->fw_load_addr; /* let isa_bus_new() create its own bridge on SysBus otherwise - * devices speficied on the command line won't find the bus and + * devices specified on the command line won't find the bus and * will fail to create. */ isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, &local_err); diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index de93c40f1a..e6bffb9e1a 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -199,7 +199,7 @@ static void ref405ep_init(MachineState *machine) #endif { bios = g_new(MemoryRegion, 1); - memory_region_init_ram(bios, NULL, "ef405ep.bios", BIOS_SIZE, + memory_region_init_rom(bios, NULL, "ef405ep.bios", BIOS_SIZE, &error_fatal); if (bios_name == NULL) @@ -223,7 +223,6 @@ static void ref405ep_init(MachineState *machine) /* Avoid an uninitialized variable warning */ bios_size = -1; } - memory_region_set_readonly(bios, true); } /* Register FPGA */ ref405ep_fpga_init(sysmem, 0xF0300000); @@ -471,7 +470,7 @@ static void taihu_405ep_init(MachineState *machine) if (bios_name == NULL) bios_name = BIOS_FILENAME; bios = g_new(MemoryRegion, 1); - memory_region_init_ram(bios, NULL, "taihu_405ep.bios", BIOS_SIZE, + memory_region_init_rom(bios, NULL, "taihu_405ep.bios", BIOS_SIZE, &error_fatal); filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { @@ -489,7 +488,6 @@ static void taihu_405ep_init(MachineState *machine) error_report("Could not load PowerPC BIOS '%s'", bios_name); exit(1); } - memory_region_set_readonly(bios, true); } /* Register Linux flash */ dinfo = drive_get(IF_PFLASH, 0, fl_idx); diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 111cc80867..44be9d25a2 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -37,7 +37,6 @@ #include "hw/boards.h" #include "qemu/error-report.h" #include "qemu/log.h" -#include "hw/ide.h" #include "hw/irq.h" #include "hw/loader.h" #include "hw/rtc/mc146818rtc.h" diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index cc10798be4..9a2bd501aa 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -103,7 +103,7 @@ #define FW_OVERHEAD 0x2800000 #define KERNEL_LOAD_ADDR FW_MAX_SIZE -#define MIN_RMA_SLOF 128UL +#define MIN_RMA_SLOF (128 * MiB) #define PHANDLE_INTC 0x00001111 @@ -217,10 +217,9 @@ static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, PowerPCCPU *cpu) sizeof(associativity)); } -/* Populate the "ibm,pa-features" property */ -static void spapr_populate_pa_features(SpaprMachineState *spapr, - PowerPCCPU *cpu, - void *fdt, int offset) +static void spapr_dt_pa_features(SpaprMachineState *spapr, + PowerPCCPU *cpu, + void *fdt, int offset) { uint8_t pa_features_206[] = { 6, 0, 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; @@ -315,8 +314,8 @@ static void add_str(GString *s, const gchar *s1) g_string_append_len(s, s1, strlen(s1) + 1); } -static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, - hwaddr size) +static int spapr_dt_memory_node(void *fdt, int nodeid, hwaddr start, + hwaddr size) { uint32_t associativity[] = { cpu_to_be32(0x4), /* length */ @@ -341,257 +340,6 @@ static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, return off; } -static int spapr_populate_memory(SpaprMachineState *spapr, void *fdt) -{ - MachineState *machine = MACHINE(spapr); - hwaddr mem_start, node_size; - int i, nb_nodes = machine->numa_state->num_nodes; - NodeInfo *nodes = machine->numa_state->nodes; - - for (i = 0, mem_start = 0; i < nb_nodes; ++i) { - if (!nodes[i].node_mem) { - continue; - } - if (mem_start >= machine->ram_size) { - node_size = 0; - } else { - node_size = nodes[i].node_mem; - if (node_size > machine->ram_size - mem_start) { - node_size = machine->ram_size - mem_start; - } - } - if (!mem_start) { - /* spapr_machine_init() checks for rma_size <= node0_size - * already */ - spapr_populate_memory_node(fdt, i, 0, spapr->rma_size); - mem_start += spapr->rma_size; - node_size -= spapr->rma_size; - } - for ( ; node_size; ) { - hwaddr sizetmp = pow2floor(node_size); - - /* mem_start != 0 here */ - if (ctzl(mem_start) < ctzl(sizetmp)) { - sizetmp = 1ULL << ctzl(mem_start); - } - - spapr_populate_memory_node(fdt, i, mem_start, sizetmp); - node_size -= sizetmp; - mem_start += sizetmp; - } - } - - return 0; -} - -static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, - SpaprMachineState *spapr) -{ - MachineState *ms = MACHINE(spapr); - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); - int index = spapr_get_vcpu_id(cpu); - uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), - 0xffffffff, 0xffffffff}; - uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() - : SPAPR_TIMEBASE_FREQ; - uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; - uint32_t page_sizes_prop[64]; - size_t page_sizes_prop_size; - unsigned int smp_threads = ms->smp.threads; - uint32_t vcpus_per_socket = smp_threads * ms->smp.cores; - uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; - int compat_smt = MIN(smp_threads, ppc_compat_max_vthreads(cpu)); - SpaprDrc *drc; - int drc_index; - uint32_t radix_AP_encodings[PPC_PAGE_SIZES_MAX_SZ]; - int i; - - drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index); - if (drc) { - drc_index = spapr_drc_index(drc); - _FDT((fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index))); - } - - _FDT((fdt_setprop_cell(fdt, offset, "reg", index))); - _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu"))); - - _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR]))); - _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size", - env->dcache_line_size))); - _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size", - env->dcache_line_size))); - _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size", - env->icache_line_size))); - _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size", - env->icache_line_size))); - - if (pcc->l1_dcache_size) { - _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size", - pcc->l1_dcache_size))); - } else { - warn_report("Unknown L1 dcache size for cpu"); - } - if (pcc->l1_icache_size) { - _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size", - pcc->l1_icache_size))); - } else { - warn_report("Unknown L1 icache size for cpu"); - } - - _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); - _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq))); - _FDT((fdt_setprop_cell(fdt, offset, "slb-size", cpu->hash64_opts->slb_size))); - _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", cpu->hash64_opts->slb_size))); - _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); - _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); - - if (env->spr_cb[SPR_PURR].oea_read) { - _FDT((fdt_setprop_cell(fdt, offset, "ibm,purr", 1))); - } - if (env->spr_cb[SPR_SPURR].oea_read) { - _FDT((fdt_setprop_cell(fdt, offset, "ibm,spurr", 1))); - } - - if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) { - _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes", - segs, sizeof(segs)))); - } - - /* Advertise VSX (vector extensions) if available - * 1 == VMX / Altivec available - * 2 == VSX available - * - * Only CPUs for which we create core types in spapr_cpu_core.c - * are possible, and all of those have VMX */ - if (spapr_get_cap(spapr, SPAPR_CAP_VSX) != 0) { - _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 2))); - } else { - _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 1))); - } - - /* Advertise DFP (Decimal Floating Point) if available - * 0 / no property == no DFP - * 1 == DFP available */ - if (spapr_get_cap(spapr, SPAPR_CAP_DFP) != 0) { - _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1))); - } - - page_sizes_prop_size = ppc_create_page_sizes_prop(cpu, page_sizes_prop, - sizeof(page_sizes_prop)); - if (page_sizes_prop_size) { - _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes", - page_sizes_prop, page_sizes_prop_size))); - } - - spapr_populate_pa_features(spapr, cpu, fdt, offset); - - _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", - cs->cpu_index / vcpus_per_socket))); - - _FDT((fdt_setprop(fdt, offset, "ibm,pft-size", - pft_size_prop, sizeof(pft_size_prop)))); - - if (ms->numa_state->num_nodes > 1) { - _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cpu)); - } - - _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt)); - - if (pcc->radix_page_info) { - for (i = 0; i < pcc->radix_page_info->count; i++) { - radix_AP_encodings[i] = - cpu_to_be32(pcc->radix_page_info->entries[i]); - } - _FDT((fdt_setprop(fdt, offset, "ibm,processor-radix-AP-encodings", - radix_AP_encodings, - pcc->radix_page_info->count * - sizeof(radix_AP_encodings[0])))); - } - - /* - * We set this property to let the guest know that it can use the large - * decrementer and its width in bits. - */ - if (spapr_get_cap(spapr, SPAPR_CAP_LARGE_DECREMENTER) != SPAPR_CAP_OFF) - _FDT((fdt_setprop_u32(fdt, offset, "ibm,dec-bits", - pcc->lrg_decr_bits))); -} - -static void spapr_populate_cpus_dt_node(void *fdt, SpaprMachineState *spapr) -{ - CPUState **rev; - CPUState *cs; - int n_cpus; - int cpus_offset; - char *nodename; - int i; - - cpus_offset = fdt_add_subnode(fdt, 0, "cpus"); - _FDT(cpus_offset); - _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1))); - _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0))); - - /* - * We walk the CPUs in reverse order to ensure that CPU DT nodes - * created by fdt_add_subnode() end up in the right order in FDT - * for the guest kernel the enumerate the CPUs correctly. - * - * The CPU list cannot be traversed in reverse order, so we need - * to do extra work. - */ - n_cpus = 0; - rev = NULL; - CPU_FOREACH(cs) { - rev = g_renew(CPUState *, rev, n_cpus + 1); - rev[n_cpus++] = cs; - } - - for (i = n_cpus - 1; i >= 0; i--) { - CPUState *cs = rev[i]; - PowerPCCPU *cpu = POWERPC_CPU(cs); - int index = spapr_get_vcpu_id(cpu); - DeviceClass *dc = DEVICE_GET_CLASS(cs); - int offset; - - if (!spapr_is_thread0_in_vcore(spapr, cpu)) { - continue; - } - - nodename = g_strdup_printf("%s@%x", dc->fw_name, index); - offset = fdt_add_subnode(fdt, cpus_offset, nodename); - g_free(nodename); - _FDT(offset); - spapr_populate_cpu_dt(cs, fdt, offset, spapr); - } - - g_free(rev); -} - -static int spapr_rng_populate_dt(void *fdt) -{ - int node; - int ret; - - node = qemu_fdt_add_subnode(fdt, "/ibm,platform-facilities"); - if (node <= 0) { - return -1; - } - ret = fdt_setprop_string(fdt, node, "device_type", - "ibm,platform-facilities"); - ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1); - ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0); - - node = fdt_add_subnode(fdt, node, "ibm,random-v1"); - if (node <= 0) { - return -1; - } - ret |= fdt_setprop_string(fdt, node, "compatible", "ibm,random"); - - return ret ? -1 : 0; -} - static uint32_t spapr_pc_dimm_node(MemoryDeviceInfoList *list, ram_addr_t addr) { MemoryDeviceInfoList *info; @@ -642,9 +390,8 @@ spapr_get_drconf_cell(uint32_t seq_lmbs, uint64_t base_addr, return elem; } -/* ibm,dynamic-memory-v2 */ -static int spapr_populate_drmem_v2(SpaprMachineState *spapr, void *fdt, - int offset, MemoryDeviceInfoList *dimms) +static int spapr_dt_dynamic_memory_v2(SpaprMachineState *spapr, void *fdt, + int offset, MemoryDeviceInfoList *dimms) { MachineState *machine = MACHINE(spapr); uint8_t *int_buf, *cur_index; @@ -735,8 +482,7 @@ static int spapr_populate_drmem_v2(SpaprMachineState *spapr, void *fdt, return 0; } -/* ibm,dynamic-memory */ -static int spapr_populate_drmem_v1(SpaprMachineState *spapr, void *fdt, +static int spapr_dt_dynamic_memory(SpaprMachineState *spapr, void *fdt, int offset, MemoryDeviceInfoList *dimms) { MachineState *machine = MACHINE(spapr); @@ -805,7 +551,8 @@ static int spapr_populate_drmem_v1(SpaprMachineState *spapr, void *fdt, * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation * of this device tree node. */ -static int spapr_populate_drconf_memory(SpaprMachineState *spapr, void *fdt) +static int spapr_dt_dynamic_reconfiguration_memory(SpaprMachineState *spapr, + void *fdt) { MachineState *machine = MACHINE(spapr); int nb_numa_nodes = machine->numa_state->num_nodes; @@ -844,9 +591,9 @@ static int spapr_populate_drconf_memory(SpaprMachineState *spapr, void *fdt) /* ibm,dynamic-memory or ibm,dynamic-memory-v2 */ dimms = qmp_memory_device_list(); if (spapr_ovec_test(spapr->ov5_cas, OV5_DRMEM_V2)) { - ret = spapr_populate_drmem_v2(spapr, fdt, offset, dimms); + ret = spapr_dt_dynamic_memory_v2(spapr, fdt, offset, dimms); } else { - ret = spapr_populate_drmem_v1(spapr, fdt, offset, dimms); + ret = spapr_dt_dynamic_memory(spapr, fdt, offset, dimms); } qapi_free_MemoryDeviceInfoList(dimms); @@ -877,30 +624,267 @@ static int spapr_populate_drconf_memory(SpaprMachineState *spapr, void *fdt) return ret; } -static int spapr_dt_cas_updates(SpaprMachineState *spapr, void *fdt, - SpaprOptionVector *ov5_updates) +static int spapr_dt_memory(SpaprMachineState *spapr, void *fdt) { + MachineState *machine = MACHINE(spapr); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); - int ret = 0, offset; + hwaddr mem_start, node_size; + int i, nb_nodes = machine->numa_state->num_nodes; + NodeInfo *nodes = machine->numa_state->nodes; + + for (i = 0, mem_start = 0; i < nb_nodes; ++i) { + if (!nodes[i].node_mem) { + continue; + } + if (mem_start >= machine->ram_size) { + node_size = 0; + } else { + node_size = nodes[i].node_mem; + if (node_size > machine->ram_size - mem_start) { + node_size = machine->ram_size - mem_start; + } + } + if (!mem_start) { + /* spapr_machine_init() checks for rma_size <= node0_size + * already */ + spapr_dt_memory_node(fdt, i, 0, spapr->rma_size); + mem_start += spapr->rma_size; + node_size -= spapr->rma_size; + } + for ( ; node_size; ) { + hwaddr sizetmp = pow2floor(node_size); + + /* mem_start != 0 here */ + if (ctzl(mem_start) < ctzl(sizetmp)) { + sizetmp = 1ULL << ctzl(mem_start); + } + + spapr_dt_memory_node(fdt, i, mem_start, sizetmp); + node_size -= sizetmp; + mem_start += sizetmp; + } + } /* Generate ibm,dynamic-reconfiguration-memory node if required */ - if (spapr_ovec_test(ov5_updates, OV5_DRCONF_MEMORY)) { + if (spapr_ovec_test(spapr->ov5_cas, OV5_DRCONF_MEMORY)) { + int ret; + g_assert(smc->dr_lmb_enabled); - ret = spapr_populate_drconf_memory(spapr, fdt); + ret = spapr_dt_dynamic_reconfiguration_memory(spapr, fdt); if (ret) { return ret; } } - offset = fdt_path_offset(fdt, "/chosen"); - if (offset < 0) { - offset = fdt_add_subnode(fdt, 0, "chosen"); - if (offset < 0) { - return offset; + return 0; +} + +static void spapr_dt_cpu(CPUState *cs, void *fdt, int offset, + SpaprMachineState *spapr) +{ + MachineState *ms = MACHINE(spapr); + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); + int index = spapr_get_vcpu_id(cpu); + uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), + 0xffffffff, 0xffffffff}; + uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() + : SPAPR_TIMEBASE_FREQ; + uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; + uint32_t page_sizes_prop[64]; + size_t page_sizes_prop_size; + unsigned int smp_threads = ms->smp.threads; + uint32_t vcpus_per_socket = smp_threads * ms->smp.cores; + uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; + int compat_smt = MIN(smp_threads, ppc_compat_max_vthreads(cpu)); + SpaprDrc *drc; + int drc_index; + uint32_t radix_AP_encodings[PPC_PAGE_SIZES_MAX_SZ]; + int i; + + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index); + if (drc) { + drc_index = spapr_drc_index(drc); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index))); + } + + _FDT((fdt_setprop_cell(fdt, offset, "reg", index))); + _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu"))); + + _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR]))); + _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size", + env->dcache_line_size))); + _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size", + env->dcache_line_size))); + _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size", + env->icache_line_size))); + _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size", + env->icache_line_size))); + + if (pcc->l1_dcache_size) { + _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size", + pcc->l1_dcache_size))); + } else { + warn_report("Unknown L1 dcache size for cpu"); + } + if (pcc->l1_icache_size) { + _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size", + pcc->l1_icache_size))); + } else { + warn_report("Unknown L1 icache size for cpu"); + } + + _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); + _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq))); + _FDT((fdt_setprop_cell(fdt, offset, "slb-size", cpu->hash64_opts->slb_size))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", cpu->hash64_opts->slb_size))); + _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); + _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); + + if (env->spr_cb[SPR_PURR].oea_read) { + _FDT((fdt_setprop_cell(fdt, offset, "ibm,purr", 1))); + } + if (env->spr_cb[SPR_SPURR].oea_read) { + _FDT((fdt_setprop_cell(fdt, offset, "ibm,spurr", 1))); + } + + if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) { + _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes", + segs, sizeof(segs)))); + } + + /* Advertise VSX (vector extensions) if available + * 1 == VMX / Altivec available + * 2 == VSX available + * + * Only CPUs for which we create core types in spapr_cpu_core.c + * are possible, and all of those have VMX */ + if (spapr_get_cap(spapr, SPAPR_CAP_VSX) != 0) { + _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 2))); + } else { + _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 1))); + } + + /* Advertise DFP (Decimal Floating Point) if available + * 0 / no property == no DFP + * 1 == DFP available */ + if (spapr_get_cap(spapr, SPAPR_CAP_DFP) != 0) { + _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1))); + } + + page_sizes_prop_size = ppc_create_page_sizes_prop(cpu, page_sizes_prop, + sizeof(page_sizes_prop)); + if (page_sizes_prop_size) { + _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes", + page_sizes_prop, page_sizes_prop_size))); + } + + spapr_dt_pa_features(spapr, cpu, fdt, offset); + + _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", + cs->cpu_index / vcpus_per_socket))); + + _FDT((fdt_setprop(fdt, offset, "ibm,pft-size", + pft_size_prop, sizeof(pft_size_prop)))); + + if (ms->numa_state->num_nodes > 1) { + _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cpu)); + } + + _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt)); + + if (pcc->radix_page_info) { + for (i = 0; i < pcc->radix_page_info->count; i++) { + radix_AP_encodings[i] = + cpu_to_be32(pcc->radix_page_info->entries[i]); + } + _FDT((fdt_setprop(fdt, offset, "ibm,processor-radix-AP-encodings", + radix_AP_encodings, + pcc->radix_page_info->count * + sizeof(radix_AP_encodings[0])))); + } + + /* + * We set this property to let the guest know that it can use the large + * decrementer and its width in bits. + */ + if (spapr_get_cap(spapr, SPAPR_CAP_LARGE_DECREMENTER) != SPAPR_CAP_OFF) + _FDT((fdt_setprop_u32(fdt, offset, "ibm,dec-bits", + pcc->lrg_decr_bits))); +} + +static void spapr_dt_cpus(void *fdt, SpaprMachineState *spapr) +{ + CPUState **rev; + CPUState *cs; + int n_cpus; + int cpus_offset; + char *nodename; + int i; + + cpus_offset = fdt_add_subnode(fdt, 0, "cpus"); + _FDT(cpus_offset); + _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1))); + _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0))); + + /* + * We walk the CPUs in reverse order to ensure that CPU DT nodes + * created by fdt_add_subnode() end up in the right order in FDT + * for the guest kernel the enumerate the CPUs correctly. + * + * The CPU list cannot be traversed in reverse order, so we need + * to do extra work. + */ + n_cpus = 0; + rev = NULL; + CPU_FOREACH(cs) { + rev = g_renew(CPUState *, rev, n_cpus + 1); + rev[n_cpus++] = cs; + } + + for (i = n_cpus - 1; i >= 0; i--) { + CPUState *cs = rev[i]; + PowerPCCPU *cpu = POWERPC_CPU(cs); + int index = spapr_get_vcpu_id(cpu); + DeviceClass *dc = DEVICE_GET_CLASS(cs); + int offset; + + if (!spapr_is_thread0_in_vcore(spapr, cpu)) { + continue; } + + nodename = g_strdup_printf("%s@%x", dc->fw_name, index); + offset = fdt_add_subnode(fdt, cpus_offset, nodename); + g_free(nodename); + _FDT(offset); + spapr_dt_cpu(cs, fdt, offset, spapr); + } + + g_free(rev); +} + +static int spapr_dt_rng(void *fdt) +{ + int node; + int ret; + + node = qemu_fdt_add_subnode(fdt, "/ibm,platform-facilities"); + if (node <= 0) { + return -1; + } + ret = fdt_setprop_string(fdt, node, "device_type", + "ibm,platform-facilities"); + ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1); + ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0); + + node = fdt_add_subnode(fdt, node, "ibm,random-v1"); + if (node <= 0) { + return -1; } - return spapr_ovec_populate_dt(fdt, offset, spapr->ov5_cas, - "ibm,architecture-vec-5"); + ret |= fdt_setprop_string(fdt, node, "compatible", "ibm,random"); + + return ret ? -1 : 0; } static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) @@ -967,6 +951,29 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) _FDT(fdt_setprop(fdt, rtas, "ibm,max-associativity-domains", maxdomains, sizeof(maxdomains))); + /* + * FWNMI reserves RTAS_ERROR_LOG_MAX for the machine check error log, + * and 16 bytes per CPU for system reset error log plus an extra 8 bytes. + * + * The system reset requirements are driven by existing Linux and PowerVM + * implementation which (contrary to PAPR) saves r3 in the error log + * structure like machine check, so Linux expects to find the saved r3 + * value at the address in r3 upon FWNMI-enabled sreset interrupt (and + * does not look at the error value). + * + * System reset interrupts are not subject to interlock like machine + * check, so this memory area could be corrupted if the sreset is + * interrupted by a machine check (or vice versa) if it was shared. To + * prevent this, system reset uses per-CPU areas for the sreset save + * area. A system reset that interrupts a system reset handler could + * still overwrite this area, but Linux doesn't try to recover in that + * case anyway. + * + * The extra 8 bytes is required because Linux's FWNMI error log check + * is off-by-one. + */ + _FDT(fdt_setprop_cell(fdt, rtas, "rtas-size", RTAS_ERROR_LOG_MAX + + ms->smp.max_cpus * sizeof(uint64_t)*2 + sizeof(uint64_t))); _FDT(fdt_setprop_cell(fdt, rtas, "rtas-error-log-max", RTAS_ERROR_LOG_MAX)); _FDT(fdt_setprop_cell(fdt, rtas, "rtas-event-scan-rate", @@ -1040,81 +1047,91 @@ static void spapr_dt_ov5_platform_support(SpaprMachineState *spapr, void *fdt, val, sizeof(val))); } -static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt) +static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset) { MachineState *machine = MACHINE(spapr); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); int chosen; - const char *boot_device = machine->boot_order; - char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); - size_t cb = 0; - char *bootlist = get_boot_devices_list(&cb); _FDT(chosen = fdt_add_subnode(fdt, 0, "chosen")); - if (machine->kernel_cmdline && machine->kernel_cmdline[0]) { - _FDT(fdt_setprop_string(fdt, chosen, "bootargs", - machine->kernel_cmdline)); - } - if (spapr->initrd_size) { - _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-start", - spapr->initrd_base)); - _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-end", - spapr->initrd_base + spapr->initrd_size)); - } + if (reset) { + const char *boot_device = machine->boot_order; + char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); + size_t cb = 0; + char *bootlist = get_boot_devices_list(&cb); + + if (machine->kernel_cmdline && machine->kernel_cmdline[0]) { + _FDT(fdt_setprop_string(fdt, chosen, "bootargs", + machine->kernel_cmdline)); + } + + if (spapr->initrd_size) { + _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-start", + spapr->initrd_base)); + _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-end", + spapr->initrd_base + spapr->initrd_size)); + } - if (spapr->kernel_size) { - uint64_t kprop[2] = { cpu_to_be64(spapr->kernel_addr), - cpu_to_be64(spapr->kernel_size) }; + if (spapr->kernel_size) { + uint64_t kprop[2] = { cpu_to_be64(spapr->kernel_addr), + cpu_to_be64(spapr->kernel_size) }; - _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel", + _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel", &kprop, sizeof(kprop))); - if (spapr->kernel_le) { - _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0)); + if (spapr->kernel_le) { + _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0)); + } } - } - if (boot_menu) { - _FDT((fdt_setprop_cell(fdt, chosen, "qemu,boot-menu", boot_menu))); - } - _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-width", graphic_width)); - _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-height", graphic_height)); - _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-depth", graphic_depth)); + if (boot_menu) { + _FDT((fdt_setprop_cell(fdt, chosen, "qemu,boot-menu", boot_menu))); + } + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-width", graphic_width)); + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-height", graphic_height)); + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-depth", graphic_depth)); - if (cb && bootlist) { - int i; + if (cb && bootlist) { + int i; - for (i = 0; i < cb; i++) { - if (bootlist[i] == '\n') { - bootlist[i] = ' '; + for (i = 0; i < cb; i++) { + if (bootlist[i] == '\n') { + bootlist[i] = ' '; + } } + _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-list", bootlist)); } - _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-list", bootlist)); - } - if (boot_device && strlen(boot_device)) { - _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-device", boot_device)); - } + if (boot_device && strlen(boot_device)) { + _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-device", boot_device)); + } + + if (!spapr->has_graphics && stdout_path) { + /* + * "linux,stdout-path" and "stdout" properties are + * deprecated by linux kernel. New platforms should only + * use the "stdout-path" property. Set the new property + * and continue using older property to remain compatible + * with the existing firmware. + */ + _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path)); + _FDT(fdt_setprop_string(fdt, chosen, "stdout-path", stdout_path)); + } - if (!spapr->has_graphics && stdout_path) { /* - * "linux,stdout-path" and "stdout" properties are deprecated by linux - * kernel. New platforms should only use the "stdout-path" property. Set - * the new property and continue using older property to remain - * compatible with the existing firmware. + * We can deal with BAR reallocation just fine, advertise it + * to the guest */ - _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path)); - _FDT(fdt_setprop_string(fdt, chosen, "stdout-path", stdout_path)); - } + if (smc->linux_pci_probe) { + _FDT(fdt_setprop_cell(fdt, chosen, "linux,pci-probe-only", 0)); + } - /* We can deal with BAR reallocation just fine, advertise it to the guest */ - if (smc->linux_pci_probe) { - _FDT(fdt_setprop_cell(fdt, chosen, "linux,pci-probe-only", 0)); - } + spapr_dt_ov5_platform_support(spapr, fdt, chosen); - spapr_dt_ov5_platform_support(spapr, fdt, chosen); + g_free(stdout_path); + g_free(bootlist); + } - g_free(stdout_path); - g_free(bootlist); + _FDT(spapr_dt_ovec(fdt, chosen, spapr->ov5_cas, "ibm,architecture-vec-5")); } static void spapr_dt_hypervisor(SpaprMachineState *spapr, void *fdt) @@ -1192,7 +1209,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) /* /interrupt controller */ spapr_irq_dt(spapr, spapr_max_server_number(spapr), fdt, PHANDLE_INTC); - ret = spapr_populate_memory(spapr, fdt); + ret = spapr_dt_memory(spapr, fdt); if (ret < 0) { error_report("couldn't setup memory nodes in fdt"); exit(1); @@ -1202,7 +1219,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) spapr_dt_vdevice(spapr->vio_bus, fdt); if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)) { - ret = spapr_rng_populate_dt(fdt); + ret = spapr_dt_rng(fdt); if (ret < 0) { error_report("could not set up rng device in the fdt"); exit(1); @@ -1217,8 +1234,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) } } - /* cpus */ - spapr_populate_cpus_dt_node(fdt, spapr); + spapr_dt_cpus(fdt, spapr); if (smc->dr_lmb_enabled) { _FDT(spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB)); @@ -1240,9 +1256,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) spapr_dt_rtas(spapr, fdt); /* /chosen */ - if (reset) { - spapr_dt_chosen(spapr, fdt); - } + spapr_dt_chosen(spapr, fdt, reset); /* /hypervisor */ if (kvm_enabled()) { @@ -1261,13 +1275,6 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) } } - /* ibm,client-architecture-support updates */ - ret = spapr_dt_cas_updates(spapr, fdt, spapr->ov5_cas); - if (ret < 0) { - error_report("couldn't setup CAS properties fdt"); - exit(1); - } - if (smc->dr_phb_enabled) { ret = spapr_dt_drc(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_PHB); if (ret < 0) { @@ -1569,7 +1576,7 @@ void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, spapr_set_all_lpcrs(0, LPCR_HR | LPCR_UPRT); } -void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr) +void spapr_setup_hpt(SpaprMachineState *spapr) { int hpt_shift; @@ -1585,9 +1592,16 @@ void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr) } spapr_reallocate_hpt(spapr, hpt_shift, &error_fatal); - if (spapr->vrma_adjust) { - spapr->rma_size = kvmppc_rma_size(spapr_node0_size(MACHINE(spapr)), - spapr->htab_shift); + if (kvm_enabled()) { + hwaddr vrma_limit = kvmppc_vrma_limit(spapr->htab_shift); + + /* Check our RMA fits in the possible VRMA */ + if (vrma_limit < spapr->rma_size) { + error_report("Unable to create %" HWADDR_PRIu + "MiB RMA (VRMA only allows %" HWADDR_PRIu "MiB", + spapr->rma_size / MiB, vrma_limit / MiB); + exit(EXIT_FAILURE); + } } } @@ -1627,7 +1641,7 @@ static void spapr_machine_reset(MachineState *machine) spapr->patb_entry = PATE1_GR; spapr_set_all_lpcrs(LPCR_HR | LPCR_UPRT, LPCR_HR | LPCR_UPRT); } else { - spapr_setup_hpt_and_vrma(spapr); + spapr_setup_hpt(spapr); } qemu_devices_reset(); @@ -1691,16 +1705,17 @@ static void spapr_machine_reset(MachineState *machine) spapr->fdt_blob = fdt; /* Set up the entry state */ - spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, fdt_addr); + spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, 0, fdt_addr, 0); first_ppc_cpu->env.gpr[5] = 0; spapr->cas_reboot = false; - spapr->mc_status = -1; - spapr->guest_machine_check_addr = -1; + spapr->fwnmi_system_reset_addr = -1; + spapr->fwnmi_machine_check_addr = -1; + spapr->fwnmi_machine_check_interlock = -1; /* Signal all vCPUs waiting on this condition */ - qemu_cond_broadcast(&spapr->mc_delivery_cond); + qemu_cond_broadcast(&spapr->fwnmi_machine_check_interlock_cond); migrate_del_blocker(spapr->fwnmi_migration_blocker); } @@ -1989,7 +2004,7 @@ static bool spapr_fwnmi_needed(void *opaque) { SpaprMachineState *spapr = (SpaprMachineState *)opaque; - return spapr->guest_machine_check_addr != -1; + return spapr->fwnmi_machine_check_addr != -1; } static int spapr_fwnmi_pre_save(void *opaque) @@ -2000,7 +2015,7 @@ static int spapr_fwnmi_pre_save(void *opaque) * Check if machine check handling is in progress and print a * warning message. */ - if (spapr->mc_status != -1) { + if (spapr->fwnmi_machine_check_interlock != -1) { warn_report("A machine check is being handled during migration. The" "handler may run and log hardware error on the destination"); } @@ -2008,15 +2023,16 @@ static int spapr_fwnmi_pre_save(void *opaque) return 0; } -static const VMStateDescription vmstate_spapr_machine_check = { - .name = "spapr_machine_check", +static const VMStateDescription vmstate_spapr_fwnmi = { + .name = "spapr_fwnmi", .version_id = 1, .minimum_version_id = 1, .needed = spapr_fwnmi_needed, .pre_save = spapr_fwnmi_pre_save, .fields = (VMStateField[]) { - VMSTATE_UINT64(guest_machine_check_addr, SpaprMachineState), - VMSTATE_INT32(mc_status, SpaprMachineState), + VMSTATE_UINT64(fwnmi_system_reset_addr, SpaprMachineState), + VMSTATE_UINT64(fwnmi_machine_check_addr, SpaprMachineState), + VMSTATE_INT32(fwnmi_machine_check_interlock, SpaprMachineState), VMSTATE_END_OF_LIST() }, }; @@ -2055,7 +2071,7 @@ static const VMStateDescription vmstate_spapr = { &vmstate_spapr_cap_large_decr, &vmstate_spapr_cap_ccf_assist, &vmstate_spapr_cap_fwnmi, - &vmstate_spapr_machine_check, + &vmstate_spapr_fwnmi, NULL } }; @@ -2641,6 +2657,42 @@ static PCIHostState *spapr_create_default_phb(void) return PCI_HOST_BRIDGE(dev); } +static hwaddr spapr_rma_size(SpaprMachineState *spapr, Error **errp) +{ + MachineState *machine = MACHINE(spapr); + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); + hwaddr rma_size = machine->ram_size; + hwaddr node0_size = spapr_node0_size(machine); + + /* RMA has to fit in the first NUMA node */ + rma_size = MIN(rma_size, node0_size); + + /* + * VRMA access is via a special 1TiB SLB mapping, so the RMA can + * never exceed that + */ + rma_size = MIN(rma_size, 1 * TiB); + + /* + * Clamp the RMA size based on machine type. This is for + * migration compatibility with older qemu versions, which limited + * the RMA size for complicated and mostly bad reasons. + */ + if (smc->rma_limit) { + rma_size = MIN(rma_size, smc->rma_limit); + } + + if (rma_size < MIN_RMA_SLOF) { + error_setg(errp, + "pSeries SLOF firmware requires >= %" HWADDR_PRIx + "ldMiB guest RMA (Real Mode Area memory)", + MIN_RMA_SLOF / MiB); + return 0; + } + + return rma_size; +} + /* pSeries LPAR / sPAPR hardware init */ static void spapr_machine_init(MachineState *machine) { @@ -2652,7 +2704,6 @@ static void spapr_machine_init(MachineState *machine) PCIHostState *phb; int i; MemoryRegion *sysmem = get_system_memory(); - hwaddr node0_size = spapr_node0_size(machine); long load_limit, fw_size; char *filename; Error *resize_hpt_err = NULL; @@ -2692,34 +2743,7 @@ static void spapr_machine_init(MachineState *machine) exit(1); } - spapr->rma_size = node0_size; - - /* With KVM, we don't actually know whether KVM supports an - * unbounded RMA (PR KVM) or is limited by the hash table size - * (HV KVM using VRMA), so we always assume the latter - * - * In that case, we also limit the initial allocations for RTAS - * etc... to 256M since we have no way to know what the VRMA size - * is going to be as it depends on the size of the hash table - * which isn't determined yet. - */ - if (kvm_enabled()) { - spapr->vrma_adjust = 1; - spapr->rma_size = MIN(spapr->rma_size, 0x10000000); - } - - /* Actually we don't support unbounded RMA anymore since we added - * proper emulation of HV mode. The max we can get is 16G which - * also happens to be what we configure for PAPR mode so make sure - * we don't do anything bigger than that - */ - spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull); - - if (spapr->rma_size > node0_size) { - error_report("Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")", - spapr->rma_size); - exit(1); - } + spapr->rma_size = spapr_rma_size(spapr, &error_fatal); /* Setup a load limit for the ramdisk leaving room for SLOF and FDT */ load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD; @@ -2869,7 +2893,7 @@ static void spapr_machine_init(MachineState *machine) spapr_create_lmb_dr_connectors(spapr); } - if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI_MCE) == SPAPR_CAP_ON) { + if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI) == SPAPR_CAP_ON) { /* Create the error string for live migration blocker */ error_setg(&spapr->fwnmi_migration_blocker, "A machine check is being handled during migration. The handler" @@ -2956,13 +2980,6 @@ static void spapr_machine_init(MachineState *machine) } } - if (spapr->rma_size < (MIN_RMA_SLOF * MiB)) { - error_report( - "pSeries SLOF firmware requires >= %ldM guest RMA (Real Mode Area memory)", - MIN_RMA_SLOF); - exit(1); - } - if (kernel_filename) { uint64_t lowaddr = 0; @@ -3045,7 +3062,7 @@ static void spapr_machine_init(MachineState *machine) kvmppc_spapr_enable_inkernel_multitce(); } - qemu_cond_init(&spapr->mc_delivery_cond); + qemu_cond_init(&spapr->fwnmi_machine_check_interlock_cond); } static int spapr_kvm_type(MachineState *machine, const char *vm_type) @@ -3223,30 +3240,6 @@ static void spapr_set_resize_hpt(Object *obj, const char *value, Error **errp) } } -static void spapr_get_vsmt(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - visit_type_uint32(v, name, (uint32_t *)opaque, errp); -} - -static void spapr_set_vsmt(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - visit_type_uint32(v, name, (uint32_t *)opaque, errp); -} - -static void spapr_get_kernel_addr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - visit_type_uint64(v, name, (uint64_t *)opaque, errp); -} - -static void spapr_set_kernel_addr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - visit_type_uint64(v, name, (uint64_t *)opaque, errp); -} - static char *spapr_get_ic_mode(Object *obj, Error **errp) { SpaprMachineState *spapr = SPAPR_MACHINE(obj); @@ -3344,17 +3337,19 @@ static void spapr_instance_init(Object *obj) object_property_set_description(obj, "resize-hpt", "Resizing of the Hash Page Table (enabled, disabled, required)", NULL); - object_property_add(obj, "vsmt", "uint32", spapr_get_vsmt, - spapr_set_vsmt, NULL, &spapr->vsmt, &error_abort); + object_property_add_uint32_ptr(obj, "vsmt", + &spapr->vsmt, OBJ_PROP_FLAG_READWRITE, + &error_abort); object_property_set_description(obj, "vsmt", "Virtual SMT: KVM behaves as if this were" " the host's SMT mode", &error_abort); + object_property_add_bool(obj, "vfio-no-msix-emulation", spapr_get_msix_emulation, NULL, NULL); - object_property_add(obj, "kernel-addr", "uint64", spapr_get_kernel_addr, - spapr_set_kernel_addr, NULL, &spapr->kernel_addr, - &error_abort); + object_property_add_uint64_ptr(obj, "kernel-addr", + &spapr->kernel_addr, OBJ_PROP_FLAG_READWRITE, + &error_abort); object_property_set_description(obj, "kernel-addr", stringify(KERNEL_LOAD_ADDR) " for -kernel is the default", @@ -3389,8 +3384,28 @@ static void spapr_machine_finalizefn(Object *obj) void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg) { + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + cpu_synchronize_state(cs); - ppc_cpu_do_system_reset(cs); + /* If FWNMI is inactive, addr will be -1, which will deliver to 0x100 */ + if (spapr->fwnmi_system_reset_addr != -1) { + uint64_t rtas_addr, addr; + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + /* get rtas addr from fdt */ + rtas_addr = spapr_get_rtas_addr(); + if (!rtas_addr) { + qemu_system_guest_panicked(NULL); + return; + } + + addr = rtas_addr + RTAS_ERROR_LOG_MAX + cs->cpu_index * sizeof(uint64_t)*2; + stq_be_phys(&address_space_memory, addr, env->gpr[3]); + stq_be_phys(&address_space_memory, addr + sizeof(uint64_t), 0); + env->gpr[3] = addr; + } + ppc_cpu_do_system_reset(cs, spapr->fwnmi_system_reset_addr); } static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) @@ -3411,8 +3426,8 @@ int spapr_lmb_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, addr = spapr_drc_index(drc) * SPAPR_MEMORY_BLOCK_SIZE; node = object_property_get_uint(OBJECT(drc->dev), PC_DIMM_NODE_PROP, &error_abort); - *fdt_start_offset = spapr_populate_memory_node(fdt, node, addr, - SPAPR_MEMORY_BLOCK_SIZE); + *fdt_start_offset = spapr_dt_memory_node(fdt, node, addr, + SPAPR_MEMORY_BLOCK_SIZE); return 0; } @@ -3813,7 +3828,7 @@ int spapr_core_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, offset = fdt_add_subnode(fdt, 0, nodename); g_free(nodename); - spapr_populate_cpu_dt(cs, fdt, offset, spapr); + spapr_dt_cpu(cs, fdt, offset, spapr); *fdt_start_offset = offset; return 0; @@ -4526,7 +4541,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->default_caps.caps[SPAPR_CAP_NESTED_KVM_HV] = SPAPR_CAP_OFF; smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_ON; smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_ON; - smc->default_caps.caps[SPAPR_CAP_FWNMI_MCE] = SPAPR_CAP_ON; + smc->default_caps.caps[SPAPR_CAP_FWNMI] = SPAPR_CAP_ON; spapr_caps_add_properties(smc, &error_abort); smc->irq = &spapr_irq_dual; smc->dr_phb_enabled = true; @@ -4604,7 +4619,8 @@ static void spapr_machine_4_2_class_options(MachineClass *mc) spapr_machine_5_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_4_2, hw_compat_4_2_len); smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_OFF; - smc->default_caps.caps[SPAPR_CAP_FWNMI_MCE] = SPAPR_CAP_OFF; + smc->default_caps.caps[SPAPR_CAP_FWNMI] = SPAPR_CAP_OFF; + smc->rma_limit = 16 * GiB; mc->nvdimm_supported = false; } diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 8b27d3ac09..679ae7959f 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -509,17 +509,14 @@ static void cap_ccf_assist_apply(SpaprMachineState *spapr, uint8_t val, } } -static void cap_fwnmi_mce_apply(SpaprMachineState *spapr, uint8_t val, +static void cap_fwnmi_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { if (!val) { return; /* Disabled by default */ } - if (tcg_enabled()) { - warn_report("Firmware Assisted Non-Maskable Interrupts(FWNMI) not " - "supported in TCG"); - } else if (kvm_enabled()) { + if (kvm_enabled()) { if (kvmppc_set_fwnmi() < 0) { error_setg(errp, "Firmware Assisted Non-Maskable Interrupts(FWNMI) " "not supported by KVM"); @@ -626,14 +623,14 @@ SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .type = "bool", .apply = cap_ccf_assist_apply, }, - [SPAPR_CAP_FWNMI_MCE] = { - .name = "fwnmi-mce", - .description = "Handle fwnmi machine check exceptions", - .index = SPAPR_CAP_FWNMI_MCE, + [SPAPR_CAP_FWNMI] = { + .name = "fwnmi", + .description = "Implements PAPR FWNMI option", + .index = SPAPR_CAP_FWNMI, .get = spapr_cap_get_bool, .set = spapr_cap_set_bool, .type = "bool", - .apply = cap_fwnmi_mce_apply, + .apply = cap_fwnmi_apply, }, }; @@ -774,7 +771,7 @@ SPAPR_CAP_MIG_STATE(hpt_maxpagesize, SPAPR_CAP_HPT_MAXPAGESIZE); SPAPR_CAP_MIG_STATE(nested_kvm_hv, SPAPR_CAP_NESTED_KVM_HV); SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER); SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST); -SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI_MCE); +SPAPR_CAP_MIG_STATE(fwnmi, SPAPR_CAP_FWNMI); void spapr_caps_init(SpaprMachineState *spapr) { diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index d09125d9af..ac1c109427 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -50,22 +50,14 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu) * the settings below ensure proper operations with TCG in absence of * a real hypervisor. * - * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for - * real mode accesses, which thankfully defaults to 0 and isn't - * accessible in guest mode. - * * Disable Power-saving mode Exit Cause exceptions for the CPU, so * we don't get spurious wakups before an RTAS start-cpu call. * For the same reason, set PSSCR_EC. */ - lpcr &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm); + lpcr &= ~(LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm); lpcr |= LPCR_LPES0 | LPCR_LPES1; env->spr[SPR_PSSCR] |= PSSCR_EC; - /* Set RMLS to the max (ie, 16G) */ - lpcr &= ~LPCR_RMLS; - lpcr |= 1ull << LPCR_RMLS_SHIFT; - ppc_store_lpcr(cpu, lpcr); /* Set a full AMOR so guest can use the AMR as it sees fit */ @@ -84,13 +76,17 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu) spapr_irq_cpu_intc_reset(spapr, cpu); } -void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3) +void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, + target_ulong r1, target_ulong r3, + target_ulong r4) { PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; env->nip = nip; + env->gpr[1] = r1; env->gpr[3] = r3; + env->gpr[4] = r4; kvmppc_set_reg_ppc_online(cpu, 1); CPU(cpu)->halted = 0; /* Enable Power-saving mode Exit Cause exceptions */ diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index e373d342eb..47e6bb12f9 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -583,7 +583,8 @@ static void spapr_dr_connector_instance_init(Object *obj) SpaprDrc *drc = SPAPR_DR_CONNECTOR(obj); SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - object_property_add_uint32_ptr(obj, "id", &drc->id, NULL); + object_property_add_uint32_ptr(obj, "id", &drc->id, OBJ_PROP_FLAG_READ, + NULL); object_property_add(obj, "index", "uint32", prop_get_index, NULL, NULL, NULL, NULL); object_property_add(obj, "fdt", "struct", prop_get_fdt, diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 8b32b7eea5..323fcef4aa 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -786,28 +786,12 @@ static void spapr_mce_dispatch_elog(PowerPCCPU *cpu, bool recovered) { SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); CPUState *cs = CPU(cpu); - uint64_t rtas_addr; CPUPPCState *env = &cpu->env; - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - target_ulong msr = 0; + uint64_t rtas_addr; struct rtas_error_log log; struct mc_extended_log *ext_elog; uint32_t summary; - /* - * Properly set bits in MSR before we invoke the handler. - * SRR0/1, DAR and DSISR are properly set by KVM - */ - if (!(*pcc->interrupts_big_endian)(cpu)) { - msr |= (1ULL << MSR_LE); - } - - if (env->msr & (1ULL << MSR_SF)) { - msr |= (1ULL << MSR_SF); - } - - msr |= (1ULL << MSR_ME); - ext_elog = g_malloc0(sizeof(*ext_elog)); summary = spapr_mce_get_elog_type(cpu, recovered, ext_elog); @@ -823,8 +807,7 @@ static void spapr_mce_dispatch_elog(PowerPCCPU *cpu, bool recovered) /* get rtas addr from fdt */ rtas_addr = spapr_get_rtas_addr(); if (!rtas_addr) { - /* Unable to fetch rtas_addr. Hence reset the guest */ - ppc_cpu_do_system_reset(cs); + qemu_system_guest_panicked(NULL); g_free(ext_elog); return; } @@ -836,12 +819,11 @@ static void spapr_mce_dispatch_elog(PowerPCCPU *cpu, bool recovered) cpu_physical_memory_write(rtas_addr + RTAS_ERROR_LOG_OFFSET + sizeof(env->gpr[3]) + sizeof(log), ext_elog, sizeof(*ext_elog)); + g_free(ext_elog); env->gpr[3] = rtas_addr + RTAS_ERROR_LOG_OFFSET; - env->msr = msr; - env->nip = spapr->guest_machine_check_addr; - g_free(ext_elog); + ppc_cpu_do_fwnmi_machine_check(cs, spapr->fwnmi_machine_check_addr); } void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) @@ -851,7 +833,7 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) int ret; Error *local_err = NULL; - if (spapr->guest_machine_check_addr == -1) { + if (spapr->fwnmi_machine_check_addr == -1) { /* * This implies that we have hit a machine check either when the * guest has not registered FWNMI (i.e., "ibm,nmi-register" not @@ -863,19 +845,19 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) return; } - while (spapr->mc_status != -1) { + while (spapr->fwnmi_machine_check_interlock != -1) { /* * Check whether the same CPU got machine check error * while still handling the mc error (i.e., before * that CPU called "ibm,nmi-interlock") */ - if (spapr->mc_status == cpu->vcpu_id) { + if (spapr->fwnmi_machine_check_interlock == cpu->vcpu_id) { qemu_system_guest_panicked(NULL); return; } - qemu_cond_wait_iothread(&spapr->mc_delivery_cond); + qemu_cond_wait_iothread(&spapr->fwnmi_machine_check_interlock_cond); /* Meanwhile if the system is reset, then just return */ - if (spapr->guest_machine_check_addr == -1) { + if (spapr->fwnmi_machine_check_addr == -1) { return; } } @@ -891,7 +873,7 @@ void spapr_mce_req_event(PowerPCCPU *cpu, bool recovered) warn_report("Received a fwnmi while migration was in progress"); } - spapr->mc_status = cpu->vcpu_id; + spapr->fwnmi_machine_check_interlock = cpu->vcpu_id; spapr_mce_dispatch_elog(cpu, recovered); } @@ -983,6 +965,19 @@ void spapr_clear_pending_events(SpaprMachineState *spapr) } } +void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr) +{ + SpaprEventLogEntry *entry = NULL, *next_entry; + + QTAILQ_FOREACH_SAFE(entry, &spapr->pending_events, next, next_entry) { + if (spapr_event_log_entry_type(entry) == RTAS_LOG_TYPE_HOTPLUG) { + QTAILQ_REMOVE(&spapr->pending_events, entry, next); + g_free(entry->extended_log); + g_free(entry); + } + } +} + void spapr_events_init(SpaprMachineState *spapr) { int epow_irq = SPAPR_IRQ_EPOW; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 934eb12d27..40c86e91eb 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1458,7 +1458,7 @@ static void spapr_check_setup_free_hpt(SpaprMachineState *spapr, spapr_free_hpt(spapr); } else if (!(patbe_new & PATE1_GR)) { /* RADIX->HASH || NOTHING->HASH : Allocate HPT */ - spapr_setup_hpt_and_vrma(spapr); + spapr_setup_hpt(spapr); } return; } @@ -1640,7 +1640,7 @@ static uint32_t cas_check_pvr(SpaprMachineState *spapr, PowerPCCPU *cpu, return best_compat; } -static bool spapr_transient_dev_before_cas(void) +static void spapr_handle_transient_dev_before_cas(SpaprMachineState *spapr) { Object *drc_container; ObjectProperty *prop; @@ -1658,10 +1658,11 @@ static bool spapr_transient_dev_before_cas(void) prop->name, NULL)); if (spapr_drc_transient(drc)) { - return true; + spapr_drc_reset(drc); } } - return false; + + spapr_clear_pending_hotplug_events(spapr); } static target_ulong h_client_architecture_support(PowerPCCPU *cpu, @@ -1834,9 +1835,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, spapr_irq_update_active_intc(spapr); - if (spapr_transient_dev_before_cas()) { - spapr->cas_reboot = true; - } + spapr_handle_transient_dev_before_cas(spapr); if (!spapr->cas_reboot) { void *fdt; @@ -1846,7 +1845,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, * (because the guest isn't going to use radix) then set it up here. */ if ((spapr->patb_entry & PATE1_GR) && !guest_radix) { /* legacy hash or new hash: */ - spapr_setup_hpt_and_vrma(spapr); + spapr_setup_hpt(spapr); } if (fdt_bufsize < sizeof(hdr)) { diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c index 74eeb8bb74..25be8082d7 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c @@ -35,6 +35,7 @@ void spapr_nvdimm_validate_opts(NVDIMMDevice *nvdimm, uint64_t size, { char *uuidstr = NULL; QemuUUID uuid; + int ret; if (size % SPAPR_MINIMUM_SCM_BLOCK_SIZE) { error_setg(errp, "NVDIMM memory size excluding the label area" @@ -43,8 +44,10 @@ void spapr_nvdimm_validate_opts(NVDIMMDevice *nvdimm, uint64_t size, return; } - uuidstr = object_property_get_str(OBJECT(nvdimm), NVDIMM_UUID_PROP, NULL); - qemu_uuid_parse(uuidstr, &uuid); + uuidstr = object_property_get_str(OBJECT(nvdimm), NVDIMM_UUID_PROP, + &error_abort); + ret = qemu_uuid_parse(uuidstr, &uuid); + g_assert(!ret); g_free(uuidstr); if (qemu_uuid_is_null(&uuid)) { diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c index 0ff6d1aeae..dd003f1763 100644 --- a/hw/ppc/spapr_ovec.c +++ b/hw/ppc/spapr_ovec.c @@ -200,8 +200,8 @@ SpaprOptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector) return ov; } -int spapr_ovec_populate_dt(void *fdt, int fdt_offset, - SpaprOptionVector *ov, const char *name) +int spapr_dt_ovec(void *fdt, int fdt_offset, + SpaprOptionVector *ov, const char *name) { uint8_t vec[OV_MAXBYTES + 1]; uint16_t vec_len; diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 656fdd2216..9fb8c8632a 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -190,7 +190,7 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, SpaprMachineState *spapr, */ newcpu->env.tb_env->tb_offset = callcpu->env.tb_env->tb_offset; - spapr_cpu_set_entry_state(newcpu, start, r3); + spapr_cpu_set_entry_state(newcpu, start, 0, r3, 0); qemu_cpu_kick(CPU(newcpu)); @@ -414,8 +414,9 @@ static void rtas_ibm_nmi_register(PowerPCCPU *cpu, uint32_t nret, target_ulong rets) { hwaddr rtas_addr; + target_ulong sreset_addr, mce_addr; - if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI_MCE) == SPAPR_CAP_OFF) { + if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI) == SPAPR_CAP_OFF) { rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); return; } @@ -426,7 +427,19 @@ static void rtas_ibm_nmi_register(PowerPCCPU *cpu, return; } - spapr->guest_machine_check_addr = rtas_ld(args, 1); + sreset_addr = rtas_ld(args, 0); + mce_addr = rtas_ld(args, 1); + + /* PAPR requires these are in the first 32M of memory and within RMA */ + if (sreset_addr >= 32 * MiB || sreset_addr >= spapr->rma_size || + mce_addr >= 32 * MiB || mce_addr >= spapr->rma_size) { + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + spapr->fwnmi_system_reset_addr = sreset_addr; + spapr->fwnmi_machine_check_addr = mce_addr; + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } @@ -436,29 +449,39 @@ static void rtas_ibm_nmi_interlock(PowerPCCPU *cpu, target_ulong args, uint32_t nret, target_ulong rets) { - if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI_MCE) == SPAPR_CAP_OFF) { + if (spapr_get_cap(spapr, SPAPR_CAP_FWNMI) == SPAPR_CAP_OFF) { rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); return; } - if (spapr->guest_machine_check_addr == -1) { + if (spapr->fwnmi_machine_check_addr == -1) { /* NMI register not called */ rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } - if (spapr->mc_status != cpu->vcpu_id) { - /* The vCPU that hit the NMI should invoke "ibm,nmi-interlock" */ - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + if (spapr->fwnmi_machine_check_interlock != cpu->vcpu_id) { + /* + * The vCPU that hit the NMI should invoke "ibm,nmi-interlock" + * This should be PARAM_ERROR, but Linux calls "ibm,nmi-interlock" + * for system reset interrupts, despite them not being interlocked. + * PowerVM silently ignores this and returns success here. Returning + * failure causes Linux to print the error "FWNMI: nmi-interlock + * failed: -3", although no other apparent ill effects, this is a + * regression for the user when enabling FWNMI. So for now, match + * PowerVM. When most Linux clients are fixed, this could be + * changed. + */ + rtas_st(rets, 0, RTAS_OUT_SUCCESS); return; } /* * vCPU issuing "ibm,nmi-interlock" is done with NMI handling, - * hence unset mc_status. + * hence unset fwnmi_machine_check_interlock. */ - spapr->mc_status = -1; - qemu_cond_signal(&spapr->mc_delivery_cond); + spapr->fwnmi_machine_check_interlock = -1; + qemu_cond_signal(&spapr->fwnmi_machine_check_interlock_cond); rtas_st(rets, 0, RTAS_OUT_SUCCESS); migrate_del_blocker(spapr->fwnmi_migration_blocker); } diff --git a/hw/rdma/vmw/pvrdma_qp_ops.c b/hw/rdma/vmw/pvrdma_qp_ops.c index bd6db858de..8050287a6c 100644 --- a/hw/rdma/vmw/pvrdma_qp_ops.c +++ b/hw/rdma/vmw/pvrdma_qp_ops.c @@ -34,13 +34,13 @@ typedef struct CompHandlerCtx { /* Send Queue WQE */ typedef struct PvrdmaSqWqe { struct pvrdma_sq_wqe_hdr hdr; - struct pvrdma_sge sge[0]; + struct pvrdma_sge sge[]; } PvrdmaSqWqe; /* Recv Queue WQE */ typedef struct PvrdmaRqWqe { struct pvrdma_rq_wqe_hdr hdr; - struct pvrdma_sge sge[0]; + struct pvrdma_sge sge[]; } PvrdmaRqWqe; /* diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index a254cad489..646553a7c3 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -145,8 +145,8 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) &error_abort); /* Mask ROM */ - memory_region_init_rom(&s->mask_rom, NULL, "riscv.sifive.e.mrom", - memmap[SIFIVE_E_MROM].size, &error_fatal); + memory_region_init_rom(&s->mask_rom, OBJECT(dev), "riscv.sifive.e.mrom", + memmap[SIFIVE_E_MROM].size, &error_fatal); memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_MROM].base, &s->mask_rom); @@ -208,9 +208,8 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) memmap[SIFIVE_E_PWM2].base, memmap[SIFIVE_E_PWM2].size); /* Flash memory */ - memory_region_init_ram(&s->xip_mem, NULL, "riscv.sifive.e.xip", - memmap[SIFIVE_E_XIP].size, &error_fatal); - memory_region_set_readonly(&s->xip_mem, true); + memory_region_init_rom(&s->xip_mem, OBJECT(dev), "riscv.sifive.e.xip", + memmap[SIFIVE_E_XIP].size, &error_fatal); memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base, &s->xip_mem); } diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 156a003642..56351c4faa 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -56,7 +56,11 @@ #include <libfdt.h> -#define BIOS_FILENAME "opensbi-riscv64-sifive_u-fw_jump.bin" +#if defined(TARGET_RISCV32) +# define BIOS_FILENAME "opensbi-riscv32-sifive_u-fw_jump.bin" +#else +# define BIOS_FILENAME "opensbi-riscv64-sifive_u-fw_jump.bin" +#endif static const struct MemmapEntry { hwaddr base; @@ -497,7 +501,7 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) &error_abort); /* boot rom */ - memory_region_init_rom(mask_rom, NULL, "riscv.sifive.u.mrom", + memory_region_init_rom(mask_rom, OBJECT(dev), "riscv.sifive.u.mrom", memmap[SIFIVE_U_MROM].size, &error_fatal); memory_region_add_subregion(system_memory, memmap[SIFIVE_U_MROM].base, mask_rom); diff --git a/hw/rtc/Makefile.objs b/hw/rtc/Makefile.objs index aa208d0d10..e4c1b8617c 100644 --- a/hw/rtc/Makefile.objs +++ b/hw/rtc/Makefile.objs @@ -12,3 +12,4 @@ obj-$(CONFIG_MC146818RTC) += mc146818rtc.o common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o common-obj-$(CONFIG_ASPEED_SOC) += aspeed_rtc.o common-obj-$(CONFIG_GOLDFISH_RTC) += goldfish_rtc.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-rtc.o diff --git a/hw/rtc/allwinner-rtc.c b/hw/rtc/allwinner-rtc.c new file mode 100644 index 0000000000..5606a51d5c --- /dev/null +++ b/hw/rtc/allwinner-rtc.c @@ -0,0 +1,411 @@ +/* + * Allwinner Real Time Clock emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu-common.h" +#include "hw/qdev-properties.h" +#include "hw/rtc/allwinner-rtc.h" +#include "trace.h" + +/* RTC registers */ +enum { + REG_LOSC = 1, /* Low Oscillator Control */ + REG_YYMMDD, /* RTC Year-Month-Day */ + REG_HHMMSS, /* RTC Hour-Minute-Second */ + REG_ALARM1_WKHHMMSS, /* Alarm1 Week Hour-Minute-Second */ + REG_ALARM1_EN, /* Alarm1 Enable */ + REG_ALARM1_IRQ_EN, /* Alarm1 IRQ Enable */ + REG_ALARM1_IRQ_STA, /* Alarm1 IRQ Status */ + REG_GP0, /* General Purpose Register 0 */ + REG_GP1, /* General Purpose Register 1 */ + REG_GP2, /* General Purpose Register 2 */ + REG_GP3, /* General Purpose Register 3 */ + + /* sun4i registers */ + REG_ALARM1_DDHHMMSS, /* Alarm1 Day Hour-Minute-Second */ + REG_CPUCFG, /* CPU Configuration Register */ + + /* sun6i registers */ + REG_LOSC_AUTOSTA, /* LOSC Auto Switch Status */ + REG_INT_OSC_PRE, /* Internal OSC Clock Prescaler */ + REG_ALARM0_COUNTER, /* Alarm0 Counter */ + REG_ALARM0_CUR_VLU, /* Alarm0 Counter Current Value */ + REG_ALARM0_ENABLE, /* Alarm0 Enable */ + REG_ALARM0_IRQ_EN, /* Alarm0 IRQ Enable */ + REG_ALARM0_IRQ_STA, /* Alarm0 IRQ Status */ + REG_ALARM_CONFIG, /* Alarm Config */ + REG_LOSC_OUT_GATING, /* LOSC Output Gating Register */ + REG_GP4, /* General Purpose Register 4 */ + REG_GP5, /* General Purpose Register 5 */ + REG_GP6, /* General Purpose Register 6 */ + REG_GP7, /* General Purpose Register 7 */ + REG_RTC_DBG, /* RTC Debug Register */ + REG_GPL_HOLD_OUT, /* GPL Hold Output Register */ + REG_VDD_RTC, /* VDD RTC Regulate Register */ + REG_IC_CHARA, /* IC Characteristics Register */ +}; + +/* RTC register flags */ +enum { + REG_LOSC_YMD = (1 << 7), + REG_LOSC_HMS = (1 << 8), +}; + +/* RTC sun4i register map (offset to name) */ +const uint8_t allwinner_rtc_sun4i_regmap[] = { + [0x0000] = REG_LOSC, + [0x0004] = REG_YYMMDD, + [0x0008] = REG_HHMMSS, + [0x000C] = REG_ALARM1_DDHHMMSS, + [0x0010] = REG_ALARM1_WKHHMMSS, + [0x0014] = REG_ALARM1_EN, + [0x0018] = REG_ALARM1_IRQ_EN, + [0x001C] = REG_ALARM1_IRQ_STA, + [0x0020] = REG_GP0, + [0x0024] = REG_GP1, + [0x0028] = REG_GP2, + [0x002C] = REG_GP3, + [0x003C] = REG_CPUCFG, +}; + +/* RTC sun6i register map (offset to name) */ +const uint8_t allwinner_rtc_sun6i_regmap[] = { + [0x0000] = REG_LOSC, + [0x0004] = REG_LOSC_AUTOSTA, + [0x0008] = REG_INT_OSC_PRE, + [0x0010] = REG_YYMMDD, + [0x0014] = REG_HHMMSS, + [0x0020] = REG_ALARM0_COUNTER, + [0x0024] = REG_ALARM0_CUR_VLU, + [0x0028] = REG_ALARM0_ENABLE, + [0x002C] = REG_ALARM0_IRQ_EN, + [0x0030] = REG_ALARM0_IRQ_STA, + [0x0040] = REG_ALARM1_WKHHMMSS, + [0x0044] = REG_ALARM1_EN, + [0x0048] = REG_ALARM1_IRQ_EN, + [0x004C] = REG_ALARM1_IRQ_STA, + [0x0050] = REG_ALARM_CONFIG, + [0x0060] = REG_LOSC_OUT_GATING, + [0x0100] = REG_GP0, + [0x0104] = REG_GP1, + [0x0108] = REG_GP2, + [0x010C] = REG_GP3, + [0x0110] = REG_GP4, + [0x0114] = REG_GP5, + [0x0118] = REG_GP6, + [0x011C] = REG_GP7, + [0x0170] = REG_RTC_DBG, + [0x0180] = REG_GPL_HOLD_OUT, + [0x0190] = REG_VDD_RTC, + [0x01F0] = REG_IC_CHARA, +}; + +static bool allwinner_rtc_sun4i_read(AwRtcState *s, uint32_t offset) +{ + /* no sun4i specific registers currently implemented */ + return false; +} + +static bool allwinner_rtc_sun4i_write(AwRtcState *s, uint32_t offset, + uint32_t data) +{ + /* no sun4i specific registers currently implemented */ + return false; +} + +static bool allwinner_rtc_sun6i_read(AwRtcState *s, uint32_t offset) +{ + const AwRtcClass *c = AW_RTC_GET_CLASS(s); + + switch (c->regmap[offset]) { + case REG_GP4: /* General Purpose Register 4 */ + case REG_GP5: /* General Purpose Register 5 */ + case REG_GP6: /* General Purpose Register 6 */ + case REG_GP7: /* General Purpose Register 7 */ + return true; + default: + break; + } + return false; +} + +static bool allwinner_rtc_sun6i_write(AwRtcState *s, uint32_t offset, + uint32_t data) +{ + const AwRtcClass *c = AW_RTC_GET_CLASS(s); + + switch (c->regmap[offset]) { + case REG_GP4: /* General Purpose Register 4 */ + case REG_GP5: /* General Purpose Register 5 */ + case REG_GP6: /* General Purpose Register 6 */ + case REG_GP7: /* General Purpose Register 7 */ + return true; + default: + break; + } + return false; +} + +static uint64_t allwinner_rtc_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwRtcState *s = AW_RTC(opaque); + const AwRtcClass *c = AW_RTC_GET_CLASS(s); + uint64_t val = 0; + + if (offset >= c->regmap_size) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + if (!c->regmap[offset]) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid register 0x%04x\n", + __func__, (uint32_t)offset); + return 0; + } + + switch (c->regmap[offset]) { + case REG_LOSC: /* Low Oscillator Control */ + val = s->regs[REG_LOSC]; + s->regs[REG_LOSC] &= ~(REG_LOSC_YMD | REG_LOSC_HMS); + break; + case REG_YYMMDD: /* RTC Year-Month-Day */ + case REG_HHMMSS: /* RTC Hour-Minute-Second */ + case REG_GP0: /* General Purpose Register 0 */ + case REG_GP1: /* General Purpose Register 1 */ + case REG_GP2: /* General Purpose Register 2 */ + case REG_GP3: /* General Purpose Register 3 */ + val = s->regs[c->regmap[offset]]; + break; + default: + if (!c->read(s, offset)) { + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register 0x%04x\n", + __func__, (uint32_t)offset); + } + val = s->regs[c->regmap[offset]]; + break; + } + + trace_allwinner_rtc_read(offset, val); + return val; +} + +static void allwinner_rtc_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + AwRtcState *s = AW_RTC(opaque); + const AwRtcClass *c = AW_RTC_GET_CLASS(s); + + if (offset >= c->regmap_size) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + if (!c->regmap[offset]) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid register 0x%04x\n", + __func__, (uint32_t)offset); + return; + } + + trace_allwinner_rtc_write(offset, val); + + switch (c->regmap[offset]) { + case REG_YYMMDD: /* RTC Year-Month-Day */ + s->regs[REG_YYMMDD] = val; + s->regs[REG_LOSC] |= REG_LOSC_YMD; + break; + case REG_HHMMSS: /* RTC Hour-Minute-Second */ + s->regs[REG_HHMMSS] = val; + s->regs[REG_LOSC] |= REG_LOSC_HMS; + break; + case REG_GP0: /* General Purpose Register 0 */ + case REG_GP1: /* General Purpose Register 1 */ + case REG_GP2: /* General Purpose Register 2 */ + case REG_GP3: /* General Purpose Register 3 */ + s->regs[c->regmap[offset]] = val; + break; + default: + if (!c->write(s, offset, val)) { + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register 0x%04x\n", + __func__, (uint32_t)offset); + } + break; + } +} + +static const MemoryRegionOps allwinner_rtc_ops = { + .read = allwinner_rtc_read, + .write = allwinner_rtc_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static void allwinner_rtc_reset(DeviceState *dev) +{ + AwRtcState *s = AW_RTC(dev); + struct tm now; + + /* Clear registers */ + memset(s->regs, 0, sizeof(s->regs)); + + /* Get current datetime */ + qemu_get_timedate(&now, 0); + + /* Set RTC with current datetime */ + if (s->base_year > 1900) { + s->regs[REG_YYMMDD] = ((now.tm_year + 1900 - s->base_year) << 16) | + ((now.tm_mon + 1) << 8) | + now.tm_mday; + s->regs[REG_HHMMSS] = (((now.tm_wday + 6) % 7) << 29) | + (now.tm_hour << 16) | + (now.tm_min << 8) | + now.tm_sec; + } +} + +static void allwinner_rtc_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + AwRtcState *s = AW_RTC(obj); + + /* Memory mapping */ + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_rtc_ops, s, + TYPE_AW_RTC, 1 * KiB); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const VMStateDescription allwinner_rtc_vmstate = { + .name = "allwinner-rtc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, AwRtcState, AW_RTC_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static Property allwinner_rtc_properties[] = { + DEFINE_PROP_INT32("base-year", AwRtcState, base_year, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void allwinner_rtc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_rtc_reset; + dc->vmsd = &allwinner_rtc_vmstate; + device_class_set_props(dc, allwinner_rtc_properties); +} + +static void allwinner_rtc_sun4i_init(Object *obj) +{ + AwRtcState *s = AW_RTC(obj); + s->base_year = 2010; +} + +static void allwinner_rtc_sun4i_class_init(ObjectClass *klass, void *data) +{ + AwRtcClass *arc = AW_RTC_CLASS(klass); + + arc->regmap = allwinner_rtc_sun4i_regmap; + arc->regmap_size = sizeof(allwinner_rtc_sun4i_regmap); + arc->read = allwinner_rtc_sun4i_read; + arc->write = allwinner_rtc_sun4i_write; +} + +static void allwinner_rtc_sun6i_init(Object *obj) +{ + AwRtcState *s = AW_RTC(obj); + s->base_year = 1970; +} + +static void allwinner_rtc_sun6i_class_init(ObjectClass *klass, void *data) +{ + AwRtcClass *arc = AW_RTC_CLASS(klass); + + arc->regmap = allwinner_rtc_sun6i_regmap; + arc->regmap_size = sizeof(allwinner_rtc_sun6i_regmap); + arc->read = allwinner_rtc_sun6i_read; + arc->write = allwinner_rtc_sun6i_write; +} + +static void allwinner_rtc_sun7i_init(Object *obj) +{ + AwRtcState *s = AW_RTC(obj); + s->base_year = 1970; +} + +static void allwinner_rtc_sun7i_class_init(ObjectClass *klass, void *data) +{ + AwRtcClass *arc = AW_RTC_CLASS(klass); + allwinner_rtc_sun4i_class_init(klass, arc); +} + +static const TypeInfo allwinner_rtc_info = { + .name = TYPE_AW_RTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_rtc_init, + .instance_size = sizeof(AwRtcState), + .class_init = allwinner_rtc_class_init, + .class_size = sizeof(AwRtcClass), + .abstract = true, +}; + +static const TypeInfo allwinner_rtc_sun4i_info = { + .name = TYPE_AW_RTC_SUN4I, + .parent = TYPE_AW_RTC, + .class_init = allwinner_rtc_sun4i_class_init, + .instance_init = allwinner_rtc_sun4i_init, +}; + +static const TypeInfo allwinner_rtc_sun6i_info = { + .name = TYPE_AW_RTC_SUN6I, + .parent = TYPE_AW_RTC, + .class_init = allwinner_rtc_sun6i_class_init, + .instance_init = allwinner_rtc_sun6i_init, +}; + +static const TypeInfo allwinner_rtc_sun7i_info = { + .name = TYPE_AW_RTC_SUN7I, + .parent = TYPE_AW_RTC, + .class_init = allwinner_rtc_sun7i_class_init, + .instance_init = allwinner_rtc_sun7i_init, +}; + +static void allwinner_rtc_register(void) +{ + type_register_static(&allwinner_rtc_info); + type_register_static(&allwinner_rtc_sun4i_info); + type_register_static(&allwinner_rtc_sun6i_info); + type_register_static(&allwinner_rtc_sun7i_info); +} + +type_init(allwinner_rtc_register) diff --git a/hw/rtc/trace-events b/hw/rtc/trace-events index c9894e1747..1bc7147d0e 100644 --- a/hw/rtc/trace-events +++ b/hw/rtc/trace-events @@ -1,5 +1,9 @@ # See docs/devel/tracing.txt for syntax documentation. +# allwinner-rtc.c +allwinner_rtc_read(uint64_t addr, uint64_t value) "addr 0x%" PRIx64 " value 0x%" PRIx64 +allwinner_rtc_write(uint64_t addr, uint64_t value) "addr 0x%" PRIx64 " value 0x%" PRIx64 + # sun4v-rtc.c sun4v_rtc_read(uint64_t addr, uint64_t value) "read: addr 0x%" PRIx64 " value 0x%" PRIx64 sun4v_rtc_write(uint64_t addr, uint64_t value) "write: addr 0x%" PRIx64 " value 0x%" PRIx64 diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 9c1ecd423c..b81942e1e6 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -538,6 +538,30 @@ static bool is_virtio_scsi_device(IplParameterBlock *iplb) return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_SCSI); } +static void update_machine_ipl_properties(IplParameterBlock *iplb) +{ + Object *machine = qdev_get_machine(); + Error *err = NULL; + + /* Sync loadparm */ + if (iplb->flags & DIAG308_FLAGS_LP_VALID) { + uint8_t *ebcdic_loadparm = iplb->loadparm; + char ascii_loadparm[8]; + int i; + + for (i = 0; i < 8 && ebcdic_loadparm[i]; i++) { + ascii_loadparm[i] = ebcdic2ascii[(uint8_t) ebcdic_loadparm[i]]; + } + ascii_loadparm[i] = 0; + object_property_set_str(machine, ascii_loadparm, "loadparm", &err); + } else { + object_property_set_str(machine, "", "loadparm", &err); + } + if (err) { + warn_report_err(err); + } +} + void s390_ipl_update_diag308(IplParameterBlock *iplb) { S390IPLState *ipl = get_ipl_device(); @@ -545,6 +569,7 @@ void s390_ipl_update_diag308(IplParameterBlock *iplb) ipl->iplb = *iplb; ipl->iplb_valid = true; ipl->netboot = is_virtio_net_device(iplb); + update_machine_ipl_properties(iplb); } IplParameterBlock *s390_ipl_get_iplb(void) diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index d4813105db..3e44abe1c6 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -173,16 +173,16 @@ static inline bool iplb_valid_len(IplParameterBlock *iplb) return be32_to_cpu(iplb->len) <= sizeof(IplParameterBlock); } -static inline bool iplb_valid_ccw(IplParameterBlock *iplb) +static inline bool iplb_valid(IplParameterBlock *iplb) { - return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN && - iplb->pbt == S390_IPL_TYPE_CCW; -} - -static inline bool iplb_valid_fcp(IplParameterBlock *iplb) -{ - return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN && - iplb->pbt == S390_IPL_TYPE_FCP; + switch (iplb->pbt) { + case S390_IPL_TYPE_FCP: + return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN; + case S390_IPL_TYPE_CCW: + return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN; + default: + return false; + } } #endif diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 50cf95b781..64f928fc7d 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -193,7 +193,7 @@ typedef struct VirtioThinintInfo { typedef struct VirtioRevInfo { uint16_t revision; uint16_t length; - uint8_t data[0]; + uint8_t data[]; } QEMU_PACKED VirtioRevInfo; /* Specify where the virtqueues for the subchannel are in guest memory. */ diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index 7d584e7732..923488beb2 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -55,6 +55,8 @@ #define VSCSI_MAX_SECTORS 4096 #define VSCSI_REQ_LIMIT 24 +/* Maximum size of a IU payload */ +#define SRP_MAX_IU_DATA_LEN (SRP_MAX_IU_LEN - sizeof(union srp_iu)) #define SRP_RSP_SENSE_DATA_LEN 18 #define SRP_REPORT_LUNS_WLUN 0xc10100000000000ULL @@ -66,7 +68,7 @@ typedef union vscsi_crq { typedef struct vscsi_req { vscsi_crq crq; - union viosrp_iu iu; + uint8_t viosrp_iu_buf[SRP_MAX_IU_LEN]; /* SCSI request tracking */ SCSIRequest *sreq; @@ -97,6 +99,11 @@ typedef struct { vscsi_req reqs[VSCSI_REQ_LIMIT]; } VSCSIState; +static union viosrp_iu *req_iu(vscsi_req *req) +{ + return (union viosrp_iu *)req->viosrp_iu_buf; +} + static struct vscsi_req *vscsi_get_req(VSCSIState *s) { vscsi_req *req; @@ -121,7 +128,7 @@ static struct vscsi_req *vscsi_find_req(VSCSIState *s, uint64_t srp_tag) for (i = 0; i < VSCSI_REQ_LIMIT; i++) { req = &s->reqs[i]; - if (req->iu.srp.cmd.tag == srp_tag) { + if (req_iu(req)->srp.cmd.tag == srp_tag) { return req; } } @@ -176,9 +183,11 @@ static int vscsi_send_iu(VSCSIState *s, vscsi_req *req, { long rc, rc1; + assert(length <= SRP_MAX_IU_LEN); + /* First copy the SRP */ rc = spapr_vio_dma_write(&s->vdev, req->crq.s.IU_data_ptr, - &req->iu, length); + &req->viosrp_iu_buf, length); if (rc) { fprintf(stderr, "vscsi_send_iu: DMA write failure !\n"); } @@ -188,7 +197,7 @@ static int vscsi_send_iu(VSCSIState *s, vscsi_req *req, req->crq.s.reserved = 0x00; req->crq.s.timeout = cpu_to_be16(0x0000); req->crq.s.IU_length = cpu_to_be16(length); - req->crq.s.IU_data_ptr = req->iu.srp.rsp.tag; /* right byte order */ + req->crq.s.IU_data_ptr = req_iu(req)->srp.rsp.tag; /* right byte order */ if (rc == 0) { req->crq.s.status = VIOSRP_OK; @@ -224,7 +233,7 @@ static void vscsi_makeup_sense(VSCSIState *s, vscsi_req *req, static int vscsi_send_rsp(VSCSIState *s, vscsi_req *req, uint8_t status, int32_t res_in, int32_t res_out) { - union viosrp_iu *iu = &req->iu; + union viosrp_iu *iu = req_iu(req); uint64_t tag = iu->srp.rsp.tag; int total_len = sizeof(iu->srp.rsp); uint8_t sol_not = iu->srp.cmd.sol_not; @@ -261,10 +270,12 @@ static int vscsi_send_rsp(VSCSIState *s, vscsi_req *req, if (status) { iu->srp.rsp.sol_not = (sol_not & 0x04) >> 2; if (req->senselen) { - req->iu.srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; - req->iu.srp.rsp.sense_data_len = cpu_to_be32(req->senselen); - memcpy(req->iu.srp.rsp.data, req->sense, req->senselen); - total_len += req->senselen; + int sense_data_len = MIN(req->senselen, SRP_MAX_IU_DATA_LEN); + + iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID; + iu->srp.rsp.sense_data_len = cpu_to_be32(sense_data_len); + memcpy(iu->srp.rsp.data, req->sense, sense_data_len); + total_len += sense_data_len; } } else { iu->srp.rsp.sol_not = (sol_not & 0x02) >> 1; @@ -285,7 +296,7 @@ static int vscsi_fetch_desc(VSCSIState *s, struct vscsi_req *req, unsigned n, unsigned buf_offset, struct srp_direct_buf *ret) { - struct srp_cmd *cmd = &req->iu.srp.cmd; + struct srp_cmd *cmd = &req_iu(req)->srp.cmd; switch (req->dma_fmt) { case SRP_NO_DATA_DESC: { @@ -473,7 +484,7 @@ static int data_out_desc_size(struct srp_cmd *cmd) static int vscsi_preprocess_desc(vscsi_req *req) { - struct srp_cmd *cmd = &req->iu.srp.cmd; + struct srp_cmd *cmd = &req_iu(req)->srp.cmd; req->cdb_offset = cmd->add_cdb_len & ~3; @@ -597,7 +608,7 @@ static const VMStateDescription vmstate_spapr_vscsi_req = { .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_BUFFER(crq.raw, vscsi_req), - VMSTATE_BUFFER(iu.srp.reserved, vscsi_req), + VMSTATE_BUFFER(viosrp_iu_buf, vscsi_req), VMSTATE_UINT32(qtag, vscsi_req), VMSTATE_BOOL(active, vscsi_req), VMSTATE_UINT32(data_len, vscsi_req), @@ -655,7 +666,7 @@ static void *vscsi_load_request(QEMUFile *f, SCSIRequest *sreq) static void vscsi_process_login(VSCSIState *s, vscsi_req *req) { - union viosrp_iu *iu = &req->iu; + union viosrp_iu *iu = req_iu(req); struct srp_login_rsp *rsp = &iu->srp.login_rsp; uint64_t tag = iu->srp.rsp.tag; @@ -671,8 +682,8 @@ static void vscsi_process_login(VSCSIState *s, vscsi_req *req) */ rsp->req_lim_delta = cpu_to_be32(VSCSI_REQ_LIMIT-2); rsp->tag = tag; - rsp->max_it_iu_len = cpu_to_be32(sizeof(union srp_iu)); - rsp->max_ti_iu_len = cpu_to_be32(sizeof(union srp_iu)); + rsp->max_it_iu_len = cpu_to_be32(SRP_MAX_IU_LEN); + rsp->max_ti_iu_len = cpu_to_be32(SRP_MAX_IU_LEN); /* direct and indirect */ rsp->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT); @@ -681,7 +692,7 @@ static void vscsi_process_login(VSCSIState *s, vscsi_req *req) static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req) { - uint8_t *cdb = req->iu.srp.cmd.cdb; + uint8_t *cdb = req_iu(req)->srp.cmd.cdb; uint8_t resp_data[36]; int rc, len, alen; @@ -770,7 +781,7 @@ static void vscsi_report_luns(VSCSIState *s, vscsi_req *req) static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) { - union srp_iu *srp = &req->iu.srp; + union srp_iu *srp = &req_iu(req)->srp; SCSIDevice *sdev; int n, lun; @@ -821,17 +832,16 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) { - union viosrp_iu *iu = &req->iu; + union viosrp_iu *iu = req_iu(req); vscsi_req *tmpreq; int i, lun = 0, resp = SRP_TSK_MGMT_COMPLETE; SCSIDevice *d; uint64_t tag = iu->srp.rsp.tag; uint8_t sol_not = iu->srp.cmd.sol_not; - fprintf(stderr, "vscsi_process_tsk_mgmt %02x\n", - iu->srp.tsk_mgmt.tsk_mgmt_func); - - d = vscsi_device_find(&s->bus, be64_to_cpu(req->iu.srp.tsk_mgmt.lun), &lun); + trace_spapr_vscsi_process_tsk_mgmt(iu->srp.tsk_mgmt.tsk_mgmt_func); + d = vscsi_device_find(&s->bus, + be64_to_cpu(req_iu(req)->srp.tsk_mgmt.lun), &lun); if (!d) { resp = SRP_TSK_MGMT_FIELDS_INVALID; } else { @@ -842,7 +852,7 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) break; } - tmpreq = vscsi_find_req(s, req->iu.srp.tsk_mgmt.task_tag); + tmpreq = vscsi_find_req(s, req_iu(req)->srp.tsk_mgmt.task_tag); if (tmpreq && tmpreq->sreq) { assert(tmpreq->sreq->hba_private); scsi_req_cancel(tmpreq->sreq); @@ -867,7 +877,8 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) for (i = 0; i < VSCSI_REQ_LIMIT; i++) { tmpreq = &s->reqs[i]; - if (tmpreq->iu.srp.cmd.lun != req->iu.srp.tsk_mgmt.lun) { + if (req_iu(tmpreq)->srp.cmd.lun + != req_iu(req)->srp.tsk_mgmt.lun) { continue; } if (!tmpreq->active || !tmpreq->sreq) { @@ -889,6 +900,7 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) } /* Compose the response here as */ + QEMU_BUILD_BUG_ON(SRP_MAX_IU_DATA_LEN < 4); memset(iu, 0, sizeof(struct srp_rsp) + 4); iu->srp.rsp.opcode = SRP_RSP; iu->srp.rsp.req_lim_delta = cpu_to_be32(1); @@ -911,7 +923,7 @@ static int vscsi_process_tsk_mgmt(VSCSIState *s, vscsi_req *req) static int vscsi_handle_srp_req(VSCSIState *s, vscsi_req *req) { - union srp_iu *srp = &req->iu.srp; + union srp_iu *srp = &req_iu(req)->srp; int done = 1; uint8_t opcode = srp->rsp.opcode; @@ -948,7 +960,7 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req) struct mad_adapter_info_data info; int rc; - sinfo = &req->iu.mad.adapter_info; + sinfo = &req_iu(req)->mad.adapter_info; #if 0 /* What for ? */ rc = spapr_vio_dma_read(&s->vdev, be64_to_cpu(sinfo->buffer), @@ -984,7 +996,7 @@ static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req) uint64_t buffer; int rc; - vcap = &req->iu.mad.capabilities; + vcap = &req_iu(req)->mad.capabilities; req_len = len = be16_to_cpu(vcap->common.length); buffer = be64_to_cpu(vcap->buffer); if (len > sizeof(cap)) { @@ -1029,7 +1041,7 @@ static int vscsi_send_capabilities(VSCSIState *s, vscsi_req *req) static int vscsi_handle_mad_req(VSCSIState *s, vscsi_req *req) { - union mad_iu *mad = &req->iu.mad; + union mad_iu *mad = &req_iu(req)->mad; bool request_handled = false; uint64_t retlen = 0; @@ -1088,7 +1100,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) * in our 256 bytes IUs. If not we'll have to increase the size * of the structure. */ - if (crq->s.IU_length > sizeof(union viosrp_iu)) { + if (crq->s.IU_length > SRP_MAX_IU_LEN) { fprintf(stderr, "VSCSI: SRP IU too long (%d bytes) !\n", crq->s.IU_length); vscsi_put_req(req); @@ -1096,7 +1108,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) } /* XXX Handle failure differently ? */ - if (spapr_vio_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu, + if (spapr_vio_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->viosrp_iu_buf, crq->s.IU_length)) { fprintf(stderr, "vscsi_got_payload: DMA read failure !\n"); vscsi_put_req(req); diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events index b0820052f8..9a4a60ca63 100644 --- a/hw/scsi/trace-events +++ b/hw/scsi/trace-events @@ -227,6 +227,7 @@ spapr_vscsi_command_complete_status(uint32_t status) "Command complete err=%"PRI spapr_vscsi_save_request(uint32_t qtag, unsigned desc, unsigned offset) "saving tag=%"PRIu32", current desc#%u, offset=0x%x" spapr_vscsi_load_request(uint32_t qtag, unsigned desc, unsigned offset) "restoring tag=%"PRIu32", current desc#%u, offset=0x%x" spapr_vscsi_process_login(void) "Got login, sending response !" +spapr_vscsi_process_tsk_mgmt(uint8_t func) "tsk_mgmt_func 0x%02x" spapr_vscsi_queue_cmd_no_drive(uint64_t lun) "Command for lun 0x%08" PRIx64 " with no drive" spapr_vscsi_queue_cmd(uint32_t qtag, unsigned cdb, const char *cmd, int lun, int ret) "Queued command tag 0x%"PRIx32" CMD 0x%x=%s LUN %d ret: %d" spapr_vscsi_do_crq(unsigned c0, unsigned c1) "crq: %02x %02x ..." diff --git a/hw/scsi/viosrp.h b/hw/scsi/viosrp.h index d8e365db1e..e5f9768e8f 100644 --- a/hw/scsi/viosrp.h +++ b/hw/scsi/viosrp.h @@ -34,6 +34,8 @@ #ifndef PPC_VIOSRP_H #define PPC_VIOSRP_H +#include "hw/scsi/srp.h" + #define SRP_VERSION "16.a" #define SRP_MAX_IU_LEN 256 #define SRP_MAX_LOC_LEN 32 @@ -47,7 +49,6 @@ union srp_iu { struct srp_tsk_mgmt tsk_mgmt; struct srp_cmd cmd; struct srp_rsp rsp; - uint8_t reserved[SRP_MAX_IU_LEN]; }; enum viosrp_crq_formats { diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs index e371281ac4..0d1df1721c 100644 --- a/hw/sd/Makefile.objs +++ b/hw/sd/Makefile.objs @@ -4,6 +4,7 @@ common-obj-$(CONFIG_SD) += sd.o core.o sdmmc-internal.o common-obj-$(CONFIG_SDHCI) += sdhci.o common-obj-$(CONFIG_SDHCI_PCI) += sdhci-pci.o +common-obj-$(CONFIG_ALLWINNER_H3) += allwinner-sdhost.o common-obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o common-obj-$(CONFIG_OMAP) += omap_mmc.o common-obj-$(CONFIG_PXA2XX) += pxa2xx_mmci.o diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c new file mode 100644 index 0000000000..f404e1fdb4 --- /dev/null +++ b/hw/sd/allwinner-sdhost.c @@ -0,0 +1,854 @@ +/* + * Allwinner (sun4i and above) SD Host Controller emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "sysemu/blockdev.h" +#include "hw/irq.h" +#include "hw/sd/allwinner-sdhost.h" +#include "migration/vmstate.h" +#include "trace.h" + +#define TYPE_AW_SDHOST_BUS "allwinner-sdhost-bus" +#define AW_SDHOST_BUS(obj) \ + OBJECT_CHECK(SDBus, (obj), TYPE_AW_SDHOST_BUS) + +/* SD Host register offsets */ +enum { + REG_SD_GCTL = 0x00, /* Global Control */ + REG_SD_CKCR = 0x04, /* Clock Control */ + REG_SD_TMOR = 0x08, /* Timeout */ + REG_SD_BWDR = 0x0C, /* Bus Width */ + REG_SD_BKSR = 0x10, /* Block Size */ + REG_SD_BYCR = 0x14, /* Byte Count */ + REG_SD_CMDR = 0x18, /* Command */ + REG_SD_CAGR = 0x1C, /* Command Argument */ + REG_SD_RESP0 = 0x20, /* Response Zero */ + REG_SD_RESP1 = 0x24, /* Response One */ + REG_SD_RESP2 = 0x28, /* Response Two */ + REG_SD_RESP3 = 0x2C, /* Response Three */ + REG_SD_IMKR = 0x30, /* Interrupt Mask */ + REG_SD_MISR = 0x34, /* Masked Interrupt Status */ + REG_SD_RISR = 0x38, /* Raw Interrupt Status */ + REG_SD_STAR = 0x3C, /* Status */ + REG_SD_FWLR = 0x40, /* FIFO Water Level */ + REG_SD_FUNS = 0x44, /* FIFO Function Select */ + REG_SD_DBGC = 0x50, /* Debug Enable */ + REG_SD_A12A = 0x58, /* Auto command 12 argument */ + REG_SD_NTSR = 0x5C, /* SD NewTiming Set */ + REG_SD_SDBG = 0x60, /* SD newTiming Set Debug */ + REG_SD_HWRST = 0x78, /* Hardware Reset Register */ + REG_SD_DMAC = 0x80, /* Internal DMA Controller Control */ + REG_SD_DLBA = 0x84, /* Descriptor List Base Address */ + REG_SD_IDST = 0x88, /* Internal DMA Controller Status */ + REG_SD_IDIE = 0x8C, /* Internal DMA Controller IRQ Enable */ + REG_SD_THLDC = 0x100, /* Card Threshold Control */ + REG_SD_DSBD = 0x10C, /* eMMC DDR Start Bit Detection Control */ + REG_SD_RES_CRC = 0x110, /* Response CRC from card/eMMC */ + REG_SD_DATA7_CRC = 0x114, /* CRC Data 7 from card/eMMC */ + REG_SD_DATA6_CRC = 0x118, /* CRC Data 6 from card/eMMC */ + REG_SD_DATA5_CRC = 0x11C, /* CRC Data 5 from card/eMMC */ + REG_SD_DATA4_CRC = 0x120, /* CRC Data 4 from card/eMMC */ + REG_SD_DATA3_CRC = 0x124, /* CRC Data 3 from card/eMMC */ + REG_SD_DATA2_CRC = 0x128, /* CRC Data 2 from card/eMMC */ + REG_SD_DATA1_CRC = 0x12C, /* CRC Data 1 from card/eMMC */ + REG_SD_DATA0_CRC = 0x130, /* CRC Data 0 from card/eMMC */ + REG_SD_CRC_STA = 0x134, /* CRC status from card/eMMC during write */ + REG_SD_FIFO = 0x200, /* Read/Write FIFO */ +}; + +/* SD Host register flags */ +enum { + SD_GCTL_FIFO_AC_MOD = (1 << 31), + SD_GCTL_DDR_MOD_SEL = (1 << 10), + SD_GCTL_CD_DBC_ENB = (1 << 8), + SD_GCTL_DMA_ENB = (1 << 5), + SD_GCTL_INT_ENB = (1 << 4), + SD_GCTL_DMA_RST = (1 << 2), + SD_GCTL_FIFO_RST = (1 << 1), + SD_GCTL_SOFT_RST = (1 << 0), +}; + +enum { + SD_CMDR_LOAD = (1 << 31), + SD_CMDR_CLKCHANGE = (1 << 21), + SD_CMDR_WRITE = (1 << 10), + SD_CMDR_AUTOSTOP = (1 << 12), + SD_CMDR_DATA = (1 << 9), + SD_CMDR_RESPONSE_LONG = (1 << 7), + SD_CMDR_RESPONSE = (1 << 6), + SD_CMDR_CMDID_MASK = (0x3f), +}; + +enum { + SD_RISR_CARD_REMOVE = (1 << 31), + SD_RISR_CARD_INSERT = (1 << 30), + SD_RISR_SDIO_INTR = (1 << 16), + SD_RISR_AUTOCMD_DONE = (1 << 14), + SD_RISR_DATA_COMPLETE = (1 << 3), + SD_RISR_CMD_COMPLETE = (1 << 2), + SD_RISR_NO_RESPONSE = (1 << 1), +}; + +enum { + SD_STAR_CARD_PRESENT = (1 << 8), +}; + +enum { + SD_IDST_INT_SUMMARY = (1 << 8), + SD_IDST_RECEIVE_IRQ = (1 << 1), + SD_IDST_TRANSMIT_IRQ = (1 << 0), + SD_IDST_IRQ_MASK = (1 << 1) | (1 << 0) | (1 << 8), + SD_IDST_WR_MASK = (0x3ff), +}; + +/* SD Host register reset values */ +enum { + REG_SD_GCTL_RST = 0x00000300, + REG_SD_CKCR_RST = 0x0, + REG_SD_TMOR_RST = 0xFFFFFF40, + REG_SD_BWDR_RST = 0x0, + REG_SD_BKSR_RST = 0x00000200, + REG_SD_BYCR_RST = 0x00000200, + REG_SD_CMDR_RST = 0x0, + REG_SD_CAGR_RST = 0x0, + REG_SD_RESP_RST = 0x0, + REG_SD_IMKR_RST = 0x0, + REG_SD_MISR_RST = 0x0, + REG_SD_RISR_RST = 0x0, + REG_SD_STAR_RST = 0x00000100, + REG_SD_FWLR_RST = 0x000F0000, + REG_SD_FUNS_RST = 0x0, + REG_SD_DBGC_RST = 0x0, + REG_SD_A12A_RST = 0x0000FFFF, + REG_SD_NTSR_RST = 0x00000001, + REG_SD_SDBG_RST = 0x0, + REG_SD_HWRST_RST = 0x00000001, + REG_SD_DMAC_RST = 0x0, + REG_SD_DLBA_RST = 0x0, + REG_SD_IDST_RST = 0x0, + REG_SD_IDIE_RST = 0x0, + REG_SD_THLDC_RST = 0x0, + REG_SD_DSBD_RST = 0x0, + REG_SD_RES_CRC_RST = 0x0, + REG_SD_DATA_CRC_RST = 0x0, + REG_SD_CRC_STA_RST = 0x0, + REG_SD_FIFO_RST = 0x0, +}; + +/* Data transfer descriptor for DMA */ +typedef struct TransferDescriptor { + uint32_t status; /* Status flags */ + uint32_t size; /* Data buffer size */ + uint32_t addr; /* Data buffer address */ + uint32_t next; /* Physical address of next descriptor */ +} TransferDescriptor; + +/* Data transfer descriptor flags */ +enum { + DESC_STATUS_HOLD = (1 << 31), /* Set when descriptor is in use by DMA */ + DESC_STATUS_ERROR = (1 << 30), /* Set when DMA transfer error occurred */ + DESC_STATUS_CHAIN = (1 << 4), /* Indicates chained descriptor. */ + DESC_STATUS_FIRST = (1 << 3), /* Set on the first descriptor */ + DESC_STATUS_LAST = (1 << 2), /* Set on the last descriptor */ + DESC_STATUS_NOIRQ = (1 << 1), /* Skip raising interrupt after transfer */ + DESC_SIZE_MASK = (0xfffffffc) +}; + +static void allwinner_sdhost_update_irq(AwSdHostState *s) +{ + uint32_t irq; + + if (s->global_ctl & SD_GCTL_INT_ENB) { + irq = s->irq_status & s->irq_mask; + } else { + irq = 0; + } + + trace_allwinner_sdhost_update_irq(irq); + qemu_set_irq(s->irq, irq); +} + +static void allwinner_sdhost_update_transfer_cnt(AwSdHostState *s, + uint32_t bytes) +{ + if (s->transfer_cnt > bytes) { + s->transfer_cnt -= bytes; + } else { + s->transfer_cnt = 0; + } + + if (!s->transfer_cnt) { + s->irq_status |= SD_RISR_DATA_COMPLETE; + } +} + +static void allwinner_sdhost_set_inserted(DeviceState *dev, bool inserted) +{ + AwSdHostState *s = AW_SDHOST(dev); + + trace_allwinner_sdhost_set_inserted(inserted); + + if (inserted) { + s->irq_status |= SD_RISR_CARD_INSERT; + s->irq_status &= ~SD_RISR_CARD_REMOVE; + s->status |= SD_STAR_CARD_PRESENT; + } else { + s->irq_status &= ~SD_RISR_CARD_INSERT; + s->irq_status |= SD_RISR_CARD_REMOVE; + s->status &= ~SD_STAR_CARD_PRESENT; + } + + allwinner_sdhost_update_irq(s); +} + +static void allwinner_sdhost_send_command(AwSdHostState *s) +{ + SDRequest request; + uint8_t resp[16]; + int rlen; + + /* Auto clear load flag */ + s->command &= ~SD_CMDR_LOAD; + + /* Clock change does not actually interact with the SD bus */ + if (!(s->command & SD_CMDR_CLKCHANGE)) { + + /* Prepare request */ + request.cmd = s->command & SD_CMDR_CMDID_MASK; + request.arg = s->command_arg; + + /* Send request to SD bus */ + rlen = sdbus_do_command(&s->sdbus, &request, resp); + if (rlen < 0) { + goto error; + } + + /* If the command has a response, store it in the response registers */ + if ((s->command & SD_CMDR_RESPONSE)) { + if (rlen == 4 && !(s->command & SD_CMDR_RESPONSE_LONG)) { + s->response[0] = ldl_be_p(&resp[0]); + s->response[1] = s->response[2] = s->response[3] = 0; + + } else if (rlen == 16 && (s->command & SD_CMDR_RESPONSE_LONG)) { + s->response[0] = ldl_be_p(&resp[12]); + s->response[1] = ldl_be_p(&resp[8]); + s->response[2] = ldl_be_p(&resp[4]); + s->response[3] = ldl_be_p(&resp[0]); + } else { + goto error; + } + } + } + + /* Set interrupt status bits */ + s->irq_status |= SD_RISR_CMD_COMPLETE; + return; + +error: + s->irq_status |= SD_RISR_NO_RESPONSE; +} + +static void allwinner_sdhost_auto_stop(AwSdHostState *s) +{ + /* + * The stop command (CMD12) ensures the SD bus + * returns to the transfer state. + */ + if ((s->command & SD_CMDR_AUTOSTOP) && (s->transfer_cnt == 0)) { + /* First save current command registers */ + uint32_t saved_cmd = s->command; + uint32_t saved_arg = s->command_arg; + + /* Prepare stop command (CMD12) */ + s->command &= ~SD_CMDR_CMDID_MASK; + s->command |= 12; /* CMD12 */ + s->command_arg = 0; + + /* Put the command on SD bus */ + allwinner_sdhost_send_command(s); + + /* Restore command values */ + s->command = saved_cmd; + s->command_arg = saved_arg; + + /* Set IRQ status bit for automatic stop done */ + s->irq_status |= SD_RISR_AUTOCMD_DONE; + } +} + +static uint32_t allwinner_sdhost_process_desc(AwSdHostState *s, + hwaddr desc_addr, + TransferDescriptor *desc, + bool is_write, uint32_t max_bytes) +{ + AwSdHostClass *klass = AW_SDHOST_GET_CLASS(s); + uint32_t num_done = 0; + uint32_t num_bytes = max_bytes; + uint8_t buf[1024]; + + /* Read descriptor */ + cpu_physical_memory_read(desc_addr, desc, sizeof(*desc)); + if (desc->size == 0) { + desc->size = klass->max_desc_size; + } else if (desc->size > klass->max_desc_size) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA descriptor buffer size " + " is out-of-bounds: %" PRIu32 " > %zu", + __func__, desc->size, klass->max_desc_size); + desc->size = klass->max_desc_size; + } + if (desc->size < num_bytes) { + num_bytes = desc->size; + } + + trace_allwinner_sdhost_process_desc(desc_addr, desc->size, + is_write, max_bytes); + + while (num_done < num_bytes) { + /* Try to completely fill the local buffer */ + uint32_t buf_bytes = num_bytes - num_done; + if (buf_bytes > sizeof(buf)) { + buf_bytes = sizeof(buf); + } + + /* Write to SD bus */ + if (is_write) { + cpu_physical_memory_read((desc->addr & DESC_SIZE_MASK) + num_done, + buf, buf_bytes); + + for (uint32_t i = 0; i < buf_bytes; i++) { + sdbus_write_data(&s->sdbus, buf[i]); + } + + /* Read from SD bus */ + } else { + for (uint32_t i = 0; i < buf_bytes; i++) { + buf[i] = sdbus_read_data(&s->sdbus); + } + cpu_physical_memory_write((desc->addr & DESC_SIZE_MASK) + num_done, + buf, buf_bytes); + } + num_done += buf_bytes; + } + + /* Clear hold flag and flush descriptor */ + desc->status &= ~DESC_STATUS_HOLD; + cpu_physical_memory_write(desc_addr, desc, sizeof(*desc)); + + return num_done; +} + +static void allwinner_sdhost_dma(AwSdHostState *s) +{ + TransferDescriptor desc; + hwaddr desc_addr = s->desc_base; + bool is_write = (s->command & SD_CMDR_WRITE); + uint32_t bytes_done = 0; + + /* Check if DMA can be performed */ + if (s->byte_count == 0 || s->block_size == 0 || + !(s->global_ctl & SD_GCTL_DMA_ENB)) { + return; + } + + /* + * For read operations, data must be available on the SD bus + * If not, it is an error and we should not act at all + */ + if (!is_write && !sdbus_data_ready(&s->sdbus)) { + return; + } + + /* Process the DMA descriptors until all data is copied */ + while (s->byte_count > 0) { + bytes_done = allwinner_sdhost_process_desc(s, desc_addr, &desc, + is_write, s->byte_count); + allwinner_sdhost_update_transfer_cnt(s, bytes_done); + + if (bytes_done <= s->byte_count) { + s->byte_count -= bytes_done; + } else { + s->byte_count = 0; + } + + if (desc.status & DESC_STATUS_LAST) { + break; + } else { + desc_addr = desc.next; + } + } + + /* Raise IRQ to signal DMA is completed */ + s->irq_status |= SD_RISR_DATA_COMPLETE | SD_RISR_SDIO_INTR; + + /* Update DMAC bits */ + s->dmac_status |= SD_IDST_INT_SUMMARY; + + if (is_write) { + s->dmac_status |= SD_IDST_TRANSMIT_IRQ; + } else { + s->dmac_status |= SD_IDST_RECEIVE_IRQ; + } +} + +static uint64_t allwinner_sdhost_read(void *opaque, hwaddr offset, + unsigned size) +{ + AwSdHostState *s = AW_SDHOST(opaque); + uint32_t res = 0; + + switch (offset) { + case REG_SD_GCTL: /* Global Control */ + res = s->global_ctl; + break; + case REG_SD_CKCR: /* Clock Control */ + res = s->clock_ctl; + break; + case REG_SD_TMOR: /* Timeout */ + res = s->timeout; + break; + case REG_SD_BWDR: /* Bus Width */ + res = s->bus_width; + break; + case REG_SD_BKSR: /* Block Size */ + res = s->block_size; + break; + case REG_SD_BYCR: /* Byte Count */ + res = s->byte_count; + break; + case REG_SD_CMDR: /* Command */ + res = s->command; + break; + case REG_SD_CAGR: /* Command Argument */ + res = s->command_arg; + break; + case REG_SD_RESP0: /* Response Zero */ + res = s->response[0]; + break; + case REG_SD_RESP1: /* Response One */ + res = s->response[1]; + break; + case REG_SD_RESP2: /* Response Two */ + res = s->response[2]; + break; + case REG_SD_RESP3: /* Response Three */ + res = s->response[3]; + break; + case REG_SD_IMKR: /* Interrupt Mask */ + res = s->irq_mask; + break; + case REG_SD_MISR: /* Masked Interrupt Status */ + res = s->irq_status & s->irq_mask; + break; + case REG_SD_RISR: /* Raw Interrupt Status */ + res = s->irq_status; + break; + case REG_SD_STAR: /* Status */ + res = s->status; + break; + case REG_SD_FWLR: /* FIFO Water Level */ + res = s->fifo_wlevel; + break; + case REG_SD_FUNS: /* FIFO Function Select */ + res = s->fifo_func_sel; + break; + case REG_SD_DBGC: /* Debug Enable */ + res = s->debug_enable; + break; + case REG_SD_A12A: /* Auto command 12 argument */ + res = s->auto12_arg; + break; + case REG_SD_NTSR: /* SD NewTiming Set */ + res = s->newtiming_set; + break; + case REG_SD_SDBG: /* SD newTiming Set Debug */ + res = s->newtiming_debug; + break; + case REG_SD_HWRST: /* Hardware Reset Register */ + res = s->hardware_rst; + break; + case REG_SD_DMAC: /* Internal DMA Controller Control */ + res = s->dmac; + break; + case REG_SD_DLBA: /* Descriptor List Base Address */ + res = s->desc_base; + break; + case REG_SD_IDST: /* Internal DMA Controller Status */ + res = s->dmac_status; + break; + case REG_SD_IDIE: /* Internal DMA Controller Interrupt Enable */ + res = s->dmac_irq; + break; + case REG_SD_THLDC: /* Card Threshold Control */ + res = s->card_threshold; + break; + case REG_SD_DSBD: /* eMMC DDR Start Bit Detection Control */ + res = s->startbit_detect; + break; + case REG_SD_RES_CRC: /* Response CRC from card/eMMC */ + res = s->response_crc; + break; + case REG_SD_DATA7_CRC: /* CRC Data 7 from card/eMMC */ + case REG_SD_DATA6_CRC: /* CRC Data 6 from card/eMMC */ + case REG_SD_DATA5_CRC: /* CRC Data 5 from card/eMMC */ + case REG_SD_DATA4_CRC: /* CRC Data 4 from card/eMMC */ + case REG_SD_DATA3_CRC: /* CRC Data 3 from card/eMMC */ + case REG_SD_DATA2_CRC: /* CRC Data 2 from card/eMMC */ + case REG_SD_DATA1_CRC: /* CRC Data 1 from card/eMMC */ + case REG_SD_DATA0_CRC: /* CRC Data 0 from card/eMMC */ + res = s->data_crc[((offset - REG_SD_DATA7_CRC) / sizeof(uint32_t))]; + break; + case REG_SD_CRC_STA: /* CRC status from card/eMMC in write operation */ + res = s->status_crc; + break; + case REG_SD_FIFO: /* Read/Write FIFO */ + if (sdbus_data_ready(&s->sdbus)) { + res = sdbus_read_data(&s->sdbus); + res |= sdbus_read_data(&s->sdbus) << 8; + res |= sdbus_read_data(&s->sdbus) << 16; + res |= sdbus_read_data(&s->sdbus) << 24; + allwinner_sdhost_update_transfer_cnt(s, sizeof(uint32_t)); + allwinner_sdhost_auto_stop(s); + allwinner_sdhost_update_irq(s); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: no data ready on SD bus\n", + __func__); + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" + HWADDR_PRIx"\n", __func__, offset); + res = 0; + break; + } + + trace_allwinner_sdhost_read(offset, res, size); + return res; +} + +static void allwinner_sdhost_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + AwSdHostState *s = AW_SDHOST(opaque); + + trace_allwinner_sdhost_write(offset, value, size); + + switch (offset) { + case REG_SD_GCTL: /* Global Control */ + s->global_ctl = value; + s->global_ctl &= ~(SD_GCTL_DMA_RST | SD_GCTL_FIFO_RST | + SD_GCTL_SOFT_RST); + allwinner_sdhost_update_irq(s); + break; + case REG_SD_CKCR: /* Clock Control */ + s->clock_ctl = value; + break; + case REG_SD_TMOR: /* Timeout */ + s->timeout = value; + break; + case REG_SD_BWDR: /* Bus Width */ + s->bus_width = value; + break; + case REG_SD_BKSR: /* Block Size */ + s->block_size = value; + break; + case REG_SD_BYCR: /* Byte Count */ + s->byte_count = value; + s->transfer_cnt = value; + break; + case REG_SD_CMDR: /* Command */ + s->command = value; + if (value & SD_CMDR_LOAD) { + allwinner_sdhost_send_command(s); + allwinner_sdhost_dma(s); + allwinner_sdhost_auto_stop(s); + } + allwinner_sdhost_update_irq(s); + break; + case REG_SD_CAGR: /* Command Argument */ + s->command_arg = value; + break; + case REG_SD_RESP0: /* Response Zero */ + s->response[0] = value; + break; + case REG_SD_RESP1: /* Response One */ + s->response[1] = value; + break; + case REG_SD_RESP2: /* Response Two */ + s->response[2] = value; + break; + case REG_SD_RESP3: /* Response Three */ + s->response[3] = value; + break; + case REG_SD_IMKR: /* Interrupt Mask */ + s->irq_mask = value; + allwinner_sdhost_update_irq(s); + break; + case REG_SD_MISR: /* Masked Interrupt Status */ + case REG_SD_RISR: /* Raw Interrupt Status */ + s->irq_status &= ~value; + allwinner_sdhost_update_irq(s); + break; + case REG_SD_STAR: /* Status */ + s->status &= ~value; + allwinner_sdhost_update_irq(s); + break; + case REG_SD_FWLR: /* FIFO Water Level */ + s->fifo_wlevel = value; + break; + case REG_SD_FUNS: /* FIFO Function Select */ + s->fifo_func_sel = value; + break; + case REG_SD_DBGC: /* Debug Enable */ + s->debug_enable = value; + break; + case REG_SD_A12A: /* Auto command 12 argument */ + s->auto12_arg = value; + break; + case REG_SD_NTSR: /* SD NewTiming Set */ + s->newtiming_set = value; + break; + case REG_SD_SDBG: /* SD newTiming Set Debug */ + s->newtiming_debug = value; + break; + case REG_SD_HWRST: /* Hardware Reset Register */ + s->hardware_rst = value; + break; + case REG_SD_DMAC: /* Internal DMA Controller Control */ + s->dmac = value; + allwinner_sdhost_update_irq(s); + break; + case REG_SD_DLBA: /* Descriptor List Base Address */ + s->desc_base = value; + break; + case REG_SD_IDST: /* Internal DMA Controller Status */ + s->dmac_status &= (~SD_IDST_WR_MASK) | (~value & SD_IDST_WR_MASK); + allwinner_sdhost_update_irq(s); + break; + case REG_SD_IDIE: /* Internal DMA Controller Interrupt Enable */ + s->dmac_irq = value; + allwinner_sdhost_update_irq(s); + break; + case REG_SD_THLDC: /* Card Threshold Control */ + s->card_threshold = value; + break; + case REG_SD_DSBD: /* eMMC DDR Start Bit Detection Control */ + s->startbit_detect = value; + break; + case REG_SD_FIFO: /* Read/Write FIFO */ + sdbus_write_data(&s->sdbus, value & 0xff); + sdbus_write_data(&s->sdbus, (value >> 8) & 0xff); + sdbus_write_data(&s->sdbus, (value >> 16) & 0xff); + sdbus_write_data(&s->sdbus, (value >> 24) & 0xff); + allwinner_sdhost_update_transfer_cnt(s, sizeof(uint32_t)); + allwinner_sdhost_auto_stop(s); + allwinner_sdhost_update_irq(s); + break; + case REG_SD_RES_CRC: /* Response CRC from card/eMMC */ + case REG_SD_DATA7_CRC: /* CRC Data 7 from card/eMMC */ + case REG_SD_DATA6_CRC: /* CRC Data 6 from card/eMMC */ + case REG_SD_DATA5_CRC: /* CRC Data 5 from card/eMMC */ + case REG_SD_DATA4_CRC: /* CRC Data 4 from card/eMMC */ + case REG_SD_DATA3_CRC: /* CRC Data 3 from card/eMMC */ + case REG_SD_DATA2_CRC: /* CRC Data 2 from card/eMMC */ + case REG_SD_DATA1_CRC: /* CRC Data 1 from card/eMMC */ + case REG_SD_DATA0_CRC: /* CRC Data 0 from card/eMMC */ + case REG_SD_CRC_STA: /* CRC status from card/eMMC in write operation */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset %" + HWADDR_PRIx"\n", __func__, offset); + break; + } +} + +static const MemoryRegionOps allwinner_sdhost_ops = { + .read = allwinner_sdhost_read, + .write = allwinner_sdhost_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl.min_access_size = 4, +}; + +static const VMStateDescription vmstate_allwinner_sdhost = { + .name = "allwinner-sdhost", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(global_ctl, AwSdHostState), + VMSTATE_UINT32(clock_ctl, AwSdHostState), + VMSTATE_UINT32(timeout, AwSdHostState), + VMSTATE_UINT32(bus_width, AwSdHostState), + VMSTATE_UINT32(block_size, AwSdHostState), + VMSTATE_UINT32(byte_count, AwSdHostState), + VMSTATE_UINT32(transfer_cnt, AwSdHostState), + VMSTATE_UINT32(command, AwSdHostState), + VMSTATE_UINT32(command_arg, AwSdHostState), + VMSTATE_UINT32_ARRAY(response, AwSdHostState, 4), + VMSTATE_UINT32(irq_mask, AwSdHostState), + VMSTATE_UINT32(irq_status, AwSdHostState), + VMSTATE_UINT32(status, AwSdHostState), + VMSTATE_UINT32(fifo_wlevel, AwSdHostState), + VMSTATE_UINT32(fifo_func_sel, AwSdHostState), + VMSTATE_UINT32(debug_enable, AwSdHostState), + VMSTATE_UINT32(auto12_arg, AwSdHostState), + VMSTATE_UINT32(newtiming_set, AwSdHostState), + VMSTATE_UINT32(newtiming_debug, AwSdHostState), + VMSTATE_UINT32(hardware_rst, AwSdHostState), + VMSTATE_UINT32(dmac, AwSdHostState), + VMSTATE_UINT32(desc_base, AwSdHostState), + VMSTATE_UINT32(dmac_status, AwSdHostState), + VMSTATE_UINT32(dmac_irq, AwSdHostState), + VMSTATE_UINT32(card_threshold, AwSdHostState), + VMSTATE_UINT32(startbit_detect, AwSdHostState), + VMSTATE_UINT32(response_crc, AwSdHostState), + VMSTATE_UINT32_ARRAY(data_crc, AwSdHostState, 8), + VMSTATE_UINT32(status_crc, AwSdHostState), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_sdhost_init(Object *obj) +{ + AwSdHostState *s = AW_SDHOST(obj); + + qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), + TYPE_AW_SDHOST_BUS, DEVICE(s), "sd-bus"); + + memory_region_init_io(&s->iomem, obj, &allwinner_sdhost_ops, s, + TYPE_AW_SDHOST, 4 * KiB); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); +} + +static void allwinner_sdhost_reset(DeviceState *dev) +{ + AwSdHostState *s = AW_SDHOST(dev); + + s->global_ctl = REG_SD_GCTL_RST; + s->clock_ctl = REG_SD_CKCR_RST; + s->timeout = REG_SD_TMOR_RST; + s->bus_width = REG_SD_BWDR_RST; + s->block_size = REG_SD_BKSR_RST; + s->byte_count = REG_SD_BYCR_RST; + s->transfer_cnt = 0; + + s->command = REG_SD_CMDR_RST; + s->command_arg = REG_SD_CAGR_RST; + + for (int i = 0; i < ARRAY_SIZE(s->response); i++) { + s->response[i] = REG_SD_RESP_RST; + } + + s->irq_mask = REG_SD_IMKR_RST; + s->irq_status = REG_SD_RISR_RST; + s->status = REG_SD_STAR_RST; + + s->fifo_wlevel = REG_SD_FWLR_RST; + s->fifo_func_sel = REG_SD_FUNS_RST; + s->debug_enable = REG_SD_DBGC_RST; + s->auto12_arg = REG_SD_A12A_RST; + s->newtiming_set = REG_SD_NTSR_RST; + s->newtiming_debug = REG_SD_SDBG_RST; + s->hardware_rst = REG_SD_HWRST_RST; + s->dmac = REG_SD_DMAC_RST; + s->desc_base = REG_SD_DLBA_RST; + s->dmac_status = REG_SD_IDST_RST; + s->dmac_irq = REG_SD_IDIE_RST; + s->card_threshold = REG_SD_THLDC_RST; + s->startbit_detect = REG_SD_DSBD_RST; + s->response_crc = REG_SD_RES_CRC_RST; + + for (int i = 0; i < ARRAY_SIZE(s->data_crc); i++) { + s->data_crc[i] = REG_SD_DATA_CRC_RST; + } + + s->status_crc = REG_SD_CRC_STA_RST; +} + +static void allwinner_sdhost_bus_class_init(ObjectClass *klass, void *data) +{ + SDBusClass *sbc = SD_BUS_CLASS(klass); + + sbc->set_inserted = allwinner_sdhost_set_inserted; +} + +static void allwinner_sdhost_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = allwinner_sdhost_reset; + dc->vmsd = &vmstate_allwinner_sdhost; +} + +static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, void *data) +{ + AwSdHostClass *sc = AW_SDHOST_CLASS(klass); + sc->max_desc_size = 8 * KiB; +} + +static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data) +{ + AwSdHostClass *sc = AW_SDHOST_CLASS(klass); + sc->max_desc_size = 64 * KiB; +} + +static TypeInfo allwinner_sdhost_info = { + .name = TYPE_AW_SDHOST, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = allwinner_sdhost_init, + .instance_size = sizeof(AwSdHostState), + .class_init = allwinner_sdhost_class_init, + .class_size = sizeof(AwSdHostClass), + .abstract = true, +}; + +static const TypeInfo allwinner_sdhost_sun4i_info = { + .name = TYPE_AW_SDHOST_SUN4I, + .parent = TYPE_AW_SDHOST, + .class_init = allwinner_sdhost_sun4i_class_init, +}; + +static const TypeInfo allwinner_sdhost_sun5i_info = { + .name = TYPE_AW_SDHOST_SUN5I, + .parent = TYPE_AW_SDHOST, + .class_init = allwinner_sdhost_sun5i_class_init, +}; + +static const TypeInfo allwinner_sdhost_bus_info = { + .name = TYPE_AW_SDHOST_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + .class_init = allwinner_sdhost_bus_class_init, +}; + +static void allwinner_sdhost_register_types(void) +{ + type_register_static(&allwinner_sdhost_info); + type_register_static(&allwinner_sdhost_sun4i_info); + type_register_static(&allwinner_sdhost_sun5i_info); + type_register_static(&allwinner_sdhost_bus_info); +} + +type_init(allwinner_sdhost_register_types) diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 91db069212..829797b597 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -255,13 +255,25 @@ static void ssi_sd_realize(SSISlave *d, Error **errp) carddev = qdev_create(BUS(&s->sdbus), TYPE_SD_CARD); if (dinfo) { qdev_prop_set_drive(carddev, "drive", blk_by_legacy_dinfo(dinfo), &err); + if (err) { + goto fail; + } } + object_property_set_bool(OBJECT(carddev), true, "spi", &err); + if (err) { + goto fail; + } + object_property_set_bool(OBJECT(carddev), true, "realized", &err); if (err) { - error_setg(errp, "failed to init SD card: %s", error_get_pretty(err)); - return; + goto fail; } + + return; + +fail: + error_propagate_prepend(errp, err, "failed to init SD card: "); } static void ssi_sd_reset(DeviceState *dev) diff --git a/hw/sd/trace-events b/hw/sd/trace-events index efcff666a2..5f09d32eb2 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -1,5 +1,12 @@ # See docs/devel/tracing.txt for syntax documentation. +# allwinner-sdhost.c +allwinner_sdhost_set_inserted(bool inserted) "inserted %u" +allwinner_sdhost_process_desc(uint64_t desc_addr, uint32_t desc_size, bool is_write, uint32_t max_bytes) "desc_addr 0x%" PRIx64 " desc_size %" PRIu32 " is_write %u max_bytes %" PRIu32 +allwinner_sdhost_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_sdhost_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32 +allwinner_sdhost_update_irq(uint32_t irq) "IRQ bits 0x%" PRIx32 + # bcm2835_sdhost.c bcm2835_sdhost_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" bcm2835_sdhost_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" diff --git a/hw/sh4/shix.c b/hw/sh4/shix.c index 68b14ee5e7..f410c08883 100644 --- a/hw/sh4/shix.c +++ b/hw/sh4/shix.c @@ -53,8 +53,7 @@ static void shix_init(MachineState *machine) cpu = SUPERH_CPU(cpu_create(machine->cpu_type)); /* Allocate memory space */ - memory_region_init_ram(rom, NULL, "shix.rom", 0x4000, &error_fatal); - memory_region_set_readonly(rom, true); + memory_region_init_rom(rom, NULL, "shix.rom", 0x4000, &error_fatal); memory_region_add_subregion(sysmem, 0x00000000, rom); memory_region_init_ram(&sdram[0], NULL, "shix.sdram1", 0x01000000, &error_fatal); diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 5fa58aa55f..8f024dab7b 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -255,8 +255,7 @@ static void leon3_generic_hw_init(MachineState *machine) /* Allocate BIOS */ prom_size = 8 * MiB; - memory_region_init_ram(prom, NULL, "Leon3.bios", prom_size, &error_fatal); - memory_region_set_readonly(prom, true); + memory_region_init_rom(prom, NULL, "Leon3.bios", prom_size, &error_fatal); memory_region_add_subregion(address_space_mem, LEON3_PROM_OFFSET, prom); /* Load boot prom */ diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index d33e84f831..6abfcb30f8 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -50,7 +50,6 @@ #include "hw/sparc/sparc64.h" #include "hw/nvram/fw_cfg.h" #include "hw/sysbus.h" -#include "hw/ide.h" #include "hw/ide/pci.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" @@ -563,7 +562,6 @@ static void sun4uv_init(MemoryRegion *address_space_mem, PCIBus *pci_bus, *pci_busA, *pci_busB; PCIDevice *ebus, *pci_dev; SysBusDevice *s; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; DeviceState *iommu, *dev; FWCfgState *fw_cfg; NICInfo *nd; @@ -663,12 +661,10 @@ static void sun4uv_init(MemoryRegion *address_space_mem, qemu_macaddr_default_if_unset(&macaddr); } - ide_drive_get(hd, ARRAY_SIZE(hd)); - pci_dev = pci_create(pci_busA, PCI_DEVFN(3, 0), "cmd646-ide"); qdev_prop_set_uint32(&pci_dev->qdev, "secondary", 1); qdev_init_nofail(&pci_dev->qdev); - pci_ide_create_devs(pci_dev, hd); + pci_ide_create_devs(pci_dev); /* Map NVRAM into I/O (ebus) space */ nvram = m48t59_init(NULL, 0, 0, NVRAM_SIZE, 1968, 59); diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 23c8d2f062..9d5c696d5a 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -31,6 +31,7 @@ #include "qapi/error.h" #include "exec/address-spaces.h" #include "qemu/units.h" +#include "trace.h" #include "hw/irq.h" #include "hw/qdev-properties.h" @@ -513,6 +514,8 @@ static void aspeed_smc_flash_set_segment(AspeedSMCState *s, int cs, s->ctrl->reg_to_segment(s, new, &seg); + trace_aspeed_smc_flash_set_segment(cs, new, seg.addr, seg.addr + seg.size); + /* The start address of CS0 is read-only */ if (cs == 0 && seg.addr != s->ctrl->flash_window_base) { qemu_log_mask(LOG_GUEST_ERROR, @@ -636,27 +639,23 @@ static inline int aspeed_smc_flash_is_4byte(const AspeedSMCFlash *fl) } } -static inline bool aspeed_smc_is_ce_stop_active(const AspeedSMCFlash *fl) +static void aspeed_smc_flash_do_select(AspeedSMCFlash *fl, bool unselect) { - const AspeedSMCState *s = fl->controller; + AspeedSMCState *s = fl->controller; - return s->regs[s->r_ctrl0 + fl->id] & CTRL_CE_STOP_ACTIVE; + trace_aspeed_smc_flash_select(fl->id, unselect ? "un" : ""); + + qemu_set_irq(s->cs_lines[fl->id], unselect); } static void aspeed_smc_flash_select(AspeedSMCFlash *fl) { - AspeedSMCState *s = fl->controller; - - s->regs[s->r_ctrl0 + fl->id] &= ~CTRL_CE_STOP_ACTIVE; - qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl)); + aspeed_smc_flash_do_select(fl, false); } static void aspeed_smc_flash_unselect(AspeedSMCFlash *fl) { - AspeedSMCState *s = fl->controller; - - s->regs[s->r_ctrl0 + fl->id] |= CTRL_CE_STOP_ACTIVE; - qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl)); + aspeed_smc_flash_do_select(fl, true); } static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl, @@ -753,6 +752,8 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) __func__, aspeed_smc_flash_mode(fl)); } + trace_aspeed_smc_flash_read(fl->id, addr, size, ret, + aspeed_smc_flash_mode(fl)); return ret; } @@ -787,11 +788,11 @@ static int aspeed_smc_num_dummies(uint8_t command) case FAST_READ: case DOR: case QOR: + case FAST_READ_4: case DOR_4: case QOR_4: return 1; case DIOR: - case FAST_READ_4: case DIOR_4: return 2; case QIOR: @@ -808,6 +809,9 @@ static bool aspeed_smc_do_snoop(AspeedSMCFlash *fl, uint64_t data, AspeedSMCState *s = fl->controller; uint8_t addr_width = aspeed_smc_flash_is_4byte(fl) ? 4 : 3; + trace_aspeed_smc_do_snoop(fl->id, s->snoop_index, s->snoop_dummies, + (uint8_t) data & 0xff); + if (s->snoop_index == SNOOP_OFF) { return false; /* Do nothing */ @@ -858,6 +862,9 @@ static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, AspeedSMCState *s = fl->controller; int i; + trace_aspeed_smc_flash_write(fl->id, addr, size, data, + aspeed_smc_flash_mode(fl)); + if (!aspeed_smc_is_writable(fl)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: flash is not writable at 0x%" HWADDR_PRIx "\n", __func__, addr); @@ -900,13 +907,25 @@ static const MemoryRegionOps aspeed_smc_flash_ops = { }, }; -static void aspeed_smc_flash_update_cs(AspeedSMCFlash *fl) +static void aspeed_smc_flash_update_ctrl(AspeedSMCFlash *fl, uint32_t value) { AspeedSMCState *s = fl->controller; + bool unselect; + + /* User mode selects the CS, other modes unselect */ + unselect = (value & CTRL_CMD_MODE_MASK) != CTRL_USERMODE; + + /* A change of CTRL_CE_STOP_ACTIVE from 0 to 1, unselects the CS */ + if (!(s->regs[s->r_ctrl0 + fl->id] & CTRL_CE_STOP_ACTIVE) && + value & CTRL_CE_STOP_ACTIVE) { + unselect = true; + } - s->snoop_index = aspeed_smc_is_ce_stop_active(fl) ? SNOOP_OFF : SNOOP_START; + s->regs[s->r_ctrl0 + fl->id] = value; - qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl)); + s->snoop_index = unselect ? SNOOP_OFF : SNOOP_START; + + aspeed_smc_flash_do_select(fl, unselect); } static void aspeed_smc_reset(DeviceState *d) @@ -972,6 +991,9 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) (s->ctrl->has_dma && addr == R_DMA_CHECKSUM) || (addr >= R_SEG_ADDR0 && addr < R_SEG_ADDR0 + s->ctrl->max_slaves) || (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->ctrl->max_slaves)) { + + trace_aspeed_smc_read(addr, size, s->regs[addr]); + return s->regs[addr]; } else { qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", @@ -1091,6 +1113,7 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s) __func__, s->regs[R_DMA_FLASH_ADDR]); return; } + trace_aspeed_smc_dma_checksum(s->regs[R_DMA_FLASH_ADDR], data); /* * When the DMA is on-going, the DMA registers are updated @@ -1225,6 +1248,8 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, addr >>= 2; + trace_aspeed_smc_write(addr, size, data); + if (addr == s->r_conf || (addr >= s->r_timings && addr < s->r_timings + s->ctrl->nregs_timings) || @@ -1232,8 +1257,7 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, s->regs[addr] = value; } else if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) { int cs = addr - s->r_ctrl0; - s->regs[addr] = value; - aspeed_smc_flash_update_cs(&s->flashes[cs]); + aspeed_smc_flash_update_ctrl(&s->flashes[cs], value); } else if (addr >= R_SEG_ADDR0 && addr < R_SEG_ADDR0 + s->ctrl->max_slaves) { int cs = addr - R_SEG_ADDR0; diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events new file mode 100644 index 0000000000..0a70629801 --- /dev/null +++ b/hw/ssi/trace-events @@ -0,0 +1,10 @@ +# aspeed_smc.c + +aspeed_smc_flash_set_segment(int cs, uint64_t reg, uint64_t start, uint64_t end) "CS%d segreg=0x%"PRIx64" [ 0x%"PRIx64" - 0x%"PRIx64" ]" +aspeed_smc_flash_read(int cs, uint64_t addr, uint32_t size, uint64_t data, int mode) "CS%d @0x%" PRIx64 " size %u: 0x%" PRIx64" mode:%d" +aspeed_smc_do_snoop(int cs, int index, int dummies, int data) "CS%d index:0x%x dummies:%d data:0x%x" +aspeed_smc_flash_write(int cs, uint64_t addr, uint32_t size, uint64_t data, int mode) "CS%d @0x%" PRIx64 " size %u: 0x%" PRIx64" mode:%d" +aspeed_smc_read(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 +aspeed_smc_dma_checksum(uint32_t addr, uint32_t data) "0x%08x: 0x%08x" +aspeed_smc_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 +aspeed_smc_flash_select(int cs, const char *prefix) "CS%d %sselect" diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig index 5e70ed5f7b..464348ba14 100644 --- a/hw/usb/Kconfig +++ b/hw/usb/Kconfig @@ -91,3 +91,8 @@ config USB_STORAGE_MTP bool default y depends on USB + +config IMX_USBPHY + bool + default y + depends on USB diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index 2b10868937..66835e5bf7 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -61,3 +61,5 @@ common-obj-$(CONFIG_XEN) += xen-usb.o xen-usb.o-cflags := $(LIBUSB_CFLAGS) xen-usb.o-libs := $(LIBUSB_LIBS) endif + +common-obj-$(CONFIG_IMX_USBPHY) += imx-usb-phy.o diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 9a78ad928b..6210427544 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -626,7 +626,7 @@ static const uint32_t oid_supported_list[] = struct rndis_response { QTAILQ_ENTRY(rndis_response) entries; uint32_t length; - uint8_t buf[0]; + uint8_t buf[]; }; typedef struct USBNetState { diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 98465990ef..d2c03681b7 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -29,7 +29,7 @@ do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0) #define DPRINTF(fmt, ...) do {} while(0) #endif -#define RECV_BUF 384 +#define RECV_BUF (512 - (2 * 8)) /* Commands */ #define FTDI_RESET 0 @@ -98,6 +98,7 @@ do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0) typedef struct { USBDevice dev; + USBEndpoint *intr; uint8_t recv_buf[RECV_BUF]; uint16_t recv_ptr; uint16_t recv_used; @@ -153,7 +154,7 @@ static const USBDescDevice desc_device = { { .bNumInterfaces = 1, .bConfigurationValue = 1, - .bmAttributes = USB_CFG_ATT_ONE, + .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, .bMaxPower = 50, .nif = 1, .ifs = &desc_iface0, @@ -331,7 +332,7 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, break; case DeviceInVendor | FTDI_GET_MDM_ST: data[0] = usb_get_modem_lines(s) | 1; - data[1] = 0; + data[1] = FTDI_THRE | FTDI_TEMT; p->actual_length = 2; break; case DeviceOutVendor | FTDI_SET_EVENT_CHR: @@ -357,13 +358,67 @@ static void usb_serial_handle_control(USBDevice *dev, USBPacket *p, } } +static void usb_serial_token_in(USBSerialState *s, USBPacket *p) +{ + const int max_packet_size = desc_iface0.eps[0].wMaxPacketSize; + int packet_len; + uint8_t header[2]; + + packet_len = p->iov.size; + if (packet_len <= 2) { + p->status = USB_RET_NAK; + return; + } + + header[0] = usb_get_modem_lines(s) | 1; + /* We do not have the uart details */ + /* handle serial break */ + if (s->event_trigger && s->event_trigger & FTDI_BI) { + s->event_trigger &= ~FTDI_BI; + header[1] = FTDI_BI; + usb_packet_copy(p, header, 2); + return; + } else { + header[1] = 0; + } + + if (!s->recv_used) { + p->status = USB_RET_NAK; + return; + } + + while (s->recv_used && packet_len > 2) { + int first_len, len; + + len = MIN(packet_len, max_packet_size); + len -= 2; + if (len > s->recv_used) { + len = s->recv_used; + } + + first_len = RECV_BUF - s->recv_ptr; + if (first_len > len) { + first_len = len; + } + usb_packet_copy(p, header, 2); + usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); + if (len > first_len) { + usb_packet_copy(p, s->recv_buf, len - first_len); + } + s->recv_used -= len; + s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; + packet_len -= len + 2; + } + + return; +} + static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) { USBSerialState *s = (USBSerialState *)dev; uint8_t devep = p->ep->nr; struct iovec *iov; - uint8_t header[2]; - int i, first_len, len; + int i; switch (p->pid) { case USB_TOKEN_OUT: @@ -381,38 +436,7 @@ static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) case USB_TOKEN_IN: if (devep != 1) goto fail; - first_len = RECV_BUF - s->recv_ptr; - len = p->iov.size; - if (len <= 2) { - p->status = USB_RET_NAK; - break; - } - header[0] = usb_get_modem_lines(s) | 1; - /* We do not have the uart details */ - /* handle serial break */ - if (s->event_trigger && s->event_trigger & FTDI_BI) { - s->event_trigger &= ~FTDI_BI; - header[1] = FTDI_BI; - usb_packet_copy(p, header, 2); - break; - } else { - header[1] = 0; - } - len -= 2; - if (len > s->recv_used) - len = s->recv_used; - if (!len) { - p->status = USB_RET_NAK; - break; - } - if (first_len > len) - first_len = len; - usb_packet_copy(p, header, 2); - usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); - if (len > first_len) - usb_packet_copy(p, s->recv_buf, len - first_len); - s->recv_used -= len; - s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; + usb_serial_token_in(s, p); break; default: @@ -459,6 +483,8 @@ static void usb_serial_read(void *opaque, const uint8_t *buf, int size) memcpy(s->recv_buf + start, buf, size); } s->recv_used += size; + + usb_wakeup(s->intr, 0); } static void usb_serial_event(void *opaque, QEMUChrEvent event) @@ -513,6 +539,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) if (qemu_chr_fe_backend_open(&s->cs) && !dev->attached) { usb_device_attach(dev, &error_abort); } + s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); } static USBDevice *usb_braille_init(USBBus *bus, const char *unused) diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 02693a26ad..ef72738ced 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -227,7 +227,7 @@ typedef struct QEMU_PACKED CCID_Parameter { typedef struct QEMU_PACKED CCID_DataBlock { CCID_BULK_IN b; uint8_t bChainParameter; - uint8_t abData[0]; + uint8_t abData[]; } CCID_DataBlock; /* 6.1.4 PC_to_RDR_XfrBlock */ @@ -235,7 +235,7 @@ typedef struct QEMU_PACKED CCID_XferBlock { CCID_Header hdr; uint8_t bBWI; /* Block Waiting Timeout */ uint16_t wLevelParameter; /* XXX currently unused */ - uint8_t abData[0]; + uint8_t abData[]; } CCID_XferBlock; typedef struct QEMU_PACKED CCID_IccPowerOn { diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 4883c1d89e..5c4b57b06b 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -18,7 +18,6 @@ #include "hw/qdev-properties.h" #include "hw/scsi/scsi.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" #include "qapi/visitor.h" diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index 5b7991cffe..3730736540 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -131,6 +131,22 @@ static const TypeInfo ehci_exynos4210_type_info = { .class_init = ehci_exynos4210_class_init, }; +static void ehci_aw_h3_class_init(ObjectClass *oc, void *data) +{ + SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + sec->capsbase = 0x0; + sec->opregbase = 0x10; + set_bit(DEVICE_CATEGORY_USB, dc->categories); +} + +static const TypeInfo ehci_aw_h3_type_info = { + .name = TYPE_AW_H3_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .class_init = ehci_aw_h3_class_init, +}; + static void ehci_tegra2_class_init(ObjectClass *oc, void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); @@ -252,6 +268,7 @@ static void ehci_sysbus_register_types(void) type_register_static(&ehci_type_info); type_register_static(&ehci_platform_type_info); type_register_static(&ehci_exynos4210_type_info); + type_register_static(&ehci_aw_h3_type_info); type_register_static(&ehci_tegra2_type_info); type_register_static(&ehci_ppc4xx_type_info); type_register_static(&ehci_fusbh200_type_info); diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 56ab2f457f..29d49c2d7e 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1301,7 +1301,6 @@ static void ehci_execute_complete(EHCIQueue *q) /* should not be triggerable */ fprintf(stderr, "USB invalid response %d\n", p->packet.status); g_assert_not_reached(); - break; } /* TODO check 4.12 for splits */ @@ -2105,9 +2104,7 @@ static void ehci_advance_state(EHCIState *ehci, int async) default: fprintf(stderr, "Bad state!\n"); - again = -1; g_assert_not_reached(); - break; } if (again < 0 || itd_count > 16) { diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 0298238f0b..edb59311c4 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -342,6 +342,7 @@ typedef struct EHCIPCIState { #define TYPE_SYS_BUS_EHCI "sysbus-ehci-usb" #define TYPE_PLATFORM_EHCI "platform-ehci-usb" #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb" +#define TYPE_AW_H3_EHCI "aw-h3-ehci-usb" #define TYPE_TEGRA2_EHCI "tegra2-ehci-usb" #define TYPE_PPC4xx_EHCI "ppc4xx-ehci-usb" #define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb" diff --git a/hw/usb/imx-usb-phy.c b/hw/usb/imx-usb-phy.c new file mode 100644 index 0000000000..e705a03a1f --- /dev/null +++ b/hw/usb/imx-usb-phy.c @@ -0,0 +1,225 @@ +/* + * i.MX USB PHY + * + * Copyright (c) 2020 Guenter Roeck <linux@roeck-us.net> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * We need to implement basic reset control in the PHY control register. + * For everything else, it is sufficient to set whatever is written. + */ + +#include "qemu/osdep.h" +#include "hw/usb/imx-usb-phy.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" + +static const VMStateDescription vmstate_imx_usbphy = { + .name = TYPE_IMX_USBPHY, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(usbphy, IMXUSBPHYState, USBPHY_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx_usbphy_softreset(IMXUSBPHYState *s) +{ + s->usbphy[USBPHY_PWD] = 0x001e1c00; + s->usbphy[USBPHY_TX] = 0x10060607; + s->usbphy[USBPHY_RX] = 0x00000000; + s->usbphy[USBPHY_CTRL] = 0xc0200000; +} + +static void imx_usbphy_reset(DeviceState *dev) +{ + IMXUSBPHYState *s = IMX_USBPHY(dev); + + s->usbphy[USBPHY_STATUS] = 0x00000000; + s->usbphy[USBPHY_DEBUG] = 0x7f180000; + s->usbphy[USBPHY_DEBUG0_STATUS] = 0x00000000; + s->usbphy[USBPHY_DEBUG1] = 0x00001000; + s->usbphy[USBPHY_VERSION] = 0x04020000; + + imx_usbphy_softreset(s); +} + +static uint64_t imx_usbphy_read(void *opaque, hwaddr offset, unsigned size) +{ + IMXUSBPHYState *s = (IMXUSBPHYState *)opaque; + uint32_t index = offset >> 2; + uint32_t value; + + switch (index) { + case USBPHY_PWD_SET: + case USBPHY_TX_SET: + case USBPHY_RX_SET: + case USBPHY_CTRL_SET: + case USBPHY_DEBUG_SET: + case USBPHY_DEBUG1_SET: + /* + * All REG_NAME_SET register access are in fact targeting the + * REG_NAME register. + */ + value = s->usbphy[index - 1]; + break; + case USBPHY_PWD_CLR: + case USBPHY_TX_CLR: + case USBPHY_RX_CLR: + case USBPHY_CTRL_CLR: + case USBPHY_DEBUG_CLR: + case USBPHY_DEBUG1_CLR: + /* + * All REG_NAME_CLR register access are in fact targeting the + * REG_NAME register. + */ + value = s->usbphy[index - 2]; + break; + case USBPHY_PWD_TOG: + case USBPHY_TX_TOG: + case USBPHY_RX_TOG: + case USBPHY_CTRL_TOG: + case USBPHY_DEBUG_TOG: + case USBPHY_DEBUG1_TOG: + /* + * All REG_NAME_TOG register access are in fact targeting the + * REG_NAME register. + */ + value = s->usbphy[index - 3]; + break; + default: + value = s->usbphy[index]; + break; + } + return (uint64_t)value; +} + +static void imx_usbphy_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMXUSBPHYState *s = (IMXUSBPHYState *)opaque; + uint32_t index = offset >> 2; + + switch (index) { + case USBPHY_CTRL: + s->usbphy[index] = value; + if (value & USBPHY_CTRL_SFTRST) { + imx_usbphy_softreset(s); + } + break; + case USBPHY_PWD: + case USBPHY_TX: + case USBPHY_RX: + case USBPHY_STATUS: + case USBPHY_DEBUG: + case USBPHY_DEBUG1: + s->usbphy[index] = value; + break; + case USBPHY_CTRL_SET: + s->usbphy[index - 1] |= value; + if (value & USBPHY_CTRL_SFTRST) { + imx_usbphy_softreset(s); + } + break; + case USBPHY_PWD_SET: + case USBPHY_TX_SET: + case USBPHY_RX_SET: + case USBPHY_DEBUG_SET: + case USBPHY_DEBUG1_SET: + /* + * All REG_NAME_SET register access are in fact targeting the + * REG_NAME register. So we change the value of the REG_NAME + * register, setting bits passed in the value. + */ + s->usbphy[index - 1] |= value; + break; + case USBPHY_PWD_CLR: + case USBPHY_TX_CLR: + case USBPHY_RX_CLR: + case USBPHY_CTRL_CLR: + case USBPHY_DEBUG_CLR: + case USBPHY_DEBUG1_CLR: + /* + * All REG_NAME_CLR register access are in fact targeting the + * REG_NAME register. So we change the value of the REG_NAME + * register, unsetting bits passed in the value. + */ + s->usbphy[index - 2] &= ~value; + break; + case USBPHY_CTRL_TOG: + s->usbphy[index - 3] ^= value; + if ((value & USBPHY_CTRL_SFTRST) && + (s->usbphy[index - 3] & USBPHY_CTRL_SFTRST)) { + imx_usbphy_softreset(s); + } + break; + case USBPHY_PWD_TOG: + case USBPHY_TX_TOG: + case USBPHY_RX_TOG: + case USBPHY_DEBUG_TOG: + case USBPHY_DEBUG1_TOG: + /* + * All REG_NAME_TOG register access are in fact targeting the + * REG_NAME register. So we change the value of the REG_NAME + * register, toggling bits passed in the value. + */ + s->usbphy[index - 3] ^= value; + break; + default: + /* Other registers are read-only */ + break; + } +} + +static const struct MemoryRegionOps imx_usbphy_ops = { + .read = imx_usbphy_read, + .write = imx_usbphy_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx_usbphy_realize(DeviceState *dev, Error **errp) +{ + IMXUSBPHYState *s = IMX_USBPHY(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &imx_usbphy_ops, s, + "imx-usbphy", 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); +} + +static void imx_usbphy_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = imx_usbphy_reset; + dc->vmsd = &vmstate_imx_usbphy; + dc->desc = "i.MX USB PHY Module"; + dc->realize = imx_usbphy_realize; +} + +static const TypeInfo imx_usbphy_info = { + .name = TYPE_IMX_USBPHY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXUSBPHYState), + .class_init = imx_usbphy_class_init, +}; + +static void imx_usbphy_register_types(void) +{ + type_register_static(&imx_usbphy_info); +} + +type_init(imx_usbphy_register_types) diff --git a/hw/usb/quirks.c b/hw/usb/quirks.c index 38a9c5634a..23ea7a23ea 100644 --- a/hw/usb/quirks.c +++ b/hw/usb/quirks.c @@ -22,10 +22,10 @@ static bool usb_id_match(const struct usb_device_id *ids, uint8_t interface_protocol) { int i; - for (i = 0; ids[i].vendor_id != -1; i++) { + for (i = 0; ids[i].terminating_entry == 0; i++) { if (ids[i].vendor_id == vendor_id && ids[i].product_id == product_id && - (ids[i].interface_class == -1 || + (ids[i].interface_protocol_used == 0 || (ids[i].interface_class == interface_class && ids[i].interface_subclass == interface_subclass && ids[i].interface_protocol == interface_protocol))) { diff --git a/hw/usb/quirks.h b/hw/usb/quirks.h index 89480befd7..50ef2f9c2e 100644 --- a/hw/usb/quirks.h +++ b/hw/usb/quirks.h @@ -21,19 +21,23 @@ #include "quirks-pl2303-ids.h" struct usb_device_id { - int vendor_id; - int product_id; - int interface_class; - int interface_subclass; - int interface_protocol; + uint16_t vendor_id; + uint16_t product_id; + uint8_t interface_class; + uint8_t interface_subclass; + uint8_t interface_protocol; + uint8_t interface_protocol_used:1, + terminating_entry:1, + reserved:6; }; #define USB_DEVICE(vendor, product) \ - .vendor_id = vendor, .product_id = product, .interface_class = -1, + .vendor_id = vendor, .product_id = product, .interface_protocol_used = 0, #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 + .interface_subclass = isubclass, .interface_protocol = iproto, \ + .interface_protocol_used = 1 static const struct usb_device_id usbredir_raw_serial_ids[] = { /* @@ -206,7 +210,7 @@ static const struct usb_device_id usbredir_raw_serial_ids[] = { { USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) }, { USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) }, - { USB_DEVICE(-1, -1) } /* Terminating Entry */ + { .terminating_entry = 1 } /* Terminating Entry */ }; static const struct usb_device_id usbredir_ftdi_serial_ids[] = { @@ -906,7 +910,7 @@ static const struct usb_device_id usbredir_ftdi_serial_ids[] = { { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID) }, { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) }, - { USB_DEVICE(-1, -1) } /* Terminating Entry */ + { .terminating_entry = 1 } /* Terminating Entry */ }; #undef USB_DEVICE diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index b2d415e5dd..b6c8ef5bc0 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -54,7 +54,7 @@ typedef struct VRingAvail { uint16_t flags; uint16_t idx; - uint16_t ring[0]; + uint16_t ring[]; } VRingAvail; typedef struct VRingUsedElem @@ -67,7 +67,7 @@ typedef struct VRingUsed { uint16_t flags; uint16_t idx; - VRingUsedElem ring[0]; + VRingUsedElem ring[]; } VRingUsed; typedef struct VRingMemoryRegionCaches { diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index 9167bbaf6d..179775db7b 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -203,7 +203,7 @@ typedef struct XenPTMSIX { uint64_t mmio_base_addr; MemoryRegion mmio; void *phys_iomem_base; - XenPTMSIXEntry msix_entry[0]; + XenPTMSIXEntry msix_entry[]; } XenPTMSIX; struct XenPCIPassthroughState { diff --git a/include/block/aio.h b/include/block/aio.h index 9dd61cee7e..cb1989105a 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -14,6 +14,9 @@ #ifndef QEMU_AIO_H #define QEMU_AIO_H +#ifdef CONFIG_LINUX_IO_URING +#include <liburing.h> +#endif #include "qemu/queue.h" #include "qemu/event_notifier.h" #include "qemu/thread.h" @@ -52,6 +55,56 @@ struct ThreadPool; struct LinuxAioState; struct LuringState; +/* Is polling disabled? */ +bool aio_poll_disabled(AioContext *ctx); + +/* Callbacks for file descriptor monitoring implementations */ +typedef struct { + /* + * update: + * @ctx: the AioContext + * @old_node: the existing handler or NULL if this file descriptor is being + * monitored for the first time + * @new_node: the new handler or NULL if this file descriptor is being + * removed + * + * Add/remove/modify a monitored file descriptor. + * + * Called with ctx->list_lock acquired. + */ + void (*update)(AioContext *ctx, AioHandler *old_node, AioHandler *new_node); + + /* + * wait: + * @ctx: the AioContext + * @ready_list: list for handlers that become ready + * @timeout: maximum duration to wait, in nanoseconds + * + * Wait for file descriptors to become ready and place them on ready_list. + * + * Called with ctx->list_lock incremented but not locked. + * + * Returns: number of ready file descriptors. + */ + int (*wait)(AioContext *ctx, AioHandlerList *ready_list, int64_t timeout); + + /* + * need_wait: + * @ctx: the AioContext + * + * Tell aio_poll() when to stop userspace polling early because ->wait() + * has fds ready. + * + * File descriptor monitoring implementations that cannot poll fd readiness + * from userspace should use aio_poll_disabled() here. This ensures that + * file descriptors are not starved by handlers that frequently make + * progress via userspace polling. + * + * Returns: true if ->wait() should be called, false otherwise. + */ + bool (*need_wait)(AioContext *ctx); +} FDMonOps; + /* * Each aio_bh_poll() call carves off a slice of the BH list, so that newly * scheduled BHs are not processed until the next aio_bh_poll() call. All @@ -65,6 +118,8 @@ struct BHListSlice { QSIMPLEQ_ENTRY(BHListSlice) next; }; +typedef QSLIST_HEAD(, AioHandler) AioHandlerSList; + struct AioContext { GSource source; @@ -150,6 +205,10 @@ struct AioContext { * locking. */ struct LuringState *linux_io_uring; + + /* State for file descriptor monitoring using Linux io_uring */ + struct io_uring fdmon_io_uring; + AioHandlerSList submit_list; #endif /* TimerLists for calling timers - one per clock type. Has its own @@ -168,13 +227,21 @@ struct AioContext { int64_t poll_grow; /* polling time growth factor */ int64_t poll_shrink; /* polling time shrink factor */ + /* + * List of handlers participating in userspace polling. Protected by + * ctx->list_lock. Iterated and modified mostly by the event loop thread + * from aio_poll() with ctx->list_lock incremented. aio_set_fd_handler() + * only touches the list to delete nodes if ctx->list_lock's count is zero. + */ + AioHandlerList poll_aio_handlers; + /* Are we in polling mode or monitoring file descriptors? */ bool poll_started; /* epoll(7) state used when built with CONFIG_EPOLL */ int epollfd; - bool epoll_enabled; - bool epoll_available; + + const FDMonOps *fdmon_ops; }; /** diff --git a/include/block/block-copy.h b/include/block/block-copy.h index 0a161724d7..aac85e1488 100644 --- a/include/block/block-copy.h +++ b/include/block/block-copy.h @@ -18,79 +18,30 @@ #include "block/block.h" #include "qemu/co-shared-resource.h" -typedef struct BlockCopyInFlightReq { - int64_t start_byte; - int64_t end_byte; - QLIST_ENTRY(BlockCopyInFlightReq) list; - CoQueue wait_queue; /* coroutines blocked on this request */ -} BlockCopyInFlightReq; - typedef void (*ProgressBytesCallbackFunc)(int64_t bytes, void *opaque); -typedef void (*ProgressResetCallbackFunc)(void *opaque); -typedef struct BlockCopyState { - /* - * BdrvChild objects are not owned or managed by block-copy. They are - * provided by block-copy user and user is responsible for appropriate - * permissions on these children. - */ - BdrvChild *source; - BdrvChild *target; - BdrvDirtyBitmap *copy_bitmap; - int64_t cluster_size; - bool use_copy_range; - int64_t copy_size; - uint64_t len; - QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs; - - BdrvRequestFlags write_flags; - - /* - * skip_unallocated: - * - * Used by sync=top jobs, which first scan the source node for unallocated - * areas and clear them in the copy_bitmap. During this process, the bitmap - * is thus not fully initialized: It may still have bits set for areas that - * are unallocated and should actually not be copied. - * - * This is indicated by skip_unallocated. - * - * In this case, block_copy() will query the source’s allocation status, - * skip unallocated regions, clear them in the copy_bitmap, and invoke - * block_copy_reset_unallocated() every time it does. - */ - bool skip_unallocated; - - /* progress_bytes_callback: called when some copying progress is done. */ - ProgressBytesCallbackFunc progress_bytes_callback; - - /* - * progress_reset_callback: called when some bytes reset from copy_bitmap - * (see @skip_unallocated above). The callee is assumed to recalculate how - * many bytes remain based on the dirty bit count of copy_bitmap. - */ - ProgressResetCallbackFunc progress_reset_callback; - void *progress_opaque; - - SharedResource *mem; -} BlockCopyState; +typedef struct BlockCopyState BlockCopyState; BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, int64_t cluster_size, BdrvRequestFlags write_flags, Error **errp); -void block_copy_set_callbacks( +void block_copy_set_progress_callback( BlockCopyState *s, ProgressBytesCallbackFunc progress_bytes_callback, - ProgressResetCallbackFunc progress_reset_callback, void *progress_opaque); +void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm); + void block_copy_state_free(BlockCopyState *s); int64_t block_copy_reset_unallocated(BlockCopyState *s, int64_t offset, int64_t *count); -int coroutine_fn block_copy(BlockCopyState *s, int64_t start, uint64_t bytes, +int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes, bool *error_is_read); +BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s); +void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip); + #endif /* BLOCK_COPY_H */ diff --git a/include/block/block-hmp-cmds.h b/include/block/block-hmp-cmds.h new file mode 100644 index 0000000000..3412e108ca --- /dev/null +++ b/include/block/block-hmp-cmds.h @@ -0,0 +1,54 @@ +/* + * HMP commands related to the block layer + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2020 Red Hat, Inc. + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef BLOCK_HMP_COMMANDS_H +#define BLOCK_HMP_COMMANDS_H + +void hmp_drive_add(Monitor *mon, const QDict *qdict); + +void hmp_commit(Monitor *mon, const QDict *qdict); +void hmp_drive_del(Monitor *mon, const QDict *qdict); + +void hmp_drive_mirror(Monitor *mon, const QDict *qdict); +void hmp_drive_backup(Monitor *mon, const QDict *qdict); + +void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict); +void hmp_block_job_cancel(Monitor *mon, const QDict *qdict); +void hmp_block_job_pause(Monitor *mon, const QDict *qdict); +void hmp_block_job_resume(Monitor *mon, const QDict *qdict); +void hmp_block_job_complete(Monitor *mon, const QDict *qdict); + +void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict); +void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict); +void hmp_snapshot_delete_blkdev_internal(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_remove(Monitor *mon, const QDict *qdict); +void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict); + +void hmp_block_resize(Monitor *mon, const QDict *qdict); +void hmp_block_stream(Monitor *mon, const QDict *qdict); +void hmp_block_passwd(Monitor *mon, const QDict *qdict); +void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict); +void hmp_eject(Monitor *mon, const QDict *qdict); + +void hmp_qemu_io(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_block_jobs(Monitor *mon, const QDict *qdict); +void hmp_info_snapshots(Monitor *mon, const QDict *qdict); + +#endif diff --git a/include/block/block.h b/include/block/block.h index cd6b5b95aa..e569a4d747 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -363,6 +363,7 @@ bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, Error **errp); void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base); +int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp); typedef struct BdrvCheckResult { diff --git a/include/block/block_int.h b/include/block/block_int.h index f422c0bff0..ae9c4da4d0 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -314,6 +314,10 @@ struct BlockDriver { */ int coroutine_fn (*bdrv_co_flush)(BlockDriverState *bs); + /* Delete a created file. */ + int coroutine_fn (*bdrv_co_delete_file)(BlockDriverState *bs, + Error **errp); + /* * Flushes all data that was already written to the OS all the way down to * the disk (for example file-posix.c calls fsync()). @@ -1216,8 +1220,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque, JobTxn *txn, Error **errp); -void hmp_drive_add_node(Monitor *mon, const char *optstr); - BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, const char *child_name, const BdrvChildRole *child_role, @@ -1226,6 +1228,9 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, void *opaque, Error **errp); void bdrv_root_unref_child(BdrvChild *child); +void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, + uint64_t *shared_perm); + /** * Sets a BdrvChild's permissions. Avoid if the parent is a BDS; use * bdrv_child_refresh_perms() instead and make the parent's @@ -1322,4 +1327,7 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, int refresh_total_sectors(BlockDriverState *bs, int64_t hint); +void bdrv_set_monitor_owned(BlockDriverState *bs); +BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp); + #endif /* BLOCK_INT_H */ diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index e2b20ecab9..8a10029418 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -105,10 +105,13 @@ for (bitmap = bdrv_dirty_bitmap_first(bs); bitmap; \ bitmap = bdrv_dirty_bitmap_next(bitmap)) char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp); -int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, - uint64_t bytes); +int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset, + int64_t bytes); +int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset, + int64_t bytes); bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, - uint64_t *offset, uint64_t *bytes); + int64_t start, int64_t end, int64_t max_dirty_count, + int64_t *dirty_start, int64_t *dirty_count); BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, Error **errp); diff --git a/include/crypto/block.h b/include/crypto/block.h index d49d2c2da9..c77ccaf9c0 100644 --- a/include/crypto/block.h +++ b/include/crypto/block.h @@ -146,6 +146,26 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, /** + * qcrypto_block_calculate_payload_offset: + * @create_opts: the encryption options + * @optprefix: name prefix for options + * @len: output for number of header bytes before payload + * @errp: pointer to a NULL-initialized error object + * + * Calculate the number of header bytes before the payload in an encrypted + * storage volume. The header is an area before the payload that is reserved + * for encryption metadata. + * + * Returns: true on success, false on error + */ +bool +qcrypto_block_calculate_payload_offset(QCryptoBlockCreateOptions *create_opts, + const char *optprefix, + size_t *len, + Error **errp); + + +/** * qcrypto_block_get_info: * @block: the block encryption object * @errp: pointer to a NULL-initialized error object @@ -269,5 +289,7 @@ uint64_t qcrypto_block_get_sector_size(QCryptoBlock *block); void qcrypto_block_free(QCryptoBlock *block); G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlock, qcrypto_block_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockCreateOptions, + qapi_free_QCryptoBlockCreateOptions) #endif /* QCRYPTO_BLOCK_H */ diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index f87f468809..c5f9fa08ab 100644 --- a/include/disas/dis-asm.h +++ b/include/disas/dis-asm.h @@ -226,6 +226,10 @@ enum bfd_architecture #define bfd_mach_nios2r2 2 bfd_arch_lm32, /* Lattice Mico32 */ #define bfd_mach_lm32 1 + bfd_arch_rx, /* Renesas RX */ +#define bfd_mach_rx 0x75 +#define bfd_mach_rx_v2 0x76 +#define bfd_mach_rx_v3 0x77 bfd_arch_last }; #define bfd_mach_s390_31 31 @@ -436,6 +440,7 @@ int print_insn_little_nios2 (bfd_vma, disassemble_info*); int print_insn_xtensa (bfd_vma, disassemble_info*); int print_insn_riscv32 (bfd_vma, disassemble_info*); int print_insn_riscv64 (bfd_vma, disassemble_info*); +int print_insn_rx(bfd_vma, disassemble_info *); #if 0 /* Fetch the disassembler for a given BFD, if that support is available. */ diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 08363969c1..30b909ebd2 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -68,40 +68,76 @@ void gdb_signalled(CPUArchState *, int); void gdbserver_fork(CPUState *); #endif /* Get or set a register. Returns the size of the register. */ -typedef int (*gdb_reg_cb)(CPUArchState *env, uint8_t *buf, int reg); +typedef int (*gdb_get_reg_cb)(CPUArchState *env, GByteArray *buf, int reg); +typedef int (*gdb_set_reg_cb)(CPUArchState *env, uint8_t *buf, int reg); void gdb_register_coprocessor(CPUState *cpu, - gdb_reg_cb get_reg, gdb_reg_cb set_reg, + gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, int num_regs, const char *xml, int g_pos); -/* The GDB remote protocol transfers values in target byte order. This means - * we can use the raw memory access routines to access the value buffer. - * Conveniently, these also handle the case where the buffer is mis-aligned. +/* + * The GDB remote protocol transfers values in target byte order. As + * the gdbstub may be batching up several register values we always + * append to the array. */ -static inline int gdb_get_reg8(uint8_t *mem_buf, uint8_t val) +static inline int gdb_get_reg8(GByteArray *buf, uint8_t val) { - stb_p(mem_buf, val); + g_byte_array_append(buf, &val, 1); return 1; } -static inline int gdb_get_reg16(uint8_t *mem_buf, uint16_t val) +static inline int gdb_get_reg16(GByteArray *buf, uint16_t val) { - stw_p(mem_buf, val); + uint16_t to_word = tswap16(val); + g_byte_array_append(buf, (uint8_t *) &to_word, 2); return 2; } -static inline int gdb_get_reg32(uint8_t *mem_buf, uint32_t val) +static inline int gdb_get_reg32(GByteArray *buf, uint32_t val) { - stl_p(mem_buf, val); + uint32_t to_long = tswap32(val); + g_byte_array_append(buf, (uint8_t *) &to_long, 4); return 4; } -static inline int gdb_get_reg64(uint8_t *mem_buf, uint64_t val) +static inline int gdb_get_reg64(GByteArray *buf, uint64_t val) { - stq_p(mem_buf, val); + uint64_t to_quad = tswap64(val); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); return 8; } +static inline int gdb_get_reg128(GByteArray *buf, uint64_t val_hi, + uint64_t val_lo) +{ + uint64_t to_quad; +#ifdef TARGET_WORDS_BIGENDIAN + to_quad = tswap64(val_hi); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); + to_quad = tswap64(val_lo); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); +#else + to_quad = tswap64(val_lo); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); + to_quad = tswap64(val_hi); + g_byte_array_append(buf, (uint8_t *) &to_quad, 8); +#endif + return 16; +} + +/** + * gdb_get_reg_ptr: get pointer to start of last element + * @len: length of element + * + * This is a helper function to extract the pointer to the last + * element for additional processing. Some front-ends do additional + * dynamic swapping of the elements based on CPU state. + */ +static inline uint8_t * gdb_get_reg_ptr(GByteArray *buf, int len) +{ + return buf->data + buf->len - len; +} + #if TARGET_LONG_BITS == 64 #define gdb_get_regl(buf, val) gdb_get_reg64(buf, val) #define ldtul_p(addr) ldq_p(addr) diff --git a/include/exec/poison.h b/include/exec/poison.h index 955eb863ab..7b9ac361dc 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -26,6 +26,7 @@ #pragma GCC poison TARGET_PPC #pragma GCC poison TARGET_PPC64 #pragma GCC poison TARGET_ABI32 +#pragma GCC poison TARGET_RX #pragma GCC poison TARGET_S390X #pragma GCC poison TARGET_SH4 #pragma GCC poison TARGET_SPARC diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 57a3f58b0c..c13327fa78 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -152,7 +152,7 @@ typedef struct AcpiSerialPortConsoleRedirection */ struct AcpiRsdtDescriptorRev1 { ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - uint32_t table_offset_entry[0]; /* Array of pointers to other */ + uint32_t table_offset_entry[]; /* Array of pointers to other */ /* ACPI tables */ } QEMU_PACKED; typedef struct AcpiRsdtDescriptorRev1 AcpiRsdtDescriptorRev1; @@ -162,7 +162,7 @@ typedef struct AcpiRsdtDescriptorRev1 AcpiRsdtDescriptorRev1; */ struct AcpiXsdtDescriptorRev2 { ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - uint64_t table_offset_entry[0]; /* Array of pointers to other */ + uint64_t table_offset_entry[]; /* Array of pointers to other */ /* ACPI tables */ } QEMU_PACKED; typedef struct AcpiXsdtDescriptorRev2 AcpiXsdtDescriptorRev2; @@ -518,7 +518,7 @@ struct AcpiDmarDeviceScope { struct { uint8_t device; uint8_t function; - } path[0]; + } path[]; } QEMU_PACKED; typedef struct AcpiDmarDeviceScope AcpiDmarDeviceScope; @@ -530,7 +530,7 @@ struct AcpiDmarHardwareUnit { uint8_t reserved; uint16_t pci_segment; /* The PCI Segment associated with this unit */ uint64_t address; /* Base address of remapping hardware register-set */ - AcpiDmarDeviceScope scope[0]; + AcpiDmarDeviceScope scope[]; } QEMU_PACKED; typedef struct AcpiDmarHardwareUnit AcpiDmarHardwareUnit; @@ -541,7 +541,7 @@ struct AcpiDmarRootPortATS { uint8_t flags; uint8_t reserved; uint16_t pci_segment; - AcpiDmarDeviceScope scope[0]; + AcpiDmarDeviceScope scope[]; } QEMU_PACKED; typedef struct AcpiDmarRootPortATS AcpiDmarRootPortATS; @@ -604,7 +604,7 @@ typedef struct AcpiIortMemoryAccess AcpiIortMemoryAccess; struct AcpiIortItsGroup { ACPI_IORT_NODE_HEADER_DEF uint32_t its_count; - uint32_t identifiers[0]; + uint32_t identifiers[]; } QEMU_PACKED; typedef struct AcpiIortItsGroup AcpiIortItsGroup; @@ -621,7 +621,7 @@ struct AcpiIortSmmu3 { uint32_t pri_gsiv; uint32_t gerr_gsiv; uint32_t sync_gsiv; - AcpiIortIdMapping id_mapping_array[0]; + AcpiIortIdMapping id_mapping_array[]; } QEMU_PACKED; typedef struct AcpiIortSmmu3 AcpiIortSmmu3; @@ -630,7 +630,7 @@ struct AcpiIortRC { AcpiIortMemoryAccess memory_properties; uint32_t ats_attribute; uint32_t pci_segment_number; - AcpiIortIdMapping id_mapping_array[0]; + AcpiIortIdMapping id_mapping_array[]; } QEMU_PACKED; typedef struct AcpiIortRC AcpiIortRC; diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h index 8af724548f..77c82a9982 100644 --- a/include/hw/arm/allwinner-a10.h +++ b/include/hw/arm/allwinner-a10.h @@ -7,9 +7,11 @@ #include "hw/timer/allwinner-a10-pit.h" #include "hw/intc/allwinner-a10-pic.h" #include "hw/net/allwinner_emac.h" +#include "hw/sd/allwinner-sdhost.h" #include "hw/ide/ahci.h" #include "hw/usb/hcd-ohci.h" #include "hw/usb/hcd-ehci.h" +#include "hw/rtc/allwinner-rtc.h" #include "target/arm/cpu.h" @@ -31,6 +33,8 @@ typedef struct AwA10State { AwA10PICState intc; AwEmacState emac; AllwinnerAHCIState sata; + AwSdHostState mmc0; + AwRtcState rtc; MemoryRegion sram_a; EHCISysBusState ehci[AW_A10_NUM_USB]; OHCISysBusState ohci[AW_A10_NUM_USB]; diff --git a/include/hw/arm/allwinner-h3.h b/include/hw/arm/allwinner-h3.h new file mode 100644 index 0000000000..82e4e59216 --- /dev/null +++ b/include/hw/arm/allwinner-h3.h @@ -0,0 +1,161 @@ +/* + * Allwinner H3 System on Chip emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/* + * The Allwinner H3 is a System on Chip containing four ARM Cortex A7 + * processor cores. Features and specifications include DDR2/DDR3 memory, + * SD/MMC storage cards, 10/100/1000Mbit Ethernet, USB 2.0, HDMI and + * various I/O modules. + * + * This implementation is based on the following datasheet: + * + * https://linux-sunxi.org/File:Allwinner_H3_Datasheet_V1.2.pdf + * + * The latest datasheet and more info can be found on the Linux Sunxi wiki: + * + * https://linux-sunxi.org/H3 + */ + +#ifndef HW_ARM_ALLWINNER_H3_H +#define HW_ARM_ALLWINNER_H3_H + +#include "qom/object.h" +#include "hw/arm/boot.h" +#include "hw/timer/allwinner-a10-pit.h" +#include "hw/intc/arm_gic.h" +#include "hw/misc/allwinner-h3-ccu.h" +#include "hw/misc/allwinner-cpucfg.h" +#include "hw/misc/allwinner-h3-dramc.h" +#include "hw/misc/allwinner-h3-sysctrl.h" +#include "hw/misc/allwinner-sid.h" +#include "hw/sd/allwinner-sdhost.h" +#include "hw/net/allwinner-sun8i-emac.h" +#include "hw/rtc/allwinner-rtc.h" +#include "target/arm/cpu.h" +#include "sysemu/block-backend.h" + +/** + * Allwinner H3 device list + * + * This enumeration is can be used refer to a particular device in the + * Allwinner H3 SoC. For example, the physical memory base address for + * each device can be found in the AwH3State object in the memmap member + * using the device enum value as index. + * + * @see AwH3State + */ +enum { + AW_H3_SRAM_A1, + AW_H3_SRAM_A2, + AW_H3_SRAM_C, + AW_H3_SYSCTRL, + AW_H3_MMC0, + AW_H3_SID, + AW_H3_EHCI0, + AW_H3_OHCI0, + AW_H3_EHCI1, + AW_H3_OHCI1, + AW_H3_EHCI2, + AW_H3_OHCI2, + AW_H3_EHCI3, + AW_H3_OHCI3, + AW_H3_CCU, + AW_H3_PIT, + AW_H3_UART0, + AW_H3_UART1, + AW_H3_UART2, + AW_H3_UART3, + AW_H3_EMAC, + AW_H3_DRAMCOM, + AW_H3_DRAMCTL, + AW_H3_DRAMPHY, + AW_H3_GIC_DIST, + AW_H3_GIC_CPU, + AW_H3_GIC_HYP, + AW_H3_GIC_VCPU, + AW_H3_RTC, + AW_H3_CPUCFG, + AW_H3_SDRAM +}; + +/** Total number of CPU cores in the H3 SoC */ +#define AW_H3_NUM_CPUS (4) + +/** + * Allwinner H3 object model + * @{ + */ + +/** Object type for the Allwinner H3 SoC */ +#define TYPE_AW_H3 "allwinner-h3" + +/** Convert input object to Allwinner H3 state object */ +#define AW_H3(obj) OBJECT_CHECK(AwH3State, (obj), TYPE_AW_H3) + +/** @} */ + +/** + * Allwinner H3 object + * + * This struct contains the state of all the devices + * which are currently emulated by the H3 SoC code. + */ +typedef struct AwH3State { + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + + ARMCPU cpus[AW_H3_NUM_CPUS]; + const hwaddr *memmap; + AwA10PITState timer; + AwH3ClockCtlState ccu; + AwCpuCfgState cpucfg; + AwH3DramCtlState dramc; + AwH3SysCtrlState sysctrl; + AwSidState sid; + AwSdHostState mmc0; + AwSun8iEmacState emac; + AwRtcState rtc; + GICState gic; + MemoryRegion sram_a1; + MemoryRegion sram_a2; + MemoryRegion sram_c; +} AwH3State; + +/** + * Emulate Boot ROM firmware setup functionality. + * + * A real Allwinner H3 SoC contains a Boot ROM + * which is the first code that runs right after + * the SoC is powered on. The Boot ROM is responsible + * for loading user code (e.g. a bootloader) from any + * of the supported external devices and writing the + * downloaded code to internal SRAM. After loading the SoC + * begins executing the code written to SRAM. + * + * This function emulates the Boot ROM by copying 32 KiB + * of data from the given block device and writes it to + * the start of the first internal SRAM memory. + * + * @s: Allwinner H3 state object pointer + * @blk: Block backend device object pointer + */ +void allwinner_h3_bootrom_setup(AwH3State *s, BlockBackend *blk); + +#endif /* HW_ARM_ALLWINNER_H3_H */ diff --git a/include/hw/arm/fsl-imx25.h b/include/hw/arm/fsl-imx25.h index 1c86bb55fb..5e196bbf05 100644 --- a/include/hw/arm/fsl-imx25.h +++ b/include/hw/arm/fsl-imx25.h @@ -27,6 +27,8 @@ #include "hw/misc/imx_rngc.h" #include "hw/i2c/imx_i2c.h" #include "hw/gpio/imx_gpio.h" +#include "hw/sd/sdhci.h" +#include "hw/usb/chipidea.h" #include "exec/memory.h" #include "target/arm/cpu.h" @@ -38,6 +40,8 @@ #define FSL_IMX25_NUM_EPITS 2 #define FSL_IMX25_NUM_I2CS 3 #define FSL_IMX25_NUM_GPIOS 4 +#define FSL_IMX25_NUM_ESDHCS 2 +#define FSL_IMX25_NUM_USBS 2 typedef struct FslIMX25State { /*< private >*/ @@ -54,6 +58,8 @@ typedef struct FslIMX25State { IMXRNGCState rngc; IMXI2CState i2c[FSL_IMX25_NUM_I2CS]; IMXGPIOState gpio[FSL_IMX25_NUM_GPIOS]; + SDHCIState esdhc[FSL_IMX25_NUM_ESDHCS]; + ChipideaState usb[FSL_IMX25_NUM_USBS]; MemoryRegion rom[2]; MemoryRegion iram; MemoryRegion iram_alias; @@ -215,10 +221,18 @@ typedef struct FslIMX25State { #define FSL_IMX25_GPIO3_SIZE 0x4000 #define FSL_IMX25_RNGC_ADDR 0x53FB0000 #define FSL_IMX25_RNGC_SIZE 0x4000 +#define FSL_IMX25_ESDHC1_ADDR 0x53FB4000 +#define FSL_IMX25_ESDHC1_SIZE 0x4000 +#define FSL_IMX25_ESDHC2_ADDR 0x53FB8000 +#define FSL_IMX25_ESDHC2_SIZE 0x4000 #define FSL_IMX25_GPIO1_ADDR 0x53FCC000 #define FSL_IMX25_GPIO1_SIZE 0x4000 #define FSL_IMX25_GPIO2_ADDR 0x53FD0000 #define FSL_IMX25_GPIO2_SIZE 0x4000 +#define FSL_IMX25_USB1_ADDR 0x53FF4000 +#define FSL_IMX25_USB1_SIZE 0x0200 +#define FSL_IMX25_USB2_ADDR 0x53FF4400 +#define FSL_IMX25_USB2_SIZE 0x0200 #define FSL_IMX25_AVIC_ADDR 0x68000000 #define FSL_IMX25_AVIC_SIZE 0x4000 #define FSL_IMX25_IRAM_ADDR 0x78000000 @@ -250,5 +264,9 @@ typedef struct FslIMX25State { #define FSL_IMX25_GPIO2_IRQ 51 #define FSL_IMX25_GPIO3_IRQ 16 #define FSL_IMX25_GPIO4_IRQ 23 +#define FSL_IMX25_ESDHC1_IRQ 9 +#define FSL_IMX25_ESDHC2_IRQ 8 +#define FSL_IMX25_USB1_IRQ 37 +#define FSL_IMX25_USB2_IRQ 35 #endif /* FSL_IMX25_H */ diff --git a/include/hw/arm/fsl-imx6.h b/include/hw/arm/fsl-imx6.h index 60eadccb42..973bcb72f7 100644 --- a/include/hw/arm/fsl-imx6.h +++ b/include/hw/arm/fsl-imx6.h @@ -30,6 +30,8 @@ #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" #include "hw/net/imx_fec.h" +#include "hw/usb/chipidea.h" +#include "hw/usb/imx-usb-phy.h" #include "exec/memory.h" #include "cpu.h" @@ -44,6 +46,8 @@ #define FSL_IMX6_NUM_ESDHCS 4 #define FSL_IMX6_NUM_ECSPIS 5 #define FSL_IMX6_NUM_WDTS 2 +#define FSL_IMX6_NUM_USB_PHYS 2 +#define FSL_IMX6_NUM_USBS 4 typedef struct FslIMX6State { /*< private >*/ @@ -62,6 +66,8 @@ typedef struct FslIMX6State { SDHCIState esdhc[FSL_IMX6_NUM_ESDHCS]; IMXSPIState spi[FSL_IMX6_NUM_ECSPIS]; IMX2WdtState wdt[FSL_IMX6_NUM_WDTS]; + IMXUSBPHYState usbphy[FSL_IMX6_NUM_USB_PHYS]; + ChipideaState usb[FSL_IMX6_NUM_USBS]; IMXFECState eth; MemoryRegion rom; MemoryRegion caam; diff --git a/include/hw/arm/fsl-imx6ul.h b/include/hw/arm/fsl-imx6ul.h index eda389aec7..1a0bab8daa 100644 --- a/include/hw/arm/fsl-imx6ul.h +++ b/include/hw/arm/fsl-imx6ul.h @@ -34,6 +34,8 @@ #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" #include "hw/net/imx_fec.h" +#include "hw/usb/chipidea.h" +#include "hw/usb/imx-usb-phy.h" #include "exec/memory.h" #include "cpu.h" @@ -54,6 +56,8 @@ enum FslIMX6ULConfiguration { FSL_IMX6UL_NUM_I2CS = 4, FSL_IMX6UL_NUM_ECSPIS = 4, FSL_IMX6UL_NUM_ADCS = 2, + FSL_IMX6UL_NUM_USB_PHYS = 2, + FSL_IMX6UL_NUM_USBS = 2, }; typedef struct FslIMX6ULState { @@ -77,6 +81,8 @@ typedef struct FslIMX6ULState { IMXFECState eth[FSL_IMX6UL_NUM_ETHS]; SDHCIState usdhc[FSL_IMX6UL_NUM_USDHCS]; IMX2WdtState wdt[FSL_IMX6UL_NUM_WDTS]; + IMXUSBPHYState usbphy[FSL_IMX6UL_NUM_USB_PHYS]; + ChipideaState usb[FSL_IMX6UL_NUM_USBS]; MemoryRegion rom; MemoryRegion caam; MemoryRegion ocram; @@ -145,6 +151,10 @@ enum FslIMX6ULMemoryMap { FSL_IMX6UL_EPIT2_ADDR = 0x020D4000, FSL_IMX6UL_EPIT1_ADDR = 0x020D0000, FSL_IMX6UL_SNVS_HP_ADDR = 0x020CC000, + FSL_IMX6UL_USBPHY2_ADDR = 0x020CA000, + FSL_IMX6UL_USBPHY2_SIZE = (4 * 1024), + FSL_IMX6UL_USBPHY1_ADDR = 0x020C9000, + FSL_IMX6UL_USBPHY1_SIZE = (4 * 1024), FSL_IMX6UL_ANALOG_ADDR = 0x020C8000, FSL_IMX6UL_CCM_ADDR = 0x020C4000, FSL_IMX6UL_WDOG2_ADDR = 0x020C0000, @@ -241,10 +251,10 @@ enum FslIMX6ULIRQs { FSL_IMX6UL_UART7_IRQ = 39, FSL_IMX6UL_UART8_IRQ = 40, - FSL_IMX6UL_USB1_IRQ = 42, - FSL_IMX6UL_USB2_IRQ = 43, + FSL_IMX6UL_USB1_IRQ = 43, + FSL_IMX6UL_USB2_IRQ = 42, FSL_IMX6UL_USB_PHY1_IRQ = 44, - FSL_IMX6UL_USB_PHY2_IRQ = 44, + FSL_IMX6UL_USB_PHY2_IRQ = 45, FSL_IMX6UL_CAAM_JQ2_IRQ = 46, FSL_IMX6UL_CAAM_ERR_IRQ = 47, diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 1f37844e5c..ca4a4b1ad1 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -85,7 +85,7 @@ typedef struct SMMUDevice { typedef struct SMMUPciBus { PCIBus *bus; - SMMUDevice *pbdev[0]; /* Parent array is sparse, so dynamically alloc */ + SMMUDevice *pbdev[]; /* Parent array is sparse, so dynamically alloc */ } SMMUPciBus; typedef struct SMMUIOTLBKey { diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 02f500cb8e..893796d3b0 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -95,6 +95,14 @@ typedef enum VirtIOMMUType { VIRT_IOMMU_VIRTIO, } VirtIOMMUType; +typedef enum VirtGICType { + VIRT_GIC_VERSION_MAX, + VIRT_GIC_VERSION_HOST, + VIRT_GIC_VERSION_2, + VIRT_GIC_VERSION_3, + VIRT_GIC_VERSION_NOSEL, +} VirtGICType; + typedef struct MemMapEntry { hwaddr base; hwaddr size; @@ -123,7 +131,7 @@ typedef struct { bool highmem_ecam; bool its; bool virt; - int32_t gic_version; + VirtGICType gic_version; VirtIOMMUType iommu; uint16_t virtio_iommu_bdf; struct arm_boot_info bootinfo; @@ -162,7 +170,7 @@ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms) uint32_t redist0_capacity = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; - assert(vms->gic_version == 3); + assert(vms->gic_version == VIRT_GIC_VERSION_3); return vms->smp_cpus > redist0_capacity ? 2 : 1; } diff --git a/include/hw/boards.h b/include/hw/boards.h index 9bc42dfb22..236d239c19 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -71,7 +71,7 @@ typedef struct CPUArchId { */ typedef struct { int len; - CPUArchId cpus[0]; + CPUArchId cpus[]; } CPUArchIdList; /** @@ -236,12 +236,14 @@ typedef struct DeviceMemoryState { * @cpus: the number of present logical processors on the machine * @cores: the number of cores in one package * @threads: the number of threads in one core + * @sockets: the number of sockets on the machine * @max_cpus: the maximum number of logical processors on the machine */ typedef struct CpuTopology { unsigned int cpus; unsigned int cores; unsigned int threads; + unsigned int sockets; unsigned int max_cpus; } CpuTopology; diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 73e9a869a4..5bf94d28cf 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -79,7 +79,6 @@ struct TranslationBlock; * @class_by_name: Callback to map -cpu command line model name to an * instantiatable CPU type. * @parse_features: Callback to parse command line arguments. - * @reset: Callback to reset the #CPUState to its initial state. * @reset_dump_flags: #CPUDumpFlags to use for reset logging. * @has_work: Callback for checking if there is work to do. * @do_interrupt: Callback for interrupt handling. @@ -165,7 +164,6 @@ typedef struct CPUClass { ObjectClass *(*class_by_name)(const char *cpu_model); void (*parse_features)(const char *typename, char *str, Error **errp); - void (*reset)(CPUState *cpu); int reset_dump_flags; bool (*has_work)(CPUState *cpu); void (*do_interrupt)(CPUState *cpu); @@ -195,7 +193,7 @@ typedef struct CPUClass { hwaddr (*get_phys_page_attrs_debug)(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); int (*asidx_from_attrs)(CPUState *cpu, MemTxAttrs attrs); - int (*gdb_read_register)(CPUState *cpu, uint8_t *buf, int reg); + int (*gdb_read_register)(CPUState *cpu, GByteArray *buf, int reg); int (*gdb_write_register)(CPUState *cpu, uint8_t *buf, int reg); bool (*debug_check_watchpoint)(CPUState *cpu, CPUWatchpoint *wp); void (*debug_excp_handler)(CPUState *cpu); @@ -1135,10 +1133,6 @@ void cpu_exec_unrealizefn(CPUState *cpu); */ bool target_words_bigendian(void); -void cpu_class_set_parent_reset(CPUClass *cc, - void (*child_reset)(CPUState *cpu), - void (**parent_reset)(CPUState *cpu)); - #ifdef NEED_CPU_H #ifdef CONFIG_SOFTMMU diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index a1c4afcda5..3870052f5f 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -114,7 +114,8 @@ struct VTDAddressSpace { struct VTDBus { PCIBus* bus; /* A reference to the bus to provide translation for */ - VTDAddressSpace *dev_as[0]; /* A table of VTDAddressSpace objects indexed by devfn */ + /* A table of VTDAddressSpace objects indexed by devfn */ + VTDAddressSpace *dev_as[]; }; struct VTDIOTLBEntry { diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index 4ff5b2da6c..b9593b9905 100644 --- a/include/hw/i386/topology.h +++ b/include/hw/i386/topology.h @@ -45,11 +45,18 @@ */ typedef uint32_t apic_id_t; -typedef struct X86CPUTopoInfo { +typedef struct X86CPUTopoIDs { unsigned pkg_id; unsigned die_id; unsigned core_id; unsigned smt_id; +} X86CPUTopoIDs; + +typedef struct X86CPUTopoInfo { + unsigned nodes_per_pkg; + unsigned dies_per_pkg; + unsigned cores_per_die; + unsigned threads_per_core; } X86CPUTopoInfo; /* Return the bit width needed for 'count' IDs @@ -63,120 +70,102 @@ static unsigned apicid_bitwidth_for_count(unsigned count) /* Bit width of the SMT_ID (thread ID) field on the APIC ID */ -static inline unsigned apicid_smt_width(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads) +static inline unsigned apicid_smt_width(X86CPUTopoInfo *topo_info) { - return apicid_bitwidth_for_count(nr_threads); + return apicid_bitwidth_for_count(topo_info->threads_per_core); } /* Bit width of the Core_ID field */ -static inline unsigned apicid_core_width(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads) +static inline unsigned apicid_core_width(X86CPUTopoInfo *topo_info) { - return apicid_bitwidth_for_count(nr_cores); + return apicid_bitwidth_for_count(topo_info->cores_per_die); } /* Bit width of the Die_ID field */ -static inline unsigned apicid_die_width(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads) +static inline unsigned apicid_die_width(X86CPUTopoInfo *topo_info) { - return apicid_bitwidth_for_count(nr_dies); + return apicid_bitwidth_for_count(topo_info->dies_per_pkg); } /* Bit offset of the Core_ID field */ -static inline unsigned apicid_core_offset(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads) +static inline unsigned apicid_core_offset(X86CPUTopoInfo *topo_info) { - return apicid_smt_width(nr_dies, nr_cores, nr_threads); + return apicid_smt_width(topo_info); } /* Bit offset of the Die_ID field */ -static inline unsigned apicid_die_offset(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads) +static inline unsigned apicid_die_offset(X86CPUTopoInfo *topo_info) { - return apicid_core_offset(nr_dies, nr_cores, nr_threads) + - apicid_core_width(nr_dies, nr_cores, nr_threads); + return apicid_core_offset(topo_info) + apicid_core_width(topo_info); } /* Bit offset of the Pkg_ID (socket ID) field */ -static inline unsigned apicid_pkg_offset(unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads) +static inline unsigned apicid_pkg_offset(X86CPUTopoInfo *topo_info) { - return apicid_die_offset(nr_dies, nr_cores, nr_threads) + - apicid_die_width(nr_dies, nr_cores, nr_threads); + return apicid_die_offset(topo_info) + apicid_die_width(topo_info); } /* 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_dies, - unsigned nr_cores, - unsigned nr_threads, - const X86CPUTopoInfo *topo) +static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info, + const X86CPUTopoIDs *topo_ids) { - return (topo->pkg_id << apicid_pkg_offset(nr_dies, nr_cores, nr_threads)) | - (topo->die_id << apicid_die_offset(nr_dies, nr_cores, nr_threads)) | - (topo->core_id << apicid_core_offset(nr_dies, nr_cores, nr_threads)) | - topo->smt_id; + return (topo_ids->pkg_id << apicid_pkg_offset(topo_info)) | + (topo_ids->die_id << apicid_die_offset(topo_info)) | + (topo_ids->core_id << apicid_core_offset(topo_info)) | + topo_ids->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_dies, - unsigned nr_cores, - unsigned nr_threads, +static inline void x86_topo_ids_from_idx(X86CPUTopoInfo *topo_info, unsigned cpu_index, - X86CPUTopoInfo *topo) + X86CPUTopoIDs *topo_ids) { - topo->pkg_id = cpu_index / (nr_dies * nr_cores * nr_threads); - topo->die_id = cpu_index / (nr_cores * nr_threads) % nr_dies; - topo->core_id = cpu_index / nr_threads % nr_cores; - topo->smt_id = cpu_index % nr_threads; + unsigned nr_dies = topo_info->dies_per_pkg; + unsigned nr_cores = topo_info->cores_per_die; + unsigned nr_threads = topo_info->threads_per_core; + + topo_ids->pkg_id = cpu_index / (nr_dies * nr_cores * nr_threads); + topo_ids->die_id = cpu_index / (nr_cores * nr_threads) % nr_dies; + topo_ids->core_id = cpu_index / nr_threads % nr_cores; + topo_ids->smt_id = cpu_index % nr_threads; } /* Calculate thread/core/package IDs for a specific topology, * based on APIC ID */ static inline void x86_topo_ids_from_apicid(apic_id_t apicid, - unsigned nr_dies, - unsigned nr_cores, - unsigned nr_threads, - X86CPUTopoInfo *topo) + X86CPUTopoInfo *topo_info, + X86CPUTopoIDs *topo_ids) { - topo->smt_id = apicid & - ~(0xFFFFFFFFUL << apicid_smt_width(nr_dies, nr_cores, nr_threads)); - topo->core_id = - (apicid >> apicid_core_offset(nr_dies, nr_cores, nr_threads)) & - ~(0xFFFFFFFFUL << apicid_core_width(nr_dies, nr_cores, nr_threads)); - topo->die_id = - (apicid >> apicid_die_offset(nr_dies, nr_cores, nr_threads)) & - ~(0xFFFFFFFFUL << apicid_die_width(nr_dies, nr_cores, nr_threads)); - topo->pkg_id = apicid >> apicid_pkg_offset(nr_dies, nr_cores, nr_threads); + topo_ids->smt_id = apicid & + ~(0xFFFFFFFFUL << apicid_smt_width(topo_info)); + topo_ids->core_id = + (apicid >> apicid_core_offset(topo_info)) & + ~(0xFFFFFFFFUL << apicid_core_width(topo_info)); + topo_ids->die_id = + (apicid >> apicid_die_offset(topo_info)) & + ~(0xFFFFFFFFUL << apicid_die_width(topo_info)); + topo_ids->pkg_id = apicid >> apicid_pkg_offset(topo_info); } /* 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_dies, - unsigned nr_cores, - unsigned nr_threads, +static inline apic_id_t x86_apicid_from_cpu_idx(X86CPUTopoInfo *topo_info, unsigned cpu_index) { - X86CPUTopoInfo topo; - x86_topo_ids_from_idx(nr_dies, nr_cores, nr_threads, cpu_index, &topo); - return apicid_from_topo_ids(nr_dies, nr_cores, nr_threads, &topo); + X86CPUTopoIDs topo_ids; + x86_topo_ids_from_idx(topo_info, cpu_index, &topo_ids); + return x86_apicid_from_topo_ids(topo_info, &topo_ids); } #endif /* HW_I386_TOPOLOGY_H */ diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 41fe37b8a3..22babcb3bb 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -21,6 +21,7 @@ #include "exec/hwaddr.h" #include "qemu/notify.h" +#include "hw/i386/topology.h" #include "hw/boards.h" #include "hw/nmi.h" #include "hw/isa/isa.h" @@ -82,6 +83,8 @@ typedef struct { #define X86_MACHINE_CLASS(class) \ OBJECT_CLASS_CHECK(X86MachineClass, class, TYPE_X86_MACHINE) +void init_topo_info(X86CPUTopoInfo *topo_info, const X86MachineState *x86ms); + uint32_t x86_cpu_apic_id_from_index(X86MachineState *pcms, unsigned int cpu_index); diff --git a/include/hw/ide.h b/include/hw/ide.h index 28d8a06439..c5ce5da4f4 100644 --- a/include/hw/ide.h +++ b/include/hw/ide.h @@ -2,23 +2,14 @@ #define HW_IDE_H #include "hw/isa/isa.h" -#include "hw/pci/pci.h" #include "exec/memory.h" -#define MAX_IDE_DEVS 2 - /* ide-isa.c */ ISADevice *isa_ide_init(ISABus *bus, int iobase, int iobase2, int isairq, DriveInfo *hd0, DriveInfo *hd1); /* ide-pci.c */ -void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, - int secondary_ide_enabled); -PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); -PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); -PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); int pci_piix3_xen_ide_unplug(DeviceState *dev, bool aux); -void via_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); /* ide-mmio.c */ void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1); diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index 1bc1fc73e5..55da35d768 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -27,6 +27,8 @@ typedef struct IDEDMAOps IDEDMAOps; #define TYPE_IDE_BUS "IDE" #define IDE_BUS(obj) OBJECT_CHECK(IDEBus, (obj), TYPE_IDE_BUS) +#define MAX_IDE_DEVS 2 + /* Bits of HD_STATUS */ #define ERR_STAT 0x01 #define INDEX_STAT 0x02 diff --git a/include/hw/ide/pci.h b/include/hw/ide/pci.h index a9f2c33e68..dd504e5a0b 100644 --- a/include/hw/ide/pci.h +++ b/include/hw/ide/pci.h @@ -2,6 +2,7 @@ #define HW_IDE_PCI_H #include "hw/ide/internal.h" +#include "hw/pci/pci.h" #define BM_STATUS_DMAING 0x01 #define BM_STATUS_ERROR 0x02 @@ -62,7 +63,7 @@ static inline IDEState *bmdma_active_if(BMDMAState *bmdma) void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d); void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val); extern MemoryRegionOps bmdma_addr_ioport_ops; -void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table); +void pci_ide_create_devs(PCIDevice *dev); extern const VMStateDescription vmstate_ide_pci; extern const MemoryRegionOps pci_ide_cmd_le_ops; diff --git a/include/hw/misc/allwinner-cpucfg.h b/include/hw/misc/allwinner-cpucfg.h new file mode 100644 index 0000000000..2c3693a8be --- /dev/null +++ b/include/hw/misc/allwinner-cpucfg.h @@ -0,0 +1,52 @@ +/* + * Allwinner CPU Configuration Module emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_MISC_ALLWINNER_CPUCFG_H +#define HW_MISC_ALLWINNER_CPUCFG_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +/** + * Object model + * @{ + */ + +#define TYPE_AW_CPUCFG "allwinner-cpucfg" +#define AW_CPUCFG(obj) \ + OBJECT_CHECK(AwCpuCfgState, (obj), TYPE_AW_CPUCFG) + +/** @} */ + +/** + * Allwinner CPU Configuration Module instance state + */ +typedef struct AwCpuCfgState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + uint32_t gen_ctrl; + uint32_t super_standby; + uint32_t entry_addr; + +} AwCpuCfgState; + +#endif /* HW_MISC_ALLWINNER_CPUCFG_H */ diff --git a/include/hw/misc/allwinner-h3-ccu.h b/include/hw/misc/allwinner-h3-ccu.h new file mode 100644 index 0000000000..eec59649f3 --- /dev/null +++ b/include/hw/misc/allwinner-h3-ccu.h @@ -0,0 +1,66 @@ +/* + * Allwinner H3 Clock Control Unit emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_MISC_ALLWINNER_H3_CCU_H +#define HW_MISC_ALLWINNER_H3_CCU_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +/** + * @name Constants + * @{ + */ + +/** Size of register I/O address space used by CCU device */ +#define AW_H3_CCU_IOSIZE (0x400) + +/** Total number of known registers */ +#define AW_H3_CCU_REGS_NUM (AW_H3_CCU_IOSIZE / sizeof(uint32_t)) + +/** @} */ + +/** + * @name Object model + * @{ + */ + +#define TYPE_AW_H3_CCU "allwinner-h3-ccu" +#define AW_H3_CCU(obj) \ + OBJECT_CHECK(AwH3ClockCtlState, (obj), TYPE_AW_H3_CCU) + +/** @} */ + +/** + * Allwinner H3 CCU object instance state. + */ +typedef struct AwH3ClockCtlState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Array of hardware registers */ + uint32_t regs[AW_H3_CCU_REGS_NUM]; + +} AwH3ClockCtlState; + +#endif /* HW_MISC_ALLWINNER_H3_CCU_H */ diff --git a/include/hw/misc/allwinner-h3-dramc.h b/include/hw/misc/allwinner-h3-dramc.h new file mode 100644 index 0000000000..bacdf236b7 --- /dev/null +++ b/include/hw/misc/allwinner-h3-dramc.h @@ -0,0 +1,106 @@ +/* + * Allwinner H3 SDRAM Controller emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_MISC_ALLWINNER_H3_DRAMC_H +#define HW_MISC_ALLWINNER_H3_DRAMC_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "exec/hwaddr.h" + +/** + * Constants + * @{ + */ + +/** Highest register address used by DRAMCOM module */ +#define AW_H3_DRAMCOM_REGS_MAXADDR (0x804) + +/** Total number of known DRAMCOM registers */ +#define AW_H3_DRAMCOM_REGS_NUM (AW_H3_DRAMCOM_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** Highest register address used by DRAMCTL module */ +#define AW_H3_DRAMCTL_REGS_MAXADDR (0x88c) + +/** Total number of known DRAMCTL registers */ +#define AW_H3_DRAMCTL_REGS_NUM (AW_H3_DRAMCTL_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** Highest register address used by DRAMPHY module */ +#define AW_H3_DRAMPHY_REGS_MAXADDR (0x4) + +/** Total number of known DRAMPHY registers */ +#define AW_H3_DRAMPHY_REGS_NUM (AW_H3_DRAMPHY_REGS_MAXADDR / \ + sizeof(uint32_t)) + +/** @} */ + +/** + * Object model + * @{ + */ + +#define TYPE_AW_H3_DRAMC "allwinner-h3-dramc" +#define AW_H3_DRAMC(obj) \ + OBJECT_CHECK(AwH3DramCtlState, (obj), TYPE_AW_H3_DRAMC) + +/** @} */ + +/** + * Allwinner H3 SDRAM Controller object instance state. + */ +typedef struct AwH3DramCtlState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Physical base address for start of RAM */ + hwaddr ram_addr; + + /** Total RAM size in megabytes */ + uint32_t ram_size; + + /** + * @name Memory Regions + * @{ + */ + + MemoryRegion row_mirror; /**< Simulates rows for RAM size detection */ + MemoryRegion row_mirror_alias; /**< Alias of the row which is mirrored */ + MemoryRegion dramcom_iomem; /**< DRAMCOM module I/O registers */ + MemoryRegion dramctl_iomem; /**< DRAMCTL module I/O registers */ + MemoryRegion dramphy_iomem; /**< DRAMPHY module I/O registers */ + + /** @} */ + + /** + * @name Hardware Registers + * @{ + */ + + uint32_t dramcom[AW_H3_DRAMCOM_REGS_NUM]; /**< Array of DRAMCOM registers */ + uint32_t dramctl[AW_H3_DRAMCTL_REGS_NUM]; /**< Array of DRAMCTL registers */ + uint32_t dramphy[AW_H3_DRAMPHY_REGS_NUM] ;/**< Array of DRAMPHY registers */ + + /** @} */ + +} AwH3DramCtlState; + +#endif /* HW_MISC_ALLWINNER_H3_DRAMC_H */ diff --git a/include/hw/misc/allwinner-h3-sysctrl.h b/include/hw/misc/allwinner-h3-sysctrl.h new file mode 100644 index 0000000000..af4119e026 --- /dev/null +++ b/include/hw/misc/allwinner-h3-sysctrl.h @@ -0,0 +1,67 @@ +/* + * Allwinner H3 System Control emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_MISC_ALLWINNER_H3_SYSCTRL_H +#define HW_MISC_ALLWINNER_H3_SYSCTRL_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +/** + * @name Constants + * @{ + */ + +/** Highest register address used by System Control device */ +#define AW_H3_SYSCTRL_REGS_MAXADDR (0x30) + +/** Total number of known registers */ +#define AW_H3_SYSCTRL_REGS_NUM ((AW_H3_SYSCTRL_REGS_MAXADDR / \ + sizeof(uint32_t)) + 1) + +/** @} */ + +/** + * @name Object model + * @{ + */ + +#define TYPE_AW_H3_SYSCTRL "allwinner-h3-sysctrl" +#define AW_H3_SYSCTRL(obj) \ + OBJECT_CHECK(AwH3SysCtrlState, (obj), TYPE_AW_H3_SYSCTRL) + +/** @} */ + +/** + * Allwinner H3 System Control object instance state + */ +typedef struct AwH3SysCtrlState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Array of hardware registers */ + uint32_t regs[AW_H3_SYSCTRL_REGS_NUM]; + +} AwH3SysCtrlState; + +#endif /* HW_MISC_ALLWINNER_H3_SYSCTRL_H */ diff --git a/include/hw/misc/allwinner-sid.h b/include/hw/misc/allwinner-sid.h new file mode 100644 index 0000000000..4c1fa4762b --- /dev/null +++ b/include/hw/misc/allwinner-sid.h @@ -0,0 +1,60 @@ +/* + * Allwinner Security ID emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_MISC_ALLWINNER_SID_H +#define HW_MISC_ALLWINNER_SID_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "qemu/uuid.h" + +/** + * Object model + * @{ + */ + +#define TYPE_AW_SID "allwinner-sid" +#define AW_SID(obj) \ + OBJECT_CHECK(AwSidState, (obj), TYPE_AW_SID) + +/** @} */ + +/** + * Allwinner Security ID object instance state + */ +typedef struct AwSidState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Control register defines how and what to read */ + uint32_t control; + + /** RdKey register contains the data retrieved by the device */ + uint32_t rdkey; + + /** Stores the emulated device identifier */ + QemuUUID identifier; + +} AwSidState; + +#endif /* HW_MISC_ALLWINNER_SID_H */ diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h index 070a694eb5..87335a991c 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -27,6 +27,7 @@ #define MACIO_H #include "hw/char/escc.h" +#include "hw/pci/pci.h" #include "hw/ide/internal.h" #include "hw/intc/heathrow_pic.h" #include "hw/misc/macio/cuda.h" diff --git a/include/hw/net/allwinner-sun8i-emac.h b/include/hw/net/allwinner-sun8i-emac.h new file mode 100644 index 0000000000..eda034e96b --- /dev/null +++ b/include/hw/net/allwinner-sun8i-emac.h @@ -0,0 +1,99 @@ +/* + * Allwinner Sun8i Ethernet MAC emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_NET_ALLWINNER_SUN8I_EMAC_H +#define HW_NET_ALLWINNER_SUN8I_EMAC_H + +#include "qom/object.h" +#include "net/net.h" +#include "hw/sysbus.h" + +/** + * Object model + * @{ + */ + +#define TYPE_AW_SUN8I_EMAC "allwinner-sun8i-emac" +#define AW_SUN8I_EMAC(obj) \ + OBJECT_CHECK(AwSun8iEmacState, (obj), TYPE_AW_SUN8I_EMAC) + +/** @} */ + +/** + * Allwinner Sun8i EMAC object instance state + */ +typedef struct AwSun8iEmacState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Interrupt output signal to notify CPU */ + qemu_irq irq; + + /** Generic Network Interface Controller (NIC) for networking API */ + NICState *nic; + + /** Generic Network Interface Controller (NIC) configuration */ + NICConf conf; + + /** + * @name Media Independent Interface (MII) + * @{ + */ + + uint8_t mii_phy_addr; /**< PHY address */ + uint32_t mii_cr; /**< Control */ + uint32_t mii_st; /**< Status */ + uint32_t mii_adv; /**< Advertised Abilities */ + + /** @} */ + + /** + * @name Hardware Registers + * @{ + */ + + uint32_t basic_ctl0; /**< Basic Control 0 */ + uint32_t basic_ctl1; /**< Basic Control 1 */ + uint32_t int_en; /**< Interrupt Enable */ + uint32_t int_sta; /**< Interrupt Status */ + uint32_t frm_flt; /**< Receive Frame Filter */ + + uint32_t rx_ctl0; /**< Receive Control 0 */ + uint32_t rx_ctl1; /**< Receive Control 1 */ + uint32_t rx_desc_head; /**< Receive Descriptor List Address */ + uint32_t rx_desc_curr; /**< Current Receive Descriptor Address */ + + uint32_t tx_ctl0; /**< Transmit Control 0 */ + uint32_t tx_ctl1; /**< Transmit Control 1 */ + uint32_t tx_desc_head; /**< Transmit Descriptor List Address */ + uint32_t tx_desc_curr; /**< Current Transmit Descriptor Address */ + uint32_t tx_flowctl; /**< Transmit Flow Control */ + + uint32_t mii_cmd; /**< Management Interface Command */ + uint32_t mii_data; /**< Management Interface Data */ + + /** @} */ + +} AwSun8iEmacState; + +#endif /* HW_NET_ALLWINNER_SUN8I_H */ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 09110961a5..42d64a0368 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -79,10 +79,10 @@ typedef enum { #define SPAPR_CAP_LARGE_DECREMENTER 0x08 /* Count Cache Flush Assist HW Instruction */ #define SPAPR_CAP_CCF_ASSIST 0x09 -/* FWNMI machine check handling */ -#define SPAPR_CAP_FWNMI_MCE 0x0A +/* Implements PAPR FWNMI option */ +#define SPAPR_CAP_FWNMI 0x0A /* Num Caps */ -#define SPAPR_CAP_NUM (SPAPR_CAP_FWNMI_MCE + 1) +#define SPAPR_CAP_NUM (SPAPR_CAP_FWNMI + 1) /* * Capability Values @@ -126,6 +126,7 @@ struct SpaprMachineClass { bool pre_4_1_migration; /* don't migrate hpt-max-page-size */ bool linux_pci_probe; bool smp_threads_vsmt; /* set VSMT to smp_threads by default */ + hwaddr rma_limit; /* clamp the RMA to this size */ void (*phb_placement)(SpaprMachineState *spapr, uint32_t index, uint64_t *buid, hwaddr *pio, @@ -156,7 +157,6 @@ struct SpaprMachineState { SpaprPendingHpt *pending_hpt; /* in-progress resize */ hwaddr rma_size; - int vrma_adjust; uint32_t fdt_size; uint32_t fdt_initial_size; void *fdt_blob; @@ -192,14 +192,22 @@ struct SpaprMachineState { * occurs during the unplug process. */ QTAILQ_HEAD(, SpaprDimmState) pending_dimm_unplugs; - /* State related to "ibm,nmi-register" and "ibm,nmi-interlock" calls */ - target_ulong guest_machine_check_addr; - /* - * mc_status is set to -1 if mc is not in progress, else is set to the CPU - * handling the mc. + /* State related to FWNMI option */ + + /* System Reset and Machine Check Notification Routine addresses + * registered by "ibm,nmi-register" RTAS call. + */ + target_ulong fwnmi_system_reset_addr; + target_ulong fwnmi_machine_check_addr; + + /* Machine Check FWNMI synchronization, fwnmi_machine_check_interlock is + * set to -1 if a FWNMI machine check is not in progress, else is set to + * the CPU that was delivered the machine check, and is set back to -1 + * when that CPU makes an "ibm,nmi-interlock" RTAS call. The cond is used + * to synchronize other CPUs. */ - int mc_status; - QemuCond mc_delivery_cond; + int fwnmi_machine_check_interlock; + QemuCond fwnmi_machine_check_interlock_cond; /*< public >*/ char *kvm_type; @@ -736,6 +744,7 @@ void spapr_load_rtas(SpaprMachineState *spapr, void *fdt, hwaddr addr); #define SPAPR_IS_PCI_LIOBN(liobn) (!!((liobn) & 0x80000000)) #define SPAPR_PCI_DMA_WINDOW_NUM(liobn) ((liobn) & 0xff) +#define RTAS_SIZE 2048 #define RTAS_ERROR_LOG_MAX 2048 /* Offset from rtas-base where error log is placed */ @@ -795,7 +804,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space); void spapr_events_init(SpaprMachineState *sm); void spapr_dt_events(SpaprMachineState *sm, void *fdt); void close_htab_fd(SpaprMachineState *spapr); -void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr); +void spapr_setup_hpt(SpaprMachineState *spapr); void spapr_free_hpt(SpaprMachineState *spapr); SpaprTceTable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn); void spapr_tce_table_enable(SpaprTceTable *tcet, @@ -824,6 +833,7 @@ int spapr_hpt_shift_for_ramsize(uint64_t ramsize); void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp); void spapr_clear_pending_events(SpaprMachineState *spapr); +void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr); int spapr_max_server_number(SpaprMachineState *spapr); void spapr_store_hpte(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte0, uint64_t pte1); diff --git a/include/hw/ppc/spapr_cpu_core.h b/include/hw/ppc/spapr_cpu_core.h index 1c4cc6559c..7aed8f555b 100644 --- a/include/hw/ppc/spapr_cpu_core.h +++ b/include/hw/ppc/spapr_cpu_core.h @@ -40,7 +40,9 @@ typedef struct SpaprCpuCoreClass { } SpaprCpuCoreClass; const char *spapr_get_cpu_core_type(const char *cpu_type); -void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3); +void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, + target_ulong r1, target_ulong r3, + target_ulong r4); typedef struct SpaprCpuState { uint64_t vpa_addr; diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h index 2bed517a2b..d4dee9e06a 100644 --- a/include/hw/ppc/spapr_ovec.h +++ b/include/hw/ppc/spapr_ovec.h @@ -72,8 +72,8 @@ void spapr_ovec_set(SpaprOptionVector *ov, long bitnr); void spapr_ovec_clear(SpaprOptionVector *ov, long bitnr); bool spapr_ovec_test(SpaprOptionVector *ov, long bitnr); SpaprOptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector); -int spapr_ovec_populate_dt(void *fdt, int fdt_offset, - SpaprOptionVector *ov, const char *name); +int spapr_dt_ovec(void *fdt, int fdt_offset, + SpaprOptionVector *ov, const char *name); /* migration */ extern const VMStateDescription vmstate_spapr_ovec; diff --git a/include/hw/registerfields.h b/include/hw/registerfields.h index 2659a58737..0407edb7ec 100644 --- a/include/hw/registerfields.h +++ b/include/hw/registerfields.h @@ -22,6 +22,14 @@ enum { A_ ## reg = (addr) }; \ enum { R_ ## reg = (addr) / 4 }; +#define REG8(reg, addr) \ + enum { A_ ## reg = (addr) }; \ + enum { R_ ## reg = (addr) }; + +#define REG16(reg, addr) \ + enum { A_ ## reg = (addr) }; \ + enum { R_ ## reg = (addr) / 2 }; + /* Define SHIFT, LENGTH and MASK constants for a field within a register */ /* This macro will define R_FOO_BAR_MASK, R_FOO_BAR_SHIFT and R_FOO_BAR_LENGTH @@ -34,6 +42,12 @@ MAKE_64BIT_MASK(shift, length)}; /* Extract a field from a register */ +#define FIELD_EX8(storage, reg, field) \ + extract8((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) +#define FIELD_EX16(storage, reg, field) \ + extract16((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH) #define FIELD_EX32(storage, reg, field) \ extract32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ R_ ## reg ## _ ## field ## _LENGTH) @@ -49,6 +63,22 @@ * Assigning values larger then the target field will result in * compilation warnings. */ +#define FIELD_DP8(storage, reg, field, val) ({ \ + struct { \ + unsigned int v:R_ ## reg ## _ ## field ## _LENGTH; \ + } v = { .v = val }; \ + uint8_t d; \ + d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, v.v); \ + d; }) +#define FIELD_DP16(storage, reg, field, val) ({ \ + struct { \ + unsigned int v:R_ ## reg ## _ ## field ## _LENGTH; \ + } v = { .v = val }; \ + uint16_t d; \ + d = deposit32((storage), R_ ## reg ## _ ## field ## _SHIFT, \ + R_ ## reg ## _ ## field ## _LENGTH, v.v); \ + d; }) #define FIELD_DP32(storage, reg, field, val) ({ \ struct { \ unsigned int v:R_ ## reg ## _ ## field ## _LENGTH; \ diff --git a/include/hw/rtc/allwinner-rtc.h b/include/hw/rtc/allwinner-rtc.h new file mode 100644 index 0000000000..7893f74795 --- /dev/null +++ b/include/hw/rtc/allwinner-rtc.h @@ -0,0 +1,134 @@ +/* + * Allwinner Real Time Clock emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_MISC_ALLWINNER_RTC_H +#define HW_MISC_ALLWINNER_RTC_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +/** + * Constants + * @{ + */ + +/** Highest register address used by RTC device */ +#define AW_RTC_REGS_MAXADDR (0x200) + +/** Total number of known registers */ +#define AW_RTC_REGS_NUM (AW_RTC_REGS_MAXADDR / sizeof(uint32_t)) + +/** @} */ + +/** + * Object model types + * @{ + */ + +/** Generic Allwinner RTC device (abstract) */ +#define TYPE_AW_RTC "allwinner-rtc" + +/** Allwinner RTC sun4i family (A10, A12) */ +#define TYPE_AW_RTC_SUN4I TYPE_AW_RTC "-sun4i" + +/** Allwinner RTC sun6i family and newer (A31, H2+, H3, etc) */ +#define TYPE_AW_RTC_SUN6I TYPE_AW_RTC "-sun6i" + +/** Allwinner RTC sun7i family (A20) */ +#define TYPE_AW_RTC_SUN7I TYPE_AW_RTC "-sun7i" + +/** @} */ + +/** + * Object model macros + * @{ + */ + +#define AW_RTC(obj) \ + OBJECT_CHECK(AwRtcState, (obj), TYPE_AW_RTC) +#define AW_RTC_CLASS(klass) \ + OBJECT_CLASS_CHECK(AwRtcClass, (klass), TYPE_AW_RTC) +#define AW_RTC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AwRtcClass, (obj), TYPE_AW_RTC) + +/** @} */ + +/** + * Allwinner RTC per-object instance state. + */ +typedef struct AwRtcState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + /** + * Actual year represented by the device when year counter is zero + * + * Can be overridden by the user using the corresponding 'base-year' + * property. The base year used by the target OS driver can vary, for + * example the Linux driver for sun6i uses 1970 while NetBSD uses 2000. + */ + int base_year; + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Array of hardware registers */ + uint32_t regs[AW_RTC_REGS_NUM]; + +} AwRtcState; + +/** + * Allwinner RTC class-level struct. + * + * This struct is filled by each sunxi device specific code + * such that the generic code can use this struct to support + * all devices. + */ +typedef struct AwRtcClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + + /** Defines device specific register map */ + const uint8_t *regmap; + + /** Size of the regmap in bytes */ + size_t regmap_size; + + /** + * Read device specific register + * + * @offset: register offset to read + * @return true if register read successful, false otherwise + */ + bool (*read)(AwRtcState *s, uint32_t offset); + + /** + * Write device specific register + * + * @offset: register offset to write + * @data: value to set in register + * @return true if register write successful, false otherwise + */ + bool (*write)(AwRtcState *s, uint32_t offset, uint32_t data); + +} AwRtcClass; + +#endif /* HW_MISC_ALLWINNER_RTC_H */ diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index bdc32a3c09..700a610f33 100644 --- a/include/hw/s390x/event-facility.h +++ b/include/hw/s390x/event-facility.h @@ -122,7 +122,7 @@ typedef struct MDBO { typedef struct MDB { MdbHeader header; - MDBO mdbo[0]; + MDBO mdbo[]; } QEMU_PACKED MDB; typedef struct SclpMsg { diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index c54413b78c..cd7b24359f 100644 --- a/include/hw/s390x/sclp.h +++ b/include/hw/s390x/sclp.h @@ -132,7 +132,7 @@ typedef struct ReadInfo { uint16_t highest_cpu; uint8_t _reserved5[124 - 122]; /* 122-123 */ uint32_t hmfai; - struct CPUEntry entries[0]; + struct CPUEntry entries[]; } QEMU_PACKED ReadInfo; typedef struct ReadCpuInfo { @@ -142,7 +142,7 @@ typedef struct ReadCpuInfo { uint16_t nr_standby; /* 12-13 */ uint16_t offset_standby; /* 14-15 */ uint8_t reserved0[24-16]; /* 16-23 */ - struct CPUEntry entries[0]; + struct CPUEntry entries[]; } QEMU_PACKED ReadCpuInfo; typedef struct ReadStorageElementInfo { @@ -151,7 +151,7 @@ typedef struct ReadStorageElementInfo { uint16_t assigned; uint16_t standby; uint8_t _reserved0[16 - 14]; /* 14-15 */ - uint32_t entries[0]; + uint32_t entries[]; } QEMU_PACKED ReadStorageElementInfo; typedef struct AttachStorageElement { @@ -159,7 +159,7 @@ typedef struct AttachStorageElement { uint8_t _reserved0[10 - 8]; /* 8-9 */ uint16_t assigned; uint8_t _reserved1[16 - 12]; /* 12-15 */ - uint32_t entries[0]; + uint32_t entries[]; } QEMU_PACKED AttachStorageElement; typedef struct AssignStorage { diff --git a/include/hw/sd/allwinner-sdhost.h b/include/hw/sd/allwinner-sdhost.h new file mode 100644 index 0000000000..d94606a853 --- /dev/null +++ b/include/hw/sd/allwinner-sdhost.h @@ -0,0 +1,135 @@ +/* + * Allwinner (sun4i and above) SD Host Controller emulation + * + * Copyright (C) 2019 Niek Linnenbank <nieklinnenbank@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_SD_ALLWINNER_SDHOST_H +#define HW_SD_ALLWINNER_SDHOST_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "hw/sd/sd.h" + +/** + * Object model types + * @{ + */ + +/** Generic Allwinner SD Host Controller (abstract) */ +#define TYPE_AW_SDHOST "allwinner-sdhost" + +/** Allwinner sun4i family (A10, A12) */ +#define TYPE_AW_SDHOST_SUN4I TYPE_AW_SDHOST "-sun4i" + +/** Allwinner sun5i family and newer (A13, H2+, H3, etc) */ +#define TYPE_AW_SDHOST_SUN5I TYPE_AW_SDHOST "-sun5i" + +/** @} */ + +/** + * Object model macros + * @{ + */ + +#define AW_SDHOST(obj) \ + OBJECT_CHECK(AwSdHostState, (obj), TYPE_AW_SDHOST) +#define AW_SDHOST_CLASS(klass) \ + OBJECT_CLASS_CHECK(AwSdHostClass, (klass), TYPE_AW_SDHOST) +#define AW_SDHOST_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AwSdHostClass, (obj), TYPE_AW_SDHOST) + +/** @} */ + +/** + * Allwinner SD Host Controller object instance state. + */ +typedef struct AwSdHostState { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + /** Secure Digital (SD) bus, which connects to SD card (if present) */ + SDBus sdbus; + + /** Maps I/O registers in physical memory */ + MemoryRegion iomem; + + /** Interrupt output signal to notify CPU */ + qemu_irq irq; + + /** Number of bytes left in current DMA transfer */ + uint32_t transfer_cnt; + + /** + * @name Hardware Registers + * @{ + */ + + uint32_t global_ctl; /**< Global Control */ + uint32_t clock_ctl; /**< Clock Control */ + uint32_t timeout; /**< Timeout */ + uint32_t bus_width; /**< Bus Width */ + uint32_t block_size; /**< Block Size */ + uint32_t byte_count; /**< Byte Count */ + + uint32_t command; /**< Command */ + uint32_t command_arg; /**< Command Argument */ + uint32_t response[4]; /**< Command Response */ + + uint32_t irq_mask; /**< Interrupt Mask */ + uint32_t irq_status; /**< Raw Interrupt Status */ + uint32_t status; /**< Status */ + + uint32_t fifo_wlevel; /**< FIFO Water Level */ + uint32_t fifo_func_sel; /**< FIFO Function Select */ + uint32_t debug_enable; /**< Debug Enable */ + uint32_t auto12_arg; /**< Auto Command 12 Argument */ + uint32_t newtiming_set; /**< SD New Timing Set */ + uint32_t newtiming_debug; /**< SD New Timing Debug */ + uint32_t hardware_rst; /**< Hardware Reset */ + uint32_t dmac; /**< Internal DMA Controller Control */ + uint32_t desc_base; /**< Descriptor List Base Address */ + uint32_t dmac_status; /**< Internal DMA Controller Status */ + uint32_t dmac_irq; /**< Internal DMA Controller IRQ Enable */ + uint32_t card_threshold; /**< Card Threshold Control */ + uint32_t startbit_detect; /**< eMMC DDR Start Bit Detection Control */ + uint32_t response_crc; /**< Response CRC */ + uint32_t data_crc[8]; /**< Data CRC */ + uint32_t status_crc; /**< Status CRC */ + + /** @} */ + +} AwSdHostState; + +/** + * Allwinner SD Host Controller class-level struct. + * + * This struct is filled by each sunxi device specific code + * such that the generic code can use this struct to support + * all devices. + */ +typedef struct AwSdHostClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + + /** Maximum buffer size in bytes per DMA descriptor */ + size_t max_desc_size; + +} AwSdHostClass; + +#endif /* HW_SD_ALLWINNER_SDHOST_H */ diff --git a/include/hw/southbridge/piix.h b/include/hw/southbridge/piix.h index 152628c6d9..02bd741209 100644 --- a/include/hw/southbridge/piix.h +++ b/include/hw/southbridge/piix.h @@ -68,7 +68,6 @@ extern PCIDevice *piix4_dev; PIIX3State *piix3_create(PCIBus *pci_bus, ISABus **isa_bus); -DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, - I2CBus **smbus, size_t ide_buses); +DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus); #endif diff --git a/include/hw/usb/imx-usb-phy.h b/include/hw/usb/imx-usb-phy.h new file mode 100644 index 0000000000..07f0235d10 --- /dev/null +++ b/include/hw/usb/imx-usb-phy.h @@ -0,0 +1,53 @@ +#ifndef IMX_USB_PHY_H +#define IMX_USB_PHY_H + +#include "hw/sysbus.h" +#include "qemu/bitops.h" + +enum IMXUsbPhyRegisters { + USBPHY_PWD, + USBPHY_PWD_SET, + USBPHY_PWD_CLR, + USBPHY_PWD_TOG, + USBPHY_TX, + USBPHY_TX_SET, + USBPHY_TX_CLR, + USBPHY_TX_TOG, + USBPHY_RX, + USBPHY_RX_SET, + USBPHY_RX_CLR, + USBPHY_RX_TOG, + USBPHY_CTRL, + USBPHY_CTRL_SET, + USBPHY_CTRL_CLR, + USBPHY_CTRL_TOG, + USBPHY_STATUS, + USBPHY_DEBUG = 0x14, + USBPHY_DEBUG_SET, + USBPHY_DEBUG_CLR, + USBPHY_DEBUG_TOG, + USBPHY_DEBUG0_STATUS, + USBPHY_DEBUG1 = 0x1c, + USBPHY_DEBUG1_SET, + USBPHY_DEBUG1_CLR, + USBPHY_DEBUG1_TOG, + USBPHY_VERSION, + USBPHY_MAX +}; + +#define USBPHY_CTRL_SFTRST BIT(31) + +#define TYPE_IMX_USBPHY "imx.usbphy" +#define IMX_USBPHY(obj) OBJECT_CHECK(IMXUSBPHYState, (obj), TYPE_IMX_USBPHY) + +typedef struct IMXUSBPHYState { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion iomem; + + uint32_t usbphy[USBPHY_MAX]; +} IMXUSBPHYState; + +#endif /* IMX_USB_PHY_H */ diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index 6f67f1020a..e653004d7c 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -41,7 +41,7 @@ typedef struct IOMMUDevice { typedef struct IOMMUPciBus { PCIBus *bus; - IOMMUDevice *pbdev[0]; /* Parent array is sparse, so dynamically alloc */ + IOMMUDevice *pbdev[]; /* Parent array is sparse, so dynamically alloc */ } IOMMUPciBus; typedef struct VirtIOIOMMU { diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 3d329853b2..e33ca5a911 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -30,8 +30,6 @@ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict); void hmp_info_migrate_parameters(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); @@ -39,7 +37,6 @@ void hmp_info_irq(Monitor *mon, const QDict *qdict); void hmp_info_pic(Monitor *mon, const QDict *qdict); void hmp_info_rdma(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_info_iothreads(Monitor *mon, const QDict *qdict); void hmp_quit(Monitor *mon, const QDict *qdict); @@ -58,18 +55,10 @@ void hmp_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_nmi(Monitor *mon, const QDict *qdict); void hmp_set_link(Monitor *mon, const QDict *qdict); -void hmp_block_passwd(Monitor *mon, const QDict *qdict); void hmp_balloon(Monitor *mon, const QDict *qdict); -void hmp_block_resize(Monitor *mon, const QDict *qdict); -void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict); -void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict); -void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict); -void hmp_drive_mirror(Monitor *mon, const QDict *qdict); -void hmp_drive_backup(Monitor *mon, const QDict *qdict); void hmp_loadvm(Monitor *mon, const QDict *qdict); void hmp_savevm(Monitor *mon, const QDict *qdict); void hmp_delvm(Monitor *mon, const QDict *qdict); -void hmp_info_snapshots(Monitor *mon, const QDict *qdict); void hmp_migrate_cancel(Monitor *mon, const QDict *qdict); void hmp_migrate_continue(Monitor *mon, const QDict *qdict); void hmp_migrate_incoming(Monitor *mon, const QDict *qdict); @@ -85,15 +74,7 @@ void hmp_migrate_start_postcopy(Monitor *mon, const QDict *qdict); void hmp_x_colo_lost_heartbeat(Monitor *mon, const QDict *qdict); void hmp_set_password(Monitor *mon, const QDict *qdict); void hmp_expire_password(Monitor *mon, const QDict *qdict); -void hmp_eject(Monitor *mon, const QDict *qdict); void hmp_change(Monitor *mon, const QDict *qdict); -void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict); -void hmp_block_stream(Monitor *mon, const QDict *qdict); -void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict); -void hmp_block_job_cancel(Monitor *mon, const QDict *qdict); -void hmp_block_job_pause(Monitor *mon, const QDict *qdict); -void hmp_block_job_resume(Monitor *mon, const QDict *qdict); -void hmp_block_job_complete(Monitor *mon, const QDict *qdict); void hmp_migrate(Monitor *mon, const QDict *qdict); void hmp_device_add(Monitor *mon, const QDict *qdict); void hmp_device_del(Monitor *mon, const QDict *qdict); @@ -104,15 +85,10 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_sendkey(Monitor *mon, const QDict *qdict); void hmp_screendump(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_remove(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_change(Monitor *mon, const QDict *qdict); void hmp_chardev_remove(Monitor *mon, const QDict *qdict); void hmp_chardev_send_break(Monitor *mon, const QDict *qdict); -void hmp_qemu_io(Monitor *mon, const QDict *qdict); void hmp_cpu_add(Monitor *mon, const QDict *qdict); void hmp_object_add(Monitor *mon, const QDict *qdict); void hmp_object_del(Monitor *mon, const QDict *qdict); diff --git a/include/net/net.h b/include/net/net.h index e175ba9677..094e966af9 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -98,6 +98,7 @@ struct NetClientState { unsigned rxfilter_notify_enabled:1; int vring_enable; int vnet_hdr_len; + bool is_netdev; QTAILQ_HEAD(, NetFilterState) filters; }; @@ -203,7 +204,6 @@ void net_cleanup(void); void hmp_host_net_add(Monitor *mon, const QDict *qdict); void hmp_host_net_remove(Monitor *mon, const QDict *qdict); void netdev_add(QemuOpts *opts, Error **errp); -void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp); int net_hub_id_for_client(NetClientState *nc, int *id); NetClientState *net_hub_port_find(int hub_id); diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 9aa426a398..5a9cf82472 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -39,7 +39,8 @@ typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); -QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name); +const QmpCommand *qmp_find_command(const QmpCommandList *cmds, + const char *name); void qmp_disable_command(QmpCommandList *cmds, const char *name); void qmp_enable_command(QmpCommandList *cmds, const char *name); @@ -47,13 +48,13 @@ bool qmp_command_is_enabled(const QmpCommand *cmd); const char *qmp_command_name(const QmpCommand *cmd); bool qmp_has_success_response(const QmpCommand *cmd); QDict *qmp_error_response(Error *err); -QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, +QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, bool allow_oob); bool qmp_is_oob(const QDict *dict); -typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque); +typedef void (*qmp_cmd_callback_fn)(const QmpCommand *cmd, void *opaque); -void qmp_for_each_command(QmpCommandList *cmds, qmp_cmd_callback_fn fn, +void qmp_for_each_command(const QmpCommandList *cmds, qmp_cmd_callback_fn fn, void *opaque); #endif diff --git a/include/qemu/cpuid.h b/include/qemu/cpuid.h index 69301700bd..09fc245b91 100644 --- a/include/qemu/cpuid.h +++ b/include/qemu/cpuid.h @@ -45,6 +45,9 @@ #ifndef bit_AVX2 #define bit_AVX2 (1 << 5) #endif +#ifndef bit_AVX512F +#define bit_AVX512F (1 << 16) +#endif #ifndef bit_BMI2 #define bit_BMI2 (1 << 8) #endif diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h index 1bf944ca3d..5e71b6d6f7 100644 --- a/include/qemu/hbitmap.h +++ b/include/qemu/hbitmap.h @@ -297,12 +297,18 @@ void hbitmap_free(HBitmap *hb); */ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first); -/* hbitmap_iter_skip_words: - * @hbi: HBitmapIter to operate on. +/* + * hbitmap_next_dirty: + * + * Find next dirty bit within selected range. If not found, return -1. * - * Internal function used by hbitmap_iter_next and hbitmap_iter_next_word. + * @hb: The HBitmap to operate on + * @start: The bit to start from. + * @count: Number of bits to proceed. If @start+@count > bitmap size, the whole + * bitmap is looked through. You can use INT64_MAX as @count to search up to + * the bitmap end. */ -unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi); +int64_t hbitmap_next_dirty(const HBitmap *hb, int64_t start, int64_t count); /* hbitmap_next_zero: * @@ -311,47 +317,28 @@ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi); * @hb: The HBitmap to operate on * @start: The bit to start from. * @count: Number of bits to proceed. If @start+@count > bitmap size, the whole - * bitmap is looked through. You can use UINT64_MAX as @count to search up to + * bitmap is looked through. You can use INT64_MAX as @count to search up to * the bitmap end. */ -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count); +int64_t hbitmap_next_zero(const HBitmap *hb, int64_t start, int64_t count); /* hbitmap_next_dirty_area: * @hb: The HBitmap to operate on - * @start: in-out parameter. - * in: the offset to start from - * out: (if area found) start of found area - * @count: in-out parameter. - * in: length of requested region - * out: length of found area - * - * If dirty area found within [@start, @start + @count), returns true and sets - * @offset and @bytes appropriately. Otherwise returns false and leaves @offset - * and @bytes unchanged. - */ -bool hbitmap_next_dirty_area(const HBitmap *hb, uint64_t *start, - uint64_t *count); - -/* hbitmap_create_meta: - * Create a "meta" hbitmap to track dirtiness of the bits in this HBitmap. - * The caller owns the created bitmap and must call hbitmap_free_meta(hb) to - * free it. - * - * Currently, we only guarantee that if a bit in the hbitmap is changed it - * will be reflected in the meta bitmap, but we do not yet guarantee the - * opposite. - * - * @hb: The HBitmap to operate on. - * @chunk_size: How many bits in @hb does one bit in the meta track. - */ -HBitmap *hbitmap_create_meta(HBitmap *hb, int chunk_size); - -/* hbitmap_free_meta: - * Free the meta bitmap of @hb. - * - * @hb: The HBitmap whose meta bitmap should be freed. - */ -void hbitmap_free_meta(HBitmap *hb); + * @start: the offset to start from + * @end: end of requested area + * @max_dirty_count: limit for out parameter dirty_count + * @dirty_start: on success: start of found area + * @dirty_count: on success: length of found area + * + * If dirty area found within [@start, @end), returns true and sets + * @dirty_start and @dirty_count appropriately. @dirty_count will not exceed + * @max_dirty_count. + * If dirty area was not found, returns false and leaves @dirty_start and + * @dirty_count unchanged. + */ +bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end, + int64_t max_dirty_count, + int64_t *dirty_start, int64_t *dirty_count); /** * hbitmap_iter_next: @@ -362,34 +349,4 @@ void hbitmap_free_meta(HBitmap *hb); */ int64_t hbitmap_iter_next(HBitmapIter *hbi); -/** - * 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/job.h b/include/qemu/job.h index bd59cd8944..32aabb1c60 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -28,6 +28,7 @@ #include "qapi/qapi-types-job.h" #include "qemu/queue.h" +#include "qemu/progress_meter.h" #include "qemu/coroutine.h" #include "block/aio.h" @@ -117,15 +118,7 @@ typedef struct Job { /** True if this job should automatically dismiss itself */ bool auto_dismiss; - /** - * Current progress. The unit is arbitrary as long as the ratio between - * progress_current and progress_total represents the estimated percentage - * of work already done. - */ - int64_t progress_current; - - /** Estimated progress_current value at the completion of the job */ - int64_t progress_total; + ProgressMeter progress; /** * Return code from @run and/or @prepare callback(s). diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h index 84ea794bcf..1aeb2cb1a6 100644 --- a/include/qemu/lockable.h +++ b/include/qemu/lockable.h @@ -50,6 +50,7 @@ qemu_make_lockable(void *x, QemuLockable *lockable) #define QEMU_LOCK_FUNC(x) ((QemuLockUnlockFunc *) \ QEMU_GENERIC(x, \ (QemuMutex *, qemu_mutex_lock), \ + (QemuRecMutex *, qemu_rec_mutex_lock), \ (CoMutex *, qemu_co_mutex_lock), \ (QemuSpin *, qemu_spin_lock), \ unknown_lock_type)) @@ -57,6 +58,7 @@ qemu_make_lockable(void *x, QemuLockable *lockable) #define QEMU_UNLOCK_FUNC(x) ((QemuLockUnlockFunc *) \ QEMU_GENERIC(x, \ (QemuMutex *, qemu_mutex_unlock), \ + (QemuRecMutex *, qemu_rec_mutex_unlock), \ (CoMutex *, qemu_co_mutex_unlock), \ (QemuSpin *, qemu_spin_unlock), \ unknown_lock_type)) @@ -65,7 +67,7 @@ qemu_make_lockable(void *x, QemuLockable *lockable) * In C++ it would be different, but then C++ wouldn't need QemuLockable * either... */ -#define QEMU_MAKE_LOCKABLE_(x) qemu_make_lockable((x), &(QemuLockable) { \ +#define QEMU_MAKE_LOCKABLE_(x) (&(QemuLockable) { \ .object = (x), \ .lock = QEMU_LOCK_FUNC(x), \ .unlock = QEMU_UNLOCK_FUNC(x), \ @@ -73,14 +75,27 @@ qemu_make_lockable(void *x, QemuLockable *lockable) /* QEMU_MAKE_LOCKABLE - Make a polymorphic QemuLockable * - * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). + * @x: a lock object (currently one of QemuMutex, QemuRecMutex, CoMutex, QemuSpin). * * Returns a QemuLockable object that can be passed around - * to a function that can operate with locks of any kind. + * to a function that can operate with locks of any kind, or + * NULL if @x is %NULL. */ #define QEMU_MAKE_LOCKABLE(x) \ QEMU_GENERIC(x, \ (QemuLockable *, (x)), \ + qemu_make_lockable((x), QEMU_MAKE_LOCKABLE_(x))) + +/* QEMU_MAKE_LOCKABLE_NONNULL - Make a polymorphic QemuLockable + * + * @x: a lock object (currently one of QemuMutex, QemuRecMutex, CoMutex, QemuSpin). + * + * Returns a QemuLockable object that can be passed around + * to a function that can operate with locks of any kind. + */ +#define QEMU_MAKE_LOCKABLE_NONNULL(x) \ + QEMU_GENERIC(x, \ + (QemuLockable *, (x)), \ QEMU_MAKE_LOCKABLE_(x)) static inline void qemu_lockable_lock(QemuLockable *x) @@ -93,4 +108,69 @@ static inline void qemu_lockable_unlock(QemuLockable *x) x->unlock(x->object); } +static inline QemuLockable *qemu_lockable_auto_lock(QemuLockable *x) +{ + qemu_lockable_lock(x); + return x; +} + +static inline void qemu_lockable_auto_unlock(QemuLockable *x) +{ + if (x) { + qemu_lockable_unlock(x); + } +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuLockable, qemu_lockable_auto_unlock) + +#define WITH_QEMU_LOCK_GUARD_(x, var) \ + for (g_autoptr(QemuLockable) var = \ + qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))); \ + var; \ + qemu_lockable_auto_unlock(var), var = NULL) + +/** + * WITH_QEMU_LOCK_GUARD - Lock a lock object for scope + * + * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). + * + * This macro defines a lock scope such that entering the scope takes the lock + * and leaving the scope releases the lock. Return statements are allowed + * within the scope and release the lock. Break and continue statements leave + * the scope early and release the lock. + * + * WITH_QEMU_LOCK_GUARD(&mutex) { + * ... + * if (error) { + * return; <-- mutex is automatically unlocked + * } + * + * if (early_exit) { + * break; <-- leave this scope early + * } + * ... + * } + */ +#define WITH_QEMU_LOCK_GUARD(x) \ + WITH_QEMU_LOCK_GUARD_((x), qemu_lockable_auto##__COUNTER__) + +/** + * QEMU_LOCK_GUARD - Lock an object until the end of the scope + * + * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). + * + * This macro takes a lock until the end of the scope. Return statements + * release the lock. + * + * ... <-- mutex not locked + * QEMU_LOCK_GUARD(&mutex); <-- mutex locked from here onwards + * ... + * if (error) { + * return; <-- mutex is automatically unlocked + * } + */ +#define QEMU_LOCK_GUARD(x) \ + g_autoptr(QemuLockable) qemu_lockable_auto##__COUNTER__ = \ + qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE((x))) + #endif diff --git a/include/qemu/progress_meter.h b/include/qemu/progress_meter.h new file mode 100644 index 0000000000..9a23ff071c --- /dev/null +++ b/include/qemu/progress_meter.h @@ -0,0 +1,58 @@ +/* + * Helper functionality for some process progress tracking. + * + * Copyright (c) 2011 IBM Corp. + * Copyright (c) 2012, 2018 Red Hat, Inc. + * Copyright (c) 2020 Virtuozzo International GmbH + * + * 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_PROGRESS_METER_H +#define QEMU_PROGRESS_METER_H + +typedef struct ProgressMeter { + /** + * Current progress. The unit is arbitrary as long as the ratio between + * current and total represents the estimated percentage + * of work already done. + */ + uint64_t current; + + /** Estimated current value at the completion of the process */ + uint64_t total; +} ProgressMeter; + +static inline void progress_work_done(ProgressMeter *pm, uint64_t done) +{ + pm->current += done; +} + +static inline void progress_set_remaining(ProgressMeter *pm, uint64_t remaining) +{ + pm->total = pm->current + remaining; +} + +static inline void progress_increase_remaining(ProgressMeter *pm, + uint64_t delta) +{ + pm->total += delta; +} + +#endif /* QEMU_PROGRESS_METER_H */ diff --git a/include/qemu/queue.h b/include/qemu/queue.h index 294db54eb1..456a5b01ee 100644 --- a/include/qemu/queue.h +++ b/include/qemu/queue.h @@ -142,6 +142,8 @@ struct { \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ + (elm)->field.le_next = NULL; \ + (elm)->field.le_prev = NULL; \ } while (/*CONSTCOND*/0) /* @@ -225,12 +227,15 @@ struct { \ } while (/*CONSTCOND*/0) #define QSLIST_REMOVE_HEAD(head, field) do { \ - (head)->slh_first = (head)->slh_first->field.sle_next; \ + typeof((head)->slh_first) elm = (head)->slh_first; \ + (head)->slh_first = elm->field.sle_next; \ + elm->field.sle_next = NULL; \ } while (/*CONSTCOND*/0) #define QSLIST_REMOVE_AFTER(slistelm, field) do { \ - (slistelm)->field.sle_next = \ - QSLIST_NEXT(QSLIST_NEXT((slistelm), field), field); \ + typeof(slistelm) next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = next->field.sle_next; \ + next->field.sle_next = NULL; \ } while (/*CONSTCOND*/0) #define QSLIST_REMOVE(head, elm, type, field) do { \ @@ -241,6 +246,7 @@ struct { \ while (curelm->field.sle_next != (elm)) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \ + (elm)->field.sle_next = NULL; \ } \ } while (/*CONSTCOND*/0) @@ -304,8 +310,10 @@ struct { \ } while (/*CONSTCOND*/0) #define QSIMPLEQ_REMOVE_HEAD(head, field) do { \ - if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL)\ + typeof((head)->sqh_first) elm = (head)->sqh_first; \ + if (((head)->sqh_first = elm->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ + elm->field.sqe_next = NULL; \ } while (/*CONSTCOND*/0) #define QSIMPLEQ_SPLIT_AFTER(head, elm, field, removed) do { \ @@ -329,6 +337,7 @@ struct { \ if ((curelm->field.sqe_next = \ curelm->field.sqe_next->field.sqe_next) == NULL) \ (head)->sqh_last = &(curelm)->field.sqe_next; \ + (elm)->field.sqe_next = NULL; \ } \ } while (/*CONSTCOND*/0) @@ -446,6 +455,8 @@ union { \ (head)->tqh_circ.tql_prev = (elm)->field.tqe_circ.tql_prev; \ (elm)->field.tqe_circ.tql_prev->tql_next = (elm)->field.tqe_next; \ (elm)->field.tqe_circ.tql_prev = NULL; \ + (elm)->field.tqe_circ.tql_next = NULL; \ + (elm)->field.tqe_next = NULL; \ } while (/*CONSTCOND*/0) /* remove @left, @right and all elements in between from @head */ diff --git a/include/qom/object.h b/include/qom/object.h index 29546496c1..784c97c0e1 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -1664,69 +1664,101 @@ ObjectProperty *object_class_property_add_tm(ObjectClass *klass, void (*get)(Object *, struct tm *, Error **), Error **errp); +typedef enum { + /* Automatically add a getter to the property */ + OBJ_PROP_FLAG_READ = 1 << 0, + /* Automatically add a setter to the property */ + OBJ_PROP_FLAG_WRITE = 1 << 1, + /* Automatically add a getter and a setter to the property */ + OBJ_PROP_FLAG_READWRITE = (OBJ_PROP_FLAG_READ | OBJ_PROP_FLAG_WRITE), +} ObjectPropertyFlags; + /** * object_property_add_uint8_ptr: * @obj: the object to add a property to * @name: the name of the property * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags * @errp: if an error occurs, a pointer to an area to store the error * * Add an integer property in memory. This function will add a * property of type 'uint8'. */ void object_property_add_uint8_ptr(Object *obj, const char *name, - const uint8_t *v, Error **errp); + const uint8_t *v, ObjectPropertyFlags flags, + Error **errp); + ObjectProperty *object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, - const uint8_t *v, Error **errp); + const uint8_t *v, + ObjectPropertyFlags flags, + Error **errp); /** * object_property_add_uint16_ptr: * @obj: the object to add a property to * @name: the name of the property * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags * @errp: if an error occurs, a pointer to an area to store the error * * Add an integer property in memory. This function will add a * property of type 'uint16'. */ void object_property_add_uint16_ptr(Object *obj, const char *name, - const uint16_t *v, Error **errp); + const uint16_t *v, + ObjectPropertyFlags flags, + Error **errp); + ObjectProperty *object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, - const uint16_t *v, Error **errp); + const uint16_t *v, + ObjectPropertyFlags flags, + Error **errp); /** * object_property_add_uint32_ptr: * @obj: the object to add a property to * @name: the name of the property * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags * @errp: if an error occurs, a pointer to an area to store the error * * Add an integer property in memory. This function will add a * property of type 'uint32'. */ void object_property_add_uint32_ptr(Object *obj, const char *name, - const uint32_t *v, Error **errp); + const uint32_t *v, + ObjectPropertyFlags flags, + Error **errp); + ObjectProperty *object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, - const uint32_t *v, Error **errp); + const uint32_t *v, + ObjectPropertyFlags flags, + Error **errp); /** * object_property_add_uint64_ptr: * @obj: the object to add a property to * @name: the name of the property * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags * @errp: if an error occurs, a pointer to an area to store the error * * Add an integer property in memory. This function will add a * property of type 'uint64'. */ void object_property_add_uint64_ptr(Object *obj, const char *name, - const uint64_t *v, Error **errp); + const uint64_t *v, + ObjectPropertyFlags flags, + Error **Errp); + ObjectProperty *object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, - const uint64_t *v, Error **errp); + const uint64_t *v, + ObjectPropertyFlags flags, + Error **Errp); /** * object_property_add_alias: diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 01392dc945..71a7a285ee 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -24,6 +24,7 @@ enum { QEMU_ARCH_NIOS2 = (1 << 17), QEMU_ARCH_HPPA = (1 << 18), QEMU_ARCH_RISCV = (1 << 19), + QEMU_ARCH_RX = (1 << 20), QEMU_ARCH_NONE = (1 << 31), }; diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index d34c4920dc..a86d99b3d8 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -57,8 +57,4 @@ QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type, Error **errp); -/* device-hotplug */ - -void hmp_commit(Monitor *mon, const QDict *qdict); -void hmp_drive_del(Monitor *mon, const QDict *qdict); #endif diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h index a9afb7e5b5..35eab06d0e 100644 --- a/include/sysemu/cryptodev.h +++ b/include/sysemu/cryptodev.h @@ -143,7 +143,7 @@ typedef struct CryptoDevBackendSymOpInfo { uint8_t *dst; uint8_t *aad_data; uint8_t *digest_result; - uint8_t data[0]; + uint8_t data[]; } CryptoDevBackendSymOpInfo; typedef struct CryptoDevBackendClass { diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 479d90bcea..ef81302e1a 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -63,9 +63,6 @@ extern int nb_option_roms; extern const char *prom_envs[MAX_PROM_ENVS]; extern unsigned int nb_prom_envs; -/* generic hotplug */ -void hmp_drive_add(Monitor *mon, const QDict *qdict); - /* pcie aer error injection */ void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict); diff --git a/include/sysemu/whpx.h b/include/sysemu/whpx.h index 4794e8effe..a84b49e749 100644 --- a/include/sysemu/whpx.h +++ b/include/sysemu/whpx.h @@ -35,4 +35,11 @@ int whpx_enabled(void); #endif /* CONFIG_WHPX */ +/* state subset only touched by the VCPU itself during runtime */ +#define WHPX_SET_RUNTIME_STATE 1 +/* state subset modified during VCPU reset */ +#define WHPX_SET_RESET_STATE 2 +/* full state set, modified during initialization or on vmload */ +#define WHPX_SET_FULL_STATE 3 + #endif /* QEMU_WHPX_H */ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 54e5446880..c48bd76b0a 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -267,7 +267,7 @@ struct TCGLabel { typedef struct TCGPool { struct TCGPool *next; int size; - uint8_t data[0] __attribute__ ((aligned)); + uint8_t data[] __attribute__ ((aligned)); } TCGPool; #define TCG_POOL_CHUNK_SIZE 32768 @@ -143,8 +143,8 @@ static JobInfo *job_query_single(Job *job, Error **errp) .id = g_strdup(job->id), .type = job_type(job), .status = job->status, - .current_progress = job->progress_current, - .total_progress = job->progress_total, + .current_progress = job->progress.current, + .total_progress = job->progress.total, .has_error = !!job->err, .error = job->err ? \ g_strdup(error_get_pretty(job->err)) : NULL, @@ -369,17 +369,17 @@ void job_unref(Job *job) void job_progress_update(Job *job, uint64_t done) { - job->progress_current += done; + progress_work_done(&job->progress, done); } void job_progress_set_remaining(Job *job, uint64_t remaining) { - job->progress_total = job->progress_current + remaining; + progress_set_remaining(&job->progress, remaining); } void job_progress_increase_remaining(Job *job, uint64_t delta) { - job->progress_total += delta; + progress_increase_remaining(&job->progress, delta); } void job_event_cancelled(Job *job) @@ -1170,15 +1170,6 @@ void memory_region_init(MemoryRegion *mr, memory_region_do_init(mr, owner, name, size); } -static void memory_region_get_addr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - MemoryRegion *mr = MEMORY_REGION(obj); - uint64_t value = mr->addr; - - visit_type_uint64(v, name, &value, errp); -} - static void memory_region_get_container(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -1242,10 +1233,8 @@ static void memory_region_initfn(Object *obj) NULL, NULL, &error_abort); op->resolve = memory_region_resolve_container; - object_property_add(OBJECT(mr), "addr", "uint64", - memory_region_get_addr, - NULL, /* memory_region_set_addr */ - NULL, NULL, &error_abort); + object_property_add_uint64_ptr(OBJECT(mr), "addr", + &mr->addr, OBJ_PROP_FLAG_READ, &error_abort); object_property_add(OBJECT(mr), "priority", "uint32", memory_region_get_priority, NULL, /* memory_region_set_priority */ @@ -1671,19 +1660,8 @@ void memory_region_init_rom_nomigrate(MemoryRegion *mr, uint64_t size, Error **errp) { - Error *err = NULL; - memory_region_init(mr, owner, name, size); - mr->ram = true; + memory_region_init_ram_shared_nomigrate(mr, owner, name, size, false, errp); mr->readonly = true; - mr->terminates = true; - mr->destructor = memory_region_destructor_ram; - mr->ram_block = qemu_ram_alloc(size, false, mr, &err); - mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; - if (err) { - mr->size = int128_zero(); - object_unparent(OBJECT(mr)); - error_propagate(errp, err); - } } void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, @@ -2830,6 +2808,9 @@ void address_space_destroy(AddressSpace *as) static const char *memory_region_type(MemoryRegion *mr) { + if (mr->alias) { + return memory_region_type(mr->alias); + } if (memory_region_is_ram_device(mr)) { return "ramd"; } else if (memory_region_is_romd(mr)) { diff --git a/migration/colo.c b/migration/colo.c index 93c5a452fb..44942c4e23 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -26,6 +26,7 @@ #include "qemu/main-loop.h" #include "qemu/rcu.h" #include "migration/failover.h" +#include "migration/ram.h" #ifdef CONFIG_REPLICATION #include "replication.h" #endif @@ -845,6 +846,8 @@ void *colo_process_incoming_thread(void *opaque) */ qemu_file_set_blocking(mis->from_src_file, true); + colo_incoming_start_dirty_log(); + bioc = qio_channel_buffer_new(COLO_BUFFER_BASE_SIZE); fb = qemu_fopen_channel_input(QIO_CHANNEL(bioc)); object_unref(OBJECT(bioc)); diff --git a/migration/migration.c b/migration/migration.c index 0b2045ccbd..c1d88ace7f 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -78,6 +78,7 @@ /*0: means nocompress, 1: best speed, ... 9: best compress ratio */ #define DEFAULT_MIGRATE_COMPRESS_LEVEL 1 /* Define default autoconverge cpu throttle migration parameters */ +#define DEFAULT_MIGRATE_THROTTLE_TRIGGER_THRESHOLD 50 #define DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL 20 #define DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT 10 #define DEFAULT_MIGRATE_MAX_CPU_THROTTLE 99 @@ -778,6 +779,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->compress_wait_thread = s->parameters.compress_wait_thread; params->has_decompress_threads = true; params->decompress_threads = s->parameters.decompress_threads; + params->has_throttle_trigger_threshold = true; + params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold; params->has_cpu_throttle_initial = true; params->cpu_throttle_initial = s->parameters.cpu_throttle_initial; params->has_cpu_throttle_increment = true; @@ -851,6 +854,7 @@ bool migration_is_setup_or_active(int state) case MIGRATION_STATUS_PRE_SWITCHOVER: case MIGRATION_STATUS_DEVICE: case MIGRATION_STATUS_WAIT_UNPLUG: + case MIGRATION_STATUS_COLO: return true; default: @@ -1169,6 +1173,15 @@ static bool migrate_params_check(MigrationParameters *params, Error **errp) return false; } + if (params->has_throttle_trigger_threshold && + (params->throttle_trigger_threshold < 1 || + params->throttle_trigger_threshold > 100)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "throttle_trigger_threshold", + "an integer in the range of 1 to 100"); + return false; + } + if (params->has_cpu_throttle_initial && (params->cpu_throttle_initial < 1 || params->cpu_throttle_initial > 99)) { @@ -1298,6 +1311,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params, dest->decompress_threads = params->decompress_threads; } + if (params->has_throttle_trigger_threshold) { + dest->throttle_trigger_threshold = params->throttle_trigger_threshold; + } + if (params->has_cpu_throttle_initial) { dest->cpu_throttle_initial = params->cpu_throttle_initial; } @@ -1382,6 +1399,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) s->parameters.decompress_threads = params->decompress_threads; } + if (params->has_throttle_trigger_threshold) { + s->parameters.throttle_trigger_threshold = params->throttle_trigger_threshold; + } + if (params->has_cpu_throttle_initial) { s->parameters.cpu_throttle_initial = params->cpu_throttle_initial; } @@ -3558,6 +3579,9 @@ static Property migration_properties[] = { DEFINE_PROP_UINT8("x-decompress-threads", MigrationState, parameters.decompress_threads, DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT), + DEFINE_PROP_UINT8("x-throttle-trigger-threshold", MigrationState, + parameters.throttle_trigger_threshold, + DEFAULT_MIGRATE_THROTTLE_TRIGGER_THRESHOLD), DEFINE_PROP_UINT8("x-cpu-throttle-initial", MigrationState, parameters.cpu_throttle_initial, DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL), @@ -3667,6 +3691,7 @@ static void migration_instance_init(Object *obj) params->has_compress_level = true; params->has_compress_threads = true; params->has_decompress_threads = true; + params->has_throttle_trigger_threshold = true; params->has_cpu_throttle_initial = true; params->has_cpu_throttle_increment = true; params->has_max_bandwidth = true; diff --git a/migration/ram.c b/migration/ram.c index 0ef68798d2..c12cfdbe26 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -896,11 +896,38 @@ static void migration_update_rates(RAMState *rs, int64_t end_time) } } +static void migration_trigger_throttle(RAMState *rs) +{ + MigrationState *s = migrate_get_current(); + uint64_t threshold = s->parameters.throttle_trigger_threshold; + + uint64_t bytes_xfer_period = ram_counters.transferred - rs->bytes_xfer_prev; + uint64_t bytes_dirty_period = rs->num_dirty_pages_period * TARGET_PAGE_SIZE; + uint64_t bytes_dirty_threshold = bytes_xfer_period * threshold / 100; + + /* During block migration the auto-converge logic incorrectly detects + * that ram migration makes no progress. Avoid this by disabling the + * throttling logic during the bulk phase of block migration. */ + if (migrate_auto_converge() && !blk_mig_bulk_active()) { + /* The following detection logic can be refined later. For now: + Check to see if the ratio between dirtied bytes and the approx. + amount of bytes that just got transferred since the last time + we were in this routine reaches the threshold. If that happens + twice, start or increase throttling. */ + + if ((bytes_dirty_period > bytes_dirty_threshold) && + (++rs->dirty_rate_high_cnt >= 2)) { + trace_migration_throttle(); + rs->dirty_rate_high_cnt = 0; + mig_throttle_guest_down(); + } + } +} + static void migration_bitmap_sync(RAMState *rs) { RAMBlock *block; int64_t end_time; - uint64_t bytes_xfer_now; ram_counters.dirty_sync_count++; @@ -927,26 +954,7 @@ static void migration_bitmap_sync(RAMState *rs) /* more than 1 second = 1000 millisecons */ if (end_time > rs->time_last_bitmap_sync + 1000) { - bytes_xfer_now = ram_counters.transferred; - - /* During block migration the auto-converge logic incorrectly detects - * that ram migration makes no progress. Avoid this by disabling the - * throttling logic during the bulk phase of block migration. */ - if (migrate_auto_converge() && !blk_mig_bulk_active()) { - /* The following detection logic can be refined later. For now: - Check to see if the dirtied bytes is 50% more than the approx. - amount of bytes that just got transferred since the last time we - were in this routine. If that happens twice, start or increase - throttling */ - - if ((rs->num_dirty_pages_period * TARGET_PAGE_SIZE > - (bytes_xfer_now - rs->bytes_xfer_prev) / 2) && - (++rs->dirty_rate_high_cnt >= 2)) { - trace_migration_throttle(); - rs->dirty_rate_high_cnt = 0; - mig_throttle_guest_down(); - } - } + migration_trigger_throttle(rs); migration_update_rates(rs, end_time); @@ -955,7 +963,7 @@ static void migration_bitmap_sync(RAMState *rs) /* reset period counters */ rs->time_last_bitmap_sync = end_time; rs->num_dirty_pages_period = 0; - rs->bytes_xfer_prev = bytes_xfer_now; + rs->bytes_xfer_prev = ram_counters.transferred; } if (migrate_use_events()) { qapi_event_send_migration_pass(ram_counters.dirty_sync_count); @@ -2734,7 +2742,7 @@ static inline void *host_from_ram_block_offset(RAMBlock *block, } static inline void *colo_cache_from_block_offset(RAMBlock *block, - ram_addr_t offset) + ram_addr_t offset, bool record_bitmap) { if (!offset_in_ramblock(block, offset)) { return NULL; @@ -2750,7 +2758,8 @@ static inline void *colo_cache_from_block_offset(RAMBlock *block, * It help us to decide which pages in ram cache should be flushed * into VM's RAM later. */ - if (!test_and_set_bit(offset >> TARGET_PAGE_BITS, block->bmap)) { + if (record_bitmap && + !test_and_set_bit(offset >> TARGET_PAGE_BITS, block->bmap)) { ram_state->migration_dirty_pages++; } return block->colo_cache + offset; @@ -2986,7 +2995,6 @@ int colo_init_ram_cache(void) } return -errno; } - memcpy(block->colo_cache, block->host, block->used_length); } } @@ -3000,19 +3008,36 @@ int colo_init_ram_cache(void) RAMBLOCK_FOREACH_NOT_IGNORED(block) { unsigned long pages = block->max_length >> TARGET_PAGE_BITS; - block->bmap = bitmap_new(pages); - bitmap_set(block->bmap, 0, pages); } } - ram_state = g_new0(RAMState, 1); - ram_state->migration_dirty_pages = 0; - qemu_mutex_init(&ram_state->bitmap_mutex); - memory_global_dirty_log_start(); + ram_state_init(&ram_state); return 0; } +/* TODO: duplicated with ram_init_bitmaps */ +void colo_incoming_start_dirty_log(void) +{ + RAMBlock *block = NULL; + /* For memory_global_dirty_log_start below. */ + qemu_mutex_lock_iothread(); + qemu_mutex_lock_ramlist(); + + memory_global_dirty_log_sync(); + WITH_RCU_READ_LOCK_GUARD() { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + ramblock_sync_dirty_bitmap(ram_state, block); + /* Discard this dirty bitmap record */ + bitmap_zero(block->bmap, block->max_length >> TARGET_PAGE_BITS); + } + memory_global_dirty_log_start(); + } + ram_state->migration_dirty_pages = 0; + qemu_mutex_unlock_ramlist(); + qemu_mutex_unlock_iothread(); +} + /* It is need to hold the global lock to call this helper */ void colo_release_ram_cache(void) { @@ -3032,9 +3057,7 @@ void colo_release_ram_cache(void) } } } - qemu_mutex_destroy(&ram_state->bitmap_mutex); - g_free(ram_state); - ram_state = NULL; + ram_state_cleanup(&ram_state); } /** @@ -3348,7 +3371,7 @@ static int ram_load_precopy(QEMUFile *f) while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { ram_addr_t addr, total_ram_bytes; - void *host = NULL; + void *host = NULL, *host_bak = NULL; uint8_t ch; /* @@ -3379,20 +3402,35 @@ static int ram_load_precopy(QEMUFile *f) RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) { RAMBlock *block = ram_block_from_stream(f, flags); + host = host_from_ram_block_offset(block, addr); /* - * After going into COLO, we should load the Page into colo_cache. + * After going into COLO stage, we should not load the page + * into SVM's memory directly, we put them into colo_cache firstly. + * NOTE: We need to keep a copy of SVM's ram in colo_cache. + * Previously, we copied all these memory in preparing stage of COLO + * while we need to stop VM, which is a time-consuming process. + * Here we optimize it by a trick, back-up every page while in + * migration process while COLO is enabled, though it affects the + * speed of the migration, but it obviously reduce the downtime of + * back-up all SVM'S memory in COLO preparing stage. */ - if (migration_incoming_in_colo_state()) { - host = colo_cache_from_block_offset(block, addr); - } else { - host = host_from_ram_block_offset(block, addr); + if (migration_incoming_colo_enabled()) { + if (migration_incoming_in_colo_state()) { + /* In COLO stage, put all pages into cache temporarily */ + host = colo_cache_from_block_offset(block, addr, true); + } else { + /* + * In migration stage but before COLO stage, + * Put all pages into both cache and SVM's memory. + */ + host_bak = colo_cache_from_block_offset(block, addr, false); + } } if (!host) { error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); ret = -EINVAL; break; } - if (!migration_incoming_in_colo_state()) { ramblock_recv_bitmap_set(block, host); } @@ -3506,6 +3544,9 @@ static int ram_load_precopy(QEMUFile *f) if (!ret) { ret = qemu_file_get_error(f); } + if (!ret && host_bak) { + memcpy(host_bak, host, TARGET_PAGE_SIZE); + } } ret |= wait_for_decompress_done(); diff --git a/migration/ram.h b/migration/ram.h index a553d40751..5ceaff7cb4 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -66,5 +66,6 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb); /* ram cache */ int colo_init_ram_cache(void); void colo_release_ram_cache(void); +void colo_incoming_start_dirty_log(void); #endif diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 6fd7aca500..58724031ea 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -47,9 +47,6 @@ #include "qapi/string-output-visitor.h" #include "qom/object_interfaces.h" #include "ui/console.h" -#include "block/nbd.h" -#include "block/qapi.h" -#include "qemu-io.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "exec/ramlist.h" @@ -410,6 +407,10 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS), params->decompress_threads); + assert(params->has_throttle_trigger_threshold); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD), + params->throttle_trigger_threshold); assert(params->has_cpu_throttle_initial); monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL), @@ -472,213 +473,6 @@ void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict) qmp_query_migrate_cache_size(NULL) >> 10); } -static void print_block_info(Monitor *mon, BlockInfo *info, - BlockDeviceInfo *inserted, bool verbose) -{ - ImageInfo *image_info; - - assert(!info || !info->has_inserted || info->inserted == inserted); - - if (info && *info->device) { - monitor_printf(mon, "%s", info->device); - if (inserted && inserted->has_node_name) { - monitor_printf(mon, " (%s)", inserted->node_name); - } - } else { - assert(info || inserted); - monitor_printf(mon, "%s", - inserted && inserted->has_node_name ? inserted->node_name - : info && info->has_qdev ? info->qdev - : "<anonymous>"); - } - - if (inserted) { - monitor_printf(mon, ": %s (%s%s%s)\n", - inserted->file, - inserted->drv, - inserted->ro ? ", read-only" : "", - inserted->encrypted ? ", encrypted" : ""); - } else { - monitor_printf(mon, ": [not inserted]\n"); - } - - if (info) { - if (info->has_qdev) { - monitor_printf(mon, " Attached to: %s\n", info->qdev); - } - if (info->has_io_status && info->io_status != BLOCK_DEVICE_IO_STATUS_OK) { - monitor_printf(mon, " I/O status: %s\n", - BlockDeviceIoStatus_str(info->io_status)); - } - - if (info->removable) { - monitor_printf(mon, " Removable device: %slocked, tray %s\n", - info->locked ? "" : "not ", - info->tray_open ? "open" : "closed"); - } - } - - - if (!inserted) { - return; - } - - monitor_printf(mon, " Cache mode: %s%s%s\n", - inserted->cache->writeback ? "writeback" : "writethrough", - inserted->cache->direct ? ", direct" : "", - inserted->cache->no_flush ? ", ignore flushes" : ""); - - if (inserted->has_backing_file) { - monitor_printf(mon, - " Backing file: %s " - "(chain depth: %" PRId64 ")\n", - inserted->backing_file, - inserted->backing_file_depth); - } - - if (inserted->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF) { - monitor_printf(mon, " Detect zeroes: %s\n", - BlockdevDetectZeroesOptions_str(inserted->detect_zeroes)); - } - - if (inserted->bps || inserted->bps_rd || inserted->bps_wr || - inserted->iops || inserted->iops_rd || inserted->iops_wr) - { - monitor_printf(mon, " I/O throttling: bps=%" PRId64 - " bps_rd=%" PRId64 " bps_wr=%" PRId64 - " bps_max=%" PRId64 - " bps_rd_max=%" PRId64 - " bps_wr_max=%" PRId64 - " iops=%" PRId64 " iops_rd=%" PRId64 - " iops_wr=%" PRId64 - " iops_max=%" PRId64 - " iops_rd_max=%" PRId64 - " iops_wr_max=%" PRId64 - " iops_size=%" PRId64 - " group=%s\n", - inserted->bps, - inserted->bps_rd, - inserted->bps_wr, - inserted->bps_max, - inserted->bps_rd_max, - inserted->bps_wr_max, - inserted->iops, - inserted->iops_rd, - inserted->iops_wr, - inserted->iops_max, - inserted->iops_rd_max, - inserted->iops_wr_max, - inserted->iops_size, - inserted->group); - } - - if (verbose) { - monitor_printf(mon, "\nImages:\n"); - image_info = inserted->image; - while (1) { - bdrv_image_info_dump(image_info); - if (image_info->has_backing_image) { - image_info = image_info->backing_image; - } else { - break; - } - } - } -} - -void hmp_info_block(Monitor *mon, const QDict *qdict) -{ - BlockInfoList *block_list, *info; - BlockDeviceInfoList *blockdev_list, *blockdev; - const char *device = qdict_get_try_str(qdict, "device"); - bool verbose = qdict_get_try_bool(qdict, "verbose", false); - bool nodes = qdict_get_try_bool(qdict, "nodes", false); - bool printed = false; - - /* Print BlockBackend information */ - if (!nodes) { - block_list = qmp_query_block(NULL); - } else { - block_list = NULL; - } - - for (info = block_list; info; info = info->next) { - if (device && strcmp(device, info->value->device)) { - continue; - } - - if (info != block_list) { - monitor_printf(mon, "\n"); - } - - print_block_info(mon, info->value, info->value->has_inserted - ? info->value->inserted : NULL, - verbose); - printed = true; - } - - qapi_free_BlockInfoList(block_list); - - if ((!device && !nodes) || printed) { - return; - } - - /* Print node information */ - blockdev_list = qmp_query_named_block_nodes(false, false, NULL); - for (blockdev = blockdev_list; blockdev; blockdev = blockdev->next) { - assert(blockdev->value->has_node_name); - if (device && strcmp(device, blockdev->value->node_name)) { - continue; - } - - if (blockdev != blockdev_list) { - monitor_printf(mon, "\n"); - } - - print_block_info(mon, NULL, blockdev->value, verbose); - } - qapi_free_BlockDeviceInfoList(blockdev_list); -} - -void hmp_info_blockstats(Monitor *mon, const QDict *qdict) -{ - BlockStatsList *stats_list, *stats; - - stats_list = qmp_query_blockstats(false, false, NULL); - - for (stats = stats_list; stats; stats = stats->next) { - if (!stats->value->has_device) { - continue; - } - - monitor_printf(mon, "%s:", stats->value->device); - monitor_printf(mon, " rd_bytes=%" PRId64 - " wr_bytes=%" PRId64 - " rd_operations=%" PRId64 - " wr_operations=%" PRId64 - " flush_operations=%" PRId64 - " wr_total_time_ns=%" PRId64 - " rd_total_time_ns=%" PRId64 - " flush_total_time_ns=%" PRId64 - " rd_merged=%" PRId64 - " wr_merged=%" PRId64 - " idle_time_ns=%" PRId64 - "\n", - stats->value->stats->rd_bytes, - stats->value->stats->wr_bytes, - stats->value->stats->rd_operations, - stats->value->stats->wr_operations, - stats->value->stats->flush_operations, - stats->value->stats->wr_total_time_ns, - stats->value->stats->rd_total_time_ns, - stats->value->stats->flush_total_time_ns, - stats->value->stats->rd_merged, - stats->value->stats->wr_merged, - stats->value->stats->idle_time_ns); - } - - qapi_free_BlockStatsList(stats_list); -} #ifdef CONFIG_VNC /* Helper for hmp_info_vnc_clients, _servers */ @@ -1058,44 +852,6 @@ void hmp_info_pci(Monitor *mon, const QDict *qdict) qapi_free_PciInfoList(info_list); } -void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) -{ - BlockJobInfoList *list; - Error *err = NULL; - - list = qmp_query_block_jobs(&err); - assert(!err); - - if (!list) { - monitor_printf(mon, "No active jobs\n"); - return; - } - - while (list) { - if (strcmp(list->value->type, "stream") == 0) { - monitor_printf(mon, "Streaming device %s: Completed %" PRId64 - " of %" PRId64 " bytes, speed limit %" PRId64 - " bytes/s\n", - list->value->device, - list->value->offset, - list->value->len, - list->value->speed); - } else { - monitor_printf(mon, "Type %s, device %s: Completed %" PRId64 - " of %" PRId64 " bytes, speed limit %" PRId64 - " bytes/s\n", - list->value->type, - list->value->device, - list->value->offset, - list->value->len, - list->value->speed); - } - list = list->next; - } - - qapi_free_BlockJobInfoList(list); -} - void hmp_info_tpm(Monitor *mon, const QDict *qdict) { TPMInfoList *info_list, *info; @@ -1313,16 +1069,6 @@ void hmp_set_link(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } -void hmp_block_passwd(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - const char *password = qdict_get_str(qdict, "password"); - Error *err = NULL; - - qmp_block_passwd(true, device, false, NULL, password, &err); - hmp_handle_error(mon, err); -} - void hmp_balloon(Monitor *mon, const QDict *qdict) { int64_t value = qdict_get_int(qdict, "value"); @@ -1332,121 +1078,6 @@ void hmp_balloon(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } -void hmp_block_resize(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - int64_t size = qdict_get_int(qdict, "size"); - Error *err = NULL; - - qmp_block_resize(true, device, false, NULL, size, &err); - hmp_handle_error(mon, err); -} - -void hmp_drive_mirror(Monitor *mon, const QDict *qdict) -{ - const char *filename = qdict_get_str(qdict, "target"); - const char *format = qdict_get_try_str(qdict, "format"); - bool reuse = qdict_get_try_bool(qdict, "reuse", false); - bool full = qdict_get_try_bool(qdict, "full", false); - Error *err = NULL; - DriveMirror mirror = { - .device = (char *)qdict_get_str(qdict, "device"), - .target = (char *)filename, - .has_format = !!format, - .format = (char *)format, - .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, - .has_mode = true, - .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS, - .unmap = true, - }; - - if (!filename) { - error_setg(&err, QERR_MISSING_PARAMETER, "target"); - hmp_handle_error(mon, err); - return; - } - qmp_drive_mirror(&mirror, &err); - hmp_handle_error(mon, err); -} - -void hmp_drive_backup(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - const char *filename = qdict_get_str(qdict, "target"); - const char *format = qdict_get_try_str(qdict, "format"); - bool reuse = qdict_get_try_bool(qdict, "reuse", false); - bool full = qdict_get_try_bool(qdict, "full", false); - bool compress = qdict_get_try_bool(qdict, "compress", false); - Error *err = NULL; - DriveBackup backup = { - .device = (char *)device, - .target = (char *)filename, - .has_format = !!format, - .format = (char *)format, - .sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, - .has_mode = true, - .mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS, - .has_compress = !!compress, - .compress = compress, - }; - - if (!filename) { - error_setg(&err, QERR_MISSING_PARAMETER, "target"); - hmp_handle_error(mon, err); - return; - } - - qmp_drive_backup(&backup, &err); - hmp_handle_error(mon, err); -} - -void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - const char *filename = qdict_get_try_str(qdict, "snapshot-file"); - const char *format = qdict_get_try_str(qdict, "format"); - bool reuse = qdict_get_try_bool(qdict, "reuse", false); - enum NewImageMode mode; - Error *err = NULL; - - if (!filename) { - /* In the future, if 'snapshot-file' is not specified, the snapshot - will be taken internally. Today it's actually required. */ - error_setg(&err, QERR_MISSING_PARAMETER, "snapshot-file"); - hmp_handle_error(mon, err); - return; - } - - mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS; - qmp_blockdev_snapshot_sync(true, device, false, NULL, - filename, false, NULL, - !!format, format, - true, mode, &err); - hmp_handle_error(mon, err); -} - -void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - const char *name = qdict_get_str(qdict, "name"); - Error *err = NULL; - - qmp_blockdev_snapshot_internal_sync(device, name, &err); - hmp_handle_error(mon, err); -} - -void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - const char *name = qdict_get_str(qdict, "name"); - const char *id = qdict_get_try_str(qdict, "id"); - Error *err = NULL; - - qmp_blockdev_snapshot_delete_internal_sync(device, !!id, id, - true, name, &err); - hmp_handle_error(mon, err); -} - void hmp_loadvm(Monitor *mon, const QDict *qdict) { int saved_vm_running = runstate_is_running(); @@ -1483,148 +1114,6 @@ void hmp_delvm(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } -void hmp_info_snapshots(Monitor *mon, const QDict *qdict) -{ - BlockDriverState *bs, *bs1; - BdrvNextIterator it1; - QEMUSnapshotInfo *sn_tab, *sn; - bool no_snapshot = true; - int nb_sns, i; - int total; - int *global_snapshots; - AioContext *aio_context; - - typedef struct SnapshotEntry { - QEMUSnapshotInfo sn; - QTAILQ_ENTRY(SnapshotEntry) next; - } SnapshotEntry; - - typedef struct ImageEntry { - const char *imagename; - QTAILQ_ENTRY(ImageEntry) next; - QTAILQ_HEAD(, SnapshotEntry) snapshots; - } ImageEntry; - - QTAILQ_HEAD(, ImageEntry) image_list = - QTAILQ_HEAD_INITIALIZER(image_list); - - ImageEntry *image_entry, *next_ie; - SnapshotEntry *snapshot_entry; - - bs = bdrv_all_find_vmstate_bs(); - if (!bs) { - monitor_printf(mon, "No available block device supports snapshots\n"); - return; - } - aio_context = bdrv_get_aio_context(bs); - - aio_context_acquire(aio_context); - nb_sns = bdrv_snapshot_list(bs, &sn_tab); - aio_context_release(aio_context); - - if (nb_sns < 0) { - monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns); - return; - } - - for (bs1 = bdrv_first(&it1); bs1; bs1 = bdrv_next(&it1)) { - int bs1_nb_sns = 0; - ImageEntry *ie; - SnapshotEntry *se; - AioContext *ctx = bdrv_get_aio_context(bs1); - - aio_context_acquire(ctx); - if (bdrv_can_snapshot(bs1)) { - sn = NULL; - bs1_nb_sns = bdrv_snapshot_list(bs1, &sn); - if (bs1_nb_sns > 0) { - no_snapshot = false; - ie = g_new0(ImageEntry, 1); - ie->imagename = bdrv_get_device_name(bs1); - QTAILQ_INIT(&ie->snapshots); - QTAILQ_INSERT_TAIL(&image_list, ie, next); - for (i = 0; i < bs1_nb_sns; i++) { - se = g_new0(SnapshotEntry, 1); - se->sn = sn[i]; - QTAILQ_INSERT_TAIL(&ie->snapshots, se, next); - } - } - g_free(sn); - } - aio_context_release(ctx); - } - - if (no_snapshot) { - monitor_printf(mon, "There is no snapshot available.\n"); - return; - } - - global_snapshots = g_new0(int, nb_sns); - total = 0; - for (i = 0; i < nb_sns; i++) { - SnapshotEntry *next_sn; - if (bdrv_all_find_snapshot(sn_tab[i].name, &bs1) == 0) { - global_snapshots[total] = i; - total++; - QTAILQ_FOREACH(image_entry, &image_list, next) { - QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots, - next, next_sn) { - if (!strcmp(sn_tab[i].name, snapshot_entry->sn.name)) { - QTAILQ_REMOVE(&image_entry->snapshots, snapshot_entry, - next); - g_free(snapshot_entry); - } - } - } - } - } - - monitor_printf(mon, "List of snapshots present on all disks:\n"); - - if (total > 0) { - bdrv_snapshot_dump(NULL); - monitor_printf(mon, "\n"); - for (i = 0; i < total; i++) { - sn = &sn_tab[global_snapshots[i]]; - /* The ID is not guaranteed to be the same on all images, so - * overwrite it. - */ - pstrcpy(sn->id_str, sizeof(sn->id_str), "--"); - bdrv_snapshot_dump(sn); - monitor_printf(mon, "\n"); - } - } else { - monitor_printf(mon, "None\n"); - } - - QTAILQ_FOREACH(image_entry, &image_list, next) { - if (QTAILQ_EMPTY(&image_entry->snapshots)) { - continue; - } - monitor_printf(mon, - "\nList of partial (non-loadable) snapshots on '%s':\n", - image_entry->imagename); - bdrv_snapshot_dump(NULL); - monitor_printf(mon, "\n"); - QTAILQ_FOREACH(snapshot_entry, &image_entry->snapshots, next) { - bdrv_snapshot_dump(&snapshot_entry->sn); - monitor_printf(mon, "\n"); - } - } - - QTAILQ_FOREACH_SAFE(image_entry, &image_list, next, next_ie) { - SnapshotEntry *next_sn; - QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots, next, - next_sn) { - g_free(snapshot_entry); - } - g_free(image_entry); - } - g_free(sn_tab); - g_free(global_snapshots); - -} - void hmp_announce_self(Monitor *mon, const QDict *qdict) { const char *interfaces_str = qdict_get_try_str(qdict, "interfaces"); @@ -1769,6 +1258,9 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_decompress_threads = true; visit_type_int(v, param, &p->decompress_threads, &err); break; + case MIGRATION_PARAMETER_THROTTLE_TRIGGER_THRESHOLD: + p->has_throttle_trigger_threshold = true; + visit_type_int(v, param, &p->throttle_trigger_threshold, &err); case MIGRATION_PARAMETER_CPU_THROTTLE_INITIAL: p->has_cpu_throttle_initial = true; visit_type_int(v, param, &p->cpu_throttle_initial, &err); @@ -1946,15 +1438,6 @@ void hmp_expire_password(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } -void hmp_eject(Monitor *mon, const QDict *qdict) -{ - bool force = qdict_get_try_bool(qdict, "force", false); - const char *device = qdict_get_str(qdict, "device"); - Error *err = NULL; - - qmp_eject(true, device, false, NULL, true, force, &err); - hmp_handle_error(mon, err); -} #ifdef CONFIG_VNC static void hmp_change_read_arg(void *opaque, const char *password, @@ -2012,101 +1495,6 @@ void hmp_change(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } -void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - char *device = (char *) qdict_get_str(qdict, "device"); - BlockIOThrottle throttle = { - .bps = qdict_get_int(qdict, "bps"), - .bps_rd = qdict_get_int(qdict, "bps_rd"), - .bps_wr = qdict_get_int(qdict, "bps_wr"), - .iops = qdict_get_int(qdict, "iops"), - .iops_rd = qdict_get_int(qdict, "iops_rd"), - .iops_wr = qdict_get_int(qdict, "iops_wr"), - }; - - /* qmp_block_set_io_throttle has separate parameters for the - * (deprecated) block device name and the qdev ID but the HMP - * version has only one, so we must decide which one to pass. */ - if (blk_by_name(device)) { - throttle.has_device = true; - throttle.device = device; - } else { - throttle.has_id = true; - throttle.id = device; - } - - qmp_block_set_io_throttle(&throttle, &err); - hmp_handle_error(mon, err); -} - -void hmp_block_stream(Monitor *mon, const QDict *qdict) -{ - Error *error = NULL; - const char *device = qdict_get_str(qdict, "device"); - const char *base = qdict_get_try_str(qdict, "base"); - int64_t speed = qdict_get_try_int(qdict, "speed", 0); - - qmp_block_stream(true, device, device, base != NULL, base, false, NULL, - false, NULL, qdict_haskey(qdict, "speed"), speed, true, - BLOCKDEV_ON_ERROR_REPORT, false, false, false, false, - &error); - - hmp_handle_error(mon, error); -} - -void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict) -{ - Error *error = NULL; - const char *device = qdict_get_str(qdict, "device"); - int64_t value = qdict_get_int(qdict, "speed"); - - qmp_block_job_set_speed(device, value, &error); - - hmp_handle_error(mon, error); -} - -void hmp_block_job_cancel(Monitor *mon, const QDict *qdict) -{ - Error *error = NULL; - const char *device = qdict_get_str(qdict, "device"); - bool force = qdict_get_try_bool(qdict, "force", false); - - qmp_block_job_cancel(device, true, force, &error); - - hmp_handle_error(mon, error); -} - -void hmp_block_job_pause(Monitor *mon, const QDict *qdict) -{ - Error *error = NULL; - const char *device = qdict_get_str(qdict, "device"); - - qmp_block_job_pause(device, &error); - - hmp_handle_error(mon, error); -} - -void hmp_block_job_resume(Monitor *mon, const QDict *qdict) -{ - Error *error = NULL; - const char *device = qdict_get_str(qdict, "device"); - - qmp_block_job_resume(device, &error); - - hmp_handle_error(mon, error); -} - -void hmp_block_job_complete(Monitor *mon, const QDict *qdict) -{ - Error *error = NULL; - const char *device = qdict_get_str(qdict, "device"); - - qmp_block_job_complete(device, &error); - - hmp_handle_error(mon, error); -} - typedef struct HMPMigrationStatus { QEMUTimer *timer; @@ -2333,105 +1721,6 @@ void hmp_screendump(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, err); } -void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) -{ - const char *uri = qdict_get_str(qdict, "uri"); - bool writable = qdict_get_try_bool(qdict, "writable", false); - bool all = qdict_get_try_bool(qdict, "all", false); - Error *local_err = NULL; - BlockInfoList *block_list, *info; - SocketAddress *addr; - BlockExportNbd export; - - if (writable && !all) { - error_setg(&local_err, "-w only valid together with -a"); - goto exit; - } - - /* First check if the address is valid and start the server. */ - addr = socket_parse(uri, &local_err); - if (local_err != NULL) { - goto exit; - } - - nbd_server_start(addr, NULL, NULL, &local_err); - qapi_free_SocketAddress(addr); - if (local_err != NULL) { - goto exit; - } - - if (!all) { - return; - } - - /* Then try adding all block devices. If one fails, close all and - * exit. - */ - block_list = qmp_query_block(NULL); - - for (info = block_list; info; info = info->next) { - if (!info->value->has_inserted) { - continue; - } - - export = (BlockExportNbd) { - .device = info->value->device, - .has_writable = true, - .writable = writable, - }; - - qmp_nbd_server_add(&export, &local_err); - - if (local_err != NULL) { - qmp_nbd_server_stop(NULL); - break; - } - } - - qapi_free_BlockInfoList(block_list); - -exit: - hmp_handle_error(mon, local_err); -} - -void hmp_nbd_server_add(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - const char *name = qdict_get_try_str(qdict, "name"); - bool writable = qdict_get_try_bool(qdict, "writable", false); - Error *local_err = NULL; - - BlockExportNbd export = { - .device = (char *) device, - .has_name = !!name, - .name = (char *) name, - .has_writable = true, - .writable = writable, - }; - - qmp_nbd_server_add(&export, &local_err); - hmp_handle_error(mon, local_err); -} - -void hmp_nbd_server_remove(Monitor *mon, const QDict *qdict) -{ - const char *name = qdict_get_str(qdict, "name"); - bool force = qdict_get_try_bool(qdict, "force", false); - Error *err = NULL; - - /* Rely on NBD_SERVER_REMOVE_MODE_SAFE being the default */ - qmp_nbd_server_remove(name, force, NBD_SERVER_REMOVE_MODE_HARD, &err); - hmp_handle_error(mon, err); -} - -void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict) -{ - Error *err = NULL; - - qmp_nbd_server_stop(&err); - hmp_handle_error(mon, err); -} - void hmp_chardev_add(Monitor *mon, const QDict *qdict) { const char *args = qdict_get_str(qdict, "args"); @@ -2498,70 +1787,6 @@ void hmp_chardev_send_break(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, local_err); } -void hmp_qemu_io(Monitor *mon, const QDict *qdict) -{ - BlockBackend *blk; - BlockBackend *local_blk = NULL; - bool qdev = qdict_get_try_bool(qdict, "qdev", false); - const char* device = qdict_get_str(qdict, "device"); - const char* command = qdict_get_str(qdict, "command"); - Error *err = NULL; - int ret; - - if (qdev) { - blk = blk_by_qdev_id(device, &err); - if (!blk) { - goto fail; - } - } else { - blk = blk_by_name(device); - if (!blk) { - BlockDriverState *bs = bdrv_lookup_bs(NULL, device, &err); - if (bs) { - blk = local_blk = blk_new(bdrv_get_aio_context(bs), - 0, BLK_PERM_ALL); - ret = blk_insert_bs(blk, bs, &err); - if (ret < 0) { - goto fail; - } - } else { - goto fail; - } - } - } - - /* - * Notably absent: Proper permission management. This is sad, but it seems - * almost impossible to achieve without changing the semantics and thereby - * limiting the use cases of the qemu-io HMP command. - * - * In an ideal world we would unconditionally create a new BlockBackend for - * qemuio_command(), but we have commands like 'reopen' and want them to - * take effect on the exact BlockBackend whose name the user passed instead - * of just on a temporary copy of it. - * - * Another problem is that deleting the temporary BlockBackend involves - * draining all requests on it first, but some qemu-iotests cases want to - * issue multiple aio_read/write requests and expect them to complete in - * the background while the monitor has already returned. - * - * This is also what prevents us from saving the original permissions and - * restoring them later: We can't revoke permissions until all requests - * have completed, and we don't know when that is nor can we really let - * anything else run before we have revoken them to avoid race conditions. - * - * What happens now is that command() in qemu-io-cmds.c can extend the - * permissions if necessary for the qemu-io command. And they simply stay - * extended, possibly resulting in a read-only guest device keeping write - * permissions. Ugly, but it appears to be the lesser evil. - */ - qemuio_command(blk, command); - -fail: - blk_unref(local_blk); - hmp_handle_error(mon, err); -} - void hmp_object_del(Monitor *mon, const QDict *qdict) { const char *id = qdict_get_str(qdict, "id"); diff --git a/monitor/misc.c b/monitor/misc.c index 1748ab3911..6c45fa490f 100644 --- a/monitor/misc.c +++ b/monitor/misc.c @@ -66,6 +66,7 @@ #include "qemu/option.h" #include "qemu/thread.h" #include "block/qapi.h" +#include "block/block-hmp-cmds.h" #include "qapi/qapi-commands-char.h" #include "qapi/qapi-commands-control.h" #include "qapi/qapi-commands-migration.h" @@ -246,8 +247,6 @@ static void monitor_init_qmp_commands(void) qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG); qmp_register_command(&qmp_commands, "device_add", qmp_device_add, QCO_NO_OPTIONS); - qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add, - QCO_NO_OPTIONS); qmp_register_command(&qmp_commands, "object-add", qmp_object_add, QCO_NO_OPTIONS); @@ -2036,13 +2035,11 @@ void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str) count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC, MAX_QUEUE_NUM); for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { - QemuOpts *opts; const char *name = ncs[i]->name; if (strncmp(str, name, len)) { continue; } - opts = qemu_opts_find(qemu_find_opts_err("netdev", NULL), name); - if (opts) { + if (ncs[i]->is_netdev) { readline_add_completion(rs, name); } } diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 3e6baba88f..8f60ccc70a 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -133,7 +133,7 @@ typedef struct { * qmp_capabilities succeeds, we go into command mode, and * @command becomes &qmp_commands. */ - QmpCommandList *commands; + const QmpCommandList *commands; bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */ bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */ /* diff --git a/monitor/qmp-cmds-control.c b/monitor/qmp-cmds-control.c index 5cd9bb817c..8f04cfa6e6 100644 --- a/monitor/qmp-cmds-control.c +++ b/monitor/qmp-cmds-control.c @@ -101,7 +101,7 @@ VersionInfo *qmp_query_version(Error **errp) return info; } -static void query_commands_cb(QmpCommand *cmd, void *opaque) +static void query_commands_cb(const QmpCommand *cmd, void *opaque) { CommandInfoList *info, **list = opaque; diff --git a/nbd/server.c b/nbd/server.c index 11a31094ff..02b1ed0801 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1909,27 +1909,98 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, return ret; } +typedef struct NBDExtentArray { + NBDExtent *extents; + unsigned int nb_alloc; + unsigned int count; + uint64_t total_length; + bool can_add; + bool converted_to_be; +} NBDExtentArray; + +static NBDExtentArray *nbd_extent_array_new(unsigned int nb_alloc) +{ + NBDExtentArray *ea = g_new0(NBDExtentArray, 1); + + ea->nb_alloc = nb_alloc; + ea->extents = g_new(NBDExtent, nb_alloc); + ea->can_add = true; + + return ea; +} + +static void nbd_extent_array_free(NBDExtentArray *ea) +{ + g_free(ea->extents); + g_free(ea); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC(NBDExtentArray, nbd_extent_array_free); + +/* Further modifications of the array after conversion are abandoned */ +static void nbd_extent_array_convert_to_be(NBDExtentArray *ea) +{ + int i; + + assert(!ea->converted_to_be); + ea->can_add = false; + ea->converted_to_be = true; + + for (i = 0; i < ea->count; i++) { + ea->extents[i].flags = cpu_to_be32(ea->extents[i].flags); + ea->extents[i].length = cpu_to_be32(ea->extents[i].length); + } +} + /* - * Populate @extents from block status. Update @bytes to be the actual - * length encoded (which may be smaller than the original), and update - * @nb_extents to the number of extents used. - * - * Returns zero on success and -errno on bdrv_block_status_above failure. + * Add extent to NBDExtentArray. If extent can't be added (no available space), + * return -1. + * For safety, when returning -1 for the first time, .can_add is set to false, + * further call to nbd_extent_array_add() will crash. + * (to avoid the situation, when after failing to add an extent (returned -1), + * user miss this failure and add another extent, which is successfully added + * (array is full, but new extent may be squashed into the last one), then we + * have invalid array with skipped extent) */ -static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, - uint64_t *bytes, NBDExtent *extents, - unsigned int *nb_extents) +static int nbd_extent_array_add(NBDExtentArray *ea, + uint32_t length, uint32_t flags) { - uint64_t remaining_bytes = *bytes; - NBDExtent *extent = extents, *extents_end = extents + *nb_extents; - bool first_extent = true; + assert(ea->can_add); + + if (!length) { + return 0; + } + + /* Extend previous extent if flags are the same */ + if (ea->count > 0 && flags == ea->extents[ea->count - 1].flags) { + uint64_t sum = (uint64_t)length + ea->extents[ea->count - 1].length; + + if (sum <= UINT32_MAX) { + ea->extents[ea->count - 1].length = sum; + ea->total_length += length; + return 0; + } + } - assert(*nb_extents); - while (remaining_bytes) { + if (ea->count >= ea->nb_alloc) { + ea->can_add = false; + return -1; + } + + ea->total_length += length; + ea->extents[ea->count] = (NBDExtent) {.length = length, .flags = flags}; + ea->count++; + + return 0; +} + +static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, NBDExtentArray *ea) +{ + while (bytes) { uint32_t flags; int64_t num; - int ret = bdrv_block_status_above(bs, NULL, offset, remaining_bytes, - &num, NULL, NULL); + int ret = bdrv_block_status_above(bs, NULL, offset, bytes, &num, + NULL, NULL); if (ret < 0) { return ret; @@ -1938,60 +2009,37 @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) | (ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0); - if (first_extent) { - extent->flags = flags; - extent->length = num; - first_extent = false; - } else if (flags == extent->flags) { - /* extend current extent */ - extent->length += num; - } else { - if (extent + 1 == extents_end) { - break; - } - - /* start new extent */ - extent++; - extent->flags = flags; - extent->length = num; + if (nbd_extent_array_add(ea, num, flags) < 0) { + return 0; } - offset += num; - remaining_bytes -= num; - } - extents_end = extent + 1; - - for (extent = extents; extent < extents_end; extent++) { - extent->flags = cpu_to_be32(extent->flags); - extent->length = cpu_to_be32(extent->length); + offset += num; + bytes -= num; } - *bytes -= remaining_bytes; - *nb_extents = extents_end - extents; - return 0; } -/* nbd_co_send_extents +/* + * nbd_co_send_extents * - * @length is only for tracing purposes (and may be smaller or larger - * than the client's original request). @last controls whether - * NBD_REPLY_FLAG_DONE is sent. @extents should already be in - * big-endian format. + * @ea is converted to BE by the function + * @last controls whether NBD_REPLY_FLAG_DONE is sent. */ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, - NBDExtent *extents, unsigned int nb_extents, - uint64_t length, bool last, - uint32_t context_id, Error **errp) + NBDExtentArray *ea, + bool last, uint32_t context_id, Error **errp) { NBDStructuredMeta chunk; - struct iovec iov[] = { {.iov_base = &chunk, .iov_len = sizeof(chunk)}, - {.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])} + {.iov_base = ea->extents, .iov_len = ea->count * sizeof(ea->extents[0])} }; - trace_nbd_co_send_extents(handle, nb_extents, context_id, length, last); + nbd_extent_array_convert_to_be(ea); + + trace_nbd_co_send_extents(handle, ea->count, context_id, ea->total_length, + last); set_be_chunk(&chunk.h, last ? NBD_REPLY_FLAG_DONE : 0, NBD_REPLY_TYPE_BLOCK_STATUS, handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len); @@ -2009,82 +2057,47 @@ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, { int ret; unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS; - NBDExtent *extents = g_new(NBDExtent, nb_extents); - uint64_t final_length = length; + g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents); - ret = blockstatus_to_extents(bs, offset, &final_length, extents, - &nb_extents); + ret = blockstatus_to_extents(bs, offset, length, ea); if (ret < 0) { - g_free(extents); return nbd_co_send_structured_error( client, handle, -ret, "can't get block status", errp); } - ret = nbd_co_send_extents(client, handle, extents, nb_extents, - final_length, last, context_id, errp); - - g_free(extents); - - return ret; + return nbd_co_send_extents(client, handle, ea, last, context_id, errp); } -/* - * Populate @extents from a dirty bitmap. Unless @dont_fragment, the - * final extent may exceed the original @length. Store in @length the - * byte length encoded (which may be smaller or larger than the - * original), and return the number of extents used. - */ -static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, - uint64_t *length, NBDExtent *extents, - unsigned int nb_extents, - bool dont_fragment) +/* Populate @ea from a dirty bitmap. */ +static void bitmap_to_extents(BdrvDirtyBitmap *bitmap, + uint64_t offset, uint64_t length, + NBDExtentArray *es) { - uint64_t begin = offset, end = offset; - uint64_t overall_end = offset + *length; - unsigned int i = 0; - BdrvDirtyBitmapIter *it; - bool dirty; + int64_t start, dirty_start, dirty_count; + int64_t end = offset + length; + bool full = false; bdrv_dirty_bitmap_lock(bitmap); - it = bdrv_dirty_iter_new(bitmap); - dirty = bdrv_dirty_bitmap_get_locked(bitmap, offset); - - assert(begin < overall_end && nb_extents); - while (begin < overall_end && i < nb_extents) { - bool next_dirty = !dirty; - - if (dirty) { - end = bdrv_dirty_bitmap_next_zero(bitmap, begin, UINT64_MAX); - } else { - bdrv_set_dirty_iter(it, begin); - end = bdrv_dirty_iter_next(it); - } - if (end == -1 || end - begin > UINT32_MAX) { - /* Cap to an aligned value < 4G beyond begin. */ - end = MIN(bdrv_dirty_bitmap_size(bitmap), - begin + UINT32_MAX + 1 - - bdrv_dirty_bitmap_granularity(bitmap)); - next_dirty = dirty; - } - if (dont_fragment && end > overall_end) { - end = overall_end; + for (start = offset; + bdrv_dirty_bitmap_next_dirty_area(bitmap, start, end, INT32_MAX, + &dirty_start, &dirty_count); + start = dirty_start + dirty_count) + { + if ((nbd_extent_array_add(es, dirty_start - start, 0) < 0) || + (nbd_extent_array_add(es, dirty_count, NBD_STATE_DIRTY) < 0)) + { + full = true; + break; } - - extents[i].length = cpu_to_be32(end - begin); - extents[i].flags = cpu_to_be32(dirty ? NBD_STATE_DIRTY : 0); - i++; - begin = end; - dirty = next_dirty; } - bdrv_dirty_iter_free(it); + if (!full) { + /* last non dirty extent */ + nbd_extent_array_add(es, end - start, 0); + } bdrv_dirty_bitmap_unlock(bitmap); - - assert(offset < end); - *length = end - offset; - return i; } static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle, @@ -2092,20 +2105,12 @@ static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle, uint32_t length, bool dont_fragment, bool last, uint32_t context_id, Error **errp) { - int ret; unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS; - NBDExtent *extents = g_new(NBDExtent, nb_extents); - uint64_t final_length = length; - - nb_extents = bitmap_to_extents(bitmap, offset, &final_length, extents, - nb_extents, dont_fragment); + g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents); - ret = nbd_co_send_extents(client, handle, extents, nb_extents, - final_length, last, context_id, errp); + bitmap_to_extents(bitmap, offset, length, ea); - g_free(extents); - - return ret; + return nbd_co_send_extents(client, handle, ea, last, context_id, errp); } /* nbd_co_receive_request @@ -194,29 +194,6 @@ NetClientState *net_hub_add_port(int hub_id, const char *name, } /** - * Find a specific client on a hub - */ -NetClientState *net_hub_find_client_by_name(int hub_id, const char *name) -{ - NetHub *hub; - NetHubPort *port; - NetClientState *peer; - - QLIST_FOREACH(hub, &hubs, next) { - if (hub->id == hub_id) { - QLIST_FOREACH(port, &hub->ports, next) { - peer = port->nc.peer; - - if (peer && strcmp(peer->name, name) == 0) { - return peer; - } - } - } - } - return NULL; -} - -/** * Find a available port on a hub; otherwise create one new port */ NetClientState *net_hub_port_find(int hub_id) @@ -15,10 +15,8 @@ #ifndef NET_HUB_H #define NET_HUB_H - NetClientState *net_hub_add_port(int hub_id, const char *name, NetClientState *hubpeer); -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); @@ -1060,6 +1060,15 @@ static int net_client_init1(const void *object, bool is_netdev, Error **errp) } return -1; } + + if (is_netdev) { + NetClientState *nc; + + nc = qemu_find_netdev(netdev->id); + assert(nc); + nc->is_netdev = true; + } + return 0; } @@ -1170,36 +1179,14 @@ void netdev_add(QemuOpts *opts, Error **errp) net_client_init(opts, true, errp); } -void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp) +void qmp_netdev_add(Netdev *netdev, Error **errp) { - Error *local_err = NULL; - QemuOptsList *opts_list; - QemuOpts *opts; - - opts_list = qemu_find_opts_err("netdev", &local_err); - if (local_err) { - goto out; - } - - opts = qemu_opts_from_qdict(opts_list, qdict, &local_err); - if (local_err) { - goto out; - } - - netdev_add(opts, &local_err); - if (local_err) { - qemu_opts_del(opts); - goto out; - } - -out: - error_propagate(errp, local_err); + net_client_init1(netdev, true, errp); } void qmp_netdev_del(const char *id, Error **errp) { NetClientState *nc; - QemuOpts *opts; nc = qemu_find_netdev(id); if (!nc) { @@ -1208,14 +1195,12 @@ void qmp_netdev_del(const char *id, Error **errp) return; } - opts = qemu_opts_find(qemu_find_opts_err("netdev", NULL), id); - if (!opts) { + if (!nc->is_netdev) { error_setg(errp, "Device '%s' is not a netdev", id); return; } qemu_del_net_client(nc); - qemu_opts_del(opts); } static void netfilter_print_info(Monitor *mon, NetFilterState *nf) diff --git a/net/queue.c b/net/queue.c index 61276ca4be..0164727e39 100644 --- a/net/queue.c +++ b/net/queue.c @@ -46,7 +46,7 @@ struct NetPacket { unsigned flags; int size; NetPacketSent *sent_cb; - uint8_t data[0]; + uint8_t data[]; }; struct NetQueue { diff --git a/net/slirp.c b/net/slirp.c index c4334ee876..77042e6df7 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -610,25 +610,13 @@ error: return -1; } -static SlirpState *slirp_lookup(Monitor *mon, const char *hub_id, - const char *name) +static SlirpState *slirp_lookup(Monitor *mon, const char *id) { - if (name) { - NetClientState *nc; - if (hub_id) { - nc = net_hub_find_client_by_name(strtol(hub_id, NULL, 0), name); - if (!nc) { - monitor_printf(mon, "unrecognized (hub-id, stackname) pair\n"); - return NULL; - } - warn_report("Using 'hub-id' is deprecated, specify the netdev id " - "directly instead"); - } else { - nc = qemu_find_netdev(name); - if (!nc) { - monitor_printf(mon, "unrecognized netdev id '%s'\n", name); - return NULL; - } + if (id) { + NetClientState *nc = qemu_find_netdev(id); + if (!nc) { + monitor_printf(mon, "unrecognized netdev id '%s'\n", id); + return NULL; } if (strcmp(nc->model, "user")) { monitor_printf(mon, "invalid device specified\n"); @@ -655,16 +643,12 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) int err; const char *arg1 = qdict_get_str(qdict, "arg1"); const char *arg2 = qdict_get_try_str(qdict, "arg2"); - const char *arg3 = qdict_get_try_str(qdict, "arg3"); - if (arg3) { - s = slirp_lookup(mon, arg1, arg2); - src_str = arg3; - } else if (arg2) { - s = slirp_lookup(mon, NULL, arg1); + if (arg2) { + s = slirp_lookup(mon, arg1); src_str = arg2; } else { - s = slirp_lookup(mon, NULL, NULL); + s = slirp_lookup(mon, NULL); src_str = arg1; } if (!s) { @@ -784,16 +768,12 @@ void hmp_hostfwd_add(Monitor *mon, const QDict *qdict) SlirpState *s; const char *arg1 = qdict_get_str(qdict, "arg1"); const char *arg2 = qdict_get_try_str(qdict, "arg2"); - const char *arg3 = qdict_get_try_str(qdict, "arg3"); - if (arg3) { - s = slirp_lookup(mon, arg1, arg2); - redir_str = arg3; - } else if (arg2) { - s = slirp_lookup(mon, NULL, arg1); + if (arg2) { + s = slirp_lookup(mon, arg1); redir_str = arg2; } else { - s = slirp_lookup(mon, NULL, NULL); + s = slirp_lookup(mon, NULL); redir_str = arg1; } if (s) { diff --git a/pc-bios/README b/pc-bios/README index d6d33d237f..f54c2743d0 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -14,7 +14,7 @@ - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at https://github.com/aik/SLOF, and the image currently in qemu is - built from git tag qemu-slof-20191217. + built from git tag qemu-slof-20200317. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as diff --git a/pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin b/pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin Binary files differnew file mode 100644 index 0000000000..bab13f597a --- /dev/null +++ b/pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin diff --git a/pc-bios/opensbi-riscv32-virt-fw_jump.bin b/pc-bios/opensbi-riscv32-virt-fw_jump.bin Binary files differindex 6c5b7b89f6..c9654e7047 100644 --- a/pc-bios/opensbi-riscv32-virt-fw_jump.bin +++ b/pc-bios/opensbi-riscv32-virt-fw_jump.bin diff --git a/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin b/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin Binary files differindex 971f2be405..77f4dc8f83 100644 --- a/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin +++ b/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin diff --git a/pc-bios/opensbi-riscv64-virt-fw_jump.bin b/pc-bios/opensbi-riscv64-virt-fw_jump.bin Binary files differindex 45a5aed1ce..31e74d12ea 100644 --- a/pc-bios/opensbi-riscv64-virt-fw_jump.bin +++ b/pc-bios/opensbi-riscv64-virt-fw_jump.bin diff --git a/pc-bios/optionrom/pvh_main.c b/pc-bios/optionrom/pvh_main.c index a015e1bf22..28e79d7fc4 100644 --- a/pc-bios/optionrom/pvh_main.c +++ b/pc-bios/optionrom/pvh_main.c @@ -29,7 +29,7 @@ asm (".code32"); /* this code will be executed in protected mode */ #define RSDP_SIGNATURE 0x2052545020445352LL /* "RSD PTR " */ #define RSDP_AREA_ADDR 0x000E0000 -#define RSDP_AREA_SIZE 2048 +#define RSDP_AREA_SIZE 0x00020000 #define EBDA_BASE_ADDR 0x0000040E #define EBDA_SIZE 1024 diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img Binary files differindex 91cdee4436..b9da9d8ecb 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index 94f53a5f1e..12a0166aae 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -136,7 +136,7 @@ typedef struct BootMapScriptHeader { typedef struct BootMapScript { BootMapScriptHeader header; - BootMapScriptEntry entry[0]; + BootMapScriptEntry entry[]; } __attribute__ ((packed)) BootMapScript; /* diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c index da13c43cc0..4eba2510b0 100644 --- a/pc-bios/s390-ccw/jump2ipl.c +++ b/pc-bios/s390-ccw/jump2ipl.c @@ -35,6 +35,7 @@ void jump_to_IPL_code(uint64_t address) { /* store the subsystem information _after_ the bootmap was loaded */ write_subsystem_identification(); + write_iplb_location(); /* prevent unknown IPL types in the guest */ if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index a21b386280..4e65b411e1 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -9,6 +9,7 @@ */ #include "libc.h" +#include "helper.h" #include "s390-arch.h" #include "s390-ccw.h" #include "cio.h" @@ -22,7 +23,7 @@ QemuIplParameters qipl; IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); static bool have_iplb; static uint16_t cutype; -LowCore const *lowcore; /* Yes, this *is* a pointer to address 0 */ +LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */ #define LOADPARM_PROMPT "PROMPT " #define LOADPARM_EMPTY " " @@ -42,6 +43,11 @@ void write_subsystem_identification(void) *zeroes = 0; } +void write_iplb_location(void) +{ + lowcore->ptr_iplb = ptr2u32(&iplb); +} + void panic(const char *string) { sclp_print(string); diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index f2dcc01e27..309ffa30d9 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -40,6 +40,7 @@ #define DEFAULT_TFTP_RETRIES 20 extern char _start[]; +void write_iplb_location(void) {} #define KERNEL_ADDR ((void *)0L) #define KERNEL_MAX_SIZE ((long)_start) diff --git a/pc-bios/s390-ccw/s390-arch.h b/pc-bios/s390-ccw/s390-arch.h index 504fc7c2f0..5f36361c02 100644 --- a/pc-bios/s390-ccw/s390-arch.h +++ b/pc-bios/s390-ccw/s390-arch.h @@ -36,7 +36,13 @@ typedef struct LowCore { /* prefix area: defined by architecture */ PSWLegacy ipl_psw; /* 0x000 */ uint32_t ccw1[2]; /* 0x008 */ - uint32_t ccw2[2]; /* 0x010 */ + union { + uint32_t ccw2[2]; /* 0x010 */ + struct { + uint32_t reserved10; + uint32_t ptr_iplb; + }; + }; uint8_t pad1[0x80 - 0x18]; /* 0x018 */ uint32_t ext_params; /* 0x080 */ uint16_t cpu_addr; /* 0x084 */ @@ -85,7 +91,7 @@ typedef struct LowCore { PSW io_new_psw; /* 0x1f0 */ } __attribute__((packed, aligned(8192))) LowCore; -extern LowCore const *lowcore; +extern LowCore *lowcore; static inline void set_prefix(uint32_t address) { diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 11bce7d73c..21f27e7990 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -57,6 +57,7 @@ void consume_io_int(void); /* main.c */ void panic(const char *string); void write_subsystem_identification(void); +void write_iplb_location(void); extern char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); unsigned int get_loadparm_index(void); diff --git a/pc-bios/s390-ccw/sclp.h b/pc-bios/s390-ccw/sclp.h index 8450161ba7..64b53cad29 100644 --- a/pc-bios/s390-ccw/sclp.h +++ b/pc-bios/s390-ccw/sclp.h @@ -95,7 +95,7 @@ typedef struct EventBufferHeader { typedef struct WriteEventData { SCCBHeader h; EventBufferHeader ebh; - char data[0]; + char data[]; } __attribute__((packed)) WriteEventData; typedef struct ReadEventData { diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin Binary files differindex 78d8b26cbb..40499a1451 100644 --- a/pc-bios/slof.bin +++ b/pc-bios/slof.bin diff --git a/plugins/core.c b/plugins/core.c index ed863011ba..51bfc94787 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -15,6 +15,7 @@ #include "qemu/error-report.h" #include "qemu/config-file.h" #include "qapi/error.h" +#include "qemu/lockable.h" #include "qemu/option.h" #include "qemu/rcu_queue.h" #include "qemu/xxhash.h" @@ -150,11 +151,11 @@ do_plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev, { struct qemu_plugin_ctx *ctx; - qemu_rec_mutex_lock(&plugin.lock); + QEMU_LOCK_GUARD(&plugin.lock); ctx = plugin_id_to_ctx_locked(id); /* if the plugin is on its way out, ignore this request */ if (unlikely(ctx->uninstalling)) { - goto out_unlock; + return; } if (func) { struct qemu_plugin_cb *cb = ctx->callbacks[ev]; @@ -178,8 +179,6 @@ do_plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev, } else { plugin_unregister_cb__locked(ctx, ev); } - out_unlock: - qemu_rec_mutex_unlock(&plugin.lock); } void plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev, diff --git a/plugins/loader.c b/plugins/loader.c index 15fc7e5515..685d334e1a 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -19,6 +19,7 @@ #include "qemu/error-report.h" #include "qemu/config-file.h" #include "qapi/error.h" +#include "qemu/lockable.h" #include "qemu/option.h" #include "qemu/rcu_queue.h" #include "qemu/qht.h" @@ -367,15 +368,14 @@ void plugin_reset_uninstall(qemu_plugin_id_t id, struct qemu_plugin_reset_data *data; struct qemu_plugin_ctx *ctx; - qemu_rec_mutex_lock(&plugin.lock); - ctx = plugin_id_to_ctx_locked(id); - if (ctx->uninstalling || (reset && ctx->resetting)) { - qemu_rec_mutex_unlock(&plugin.lock); - return; + WITH_QEMU_LOCK_GUARD(&plugin.lock) { + ctx = plugin_id_to_ctx_locked(id); + if (ctx->uninstalling || (reset && ctx->resetting)) { + return; + } + ctx->resetting = reset; + ctx->uninstalling = !reset; } - ctx->resetting = reset; - ctx->uninstalling = !reset; - qemu_rec_mutex_unlock(&plugin.lock); data = g_new(struct qemu_plugin_reset_data, 1); data->ctx = ctx; diff --git a/python/qemu/machine.py b/python/qemu/machine.py index 183d8f3d38..f53abfa492 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -270,7 +270,8 @@ class QEMUMachine(object): self._vm_monitor = os.path.join(self._sock_dir, self._name + "-monitor.sock") self._remove_files.append(self._vm_monitor) - self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor, server=True) + self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor, server=True, + nickname=self._name) def _post_launch(self): if self._qmp: diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index f40586eedd..d6c9b2f4b1 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -46,7 +46,7 @@ class QEMUMonitorProtocol: #: Logger object for debugging messages logger = logging.getLogger('QMP') - def __init__(self, address, server=False): + def __init__(self, address, server=False, nickname=None): """ Create a QEMUMonitorProtocol class. @@ -62,6 +62,9 @@ class QEMUMonitorProtocol: self.__address = address self.__sock = self.__get_sock() self.__sockfile = None + self._nickname = nickname + if self._nickname: + self.logger = logging.getLogger('QMP').getChild(self._nickname) if server: self.__sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.__sock.bind(self.__address) diff --git a/qapi/audio.json b/qapi/audio.json index d8c507cced..c31251f45b 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -273,6 +273,20 @@ # # An enumeration of possible audio formats. # +# @u8: unsigned 8 bit integer +# +# @s8: signed 8 bit integer +# +# @u16: unsigned 16 bit integer +# +# @s16: signed 16 bit integer +# +# @u32: unsigned 32 bit integer +# +# @s32: signed 32 bit integer +# +# @f32: single precision floating-point (since 5.0) +# # Since: 4.0 ## { 'enum': 'AudioFormat', diff --git a/qapi/block-core.json b/qapi/block-core.json index 9758fc48d2..943df1926a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -297,7 +297,7 @@ # # @encrypted: true if the backing device is encrypted # -# @encryption_key_missing: Deprecated; always false +# @encryption_key_missing: always false # # @detect_zeroes: detect and optimize zero writes (Since 2.1) # @@ -363,13 +363,19 @@ # @dirty-bitmaps: dirty bitmaps information (only present if node # has one or more dirty bitmaps) (Since 4.2) # +# Features: +# @deprecated: Member @encryption_key_missing is deprecated. It is +# always false. +# # Since: 0.14.0 # ## { 'struct': 'BlockDeviceInfo', 'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str', '*backing_file': 'str', 'backing_file_depth': 'int', - 'encrypted': 'bool', 'encryption_key_missing': 'bool', + 'encrypted': 'bool', + 'encryption_key_missing': { 'type': 'bool', + 'features': [ 'deprecated' ] }, 'detect_zeroes': 'BlockdevDetectZeroesOptions', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', @@ -475,7 +481,7 @@ # # @granularity: granularity of the dirty bitmap in bytes (since 1.4) # -# @status: Deprecated in favor of @recording and @locked. (since 2.4) +# @status: current status of the dirty bitmap (since 2.4) # # @recording: true if the bitmap is recording new writes from the guest. # Replaces `active` and `disabled` statuses. (since 4.0) @@ -492,11 +498,17 @@ # @busy to be false. This bitmap cannot be used. To remove # it, use @block-dirty-bitmap-remove. (Since 4.0) # +# Features: +# @deprecated: Member @status is deprecated. Use @recording and +# @locked instead. +# # Since: 1.3 ## { 'struct': 'BlockDirtyInfo', 'data': {'*name': 'str', 'count': 'int', 'granularity': 'uint32', - 'recording': 'bool', 'busy': 'bool', 'status': 'DirtyBitmapStatus', + 'recording': 'bool', 'busy': 'bool', + 'status': { 'type': 'DirtyBitmapStatus', + 'features': [ 'deprecated' ] }, 'persistent': 'bool', '*inconsistent': 'bool' } } ## @@ -587,7 +599,6 @@ # # @dirty-bitmaps: dirty bitmaps information (only present if the # driver has one or more dirty bitmaps) (Since 2.0) -# Deprecated in 4.2; see BlockDeviceInfo instead. # # @io-status: @BlockDeviceIoStatus. Only present if the device # supports it and the VM is configured to stop on errors @@ -597,13 +608,18 @@ # @inserted: @BlockDeviceInfo describing the device if media is # present # +# Features: +# @deprecated: Member @dirty-bitmaps is deprecated. Use @inserted +# member @dirty-bitmaps instead. +# # Since: 0.14.0 ## { 'struct': 'BlockInfo', 'data': {'device': 'str', '*qdev': 'str', 'type': 'str', 'removable': 'bool', 'locked': 'bool', '*inserted': 'BlockDeviceInfo', '*tray_open': 'bool', '*io-status': 'BlockDeviceIoStatus', - '*dirty-bitmaps': ['BlockDirtyInfo'] } } + '*dirty-bitmaps': { 'type': ['BlockDirtyInfo'], + 'features': [ 'deprecated' ] } } } ## # @BlockMeasureInfo: @@ -1472,6 +1488,12 @@ # # For the arguments, see the documentation of BlockdevSnapshot. # +# Features: +# @allow-write-only-overlay: If present, the check whether this operation is safe +# was relaxed so that it can be used to change +# backing file of a destination of a blockdev-mirror. +# (since 5.0) +# # Since: 2.5 # # Example: @@ -1492,7 +1514,8 @@ # ## { 'command': 'blockdev-snapshot', - 'data': 'BlockdevSnapshot' } + 'data': 'BlockdevSnapshot', + 'features': [ 'allow-write-only-overlay' ] } ## # @change-backing-file: @@ -1544,7 +1567,7 @@ # @base: Same as @base-node, except that it is a file name rather than a node # name. This must be the exact filename string that was used to open the # node; other strings, even if addressing the same file, are not -# accepted (deprecated, use @base-node instead) +# accepted # # @top-node: The node name of the backing image within the image chain # which contains the topmost data to be committed down. If @@ -1553,7 +1576,7 @@ # @top: Same as @top-node, except that it is a file name rather than a node # name. This must be the exact filename string that was used to open the # node; other strings, even if addressing the same file, are not -# accepted (deprecated, use @base-node instead) +# accepted # # @backing-file: The backing file string to write into the overlay # image of 'top'. If 'top' is the active layer, @@ -1607,6 +1630,10 @@ # list without user intervention. # Defaults to true. (Since 3.1) # +# Features: +# @deprecated: Members @base and @top are deprecated. Use @base-node +# and @top-node instead. +# # Returns: - Nothing on success # - If @device does not exist, DeviceNotFound # - Any other error returns a GenericError. @@ -1623,7 +1650,9 @@ ## { 'command': 'block-commit', 'data': { '*job-id': 'str', 'device': 'str', '*base-node': 'str', - '*base': 'str', '*top-node': 'str', '*top': 'str', + '*base': { 'type': 'str', 'features': [ 'deprecated' ] }, + '*top-node': 'str', + '*top': { 'type': 'str', 'features': [ 'deprecated' ] }, '*backing-file': 'str', '*speed': 'int', '*on-error': 'BlockdevOnError', '*filter-node-name': 'str', @@ -2289,7 +2318,7 @@ # # A set of parameters describing block throttling. # -# @device: Block device name (deprecated, use @id instead) +# @device: Block device name # # @id: The name or QOM path of the guest device (since: 2.8) # @@ -2357,10 +2386,14 @@ # # @group: throttle group name (Since 2.4) # +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# # Since: 1.1 ## { 'struct': 'BlockIOThrottle', - 'data': { '*device': 'str', '*id': 'str', 'bps': 'int', 'bps_rd': 'int', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, + '*id': 'str', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', '*bps_max': 'int', '*bps_rd_max': 'int', '*bps_wr_max': 'int', '*iops_max': 'int', diff --git a/qapi/block.json b/qapi/block.json index 97bf52b7c7..2ddbfa8306 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -90,15 +90,18 @@ ## # @eject: # -# Ejects a device from a removable drive. +# Ejects the medium from a removable drive. # -# @device: Block device name (deprecated, use @id instead) +# @device: Block device name # # @id: The name or QOM path of the guest device (since: 2.8) # # @force: If true, eject regardless of whether the drive is locked. # If not specified, the default value is false. # +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# # Returns: - Nothing on success # - If @device is not a valid block device, DeviceNotFound # Notes: Ejecting a device with no media results in success @@ -111,7 +114,7 @@ # <- { "return": {} } ## { 'command': 'eject', - 'data': { '*device': 'str', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, '*id': 'str', '*force': 'bool' } } @@ -134,7 +137,7 @@ # to it # - if the guest device does not have an actual tray # -# @device: Block device name (deprecated, use @id instead) +# @device: Block device name # # @id: The name or QOM path of the guest device (since: 2.8) # @@ -143,6 +146,9 @@ # immediately); if true, the tray will be opened regardless of whether # it is locked # +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# # Since: 2.5 # # Example: @@ -161,7 +167,7 @@ # ## { 'command': 'blockdev-open-tray', - 'data': { '*device': 'str', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, '*id': 'str', '*force': 'bool' } } @@ -174,10 +180,13 @@ # # If the tray was already closed before, this will be a no-op. # -# @device: Block device name (deprecated, use @id instead) +# @device: Block device name # # @id: The name or QOM path of the guest device (since: 2.8) # +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# # Since: 2.5 # # Example: @@ -196,7 +205,7 @@ # ## { 'command': 'blockdev-close-tray', - 'data': { '*device': 'str', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, '*id': 'str' } } ## @@ -303,7 +312,7 @@ # combines blockdev-open-tray, blockdev-remove-medium, blockdev-insert-medium # and blockdev-close-tray). # -# @device: Block device name (deprecated, use @id instead) +# @device: Block device name # # @id: The name or QOM path of the guest device # (since: 2.8) @@ -316,6 +325,9 @@ # @read-only-mode: change the read-only mode of the device; defaults # to 'retain' # +# Features: +# @deprecated: Member @device is deprecated. Use @id instead. +# # Since: 2.5 # # Examples: @@ -350,7 +362,7 @@ # ## { 'command': 'blockdev-change-medium', - 'data': { '*device': 'str', + 'data': { '*device': { 'type': 'str', 'features': [ 'deprecated' ] }, '*id': 'str', 'filename': 'str', '*format': 'str', diff --git a/qapi/char.json b/qapi/char.json index 6907b2bfdb..daceb20f84 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -258,6 +258,7 @@ # @server: create server socket (default: true) # @wait: wait for incoming connection on server # sockets (default: false). +# Silently ignored with server: false. This use is deprecated. # @nodelay: set TCP_NODELAY socket option (default: false) # @telnet: enable telnet protocol on server # sockets (default: false) diff --git a/qapi/control.json b/qapi/control.json index 85b12fe0fb..6b816bb61f 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -174,13 +174,15 @@ # # Return information on QMP events. # +# Features: +# @deprecated: This command is deprecated, because its output doesn't +# reflect compile-time configuration. Use 'query-qmp-schema' +# instead. +# # Returns: A list of @EventInfo. # # Since: 1.2.0 # -# Note: This command is deprecated, because its output doesn't reflect -# compile-time configuration. Use query-qmp-schema instead. -# # Example: # # -> { "execute": "query-events" } @@ -198,7 +200,8 @@ # Note: This example has been shortened as the real response is too long. # ## -{ 'command': 'query-events', 'returns': ['EventInfo'] } +{ 'command': 'query-events', 'returns': ['EventInfo'], + 'features': [ 'deprecated' ] } ## # @quit: diff --git a/qapi/introspect.json b/qapi/introspect.json index 8756e7920e..b1aabd4cfd 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -89,12 +89,18 @@ # # @meta-type: the entity's meta type, inherited from @base. # +# @features: names of features associated with the entity, in no +# particular order. +# (since 4.1 for object types, 4.2 for commands, 5.0 for +# the rest) +# # Additional members depend on the value of @meta-type. # # Since: 2.5 ## { 'union': 'SchemaInfo', - 'base': { 'name': 'str', 'meta-type': 'SchemaMetaType' }, + 'base': { 'name': 'str', 'meta-type': 'SchemaMetaType', + '*features': [ 'str' ] }, 'discriminator': 'meta-type', 'data': { 'builtin': 'SchemaInfoBuiltin', @@ -174,9 +180,6 @@ # and may even differ from the order of the values of the # enum type of the @tag. # -# @features: names of features associated with the type, in no particular -# order. (since: 4.1) -# # Values of this type are JSON object on the wire. # # Since: 2.5 @@ -184,8 +187,7 @@ { 'struct': 'SchemaInfoObject', 'data': { 'members': [ 'SchemaInfoObjectMember' ], '*tag': 'str', - '*variants': [ 'SchemaInfoObjectVariant' ], - '*features': [ 'str' ] } } + '*variants': [ 'SchemaInfoObjectVariant' ] } } ## # @SchemaInfoObjectMember: @@ -204,11 +206,15 @@ # Future extension: if present and non-null, the parameter # is optional, and defaults to this value. # +# @features: names of features associated with the member, in no +# particular order. (since 5.0) +# # Since: 2.5 ## { 'struct': 'SchemaInfoObjectMember', - 'data': { 'name': 'str', 'type': 'str', '*default': 'any' } } + 'data': { 'name': 'str', 'type': 'str', '*default': 'any', # @default's type must be null or match @type + '*features': [ 'str' ] } } ## # @SchemaInfoObjectVariant: @@ -266,17 +272,13 @@ # @allow-oob: whether the command allows out-of-band execution, # defaults to false (Since: 2.12) # -# @features: names of features associated with the command, in no particular -# order. (since 4.2) -# # TODO: @success-response (currently irrelevant, because it's QGA, not QMP) # # Since: 2.5 ## { 'struct': 'SchemaInfoCommand', 'data': { 'arg-type': 'str', 'ret-type': 'str', - '*allow-oob': 'bool', - '*features': [ 'str' ] } } + '*allow-oob': 'bool' } } ## # @SchemaInfoEvent: diff --git a/qapi/machine.json b/qapi/machine.json index 6c11e3cf3a..ff7b5032e3 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -16,6 +16,8 @@ # individual target constants are not documented here, for the time # being. # +# @rx: since 5.0 +# # Notes: The resulting QMP strings can be appended to the "qemu-system-" # prefix to produce the corresponding QEMU executable name. This # is true even for "qemu-system-x86_64". @@ -26,7 +28,7 @@ 'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32', 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64', 'mips64el', 'mipsel', 'moxie', 'nios2', 'or1k', 'ppc', - 'ppc64', 'riscv32', 'riscv64', 's390x', 'sh4', + 'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4', 'sh4eb', 'sparc', 'sparc64', 'tricore', 'unicore32', 'x86_64', 'xtensa', 'xtensaeb' ] } @@ -184,8 +186,11 @@ # This command causes vCPU threads to exit to userspace, which causes # a small interruption to guest CPU execution. This will have a negative # impact on realtime guests and other latency sensitive guest workloads. -# It is recommended to use @query-cpus-fast instead of this command to -# avoid the vCPU interruption. +# +# Features: +# @deprecated: This command is deprecated, because it interferes with +# the guest. Use 'query-cpus-fast' instead to avoid the vCPU +# interruption. # # Returns: a list of @CpuInfo for each virtual CPU # @@ -216,12 +221,9 @@ # ] # } # -# Notes: This interface is deprecated (since 2.12.0), and it is strongly -# recommended that you avoid using it. Use @query-cpus-fast to -# obtain information about virtual CPUs. -# ## -{ 'command': 'query-cpus', 'returns': ['CpuInfo'] } +{ 'command': 'query-cpus', 'returns': ['CpuInfo'], + 'features': [ 'deprecated' ] } ## # @CpuInfoFast: @@ -237,12 +239,14 @@ # @props: properties describing to which node/socket/core/thread # virtual CPU belongs to, provided if supported by board # -# @arch: base architecture of the cpu; deprecated since 3.0.0 in favor -# of @target +# @arch: base architecture of the cpu # # @target: the QEMU system emulation target, which determines which # additional fields will be listed (since 3.0) # +# Features: +# @deprecated: Member @arch is deprecated. Use @target instead. +# # Since: 2.12 # ## @@ -251,7 +255,8 @@ 'qom-path' : 'str', 'thread-id' : 'int', '*props' : 'CpuInstanceProperties', - 'arch' : 'CpuInfoArch', + 'arch' : { 'type': 'CpuInfoArch', + 'features': [ 'deprecated' ] }, 'target' : 'SysEmuTarget' }, 'discriminator' : 'target', 'data' : { 's390x' : 'CpuInfoS390' } } @@ -307,21 +312,22 @@ # # @id: ID of CPU to be created, valid values [0..max_cpus) # +# Features: +# @deprecated: This command is deprecated. Use `device_add` instead. +# See the `query-hotpluggable-cpus` command for details. +# # Returns: Nothing on success # # Since: 1.5 # -# Note: This command is deprecated. The `device_add` command should be -# used instead. See the `query-hotpluggable-cpus` command for -# details. -# # Example: # # -> { "execute": "cpu-add", "arguments": { "id": 2 } } # <- { "return": {} } # ## -{ 'command': 'cpu-add', 'data': {'id': 'int'} } +{ 'command': 'cpu-add', 'data': {'id': 'int'}, + 'features': [ 'deprecated' ] } ## # @MachineInfo: diff --git a/qapi/migration.json b/qapi/migration.json index d44d99cd78..eca2981d0a 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -540,6 +540,10 @@ # compression, so set the decompress-threads to the number about 1/4 # of compress-threads is adequate. # +# @throttle-trigger-threshold: The ratio of bytes_dirty_period and bytes_xfer_period +# to trigger throttling. It is expressed as percentage. +# The default value is 50. (Since 5.0) +# # @cpu-throttle-initial: Initial percentage of time guest cpus are throttled # when migration auto-converge is activated. The # default value is 20. (Since 2.7) @@ -625,7 +629,7 @@ 'data': ['announce-initial', 'announce-max', 'announce-rounds', 'announce-step', 'compress-level', 'compress-threads', 'decompress-threads', - 'compress-wait-thread', + 'compress-wait-thread', 'throttle-trigger-threshold', 'cpu-throttle-initial', 'cpu-throttle-increment', 'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth', 'downtime-limit', 'x-checkpoint-delay', 'block-incremental', @@ -660,6 +664,10 @@ # # @decompress-threads: decompression thread count # +# @throttle-trigger-threshold: The ratio of bytes_dirty_period and bytes_xfer_period +# to trigger throttling. It is expressed as percentage. +# The default value is 50. (Since 5.0) +# # @cpu-throttle-initial: Initial percentage of time guest cpus are # throttled when migration auto-converge is activated. # The default value is 20. (Since 2.7) @@ -752,6 +760,7 @@ '*compress-threads': 'int', '*compress-wait-thread': 'bool', '*decompress-threads': 'int', + '*throttle-trigger-threshold': 'int', '*cpu-throttle-initial': 'int', '*cpu-throttle-increment': 'int', '*tls-creds': 'StrOrNull', @@ -813,6 +822,10 @@ # # @decompress-threads: decompression thread count # +# @throttle-trigger-threshold: The ratio of bytes_dirty_period and bytes_xfer_period +# to trigger throttling. It is expressed as percentage. +# The default value is 50. (Since 5.0) +# # @cpu-throttle-initial: Initial percentage of time guest cpus are # throttled when migration auto-converge is activated. # (Since 2.7) @@ -905,6 +918,7 @@ '*compress-threads': 'uint8', '*compress-wait-thread': 'bool', '*decompress-threads': 'uint8', + '*throttle-trigger-threshold': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', '*tls-creds': 'str', @@ -1196,9 +1210,11 @@ # # @value: maximum downtime in seconds # -# Returns: nothing on success +# Features: +# @deprecated: This command is deprecated. Use +# 'migrate-set-parameters' instead. # -# Notes: This command is deprecated in favor of 'migrate-set-parameters' +# Returns: nothing on success # # Since: 0.14.0 # @@ -1208,7 +1224,8 @@ # <- { "return": {} } # ## -{ 'command': 'migrate_set_downtime', 'data': {'value': 'number'} } +{ 'command': 'migrate_set_downtime', 'data': {'value': 'number'}, + 'features': [ 'deprecated' ] } ## # @migrate_set_speed: @@ -1217,9 +1234,11 @@ # # @value: maximum speed in bytes per second. # -# Returns: nothing on success +# Features: +# @deprecated: This command is deprecated. Use +# 'migrate-set-parameters' instead. # -# Notes: This command is deprecated in favor of 'migrate-set-parameters' +# Returns: nothing on success # # Since: 0.14.0 # @@ -1229,7 +1248,8 @@ # <- { "return": {} } # ## -{ 'command': 'migrate_set_speed', 'data': {'value': 'int'} } +{ 'command': 'migrate_set_speed', 'data': {'value': 'int'}, + 'features': [ 'deprecated' ] } ## # @migrate-set-cache-size: @@ -1238,13 +1258,15 @@ # # @value: cache size in bytes # +# Features: +# @deprecated: This command is deprecated. Use +# 'migrate-set-parameters' instead. +# # The size will be rounded down to the nearest power of 2. # The cache size can be modified before and during ongoing migration # # Returns: nothing on success # -# Notes: This command is deprecated in favor of 'migrate-set-parameters' -# # Since: 1.2 # # Example: @@ -1254,16 +1276,19 @@ # <- { "return": {} } # ## -{ 'command': 'migrate-set-cache-size', 'data': {'value': 'int'} } +{ 'command': 'migrate-set-cache-size', 'data': {'value': 'int'}, + 'features': [ 'deprecated' ] } ## # @query-migrate-cache-size: # # Query migration XBZRLE cache size # -# Returns: XBZRLE cache size in bytes +# Features: +# @deprecated: This command is deprecated. Use +# 'query-migrate-parameters' instead. # -# Notes: This command is deprecated in favor of 'query-migrate-parameters' +# Returns: XBZRLE cache size in bytes # # Since: 1.2 # @@ -1273,7 +1298,8 @@ # <- { "return": 67108864 } # ## -{ 'command': 'query-migrate-cache-size', 'returns': 'int' } +{ 'command': 'query-migrate-cache-size', 'returns': 'int', + 'features': [ 'deprecated' ] } ## # @migrate: diff --git a/qapi/misc.json b/qapi/misc.json index c18fe681fb..99b90ac80b 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -872,14 +872,14 @@ # If @device is 'vnc' and @target is 'password', this is the new VNC # password to set. See change-vnc-password for additional notes. # +# Features: +# @deprecated: This command is deprecated. For changing block +# devices, use 'blockdev-change-medium' instead; for changing VNC +# parameters, use 'change-vnc-password' instead. +# # Returns: - Nothing on success. # - If @device is not a valid block device, DeviceNotFound # -# Notes: This interface is deprecated, and it is strongly recommended that you -# avoid using it. For changing block devices, use -# blockdev-change-medium; for changing VNC parameters, use -# change-vnc-password. -# # Since: 0.14.0 # # Example: @@ -900,7 +900,8 @@ # ## { 'command': 'change', - 'data': {'device': 'str', 'target': 'str', '*arg': 'str'} } + 'data': {'device': 'str', 'target': 'str', '*arg': 'str'}, + 'features': [ 'deprecated' ] } ## # @xen-set-global-dirty-log: diff --git a/qapi/net.json b/qapi/net.json index 1cb9a7d782..cebb1b52e3 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -39,18 +39,8 @@ # # Add a network backend. # -# @type: the type of network backend. Possible values are listed in -# NetClientDriver (excluding 'none' and 'nic') -# -# @id: the name of the new network backend -# # Additional arguments depend on the type. # -# TODO: This command effectively bypasses QAPI completely due to its -# "additional arguments" business. It shouldn't have been added to -# the schema in this form. It should be qapified properly, or -# replaced by a properly qapified command. -# # Since: 0.14.0 # # Returns: Nothing on success @@ -64,9 +54,7 @@ # <- { "return": {} } # ## -{ 'command': 'netdev_add', - 'data': {'type': 'str', 'id': 'str'}, - 'gen': false } # so we can get the additional arguments +{ 'command': 'netdev_add', 'data': 'Netdev', 'boxed': true } ## # @netdev_del: diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index bc264b3c9b..c30c7ff9e1 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -19,20 +19,13 @@ #include "sysemu/runstate.h" #include "qapi/qmp/qbool.h" -static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, +static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob, Error **errp) { const char *exec_key = NULL; const QDictEntry *ent; const char *arg_name; const QObject *arg_obj; - QDict *dict; - - dict = qobject_to(QDict, request); - if (!dict) { - error_setg(errp, "QMP input must be a JSON object"); - return NULL; - } for (ent = qdict_first(dict); ent; ent = qdict_next(dict, ent)) { @@ -75,19 +68,50 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, return dict; } -static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, - bool allow_oob, Error **errp) +QDict *qmp_error_response(Error *err) +{ + QDict *rsp; + + rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }", + QapiErrorClass_str(error_get_class(err)), + error_get_pretty(err)); + error_free(err); + return rsp; +} + +/* + * Does @qdict look like a command to be run out-of-band? + */ +bool qmp_is_oob(const QDict *dict) +{ + return qdict_haskey(dict, "exec-oob") + && !qdict_haskey(dict, "execute"); +} + +QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, + bool allow_oob) { - Error *local_err = NULL; + Error *err = NULL; bool oob; const char *command; - QDict *args, *dict; - QmpCommand *cmd; + QDict *args; + const QmpCommand *cmd; + QDict *dict; + QObject *id; QObject *ret = NULL; + QDict *rsp = NULL; - dict = qmp_dispatch_check_obj(request, allow_oob, errp); + dict = qobject_to(QDict, request); if (!dict) { - return NULL; + id = NULL; + error_setg(&err, "QMP input must be a JSON object"); + goto out; + } + + id = qdict_get(dict, "id"); + + if (!qmp_dispatch_check_obj(dict, allow_oob, &err)) { + goto out; } command = qdict_get_try_str(dict, "execute"); @@ -99,27 +123,27 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, } cmd = qmp_find_command(cmds, command); if (cmd == NULL) { - error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, "The command %s has not been found", command); - return NULL; + goto out; } if (!cmd->enabled) { - error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, "The command %s has been disabled for this instance", command); - return NULL; + goto out; } if (oob && !(cmd->options & QCO_ALLOW_OOB)) { - error_setg(errp, "The command %s does not support OOB", + error_setg(&err, "The command %s does not support OOB", command); - return NULL; + goto out; } if (runstate_check(RUN_STATE_PRECONFIG) && !(cmd->options & QCO_ALLOW_PRECONFIG)) { - error_setg(errp, "The command '%s' isn't permitted in '%s' state", + error_setg(&err, "The command '%s' isn't permitted in '%s' state", cmd->name, RunState_str(RUN_STATE_PRECONFIG)); - return NULL; + goto out; } if (!qdict_haskey(dict, "arguments")) { @@ -128,62 +152,36 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, args = qdict_get_qdict(dict, "arguments"); qobject_ref(args); } + cmd->fn(args, &ret, &err); + qobject_unref(args); + if (err) { + goto out; + } - cmd->fn(args, &ret, &local_err); - if (local_err) { - error_propagate(errp, local_err); - } else if (cmd->options & QCO_NO_SUCCESS_RESP) { + if (cmd->options & QCO_NO_SUCCESS_RESP) { g_assert(!ret); + return NULL; } else if (!ret) { - /* TODO turn into assertion */ + /* + * When the command's schema has no 'returns', cmd->fn() + * leaves @ret null. The QMP spec calls for an empty object + * then; supply it. + */ ret = QOBJECT(qdict_new()); } - qobject_unref(args); - - return ret; -} - -QDict *qmp_error_response(Error *err) -{ - QDict *rsp; - - rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }", - QapiErrorClass_str(error_get_class(err)), - error_get_pretty(err)); - error_free(err); - return rsp; -} - -/* - * Does @qdict look like a command to be run out-of-band? - */ -bool qmp_is_oob(const QDict *dict) -{ - return qdict_haskey(dict, "exec-oob") - && !qdict_haskey(dict, "execute"); -} - -QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, - bool allow_oob) -{ - Error *err = NULL; - QDict *dict = qobject_to(QDict, request); - QObject *ret, *id = dict ? qdict_get(dict, "id") : NULL; - QDict *rsp; + rsp = qdict_new(); + qdict_put_obj(rsp, "return", ret); - ret = do_qmp_dispatch(cmds, request, allow_oob, &err); +out: if (err) { + assert(!rsp); rsp = qmp_error_response(err); - } else if (ret) { - rsp = qdict_new(); - qdict_put_obj(rsp, "return", ret); - } else { - /* Can only happen for commands with QCO_NO_SUCCESS_RESP */ - rsp = NULL; } - if (rsp && id) { + assert(rsp); + + if (id) { qdict_put_obj(rsp, "id", qobject_ref(id)); } diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index ca00f74795..d0f9a1d3e3 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -27,7 +27,7 @@ void qmp_register_command(QmpCommandList *cmds, const char *name, QTAILQ_INSERT_TAIL(cmds, cmd, node); } -QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name) +const QmpCommand *qmp_find_command(const QmpCommandList *cmds, const char *name) { QmpCommand *cmd; @@ -77,10 +77,10 @@ bool qmp_has_success_response(const QmpCommand *cmd) return !(cmd->options & QCO_NO_SUCCESS_RESP); } -void qmp_for_each_command(QmpCommandList *cmds, qmp_cmd_callback_fn fn, +void qmp_for_each_command(const QmpCommandList *cmds, qmp_cmd_callback_fn fn, void *opaque) { - QmpCommand *cmd; + const QmpCommand *cmd; QTAILQ_FOREACH(cmd, cmds, node) { fn(cmd, opaque); diff --git a/qemu-img.c b/qemu-img.c index 804630a368..afddf33f08 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -817,6 +817,8 @@ static int img_check(int argc, char **argv) check->corruptions_fixed); } + qapi_free_ImageCheck(check); + check = g_new0(ImageCheck, 1); ret = collect_image_check(bs, check, filename, fmt, 0); check->leaks_fixed = leaks_fixed; @@ -882,9 +884,9 @@ static void run_block_job(BlockJob *job, Error **errp) do { float progress = 0.0f; aio_poll(aio_context, true); - if (job->job.progress_total) { - progress = (float)job->job.progress_current / - job->job.progress_total * 100.f; + if (job->job.progress.total) { + progress = (float)job->job.progress.current / + job->job.progress.total * 100.f; } qemu_progress_print(progress, 0); } while (!job_is_ready(&job->job) && !job_is_completed(&job->job)); @@ -4932,10 +4934,8 @@ static int img_measure(int argc, char **argv) filename = argv[optind]; } - if (!filename && - (object_opts || image_opts || fmt || snapshot_name || sn_opts)) { - error_report("--object, --image-opts, -f, and -l " - "require a filename argument."); + if (!filename && (image_opts || fmt || snapshot_name || sn_opts)) { + error_report("--image-opts, -f, and -l require a filename argument."); goto out; } if (filename && img_size != UINT64_MAX) { diff --git a/qemu-options.hx b/qemu-options.hx index f9fefd43be..962a5ebaa6 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1,10 +1,10 @@ -HXCOMM Use DEFHEADING() to define headings in both help text and texi -HXCOMM Text between STEXI and ETEXI are copied to texi version and -HXCOMM discarded from C version +HXCOMM Use DEFHEADING() to define headings in both help text and rST. +HXCOMM Text between SRST and ERST is copied to the rST version and +HXCOMM discarded from C version. HXCOMM DEF(option, HAS_ARG/0, opt_enum, opt_help, arch_mask) is used to HXCOMM construct option structures, enums and help message for specified HXCOMM architectures. -HXCOMM HXCOMM can be used for comments, discarded from both texi and C +HXCOMM HXCOMM can be used for comments, discarded from both rST and C. DEFHEADING(Standard options:) @@ -551,7 +551,7 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, " in|out.frequency= frequency to use with fixed settings\n" " in|out.channels= number of channels to use with fixed settings\n" " in|out.format= sample format to use with fixed settings\n" - " valid values: s8, s16, s32, u8, u16, u32\n" + " valid values: s8, s16, s32, u8, u16, u32, f32\n" " in|out.voices= number of voices to use\n" " in|out.buffer-length= length of buffer in microseconds\n" "-audiodev none,id=id,[,prop[=value][,...]]\n" @@ -647,7 +647,7 @@ SRST ``in|out.format=format`` Specify the sample format to use when using fixed-settings. Valid values are: ``s8``, ``s16``, ``s32``, ``u8``, ``u16``, - ``u32``. Default is ``s16``. + ``u32``, ``f32``. Default is ``s16``. ``in|out.voices=voices`` Specify the number of voices to use. Default is 1. diff --git a/qga/commands.c b/qga/commands.c index 43c323cead..f8852beb9c 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -54,7 +54,7 @@ void qmp_guest_ping(Error **errp) slog("guest-ping called"); } -static void qmp_command_info(QmpCommand *cmd, void *opaque) +static void qmp_command_info(const QmpCommand *cmd, void *opaque) { GuestAgentInfo *info = opaque; GuestAgentCommandInfo *cmd_info; diff --git a/qga/main.c b/qga/main.c index e5c39c189a..8ee2736f8e 100644 --- a/qga/main.c +++ b/qga/main.c @@ -359,7 +359,7 @@ static gint ga_strcmp(gconstpointer str1, gconstpointer str2) } /* disable commands that aren't safe for fsfreeze */ -static void ga_disable_non_whitelisted(QmpCommand *cmd, void *opaque) +static void ga_disable_non_whitelisted(const QmpCommand *cmd, void *opaque) { bool whitelisted = false; int i = 0; @@ -378,7 +378,7 @@ static void ga_disable_non_whitelisted(QmpCommand *cmd, void *opaque) } /* [re-]enable all commands, except those explicitly blacklisted by user */ -static void ga_enable_non_blacklisted(QmpCommand *cmd, void *opaque) +static void ga_enable_non_blacklisted(const QmpCommand *cmd, void *opaque) { GList *blacklist = opaque; const char *name = qmp_command_name(cmd); @@ -918,7 +918,7 @@ int64_t ga_get_fd_handle(GAState *s, Error **errp) return handle; } -static void ga_print_cmd(QmpCommand *cmd, void *opaque) +static void ga_print_cmd(const QmpCommand *cmd, void *opaque) { printf("%s\n", qmp_command_name(cmd)); } diff --git a/qom/object.c b/qom/object.c index 555c8b9d07..1812f79224 100644 --- a/qom/object.c +++ b/qom/object.c @@ -2498,6 +2498,22 @@ static void property_get_uint8_ptr(Object *obj, Visitor *v, const char *name, visit_type_uint8(v, name, &value, errp); } +static void property_set_uint8_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint8_t *field = opaque; + uint8_t value; + Error *local_err = NULL; + + visit_type_uint8(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + *field = value; +} + static void property_get_uint16_ptr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -2505,6 +2521,22 @@ static void property_get_uint16_ptr(Object *obj, Visitor *v, const char *name, visit_type_uint16(v, name, &value, errp); } +static void property_set_uint16_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint16_t *field = opaque; + uint16_t value; + Error *local_err = NULL; + + visit_type_uint16(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + *field = value; +} + static void property_get_uint32_ptr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -2512,6 +2544,22 @@ static void property_get_uint32_ptr(Object *obj, Visitor *v, const char *name, visit_type_uint32(v, name, &value, errp); } +static void property_set_uint32_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t *field = opaque; + uint32_t value; + Error *local_err = NULL; + + visit_type_uint32(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + *field = value; +} + static void property_get_uint64_ptr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -2519,68 +2567,184 @@ static void property_get_uint64_ptr(Object *obj, Visitor *v, const char *name, visit_type_uint64(v, name, &value, errp); } +static void property_set_uint64_ptr(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint64_t *field = opaque; + uint64_t value; + Error *local_err = NULL; + + visit_type_uint64(v, name, &value, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + *field = value; +} + void object_property_add_uint8_ptr(Object *obj, const char *name, - const uint8_t *v, Error **errp) + const uint8_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_property_add(obj, name, "uint8", property_get_uint8_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint8_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint8_ptr; + } + + object_property_add(obj, name, "uint8", + getter, setter, NULL, (void *)v, errp); } ObjectProperty * object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, - const uint8_t *v, Error **errp) + const uint8_t *v, + ObjectPropertyFlags flags, + Error **errp) { + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint8_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint8_ptr; + } + return object_class_property_add(klass, name, "uint8", - property_get_uint8_ptr, - NULL, NULL, (void *)v, errp); + getter, setter, NULL, (void *)v, errp); } void object_property_add_uint16_ptr(Object *obj, const char *name, - const uint16_t *v, Error **errp) + const uint16_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_property_add(obj, name, "uint16", property_get_uint16_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint16_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint16_ptr; + } + + object_property_add(obj, name, "uint16", + getter, setter, NULL, (void *)v, errp); } ObjectProperty * object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, - const uint16_t *v, Error **errp) + const uint16_t *v, + ObjectPropertyFlags flags, + Error **errp) { + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint16_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint16_ptr; + } + return object_class_property_add(klass, name, "uint16", - property_get_uint16_ptr, - NULL, NULL, (void *)v, errp); + getter, setter, NULL, (void *)v, errp); } void object_property_add_uint32_ptr(Object *obj, const char *name, - const uint32_t *v, Error **errp) + const uint32_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_property_add(obj, name, "uint32", property_get_uint32_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint32_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint32_ptr; + } + + object_property_add(obj, name, "uint32", + getter, setter, NULL, (void *)v, errp); } ObjectProperty * object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, - const uint32_t *v, Error **errp) + const uint32_t *v, + ObjectPropertyFlags flags, + Error **errp) { + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint32_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint32_ptr; + } + return object_class_property_add(klass, name, "uint32", - property_get_uint32_ptr, - NULL, NULL, (void *)v, errp); + getter, setter, NULL, (void *)v, errp); } void object_property_add_uint64_ptr(Object *obj, const char *name, - const uint64_t *v, Error **errp) + const uint64_t *v, + ObjectPropertyFlags flags, + Error **errp) { - object_property_add(obj, name, "uint64", property_get_uint64_ptr, - NULL, NULL, (void *)v, errp); + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint64_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint64_ptr; + } + + object_property_add(obj, name, "uint64", + getter, setter, NULL, (void *)v, errp); } ObjectProperty * object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, - const uint64_t *v, Error **errp) + const uint64_t *v, + ObjectPropertyFlags flags, + Error **errp) { + ObjectPropertyAccessor *getter = NULL; + ObjectPropertyAccessor *setter = NULL; + + if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { + getter = property_get_uint64_ptr; + } + + if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { + setter = property_set_uint64_ptr; + } + return object_class_property_add(klass, name, "uint64", - property_get_uint64_ptr, - NULL, NULL, (void *)v, errp); + getter, setter, NULL, (void *)v, errp); } typedef struct { diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c index 49db926fcc..435193b036 100644 --- a/qom/qom-qmp-cmds.c +++ b/qom/qom-qmp-cmds.c @@ -247,26 +247,22 @@ void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp) QDict *pdict; Visitor *v; Object *obj; - const char *type; - const char *id; + g_autofree char *type = NULL; + g_autofree char *id = NULL; - type = qdict_get_try_str(qdict, "qom-type"); + type = g_strdup(qdict_get_try_str(qdict, "qom-type")); if (!type) { error_setg(errp, QERR_MISSING_PARAMETER, "qom-type"); return; - } else { - type = g_strdup(type); - qdict_del(qdict, "qom-type"); } + qdict_del(qdict, "qom-type"); - id = qdict_get_try_str(qdict, "id"); + id = g_strdup(qdict_get_try_str(qdict, "id")); if (!id) { error_setg(errp, QERR_MISSING_PARAMETER, "id"); return; - } else { - id = g_strdup(id); - qdict_del(qdict, "id"); } + qdict_del(qdict, "id"); props = qdict_get(qdict, "props"); if (props) { diff --git a/roms/Makefile b/roms/Makefile index 28e1e557b0..f9acf39954 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -66,6 +66,7 @@ default help: @echo " efi -- update UEFI (edk2) platform firmware" @echo " opensbi32-virt -- update OpenSBI for 32-bit virt machine" @echo " opensbi64-virt -- update OpenSBI for 64-bit virt machine" + @echo " opensbi32-sifive_u -- update OpenSBI for 32-bit sifive_u machine" @echo " opensbi64-sifive_u -- update OpenSBI for 64-bit sifive_u machine" @echo " bios-microvm -- update bios-microvm.bin (qboot)" @echo " clean -- delete the files generated by the previous" \ @@ -181,6 +182,12 @@ opensbi64-virt: PLATFORM="qemu/virt" cp opensbi/build/platform/qemu/virt/firmware/fw_jump.bin ../pc-bios/opensbi-riscv64-virt-fw_jump.bin +opensbi32-sifive_u: + $(MAKE) -C opensbi \ + CROSS_COMPILE=$(riscv32_cross_prefix) \ + PLATFORM="sifive/fu540" + cp opensbi/build/platform/sifive/fu540/firmware/fw_jump.bin ../pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin + opensbi64-sifive_u: $(MAKE) -C opensbi \ CROSS_COMPILE=$(riscv64_cross_prefix) \ diff --git a/roms/SLOF b/roms/SLOF -Subproject 9546892a80d5a4c73deea6719de46372f007f4a +Subproject ab6984f5a6d054e1f634dda855b32e535711197 diff --git a/roms/opensbi b/roms/opensbi -Subproject be92da280d87c38a2e0adc5d3f43bab7b5468f0 +Subproject ac5e821d50be631f26274765a59bc1b444ffd86 diff --git a/scripts/coccinelle/cpu-reset.cocci b/scripts/coccinelle/cpu-reset.cocci new file mode 100644 index 0000000000..396a724e51 --- /dev/null +++ b/scripts/coccinelle/cpu-reset.cocci @@ -0,0 +1,47 @@ +// Convert targets using the old CPUState reset to DeviceState reset +// +// Copyright Linaro Ltd 2020 +// This work is licensed under the terms of the GNU GPLv2 or later. +// +// spatch --macro-file scripts/cocci-macro-file.h \ +// --sp-file scripts/coccinelle/cpu-reset.cocci \ +// --keep-comments --smpl-spacing --in-place --include-headers --dir target +// +// For simplicity we assume some things about the code we're modifying +// that happen to be true for all our targets: +// * all cpu_class_set_parent_reset() callsites have a 'DeviceClass *dc' local +// * the parent reset field in the target CPU class is 'parent_reset' +// * no reset function already has a 'dev' local + +@@ +identifier cpu, x; +typedef CPUState; +@@ +struct x { +... +- void (*parent_reset)(CPUState *cpu); ++ DeviceReset parent_reset; +... +}; +@ rule1 @ +identifier resetfn; +expression resetfield; +identifier cc; +@@ +- cpu_class_set_parent_reset(cc, resetfn, resetfield) ++ device_class_set_parent_reset(dc, resetfn, resetfield) +@@ +identifier rule1.resetfn; +identifier cpu, cc; +typedef CPUState, DeviceState; +@@ +-resetfn(CPUState *cpu) +-{ ++resetfn(DeviceState *dev) ++{ ++ CPUState *cpu = CPU(dev); +<... +- cc->parent_reset(cpu); ++ cc->parent_reset(dev); +...> +} diff --git a/scripts/coccinelle/memory-region-housekeeping.cocci b/scripts/coccinelle/memory-region-housekeeping.cocci new file mode 100644 index 0000000000..c768d8140a --- /dev/null +++ b/scripts/coccinelle/memory-region-housekeeping.cocci @@ -0,0 +1,159 @@ +/* + Usage: + + spatch \ + --macro-file scripts/cocci-macro-file.h \ + --sp-file scripts/coccinelle/memory-region-housekeeping.cocci \ + --keep-comments \ + --in-place \ + --dir . + +*/ + + +// Replace memory_region_init_ram(readonly) by memory_region_init_rom() +@@ +expression E1, E2, E3, E4, E5; +symbol true; +@@ +( +- memory_region_init_ram(E1, E2, E3, E4, E5); ++ memory_region_init_rom(E1, E2, E3, E4, E5); + ... WHEN != E1 +- memory_region_set_readonly(E1, true); +| +- memory_region_init_ram_nomigrate(E1, E2, E3, E4, E5); ++ memory_region_init_rom_nomigrate(E1, E2, E3, E4, E5); + ... WHEN != E1 +- memory_region_set_readonly(E1, true); +) + + +@possible_memory_region_init_rom@ +expression E1, E2, E3, E4, E5; +position p; +@@ +( + memory_region_init_ram@p(E1, E2, E3, E4, E5); + ... + memory_region_set_readonly(E1, true); +| + memory_region_init_ram_nomigrate@p(E1, E2, E3, E4, E5); + ... + memory_region_set_readonly(E1, true); +) +@script:python@ +p << possible_memory_region_init_rom.p; +@@ +cocci.print_main("potential use of memory_region_init_rom*() in ", p) + + +// Do not call memory_region_set_readonly() on ROM alias +@@ +expression ROM, E1, E2, E3, E4; +expression ALIAS, E5, E6, E7, E8; +@@ +( + memory_region_init_rom(ROM, E1, E2, E3, E4); +| + memory_region_init_rom_nomigrate(ROM, E1, E2, E3, E4); +) + ... + memory_region_init_alias(ALIAS, E5, E6, ROM, E7, E8); +- memory_region_set_readonly(ALIAS, true); + + +// Replace by-hand memory_region_init_ram_nomigrate/vmstate_register_ram +// code sequences with use of the new memory_region_init_ram function. +// Similarly for the _rom and _rom_device functions. +// We don't try to replace sequences with a non-NULL owner, because +// there are none in the tree that can be automatically converted +// (and only a handful that can be manually converted). +@@ +expression MR; +expression NAME; +expression SIZE; +expression ERRP; +@@ +-memory_region_init_ram_nomigrate(MR, NULL, NAME, SIZE, ERRP); ++memory_region_init_ram(MR, NULL, NAME, SIZE, ERRP); + ... +-vmstate_register_ram_global(MR); +@@ +expression MR; +expression NAME; +expression SIZE; +expression ERRP; +@@ +-memory_region_init_rom_nomigrate(MR, NULL, NAME, SIZE, ERRP); ++memory_region_init_rom(MR, NULL, NAME, SIZE, ERRP); + ... +-vmstate_register_ram_global(MR); +@@ +expression MR; +expression OPS; +expression OPAQUE; +expression NAME; +expression SIZE; +expression ERRP; +@@ +-memory_region_init_rom_device_nomigrate(MR, NULL, OPS, OPAQUE, NAME, SIZE, ERRP); ++memory_region_init_rom_device(MR, NULL, OPS, OPAQUE, NAME, SIZE, ERRP); + ... +-vmstate_register_ram_global(MR); + + +// Device is owner +@@ +typedef DeviceState; +identifier device_fn, dev, obj; +expression E1, E2, E3, E4, E5; +@@ +static void device_fn(DeviceState *dev, ...) +{ + ... + Object *obj = OBJECT(dev); + <+... +( +- memory_region_init(E1, NULL, E2, E3); ++ memory_region_init(E1, obj, E2, E3); +| +- memory_region_init_io(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_io(E1, obj, E2, E3, E4, E5); +| +- memory_region_init_alias(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_alias(E1, obj, E2, E3, E4, E5); +| +- memory_region_init_rom(E1, NULL, E2, E3, E4); ++ memory_region_init_rom(E1, obj, E2, E3, E4); +| +- memory_region_init_ram_shared_nomigrate(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_ram_shared_nomigrate(E1, obj, E2, E3, E4, E5); +) + ...+> +} +@@ +identifier device_fn, dev; +expression E1, E2, E3, E4, E5; +@@ +static void device_fn(DeviceState *dev, ...) +{ + <+... +( +- memory_region_init(E1, NULL, E2, E3); ++ memory_region_init(E1, OBJECT(dev), E2, E3); +| +- memory_region_init_io(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_io(E1, OBJECT(dev), E2, E3, E4, E5); +| +- memory_region_init_alias(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_alias(E1, OBJECT(dev), E2, E3, E4, E5); +| +- memory_region_init_rom(E1, NULL, E2, E3, E4); ++ memory_region_init_rom(E1, OBJECT(dev), E2, E3, E4); +| +- memory_region_init_ram_shared_nomigrate(E1, NULL, E2, E3, E4, E5); ++ memory_region_init_ram_shared_nomigrate(E1, OBJECT(dev), E2, E3, E4, E5); +) + ...+> +} diff --git a/scripts/coccinelle/memory-region-init-ram.cocci b/scripts/coccinelle/memory-region-init-ram.cocci deleted file mode 100644 index d290150872..0000000000 --- a/scripts/coccinelle/memory-region-init-ram.cocci +++ /dev/null @@ -1,38 +0,0 @@ -// Replace by-hand memory_region_init_ram_nomigrate/vmstate_register_ram -// code sequences with use of the new memory_region_init_ram function. -// Similarly for the _rom and _rom_device functions. -// We don't try to replace sequences with a non-NULL owner, because -// there are none in the tree that can be automatically converted -// (and only a handful that can be manually converted). -@@ -expression MR; -expression NAME; -expression SIZE; -expression ERRP; -@@ --memory_region_init_ram_nomigrate(MR, NULL, NAME, SIZE, ERRP); -+memory_region_init_ram(MR, NULL, NAME, SIZE, ERRP); - ... --vmstate_register_ram_global(MR); -@@ -expression MR; -expression NAME; -expression SIZE; -expression ERRP; -@@ --memory_region_init_rom_nomigrate(MR, NULL, NAME, SIZE, ERRP); -+memory_region_init_rom(MR, NULL, NAME, SIZE, ERRP); - ... --vmstate_register_ram_global(MR); -@@ -expression MR; -expression OPS; -expression OPAQUE; -expression NAME; -expression SIZE; -expression ERRP; -@@ --memory_region_init_rom_device_nomigrate(MR, NULL, OPS, OPAQUE, NAME, SIZE, ERRP); -+memory_region_init_rom_device(MR, NULL, OPS, OPAQUE, NAME, SIZE, ERRP); - ... --vmstate_register_ram_global(MR); diff --git a/scripts/hxtool b/scripts/hxtool index 0003e7b673..7b1452f3cf 100644 --- a/scripts/hxtool +++ b/scripts/hxtool @@ -7,7 +7,7 @@ hxtoh() case $str in HXCOMM*) ;; - STEXI*|ETEXI*|SRST*|ERST*) flag=$(($flag^1)) + SRST*|ERST*) flag=$(($flag^1)) ;; *) test $flag -eq 1 && printf "%s\n" "$str" @@ -16,84 +16,8 @@ hxtoh() done } -print_texi_heading() -{ - if test "$*" != ""; then - title="$*" - printf "@subsection %s\n" "${title%:}" - fi -} - -hxtotexi() -{ - flag=0 - rstflag=0 - line=1 - while read -r str; do - case "$str" in - HXCOMM*) - ;; - STEXI*) - if test $rstflag -eq 1 ; then - printf "line %d: syntax error: expected ERST, found '%s'\n" "$line" "$str" >&2 - exit 1 - fi - if test $flag -eq 1 ; then - printf "line %d: syntax error: expected ETEXI, found '%s'\n" "$line" "$str" >&2 - exit 1 - fi - flag=1 - ;; - ETEXI*) - if test $rstflag -eq 1 ; then - printf "line %d: syntax error: expected ERST, found '%s'\n" "$line" "$str" >&2 - exit 1 - fi - if test $flag -ne 1 ; then - printf "line %d: syntax error: expected STEXI, found '%s'\n" "$line" "$str" >&2 - exit 1 - fi - flag=0 - ;; - SRST*) - if test $rstflag -eq 1 ; then - printf "line %d: syntax error: expected ERST, found '%s'\n" "$line" "$str" >&2 - exit 1 - fi - if test $flag -eq 1 ; then - printf "line %d: syntax error: expected ETEXI, found '%s'\n" "$line" "$str" >&2 - exit 1 - fi - rstflag=1 - ;; - ERST*) - if test $flag -eq 1 ; then - printf "line %d: syntax error: expected ETEXI, found '%s'\n" "$line" "$str" >&2 - exit 1 - fi - if test $rstflag -ne 1 ; then - printf "line %d: syntax error: expected SRST, found '%s'\n" "$line" "$str" >&2 - exit 1 - fi - rstflag=0 - ;; - DEFHEADING*) - print_texi_heading "$(expr "$str" : "DEFHEADING(\(.*\))")" - ;; - ARCHHEADING*) - print_texi_heading "$(expr "$str" : "ARCHHEADING(\(.*\),.*)")" - ;; - *) - test $flag -eq 1 && printf '%s\n' "$str" - ;; - esac - line=$((line+1)) - done -} - case "$1" in "-h") hxtoh ;; -"-t") hxtotexi ;; *) exit 1 ;; esac diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 0e13e82989..bc30876c88 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -283,9 +283,9 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); prefix=self._prefix)) self._genc.add(gen_registry(self._regy.get_content(), self._prefix)) - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): if not gen: return # FIXME: If T is a user-defined type, the user is responsible diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 1787a53d91..92f584edcf 100644 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -243,34 +243,34 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor): def write(self, output_dir): self._gen.write(output_dir) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): doc = self.cur_doc self._gen.add(texi_type('Enum', doc, ifcond, texi_members(doc, 'Values', member_func=texi_enum_value))) - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): doc = self.cur_doc if base and base.is_implicit(): base = None self._gen.add(texi_type('Object', doc, ifcond, texi_members(doc, 'Members', base, variants))) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): doc = self.cur_doc self._gen.add(texi_type('Alternate', doc, ifcond, texi_members(doc, 'Members'))) - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): doc = self.cur_doc self._gen.add(texi_msg('Command', doc, ifcond, texi_arguments(doc, arg_type if boxed else None))) - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): doc = self.cur_doc self._gen.add(texi_msg('Event', doc, ifcond, texi_arguments(doc, diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index a98b9f5099..b544af5a1c 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -189,7 +189,7 @@ void %(event_emit)s(%(event_enum)s event, QDict *qdict); event_emit=self._event_emit_name, event_enum=self._event_enum_name)) - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_event_send_decl(name, arg_type, boxed)) self._genc.add(gen_event_send(name, arg_type, boxed, diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index fecf466fa7..2942520399 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -167,8 +167,9 @@ def check_type(value, info, source, allow_optional=True, permit_upper=permit_upper) if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'): raise QAPISemError(info, "%s uses reserved name" % key_source) - check_keys(arg, info, key_source, ['type'], ['if']) + check_keys(arg, info, key_source, ['type'], ['if', 'features']) check_if(arg, info, key_source) + check_features(arg.get('features'), info) check_type(arg['type'], info, key_source, allow_array=True) @@ -219,7 +220,6 @@ def check_struct(expr, info): check_type(members, info, "'data'", allow_dict=name) check_type(expr.get('base'), info, "'base'") - check_features(expr.get('features'), info) def check_union(expr, info): @@ -267,7 +267,6 @@ def check_command(expr, info): raise QAPISemError(info, "'boxed': true requires 'data'") check_type(args, info, "'data'", allow_dict=not boxed) check_type(rets, info, "'returns'", allow_array=True) - check_features(expr.get('features'), info) def check_event(expr, info): @@ -319,18 +318,18 @@ def check_exprs(exprs): if meta == 'enum': check_keys(expr, info, meta, - ['enum', 'data'], ['if', 'prefix']) + ['enum', 'data'], ['if', 'features', 'prefix']) check_enum(expr, info) elif meta == 'union': check_keys(expr, info, meta, ['union', 'data'], - ['base', 'discriminator', 'if']) + ['base', 'discriminator', 'if', 'features']) normalize_members(expr.get('base')) normalize_members(expr['data']) check_union(expr, info) elif meta == 'alternate': check_keys(expr, info, meta, - ['alternate', 'data'], ['if']) + ['alternate', 'data'], ['if', 'features']) normalize_members(expr['data']) check_alternate(expr, info) elif meta == 'struct': @@ -348,13 +347,14 @@ def check_exprs(exprs): check_command(expr, info) elif meta == 'event': check_keys(expr, info, meta, - ['event'], ['data', 'boxed', 'if']) + ['event'], ['data', 'boxed', 'if', 'features']) normalize_members(expr.get('data')) check_event(expr, info) else: assert False, 'unexpected meta type' check_if(expr, info, meta) + check_features(expr.get('features'), info) check_flags(expr, info) return exprs diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index b5537eddc0..23652be810 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -16,7 +16,19 @@ from qapi.schema import (QAPISchemaArrayType, QAPISchemaBuiltinType, QAPISchemaType) -def to_qlit(obj, level=0, suppress_first_indent=False): +def _make_tree(obj, ifcond, features, extra=None): + if extra is None: + extra = {} + if ifcond: + extra['if'] = ifcond + if features: + obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] + if extra: + return (obj, extra) + return obj + + +def _tree_to_qlit(obj, level=0, suppress_first_indent=False): def indent(level): return level * 4 * ' ' @@ -30,7 +42,7 @@ def to_qlit(obj, level=0, suppress_first_indent=False): ret += indent(level) + '/* %s */\n' % comment if ifcond: ret += gen_if(ifcond) - ret += to_qlit(ifobj, level) + ret += _tree_to_qlit(ifobj, level) if ifcond: ret += '\n' + gen_endif(ifcond) return ret @@ -43,7 +55,7 @@ def to_qlit(obj, level=0, suppress_first_indent=False): elif isinstance(obj, str): ret += 'QLIT_QSTR(' + to_c_string(obj) + ')' elif isinstance(obj, list): - elts = [to_qlit(elt, level + 1).strip('\n') + elts = [_tree_to_qlit(elt, level + 1).strip('\n') for elt in obj] elts.append(indent(level + 1) + "{}") ret += 'QLIT_QLIST(((QLitObject[]) {\n' @@ -53,7 +65,8 @@ def to_qlit(obj, level=0, suppress_first_indent=False): elts = [] for key, value in sorted(obj.items()): elts.append(indent(level + 1) + '{ %s, %s }' % - (to_c_string(key), to_qlit(value, level + 1, True))) + (to_c_string(key), + _tree_to_qlit(value, level + 1, True))) elts.append(indent(level + 1) + '{}') ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n' ret += ',\n'.join(elts) + '\n' @@ -79,7 +92,7 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): ' * QAPI/QMP schema introspection', __doc__) self._unmask = unmask self._schema = None - self._qlits = [] + self._trees = [] self._used_types = [] self._name_map = {} self._genc.add(mcgen(''' @@ -108,9 +121,9 @@ extern const QLitObject %(c_name)s; const QLitObject %(c_name)s = %(c_string)s; ''', c_name=c_name(name), - c_string=to_qlit(self._qlits))) + c_string=_tree_to_qlit(self._trees))) self._schema = None - self._qlits = [] + self._trees = [] self._used_types = [] self._name_map = {} @@ -144,89 +157,78 @@ const QLitObject %(c_name)s = %(c_string)s; return '[' + self._use_type(typ.element_type) + ']' return self._name(typ.name) - def _gen_qlit(self, name, mtype, obj, ifcond): - extra = {} + def _gen_tree(self, name, mtype, obj, ifcond, features): + extra = None if mtype not in ('command', 'event', 'builtin', 'array'): if not self._unmask: # Output a comment to make it easy to map masked names # back to the source when reading the generated output. - extra['comment'] = '"%s" = %s' % (self._name(name), name) + extra = {'comment': '"%s" = %s' % (self._name(name), name)} name = self._name(name) obj['name'] = name obj['meta-type'] = mtype - if ifcond: - extra['if'] = ifcond - if extra: - self._qlits.append((obj, extra)) - else: - self._qlits.append(obj) + self._trees.append(_make_tree(obj, ifcond, features, extra)) def _gen_member(self, member): - ret = {'name': member.name, 'type': self._use_type(member.type)} + obj = {'name': member.name, 'type': self._use_type(member.type)} if member.optional: - ret['default'] = None - if member.ifcond: - ret = (ret, {'if': member.ifcond}) - return ret + obj['default'] = None + return _make_tree(obj, member.ifcond, member.features) def _gen_variants(self, tag_name, variants): return {'tag': tag_name, 'variants': [self._gen_variant(v) for v in variants]} def _gen_variant(self, variant): - return ({'case': variant.name, 'type': self._use_type(variant.type)}, - {'if': variant.ifcond}) + obj = {'case': variant.name, 'type': self._use_type(variant.type)} + return _make_tree(obj, variant.ifcond, None) def visit_builtin_type(self, name, info, json_type): - self._gen_qlit(name, 'builtin', {'json-type': json_type}, []) + self._gen_tree(name, 'builtin', {'json-type': json_type}, [], None) - def visit_enum_type(self, name, info, ifcond, members, prefix): - self._gen_qlit(name, 'enum', - {'values': - [(m.name, {'if': m.ifcond}) for m in members]}, - ifcond) + def visit_enum_type(self, name, info, ifcond, features, members, prefix): + self._gen_tree(name, 'enum', + {'values': [_make_tree(m.name, m.ifcond, None) + for m in members]}, + ifcond, features) def visit_array_type(self, name, info, ifcond, element_type): element = self._use_type(element_type) - self._gen_qlit('[' + element + ']', 'array', {'element-type': element}, - ifcond) + self._gen_tree('[' + element + ']', 'array', {'element-type': element}, + ifcond, None) - def visit_object_type_flat(self, name, info, ifcond, members, variants, - features): + def visit_object_type_flat(self, name, info, ifcond, features, + members, variants): obj = {'members': [self._gen_member(m) for m in members]} if variants: obj.update(self._gen_variants(variants.tag_member.name, variants.variants)) - if features: - obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] - self._gen_qlit(name, 'object', obj, ifcond) + self._gen_tree(name, 'object', obj, ifcond, features) - def visit_alternate_type(self, name, info, ifcond, variants): - self._gen_qlit(name, 'alternate', + def visit_alternate_type(self, name, info, ifcond, features, variants): + self._gen_tree(name, 'alternate', {'members': [ - ({'type': self._use_type(m.type)}, {'if': m.ifcond}) - for m in variants.variants]}, ifcond) - - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + _make_tree({'type': self._use_type(m.type)}, + m.ifcond, None) + for m in variants.variants]}, + ifcond, features) + + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): arg_type = arg_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type obj = {'arg-type': self._use_type(arg_type), 'ret-type': self._use_type(ret_type)} if allow_oob: obj['allow-oob'] = allow_oob + self._gen_tree(name, 'command', obj, ifcond, features) - if features: - obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] - - self._gen_qlit(name, 'command', obj, ifcond) - - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type - self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)}, - ifcond) + self._gen_tree(name, 'event', {'arg-type': self._use_type(arg_type)}, + ifcond, features) def gen_introspect(schema, output_dir, prefix, opt_unmask): diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index d759308b4e..78309a00f0 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -53,13 +53,13 @@ class QAPISchemaEntity: seen = {} for f in self.features: f.check_clash(self.info, seen) - if self.doc: - self.doc.connect_feature(f) - self._checked = True def connect_doc(self, doc=None): - pass + doc = doc or self.doc + if doc: + for f in self.features: + doc.connect_feature(f) def check_doc(self): if self.doc: @@ -109,29 +109,29 @@ class QAPISchemaVisitor: def visit_builtin_type(self, name, info, json_type): pass - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): pass def visit_array_type(self, name, info, ifcond, element_type): pass - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): pass - def visit_object_type_flat(self, name, info, ifcond, members, variants, - features): + def visit_object_type_flat(self, name, info, ifcond, features, + members, variants): pass - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): pass - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): pass - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): pass @@ -193,6 +193,12 @@ class QAPISchemaType(QAPISchemaEntity): return None return self.name + def check(self, schema): + QAPISchemaEntity.check(self, schema) + if 'deprecated' in [f.name for f in self.features]: + raise QAPISemError( + self.info, "feature 'deprecated' is not supported for types") + def describe(self): assert self.meta return "%s type '%s'" % (self.meta, self.name) @@ -234,8 +240,8 @@ class QAPISchemaBuiltinType(QAPISchemaType): class QAPISchemaEnumType(QAPISchemaType): meta = 'enum' - def __init__(self, name, info, doc, ifcond, members, prefix): - super().__init__(name, info, doc, ifcond) + def __init__(self, name, info, doc, ifcond, features, members, prefix): + super().__init__(name, info, doc, ifcond, features) for m in members: assert isinstance(m, QAPISchemaEnumMember) m.set_defined_in(name) @@ -250,10 +256,10 @@ class QAPISchemaEnumType(QAPISchemaType): m.check_clash(self.info, seen) def connect_doc(self, doc=None): + super().connect_doc(doc) doc = doc or self.doc - if doc: - for m in self.members: - doc.connect_member(m) + for m in self.members: + m.connect_doc(doc) def is_implicit(self): # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() @@ -270,15 +276,16 @@ class QAPISchemaEnumType(QAPISchemaType): def visit(self, visitor): super().visit(visitor) - visitor.visit_enum_type(self.name, self.info, self.ifcond, - self.members, self.prefix) + visitor.visit_enum_type( + self.name, self.info, self.ifcond, self.features, + self.members, self.prefix) class QAPISchemaArrayType(QAPISchemaType): meta = 'array' def __init__(self, name, info, element_type): - super().__init__(name, info, None, None) + super().__init__(name, info, None) assert isinstance(element_type, str) self._element_type_name = element_type self.element_type = None @@ -324,8 +331,8 @@ class QAPISchemaArrayType(QAPISchemaType): class QAPISchemaObjectType(QAPISchemaType): - def __init__(self, name, info, doc, ifcond, - base, local_members, variants, features): + def __init__(self, name, info, doc, ifcond, features, + base, local_members, variants): # struct has local_members, optional base, and no variants # flat union has base, variants, and no local_members # simple union has local_members, variants, and no base @@ -336,7 +343,7 @@ class QAPISchemaObjectType(QAPISchemaType): assert isinstance(m, QAPISchemaObjectTypeMember) m.set_defined_in(name) if variants is not None: - assert isinstance(variants, QAPISchemaObjectTypeVariants) + assert isinstance(variants, QAPISchemaVariants) variants.set_defined_in(name) self._base_name = base self.base = None @@ -392,12 +399,12 @@ class QAPISchemaObjectType(QAPISchemaType): m.check_clash(info, seen) def connect_doc(self, doc=None): + super().connect_doc(doc) doc = doc or self.doc - if doc: - if self.base and self.base.is_implicit(): - self.base.connect_doc(doc) - for m in self.local_members: - doc.connect_member(m) + if self.base and self.base.is_implicit(): + self.base.connect_doc(doc) + for m in self.local_members: + m.connect_doc(doc) @property def ifcond(self): @@ -433,93 +440,82 @@ class QAPISchemaObjectType(QAPISchemaType): def visit(self, visitor): super().visit(visitor) - visitor.visit_object_type(self.name, self.info, self.ifcond, - self.base, self.local_members, self.variants, - self.features) - visitor.visit_object_type_flat(self.name, self.info, self.ifcond, - self.members, self.variants, - self.features) - - -class QAPISchemaMember: - """ Represents object members, enum members and features """ - role = 'member' - - def __init__(self, name, info, ifcond=None): - assert isinstance(name, str) - self.name = name - self.info = info - self.ifcond = ifcond or [] - self.defined_in = None - - def set_defined_in(self, name): - assert not self.defined_in - self.defined_in = name - - def check_clash(self, info, seen): - cname = c_name(self.name) - if cname in seen: - raise QAPISemError( - info, - "%s collides with %s" - % (self.describe(info), seen[cname].describe(info))) - seen[cname] = self + visitor.visit_object_type( + self.name, self.info, self.ifcond, self.features, + self.base, self.local_members, self.variants) + visitor.visit_object_type_flat( + self.name, self.info, self.ifcond, self.features, + self.members, self.variants) - def describe(self, info): - role = self.role - defined_in = self.defined_in - assert defined_in - - if defined_in.startswith('q_obj_'): - # See QAPISchema._make_implicit_object_type() - reverse the - # mapping there to create a nice human-readable description - defined_in = defined_in[6:] - if defined_in.endswith('-arg'): - # Implicit type created for a command's dict 'data' - assert role == 'member' - role = 'parameter' - elif defined_in.endswith('-base'): - # Implicit type created for a flat union's dict 'base' - role = 'base ' + role - else: - # Implicit type created for a simple union's branch - assert defined_in.endswith('-wrapper') - # Unreachable and not implemented - assert False - elif defined_in.endswith('Kind'): - # See QAPISchema._make_implicit_enum_type() - # Implicit enum created for simple union's branches - assert role == 'value' - role = 'branch' - elif defined_in != info.defn_name: - return "%s '%s' of type '%s'" % (role, self.name, defined_in) - return "%s '%s'" % (role, self.name) +class QAPISchemaAlternateType(QAPISchemaType): + meta = 'alternate' -class QAPISchemaEnumMember(QAPISchemaMember): - role = 'value' + def __init__(self, name, info, doc, ifcond, features, variants): + super().__init__(name, info, doc, ifcond, features) + assert isinstance(variants, QAPISchemaVariants) + assert variants.tag_member + variants.set_defined_in(name) + variants.tag_member.set_defined_in(self.name) + self.variants = variants + def check(self, schema): + super().check(schema) + self.variants.tag_member.check(schema) + # Not calling self.variants.check_clash(), because there's nothing + # to clash with + self.variants.check(schema, {}) + # Alternate branch names have no relation to the tag enum values; + # so we have to check for potential name collisions ourselves. + seen = {} + types_seen = {} + for v in self.variants.variants: + v.check_clash(self.info, seen) + qtype = v.type.alternate_qtype() + if not qtype: + raise QAPISemError( + self.info, + "%s cannot use %s" + % (v.describe(self.info), v.type.describe())) + conflicting = set([qtype]) + if qtype == 'QTYPE_QSTRING': + if isinstance(v.type, QAPISchemaEnumType): + for m in v.type.members: + if m.name in ['on', 'off']: + conflicting.add('QTYPE_QBOOL') + if re.match(r'[-+0-9.]', m.name): + # lazy, could be tightened + conflicting.add('QTYPE_QNUM') + else: + conflicting.add('QTYPE_QNUM') + conflicting.add('QTYPE_QBOOL') + for qt in conflicting: + if qt in types_seen: + raise QAPISemError( + self.info, + "%s can't be distinguished from '%s'" + % (v.describe(self.info), types_seen[qt])) + types_seen[qt] = v.name -class QAPISchemaFeature(QAPISchemaMember): - role = 'feature' + def connect_doc(self, doc=None): + super().connect_doc(doc) + doc = doc or self.doc + for v in self.variants.variants: + v.connect_doc(doc) + def c_type(self): + return c_name(self.name) + pointer_suffix -class QAPISchemaObjectTypeMember(QAPISchemaMember): - def __init__(self, name, info, typ, optional, ifcond=None): - super().__init__(name, info, ifcond) - assert isinstance(typ, str) - assert isinstance(optional, bool) - self._type_name = typ - self.type = None - self.optional = optional + def json_type(self): + return 'value' - def check(self, schema): - assert self.defined_in - self.type = schema.resolve_type(self._type_name, self.info, - self.describe) + def visit(self, visitor): + super().visit(visitor) + visitor.visit_alternate_type( + self.name, self.info, self.ifcond, self.features, self.variants) -class QAPISchemaObjectTypeVariants: +class QAPISchemaVariants: def __init__(self, tag_name, info, tag_member, variants): # Flat unions pass tag_name but not tag_member. # Simple unions and alternates pass tag_member but not tag_name. @@ -529,7 +525,7 @@ class QAPISchemaObjectTypeVariants: assert (isinstance(tag_name, str) or isinstance(tag_member, QAPISchemaObjectTypeMember)) for v in variants: - assert isinstance(v, QAPISchemaObjectTypeVariant) + assert isinstance(v, QAPISchemaVariant) self._tag_name = tag_name self.info = info self.tag_member = tag_member @@ -579,8 +575,8 @@ class QAPISchemaObjectTypeVariants: cases = {v.name for v in self.variants} for m in self.tag_member.type.members: if m.name not in cases: - v = QAPISchemaObjectTypeVariant(m.name, self.info, - 'q_empty', m.ifcond) + v = QAPISchemaVariant(m.name, self.info, + 'q_empty', m.ifcond) v.set_defined_in(self.tag_member.defined_in) self.variants.append(v) if not self.variants: @@ -610,86 +606,114 @@ class QAPISchemaObjectTypeVariants: v.type.check_clash(info, dict(seen)) -class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): - role = 'branch' +class QAPISchemaMember: + """ Represents object members, enum members and features """ + role = 'member' - def __init__(self, name, info, typ, ifcond=None): - super().__init__(name, info, typ, False, ifcond) + def __init__(self, name, info, ifcond=None): + assert isinstance(name, str) + self.name = name + self.info = info + self.ifcond = ifcond or [] + self.defined_in = None + def set_defined_in(self, name): + assert not self.defined_in + self.defined_in = name -class QAPISchemaAlternateType(QAPISchemaType): - meta = 'alternate' + def check_clash(self, info, seen): + cname = c_name(self.name) + if cname in seen: + raise QAPISemError( + info, + "%s collides with %s" + % (self.describe(info), seen[cname].describe(info))) + seen[cname] = self + + def connect_doc(self, doc): + if doc: + doc.connect_member(self) + + def describe(self, info): + role = self.role + defined_in = self.defined_in + assert defined_in + + if defined_in.startswith('q_obj_'): + # See QAPISchema._make_implicit_object_type() - reverse the + # mapping there to create a nice human-readable description + defined_in = defined_in[6:] + if defined_in.endswith('-arg'): + # Implicit type created for a command's dict 'data' + assert role == 'member' + role = 'parameter' + elif defined_in.endswith('-base'): + # Implicit type created for a flat union's dict 'base' + role = 'base ' + role + else: + # Implicit type created for a simple union's branch + assert defined_in.endswith('-wrapper') + # Unreachable and not implemented + assert False + elif defined_in.endswith('Kind'): + # See QAPISchema._make_implicit_enum_type() + # Implicit enum created for simple union's branches + assert role == 'value' + role = 'branch' + elif defined_in != info.defn_name: + return "%s '%s' of type '%s'" % (role, self.name, defined_in) + return "%s '%s'" % (role, self.name) + + +class QAPISchemaEnumMember(QAPISchemaMember): + role = 'value' + + +class QAPISchemaFeature(QAPISchemaMember): + role = 'feature' - def __init__(self, name, info, doc, ifcond, variants): - super().__init__(name, info, doc, ifcond) - assert isinstance(variants, QAPISchemaObjectTypeVariants) - assert variants.tag_member - variants.set_defined_in(name) - variants.tag_member.set_defined_in(self.name) - self.variants = variants + +class QAPISchemaObjectTypeMember(QAPISchemaMember): + def __init__(self, name, info, typ, optional, ifcond=None, features=None): + super().__init__(name, info, ifcond) + assert isinstance(typ, str) + assert isinstance(optional, bool) + for f in features or []: + assert isinstance(f, QAPISchemaFeature) + f.set_defined_in(name) + self._type_name = typ + self.type = None + self.optional = optional + self.features = features or [] def check(self, schema): - super().check(schema) - self.variants.tag_member.check(schema) - # Not calling self.variants.check_clash(), because there's nothing - # to clash with - self.variants.check(schema, {}) - # Alternate branch names have no relation to the tag enum values; - # so we have to check for potential name collisions ourselves. + assert self.defined_in + self.type = schema.resolve_type(self._type_name, self.info, + self.describe) seen = {} - types_seen = {} - for v in self.variants.variants: - v.check_clash(self.info, seen) - qtype = v.type.alternate_qtype() - if not qtype: - raise QAPISemError( - self.info, - "%s cannot use %s" - % (v.describe(self.info), v.type.describe())) - conflicting = set([qtype]) - if qtype == 'QTYPE_QSTRING': - if isinstance(v.type, QAPISchemaEnumType): - for m in v.type.members: - if m.name in ['on', 'off']: - conflicting.add('QTYPE_QBOOL') - if re.match(r'[-+0-9.]', m.name): - # lazy, could be tightened - conflicting.add('QTYPE_QNUM') - else: - conflicting.add('QTYPE_QNUM') - conflicting.add('QTYPE_QBOOL') - for qt in conflicting: - if qt in types_seen: - raise QAPISemError( - self.info, - "%s can't be distinguished from '%s'" - % (v.describe(self.info), types_seen[qt])) - types_seen[qt] = v.name + for f in self.features: + f.check_clash(self.info, seen) - def connect_doc(self, doc=None): - doc = doc or self.doc + def connect_doc(self, doc): + super().connect_doc(doc) if doc: - for v in self.variants.variants: - doc.connect_member(v) + for f in self.features: + doc.connect_feature(f) - def c_type(self): - return c_name(self.name) + pointer_suffix - def json_type(self): - return 'value' +class QAPISchemaVariant(QAPISchemaObjectTypeMember): + role = 'branch' - def visit(self, visitor): - super().visit(visitor) - visitor.visit_alternate_type(self.name, self.info, self.ifcond, - self.variants) + def __init__(self, name, info, typ, ifcond=None): + super().__init__(name, info, typ, False, ifcond) class QAPISchemaCommand(QAPISchemaEntity): meta = 'command' - def __init__(self, name, info, doc, ifcond, arg_type, ret_type, - gen, success_response, boxed, allow_oob, allow_preconfig, - features): + def __init__(self, name, info, doc, ifcond, features, + arg_type, ret_type, + gen, success_response, boxed, allow_oob, allow_preconfig): super().__init__(name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) @@ -733,6 +757,7 @@ class QAPISchemaCommand(QAPISchemaEntity): % self.ret_type.describe()) def connect_doc(self, doc=None): + super().connect_doc(doc) doc = doc or self.doc if doc: if self.arg_type and self.arg_type.is_implicit(): @@ -740,19 +765,17 @@ class QAPISchemaCommand(QAPISchemaEntity): def visit(self, visitor): super().visit(visitor) - visitor.visit_command(self.name, self.info, self.ifcond, - self.arg_type, self.ret_type, - self.gen, self.success_response, - self.boxed, self.allow_oob, - self.allow_preconfig, - self.features) + visitor.visit_command( + self.name, self.info, self.ifcond, self.features, + self.arg_type, self.ret_type, self.gen, self.success_response, + self.boxed, self.allow_oob, self.allow_preconfig) class QAPISchemaEvent(QAPISchemaEntity): meta = 'event' - def __init__(self, name, info, doc, ifcond, arg_type, boxed): - super().__init__(name, info, doc, ifcond) + def __init__(self, name, info, doc, ifcond, features, arg_type, boxed): + super().__init__(name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type self.arg_type = None @@ -775,6 +798,7 @@ class QAPISchemaEvent(QAPISchemaEntity): % self.arg_type.describe()) def connect_doc(self, doc=None): + super().connect_doc(doc) doc = doc or self.doc if doc: if self.arg_type and self.arg_type.is_implicit(): @@ -782,8 +806,9 @@ class QAPISchemaEvent(QAPISchemaEntity): def visit(self, visitor): super().visit(visitor) - visitor.visit_event(self.name, self.info, self.ifcond, - self.arg_type, self.boxed) + visitor.visit_event( + self.name, self.info, self.ifcond, self.features, + self.arg_type, self.boxed) class QAPISchema: @@ -888,7 +913,7 @@ class QAPISchema: ('null', 'null', 'QNull' + pointer_suffix)]: self._def_builtin_type(*t) self.the_empty_object_type = QAPISchemaObjectType( - 'q_empty', None, None, None, None, [], None, []) + 'q_empty', None, None, None, None, None, [], None) self._def_entity(self.the_empty_object_type) qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', @@ -896,10 +921,12 @@ class QAPISchema: qtype_values = self._make_enum_members( [{'name': n} for n in qtypes], None) - self._def_entity(QAPISchemaEnumType('QType', None, None, None, + self._def_entity(QAPISchemaEnumType('QType', None, None, None, None, qtype_values, 'QTYPE')) def _make_features(self, features, info): + if features is None: + return [] return [QAPISchemaFeature(f['name'], info, f.get('if')) for f in features] @@ -911,7 +938,8 @@ class QAPISchema: # See also QAPISchemaObjectTypeMember.describe() name = name + 'Kind' # reserved by check_defn_name_str() self._def_entity(QAPISchemaEnumType( - name, info, None, ifcond, self._make_enum_members(values, info), + name, info, None, ifcond, None, + self._make_enum_members(values, info), None)) return name @@ -939,8 +967,8 @@ class QAPISchema: # TODO kill simple unions or implement the disjunction assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access else: - self._def_entity(QAPISchemaObjectType(name, info, None, ifcond, - None, members, None, [])) + self._def_entity(QAPISchemaObjectType( + name, info, None, ifcond, None, None, members, None)) return name def _def_enum_type(self, expr, info, doc): @@ -948,11 +976,12 @@ class QAPISchema: data = expr['data'] prefix = expr.get('prefix') ifcond = expr.get('if') + features = self._make_features(expr.get('features'), info) self._def_entity(QAPISchemaEnumType( - name, info, doc, ifcond, + name, info, doc, ifcond, features, self._make_enum_members(data, info), prefix)) - def _make_member(self, name, typ, ifcond, info): + def _make_member(self, name, typ, ifcond, features, info): optional = False if name.startswith('*'): name = name[1:] @@ -960,10 +989,12 @@ class QAPISchema: if isinstance(typ, list): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) - return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond) + return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond, + self._make_features(features, info)) def _make_members(self, data, info): - return [self._make_member(key, value['type'], value.get('if'), info) + return [self._make_member(key, value['type'], value.get('if'), + value.get('features'), info) for (key, value) in data.items()] def _def_struct_type(self, expr, info, doc): @@ -971,15 +1002,14 @@ class QAPISchema: base = expr.get('base') data = expr['data'] ifcond = expr.get('if') - features = expr.get('features', []) + features = self._make_features(expr.get('features'), info) self._def_entity(QAPISchemaObjectType( - name, info, doc, ifcond, base, + name, info, doc, ifcond, features, base, self._make_members(data, info), - None, - self._make_features(features, info))) + None)) def _make_variant(self, case, typ, ifcond, info): - return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) + return QAPISchemaVariant(case, info, typ, ifcond) def _make_simple_variant(self, case, typ, ifcond, info): if isinstance(typ, list): @@ -987,14 +1017,15 @@ class QAPISchema: typ = self._make_array_type(typ[0], info) typ = self._make_implicit_object_type( typ, info, self.lookup_type(typ), - 'wrapper', [self._make_member('data', typ, None, info)]) - return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) + 'wrapper', [self._make_member('data', typ, None, None, info)]) + return QAPISchemaVariant(case, info, typ, ifcond) def _def_union_type(self, expr, info, doc): name = expr['union'] data = expr['data'] base = expr.get('base') ifcond = expr.get('if') + features = self._make_features(expr.get('features'), info) tag_name = expr.get('discriminator') tag_member = None if isinstance(base, dict): @@ -1015,22 +1046,23 @@ class QAPISchema: tag_member = QAPISchemaObjectTypeMember('type', info, typ, False) members = [tag_member] self._def_entity( - QAPISchemaObjectType(name, info, doc, ifcond, base, members, - QAPISchemaObjectTypeVariants( - tag_name, info, tag_member, variants), - [])) + QAPISchemaObjectType(name, info, doc, ifcond, features, + base, members, + QAPISchemaVariants( + tag_name, info, tag_member, variants))) def _def_alternate_type(self, expr, info, doc): name = expr['alternate'] data = expr['data'] ifcond = expr.get('if') + features = self._make_features(expr.get('features'), info) variants = [self._make_variant(key, value['type'], value.get('if'), info) for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) self._def_entity( - QAPISchemaAlternateType(name, info, doc, ifcond, - QAPISchemaObjectTypeVariants( + QAPISchemaAlternateType(name, info, doc, ifcond, features, + QAPISchemaVariants( None, info, tag_member, variants))) def _def_command(self, expr, info, doc): @@ -1043,27 +1075,31 @@ class QAPISchema: allow_oob = expr.get('allow-oob', False) allow_preconfig = expr.get('allow-preconfig', False) ifcond = expr.get('if') - features = expr.get('features', []) + features = self._make_features(expr.get('features'), info) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, ifcond, 'arg', self._make_members(data, info)) + name, info, ifcond, + 'arg', self._make_members(data, info)) if isinstance(rets, list): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) - self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets, + self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features, + data, rets, gen, success_response, - boxed, allow_oob, allow_preconfig, - self._make_features(features, info))) + boxed, allow_oob, allow_preconfig)) def _def_event(self, expr, info, doc): name = expr['event'] data = expr.get('data') boxed = expr.get('boxed', False) ifcond = expr.get('if') + features = self._make_features(expr.get('features'), info) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, ifcond, 'arg', self._make_members(data, info)) - self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) + name, info, ifcond, + 'arg', self._make_members(data, info)) + self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features, + data, boxed)) def _def_exprs(self, exprs): for expr_elem in exprs: diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 3c83b6e4be..3ad33af4ee 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -278,7 +278,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_type_cleanup_decl(name)) self._genc.add(gen_type_cleanup(name)) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): with ifcontext(ifcond, self._genh, self._genc): self._genh.preamble_add(gen_enum(name, members, prefix)) self._genc.add(gen_enum_lookup(name, members, prefix)) @@ -289,8 +289,8 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_array(name, element_type)) self._gen_type_cleanup(name) - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return @@ -306,7 +306,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): # implicit types won't be directly allocated/freed self._gen_type_cleanup(name) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): with ifcontext(ifcond, self._genh): self._genh.preamble_add(gen_fwd_object_or_array(name)) self._genh.add(gen_object(name, ifcond, None, diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 421e5bd8cd..23d9194aa4 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -316,7 +316,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): ''', types=types)) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name, scalar=True)) self._genc.add(gen_visit_enum(name)) @@ -326,8 +326,8 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_list(name, element_type)) - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return @@ -342,7 +342,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_object(name, base, members, variants)) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_alternate(name, variants)) diff --git a/scripts/simplebench/bench-example.py b/scripts/simplebench/bench-example.py new file mode 100644 index 0000000000..c642a5b891 --- /dev/null +++ b/scripts/simplebench/bench-example.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# +# Benchmark example +# +# Copyright (c) 2019 Virtuozzo International 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 <http://www.gnu.org/licenses/>. +# + +import simplebench +from bench_block_job import bench_block_copy, drv_file, drv_nbd + + +def bench_func(env, case): + """ Handle one "cell" of benchmarking table. """ + return bench_block_copy(env['qemu_binary'], env['cmd'], + case['source'], case['target']) + + +# You may set the following five variables to correct values, to turn this +# example to real benchmark. +ssd_source = '/path-to-raw-source-image-at-ssd' +ssd_target = '/path-to-raw-target-image-at-ssd' +hdd_target = '/path-to-raw-source-image-at-hdd' +nbd_ip = 'nbd-ip-addr' +nbd_port = 'nbd-port-number' + +# Test-cases are "rows" in benchmark resulting table, 'id' is a caption for +# the row, other fields are handled by bench_func. +test_cases = [ + { + 'id': 'ssd -> ssd', + 'source': drv_file(ssd_source), + 'target': drv_file(ssd_target) + }, + { + 'id': 'ssd -> hdd', + 'source': drv_file(ssd_source), + 'target': drv_file(hdd_target) + }, + { + 'id': 'ssd -> nbd', + 'source': drv_file(ssd_source), + 'target': drv_nbd(nbd_ip, nbd_port) + }, +] + +# Test-envs are "columns" in benchmark resulting table, 'id is a caption for +# the column, other fields are handled by bench_func. +test_envs = [ + { + 'id': 'backup-1', + 'cmd': 'blockdev-backup', + 'qemu_binary': '/path-to-qemu-binary-1' + }, + { + 'id': 'backup-2', + 'cmd': 'blockdev-backup', + 'qemu_binary': '/path-to-qemu-binary-2' + }, + { + 'id': 'mirror', + 'cmd': 'blockdev-mirror', + 'qemu_binary': '/path-to-qemu-binary-1' + } +] + +result = simplebench.bench(bench_func, test_envs, test_cases, count=3) +print(simplebench.ascii(result)) diff --git a/scripts/simplebench/bench_block_job.py b/scripts/simplebench/bench_block_job.py new file mode 100755 index 0000000000..9808d696cf --- /dev/null +++ b/scripts/simplebench/bench_block_job.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# +# Benchmark block jobs +# +# Copyright (c) 2019 Virtuozzo International 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 <http://www.gnu.org/licenses/>. +# + + +import sys +import os +import socket +import json + +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) +from qemu.machine import QEMUMachine +from qemu.qmp import QMPConnectError + + +def bench_block_job(cmd, cmd_args, qemu_args): + """Benchmark block-job + + cmd -- qmp command to run block-job (like blockdev-backup) + cmd_args -- dict of qmp command arguments + qemu_args -- list of Qemu command line arguments, including path to Qemu + binary + + Returns {'seconds': int} on success and {'error': str} on failure, dict may + contain addional 'vm-log' field. Return value is compatible with + simplebench lib. + """ + + vm = QEMUMachine(qemu_args[0], args=qemu_args[1:]) + + try: + vm.launch() + except OSError as e: + return {'error': 'popen failed: ' + str(e)} + except (QMPConnectError, socket.timeout): + return {'error': 'qemu failed: ' + str(vm.get_log())} + + try: + res = vm.qmp(cmd, **cmd_args) + if res != {'return': {}}: + vm.shutdown() + return {'error': '"{}" command failed: {}'.format(cmd, str(res))} + + e = vm.event_wait('JOB_STATUS_CHANGE') + assert e['data']['status'] == 'created' + start_ms = e['timestamp']['seconds'] * 1000000 + \ + e['timestamp']['microseconds'] + + e = vm.events_wait((('BLOCK_JOB_READY', None), + ('BLOCK_JOB_COMPLETED', None), + ('BLOCK_JOB_FAILED', None)), timeout=True) + if e['event'] not in ('BLOCK_JOB_READY', 'BLOCK_JOB_COMPLETED'): + vm.shutdown() + return {'error': 'block-job failed: ' + str(e), + 'vm-log': vm.get_log()} + end_ms = e['timestamp']['seconds'] * 1000000 + \ + e['timestamp']['microseconds'] + finally: + vm.shutdown() + + return {'seconds': (end_ms - start_ms) / 1000000.0} + + +# Bench backup or mirror +def bench_block_copy(qemu_binary, cmd, source, target): + """Helper to run bench_block_job() for mirror or backup""" + assert cmd in ('blockdev-backup', 'blockdev-mirror') + + source['node-name'] = 'source' + target['node-name'] = 'target' + + return bench_block_job(cmd, + {'job-id': 'job0', 'device': 'source', + 'target': 'target', 'sync': 'full'}, + [qemu_binary, + '-blockdev', json.dumps(source), + '-blockdev', json.dumps(target)]) + + +def drv_file(filename): + return {'driver': 'file', 'filename': filename, + 'cache': {'direct': True}, 'aio': 'native'} + + +def drv_nbd(host, port): + return {'driver': 'nbd', + 'server': {'type': 'inet', 'host': host, 'port': port}} + + +if __name__ == '__main__': + import sys + + if len(sys.argv) < 4: + print('USAGE: {} <qmp block-job command name> ' + '<json string of arguments for the command> ' + '<qemu binary path and arguments>'.format(sys.argv[0])) + exit(1) + + res = bench_block_job(sys.argv[1], json.loads(sys.argv[2]), sys.argv[3:]) + if 'seconds' in res: + print('{:.2f}'.format(res['seconds'])) + else: + print(res) diff --git a/scripts/simplebench/simplebench.py b/scripts/simplebench/simplebench.py new file mode 100644 index 0000000000..59e7314ff6 --- /dev/null +++ b/scripts/simplebench/simplebench.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# +# Simple benchmarking framework +# +# Copyright (c) 2019 Virtuozzo International 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 <http://www.gnu.org/licenses/>. +# + + +def bench_one(test_func, test_env, test_case, count=5, initial_run=True): + """Benchmark one test-case + + test_func -- benchmarking function with prototype + test_func(env, case), which takes test_env and test_case + arguments and returns {'seconds': int} (which is benchmark + result) on success and {'error': str} on error. Returned + dict may contain any other additional fields. + test_env -- test environment - opaque first argument for test_func + test_case -- test case - opaque second argument for test_func + count -- how many times to call test_func, to calculate average + initial_run -- do initial run of test_func, which don't get into result + + Returns dict with the following fields: + 'runs': list of test_func results + 'average': average seconds per run (exists only if at least one run + succeeded) + 'delta': maximum delta between test_func result and the average + (exists only if at least one run succeeded) + 'n-failed': number of failed runs (exists only if at least one run + failed) + """ + if initial_run: + print(' #initial run:') + print(' ', test_func(test_env, test_case)) + + runs = [] + for i in range(count): + print(' #run {}'.format(i+1)) + res = test_func(test_env, test_case) + print(' ', res) + runs.append(res) + + result = {'runs': runs} + + successed = [r for r in runs if ('seconds' in r)] + if successed: + avg = sum(r['seconds'] for r in successed) / len(successed) + result['average'] = avg + result['delta'] = max(abs(r['seconds'] - avg) for r in successed) + + if len(successed) < count: + result['n-failed'] = count - len(successed) + + return result + + +def ascii_one(result): + """Return ASCII representation of bench_one() returned dict.""" + if 'average' in result: + s = '{:.2f} +- {:.2f}'.format(result['average'], result['delta']) + if 'n-failed' in result: + s += '\n({} failed)'.format(result['n-failed']) + return s + else: + return 'FAILED' + + +def bench(test_func, test_envs, test_cases, *args, **vargs): + """Fill benchmark table + + test_func -- benchmarking function, see bench_one for description + test_envs -- list of test environments, see bench_one + test_cases -- list of test cases, see bench_one + args, vargs -- additional arguments for bench_one + + Returns dict with the following fields: + 'envs': test_envs + 'cases': test_cases + 'tab': filled 2D array, where cell [i][j] is bench_one result for + test_cases[i] for test_envs[j] (i.e., rows are test cases and + columns are test environments) + """ + tab = {} + results = { + 'envs': test_envs, + 'cases': test_cases, + 'tab': tab + } + n = 1 + n_tests = len(test_envs) * len(test_cases) + for env in test_envs: + for case in test_cases: + print('Testing {}/{}: {} :: {}'.format(n, n_tests, + env['id'], case['id'])) + if case['id'] not in tab: + tab[case['id']] = {} + tab[case['id']][env['id']] = bench_one(test_func, env, case, + *args, **vargs) + n += 1 + + print('Done') + return results + + +def ascii(results): + """Return ASCII representation of bench() returned dict.""" + from tabulate import tabulate + + tab = [[""] + [c['id'] for c in results['envs']]] + for case in results['cases']: + row = [case['id']] + for env in results['envs']: + row.append(ascii_one(results['tab'][case['id']][env['id']])) + tab.append(row) + + return tabulate(tab) diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index 0659ceef09..181ed4a186 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -421,10 +421,13 @@ static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, int rq_servact = cdb[1]; int rq_scope = cdb[2] >> 4; int rq_type = cdb[2] & 0xf; - struct prout_param_descriptor paramp; + g_autofree struct prout_param_descriptor *paramp = NULL; char transportids[PR_HELPER_DATA_SIZE]; int r; + paramp = g_malloc0(sizeof(struct prout_param_descriptor) + + sizeof(struct transportid *) * MPATH_MX_TIDS); + if (sz < PR_OUT_FIXED_PARAM_SIZE) { /* Illegal request, Parameter list length error. This isn't fatal; * we have read the data, send an error without closing the socket. @@ -454,10 +457,9 @@ static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, * used by libmpathpersist (which, of course, will immediately * do the opposite). */ - memset(¶mp, 0, sizeof(paramp)); - memcpy(¶mp.key, ¶m[0], 8); - memcpy(¶mp.sa_key, ¶m[8], 8); - paramp.sa_flags = param[20]; + memcpy(¶mp->key, ¶m[0], 8); + memcpy(¶mp->sa_key, ¶m[8], 8); + paramp->sa_flags = param[20]; if (sz > PR_OUT_FIXED_PARAM_SIZE) { size_t transportid_len; int i, j; @@ -520,12 +522,13 @@ static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, return CHECK_CONDITION; } - paramp.trnptid_list[paramp.num_transportid++] = id; + assert(paramp->num_transportid < MPATH_MX_TIDS); + paramp->trnptid_list[paramp->num_transportid++] = id; } } r = mpath_persistent_reserve_out(fd, rq_servact, rq_scope, rq_type, - ¶mp, noisy, verbose); + paramp, noisy, verbose); return mpath_reconstruct_sense(fd, r, sense); } #endif diff --git a/softmmu/vl.c b/softmmu/vl.c index ff2685dff8..1d33a28340 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -3789,6 +3789,22 @@ void qemu_init(int argc, char **argv, char **envp) */ loc_set_none(); + /* + * Check for -cpu help and -device help before we call select_machine(), + * which will return an error if the architecture has no default machine + * type and the user did not specify one, so that the user doesn't need + * to say '-cpu help -machine something'. + */ + if (cpu_option && is_help_option(cpu_option)) { + list_cpus(cpu_option); + exit(0); + } + + if (qemu_opts_foreach(qemu_find_opts("device"), + device_help_func, NULL, NULL)) { + exit(0); + } + user_register_global_props(); replay_configure(icount_opts); @@ -3877,11 +3893,6 @@ void qemu_init(int argc, char **argv, char **envp) qemu_set_hw_version(machine_class->hw_version); } - if (cpu_option && is_help_option(cpu_option)) { - list_cpus(cpu_option); - exit(0); - } - if (!trace_init_backends()) { exit(1); } @@ -3935,6 +3946,7 @@ void qemu_init(int argc, char **argv, char **envp) current_machine->smp.max_cpus = machine_class->default_cpus; current_machine->smp.cores = 1; current_machine->smp.threads = 1; + current_machine->smp.sockets = 1; machine_class->smp_parse(current_machine, qemu_opts_find(qemu_find_opts("smp-opts"), NULL)); @@ -4112,11 +4124,6 @@ void qemu_init(int argc, char **argv, char **envp) fsdev_init_func, NULL, &error_fatal); #endif - if (qemu_opts_foreach(qemu_find_opts("device"), - device_help_func, NULL, NULL)) { - exit(0); - } - /* * Note: we need to create block backends before * machine_set_property(), so machine properties can refer to diff --git a/target/alpha/cpu-qom.h b/target/alpha/cpu-qom.h index 6f0a0adb9e..08832fa767 100644 --- a/target/alpha/cpu-qom.h +++ b/target/alpha/cpu-qom.h @@ -44,7 +44,7 @@ typedef struct AlphaCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } AlphaCPUClass; typedef struct AlphaCPU AlphaCPU; diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index 3f782c0efe..be29bdd530 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -280,7 +280,7 @@ void alpha_cpu_do_interrupt(CPUState *cpu); bool alpha_cpu_exec_interrupt(CPUState *cpu, int int_req); void alpha_cpu_dump_state(CPUState *cs, FILE *f, int flags); hwaddr alpha_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int alpha_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int alpha_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int alpha_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void alpha_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, diff --git a/target/alpha/gdbstub.c b/target/alpha/gdbstub.c index 7f9cc092a9..0cd76ddaa9 100644 --- a/target/alpha/gdbstub.c +++ b/target/alpha/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int alpha_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int alpha_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { AlphaCPU *cpu = ALPHA_CPU(cs); CPUAlphaState *env = &cpu->env; diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index 3a9d31ea9d..d95568bf05 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -51,7 +51,7 @@ typedef struct ARMCPUClass { const ARMCPUInfo *info; DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } ARMCPUClass; typedef struct ARMCPU ARMCPU; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 3623ecefbd..a79f233b17 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -155,14 +155,14 @@ static void cp_reg_check_reset(gpointer key, gpointer value, gpointer opaque) assert(oldvalue == newvalue); } -/* CPUClass::reset() */ -static void arm_cpu_reset(CPUState *s) +static void arm_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); ARMCPU *cpu = ARM_CPU(s); ARMCPUClass *acc = ARM_CPU_GET_CLASS(cpu); CPUARMState *env = &cpu->env; - acc->parent_reset(s); + acc->parent_reset(dev); memset(env, 0, offsetof(CPUARMState, end_reset_fields)); @@ -195,9 +195,10 @@ static void arm_cpu_reset(CPUState *s) env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 20, 2, 3); /* and to the SVE instructions */ env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 16, 2, 3); - /* with maximum vector length */ - env->vfp.zcr_el[1] = cpu_isar_feature(aa64_sve, cpu) ? - cpu->sve_max_vq - 1 : 0; + /* with reasonable vector length */ + if (cpu_isar_feature(aa64_sve, cpu)) { + env->vfp.zcr_el[1] = MIN(cpu->sve_max_vq - 1, 3); + } /* * Enable TBI0 and TBI1. While the real kernel only enables TBI0, * turning on both here will produce smaller code and otherwise @@ -1153,22 +1154,6 @@ static void arm_set_pmu(Object *obj, bool value, Error **errp) cpu->has_pmu = value; } -static void arm_get_init_svtor(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ARMCPU *cpu = ARM_CPU(obj); - - visit_type_uint32(v, name, &cpu->init_svtor, errp); -} - -static void arm_set_init_svtor(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - ARMCPU *cpu = ARM_CPU(obj); - - visit_type_uint32(v, name, &cpu->init_svtor, errp); -} - unsigned int gt_cntfrq_period_ns(ARMCPU *cpu) { /* @@ -1288,9 +1273,9 @@ void arm_cpu_post_init(Object *obj) * a simple DEFINE_PROP_UINT32 for this because we want to permit * the property to be set after realize. */ - object_property_add(obj, "init-svtor", "uint32", - arm_get_init_svtor, arm_set_init_svtor, - NULL, NULL, &error_abort); + object_property_add_uint32_ptr(obj, "init-svtor", + &cpu->init_svtor, + OBJ_PROP_FLAG_READWRITE, &error_abort); } qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property); @@ -2801,7 +2786,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) &acc->parent_realize); device_class_set_props(dc, arm_cpu_properties); - cpu_class_set_parent_reset(cc, arm_cpu_reset, &acc->parent_reset); + device_class_set_parent_reset(dc, arm_cpu_reset, &acc->parent_reset); cc->class_by_name = arm_cpu_class_by_name; cc->has_work = arm_cpu_has_work; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 4ffd991b6f..8b9f2961ba 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -128,14 +128,20 @@ enum { /** * DynamicGDBXMLInfo: * @desc: Contains the XML descriptions. - * @num_cpregs: Number of the Coprocessor registers seen by GDB. - * @cpregs_keys: Array that contains the corresponding Key of - * a given cpreg with the same order of the cpreg in the XML description. + * @num: Number of the registers in this XML seen by GDB. + * @data: A union with data specific to the set of registers + * @cpregs_keys: Array that contains the corresponding Key of + * a given cpreg with the same order of the cpreg + * in the XML description. */ typedef struct DynamicGDBXMLInfo { char *desc; - int num_cpregs; - uint32_t *cpregs_keys; + int num; + union { + struct { + uint32_t *keys; + } cpregs; + } data; } DynamicGDBXMLInfo; /* CPU state for each instance of a generic timer (in cp15 c14) */ @@ -749,7 +755,8 @@ struct ARMCPU { uint64_t *cpreg_vmstate_values; int32_t cpreg_vmstate_array_len; - DynamicGDBXMLInfo dyn_xml; + DynamicGDBXMLInfo dyn_sysreg_xml; + DynamicGDBXMLInfo dyn_svereg_xml; /* Timers used by the generic (architected) timer */ QEMUTimer *gt_timer[NUM_GTIMERS]; @@ -968,13 +975,15 @@ bool arm_cpu_exec_interrupt(CPUState *cpu, int int_req); hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); -int arm_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int arm_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); -/* Dynamically generates for gdb stub an XML description of the sysregs from - * the cp_regs hashtable. Returns the registered sysregs number. +/* + * Helpers to dynamically generates XML descriptions of the sysregs + * and SVE registers. Returns the number of registers in each set. */ -int arm_gen_dynamic_xml(CPUState *cpu); +int arm_gen_dynamic_sysreg_xml(CPUState *cpu, int base_reg); +int arm_gen_dynamic_svereg_xml(CPUState *cpu, int base_reg); /* Returns the dynamically generated XML for the gdb stub. * Returns a pointer to the XML contents for the specified XML file or NULL @@ -988,7 +997,7 @@ int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); #ifdef TARGET_AARCH64 -int aarch64_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int aarch64_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq); void aarch64_sve_change_el(CPUARMState *env, int old_el, diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index 1239abd984..d9ef7d2187 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -24,6 +24,7 @@ typedef struct RegisterSysregXmlParam { CPUState *cs; GString *s; + int n; } RegisterSysregXmlParam; /* Old gdb always expect FPA registers. Newer (xml-aware) gdb only expect @@ -32,7 +33,7 @@ typedef struct RegisterSysregXmlParam { We hack round this by giving the FPA regs zero size when talking to a newer gdb. */ -int arm_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int arm_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -106,15 +107,16 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -static void arm_gen_one_xml_reg_tag(GString *s, DynamicGDBXMLInfo *dyn_xml, - ARMCPRegInfo *ri, uint32_t ri_key, - int bitsize) +static void arm_gen_one_xml_sysreg_tag(GString *s, DynamicGDBXMLInfo *dyn_xml, + ARMCPRegInfo *ri, uint32_t ri_key, + int bitsize, int regnum) { g_string_append_printf(s, "<reg name=\"%s\"", ri->name); g_string_append_printf(s, " bitsize=\"%d\"", bitsize); + g_string_append_printf(s, " regnum=\"%d\"", regnum); g_string_append_printf(s, " group=\"cp_regs\"/>"); - dyn_xml->num_cpregs++; - dyn_xml->cpregs_keys[dyn_xml->num_cpregs - 1] = ri_key; + dyn_xml->data.cpregs.keys[dyn_xml->num] = ri_key; + dyn_xml->num++; } static void arm_register_sysreg_for_xml(gpointer key, gpointer value, @@ -126,12 +128,13 @@ static void arm_register_sysreg_for_xml(gpointer key, gpointer value, GString *s = param->s; ARMCPU *cpu = ARM_CPU(param->cs); CPUARMState *env = &cpu->env; - DynamicGDBXMLInfo *dyn_xml = &cpu->dyn_xml; + DynamicGDBXMLInfo *dyn_xml = &cpu->dyn_sysreg_xml; if (!(ri->type & (ARM_CP_NO_RAW | ARM_CP_NO_GDB))) { if (arm_feature(env, ARM_FEATURE_AARCH64)) { if (ri->state == ARM_CP_STATE_AA64) { - arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 64); + arm_gen_one_xml_sysreg_tag(s , dyn_xml, ri, ri_key, 64, + param->n++); } } else { if (ri->state == ARM_CP_STATE_AA32) { @@ -140,38 +143,174 @@ static void arm_register_sysreg_for_xml(gpointer key, gpointer value, return; } if (ri->type & ARM_CP_64BIT) { - arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 64); + arm_gen_one_xml_sysreg_tag(s , dyn_xml, ri, ri_key, 64, + param->n++); } else { - arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 32); + arm_gen_one_xml_sysreg_tag(s , dyn_xml, ri, ri_key, 32, + param->n++); } } } } } -int arm_gen_dynamic_xml(CPUState *cs) +int arm_gen_dynamic_sysreg_xml(CPUState *cs, int base_reg) { ARMCPU *cpu = ARM_CPU(cs); GString *s = g_string_new(NULL); - RegisterSysregXmlParam param = {cs, s}; + RegisterSysregXmlParam param = {cs, s, base_reg}; - cpu->dyn_xml.num_cpregs = 0; - cpu->dyn_xml.cpregs_keys = g_new(uint32_t, g_hash_table_size(cpu->cp_regs)); + cpu->dyn_sysreg_xml.num = 0; + cpu->dyn_sysreg_xml.data.cpregs.keys = g_new(uint32_t, g_hash_table_size(cpu->cp_regs)); g_string_printf(s, "<?xml version=\"1.0\"?>"); g_string_append_printf(s, "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"); g_string_append_printf(s, "<feature name=\"org.qemu.gdb.arm.sys.regs\">"); g_hash_table_foreach(cpu->cp_regs, arm_register_sysreg_for_xml, ¶m); g_string_append_printf(s, "</feature>"); - cpu->dyn_xml.desc = g_string_free(s, false); - return cpu->dyn_xml.num_cpregs; + cpu->dyn_sysreg_xml.desc = g_string_free(s, false); + return cpu->dyn_sysreg_xml.num; } +struct TypeSize { + const char *gdb_type; + int size; + const char sz, suffix; +}; + +static const struct TypeSize vec_lanes[] = { + /* quads */ + { "uint128", 128, 'q', 'u' }, + { "int128", 128, 'q', 's' }, + /* 64 bit */ + { "uint64", 64, 'd', 'u' }, + { "int64", 64, 'd', 's' }, + { "ieee_double", 64, 'd', 'f' }, + /* 32 bit */ + { "uint32", 32, 's', 'u' }, + { "int32", 32, 's', 's' }, + { "ieee_single", 32, 's', 'f' }, + /* 16 bit */ + { "uint16", 16, 'h', 'u' }, + { "int16", 16, 'h', 's' }, + { "ieee_half", 16, 'h', 'f' }, + /* bytes */ + { "uint8", 8, 'b', 'u' }, + { "int8", 8, 'b', 's' }, +}; + + +int arm_gen_dynamic_svereg_xml(CPUState *cs, int base_reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + GString *s = g_string_new(NULL); + DynamicGDBXMLInfo *info = &cpu->dyn_svereg_xml; + g_autoptr(GString) ts = g_string_new(""); + int i, bits, reg_width = (cpu->sve_max_vq * 128); + info->num = 0; + g_string_printf(s, "<?xml version=\"1.0\"?>"); + g_string_append_printf(s, "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"); + g_string_append_printf(s, "<feature name=\"org.qemu.gdb.aarch64.sve\">"); + + /* First define types and totals in a whole VL */ + for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) { + int count = reg_width / vec_lanes[i].size; + g_string_printf(ts, "vq%d%c%c", count, + vec_lanes[i].sz, vec_lanes[i].suffix); + g_string_append_printf(s, + "<vector id=\"%s\" type=\"%s\" count=\"%d\"/>", + ts->str, vec_lanes[i].gdb_type, count); + } + /* + * Now define a union for each size group containing unsigned and + * signed and potentially float versions of each size from 128 to + * 8 bits. + */ + for (bits = 128; bits >= 8; bits /= 2) { + int count = reg_width / bits; + g_string_append_printf(s, "<union id=\"vq%dn\">", count); + for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) { + if (vec_lanes[i].size == bits) { + g_string_append_printf(s, "<field name=\"%c\" type=\"vq%d%c%c\"/>", + vec_lanes[i].suffix, + count, + vec_lanes[i].sz, vec_lanes[i].suffix); + } + } + g_string_append(s, "</union>"); + } + /* And now the final union of unions */ + g_string_append(s, "<union id=\"vq\">"); + for (bits = 128; bits >= 8; bits /= 2) { + int count = reg_width / bits; + for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) { + if (vec_lanes[i].size == bits) { + g_string_append_printf(s, "<field name=\"%c\" type=\"vq%dn\"/>", + vec_lanes[i].sz, count); + break; + } + } + } + g_string_append(s, "</union>"); + + /* Then define each register in parts for each vq */ + for (i = 0; i < 32; i++) { + g_string_append_printf(s, + "<reg name=\"z%d\" bitsize=\"%d\"" + " regnum=\"%d\" group=\"vector\"" + " type=\"vq\"/>", + i, reg_width, base_reg++); + info->num++; + } + /* fpscr & status registers */ + g_string_append_printf(s, "<reg name=\"fpsr\" bitsize=\"32\"" + " regnum=\"%d\" group=\"float\"" + " type=\"int\"/>", base_reg++); + g_string_append_printf(s, "<reg name=\"fpcr\" bitsize=\"32\"" + " regnum=\"%d\" group=\"float\"" + " type=\"int\"/>", base_reg++); + info->num += 2; + /* + * Predicate registers aren't so big they are worth splitting up + * but we do need to define a type to hold the array of quad + * references. + */ + g_string_append_printf(s, + "<vector id=\"vqp\" type=\"uint16\" count=\"%d\"/>", + cpu->sve_max_vq); + for (i = 0; i < 16; i++) { + g_string_append_printf(s, + "<reg name=\"p%d\" bitsize=\"%d\"" + " regnum=\"%d\" group=\"vector\"" + " type=\"vqp\"/>", + i, cpu->sve_max_vq * 16, base_reg++); + info->num++; + } + g_string_append_printf(s, + "<reg name=\"ffr\" bitsize=\"%d\"" + " regnum=\"%d\" group=\"vector\"" + " type=\"vqp\"/>", + cpu->sve_max_vq * 16, base_reg++); + g_string_append_printf(s, + "<reg name=\"vg\" bitsize=\"64\"" + " regnum=\"%d\" group=\"vector\"" + " type=\"uint32\"/>", + base_reg++); + info->num += 2; + g_string_append_printf(s, "</feature>"); + cpu->dyn_svereg_xml.desc = g_string_free(s, false); + + return cpu->dyn_svereg_xml.num; +} + + const char *arm_gdb_get_dynamic_xml(CPUState *cs, const char *xmlname) { ARMCPU *cpu = ARM_CPU(cs); if (strcmp(xmlname, "system-registers.xml") == 0) { - return cpu->dyn_xml.desc; + return cpu->dyn_sysreg_xml.desc; + } else if (strcmp(xmlname, "sve-registers.xml") == 0) { + return cpu->dyn_svereg_xml.desc; } return NULL; } diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 665ebb3ef6..35d0b80c2d 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -20,7 +20,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int aarch64_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; diff --git a/target/arm/helper.c b/target/arm/helper.c index f91e5d5345..d2ec2c5351 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -48,30 +48,27 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, static void switch_mode(CPUARMState *env, int mode); -static int vfp_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg) +static int vfp_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) { ARMCPU *cpu = env_archcpu(env); int nregs = cpu_isar_feature(aa32_simd_r32, cpu) ? 32 : 16; /* VFP data registers are always little-endian. */ if (reg < nregs) { - stq_le_p(buf, *aa32_vfp_dreg(env, reg)); - return 8; + return gdb_get_reg64(buf, *aa32_vfp_dreg(env, reg)); } if (arm_feature(env, ARM_FEATURE_NEON)) { /* Aliases for Q regs. */ nregs += 16; if (reg < nregs) { uint64_t *q = aa32_vfp_qreg(env, reg - 32); - stq_le_p(buf, q[0]); - stq_le_p(buf + 8, q[1]); - return 16; + return gdb_get_reg128(buf, q[0], q[1]); } } switch (reg - nregs) { - case 0: stl_p(buf, env->vfp.xregs[ARM_VFP_FPSID]); return 4; - case 1: stl_p(buf, vfp_get_fpscr(env)); return 4; - case 2: stl_p(buf, env->vfp.xregs[ARM_VFP_FPEXC]); return 4; + case 0: return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPSID]); break; + case 1: return gdb_get_reg32(buf, vfp_get_fpscr(env)); break; + case 2: return gdb_get_reg32(buf, env->vfp.xregs[ARM_VFP_FPEXC]); break; } return 0; } @@ -102,25 +99,21 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) return 0; } -static int aarch64_fpu_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg) +static int aarch64_fpu_gdb_get_reg(CPUARMState *env, GByteArray *buf, int reg) { switch (reg) { case 0 ... 31: - /* 128 bit FP register */ - { - uint64_t *q = aa64_vfp_qreg(env, reg); - stq_le_p(buf, q[0]); - stq_le_p(buf + 8, q[1]); - return 16; - } + { + /* 128 bit FP register - quads are in LE order */ + uint64_t *q = aa64_vfp_qreg(env, reg); + return gdb_get_reg128(buf, q[1], q[0]); + } case 32: /* FPSR */ - stl_p(buf, vfp_get_fpsr(env)); - return 4; + return gdb_get_reg32(buf, vfp_get_fpsr(env)); case 33: /* FPCR */ - stl_p(buf, vfp_get_fpcr(env)); - return 4; + return gdb_get_reg32(buf,vfp_get_fpcr(env)); default: return 0; } @@ -209,13 +202,22 @@ static void write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri, } } -static int arm_gdb_get_sysreg(CPUARMState *env, uint8_t *buf, int reg) +/** + * arm_get/set_gdb_*: get/set a gdb register + * @env: the CPU state + * @buf: a buffer to copy to/from + * @reg: register number (offset from start of group) + * + * We return the number of bytes copied + */ + +static int arm_gdb_get_sysreg(CPUARMState *env, GByteArray *buf, int reg) { ARMCPU *cpu = env_archcpu(env); const ARMCPRegInfo *ri; uint32_t key; - key = cpu->dyn_xml.cpregs_keys[reg]; + key = cpu->dyn_sysreg_xml.data.cpregs.keys[reg]; ri = get_arm_cp_reginfo(cpu->cp_regs, key); if (ri) { if (cpreg_field_is_64bit(ri)) { @@ -232,6 +234,102 @@ static int arm_gdb_set_sysreg(CPUARMState *env, uint8_t *buf, int reg) return 0; } +#ifdef TARGET_AARCH64 +static int arm_gdb_get_svereg(CPUARMState *env, GByteArray *buf, int reg) +{ + ARMCPU *cpu = env_archcpu(env); + + switch (reg) { + /* The first 32 registers are the zregs */ + case 0 ... 31: + { + int vq, len = 0; + for (vq = 0; vq < cpu->sve_max_vq; vq++) { + len += gdb_get_reg128(buf, + env->vfp.zregs[reg].d[vq * 2 + 1], + env->vfp.zregs[reg].d[vq * 2]); + } + return len; + } + case 32: + return gdb_get_reg32(buf, vfp_get_fpsr(env)); + case 33: + return gdb_get_reg32(buf, vfp_get_fpcr(env)); + /* then 16 predicates and the ffr */ + case 34 ... 50: + { + int preg = reg - 34; + int vq, len = 0; + for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { + len += gdb_get_reg64(buf, env->vfp.pregs[preg].p[vq / 4]); + } + return len; + } + case 51: + { + /* + * We report in Vector Granules (VG) which is 64bit in a Z reg + * while the ZCR works in Vector Quads (VQ) which is 128bit chunks. + */ + int vq = sve_zcr_len_for_el(env, arm_current_el(env)) + 1; + return gdb_get_reg32(buf, vq * 2); + } + default: + /* gdbstub asked for something out our range */ + qemu_log_mask(LOG_UNIMP, "%s: out of range register %d", __func__, reg); + break; + } + + return 0; +} + +static int arm_gdb_set_svereg(CPUARMState *env, uint8_t *buf, int reg) +{ + ARMCPU *cpu = env_archcpu(env); + + /* The first 32 registers are the zregs */ + switch (reg) { + /* The first 32 registers are the zregs */ + case 0 ... 31: + { + int vq, len = 0; + uint64_t *p = (uint64_t *) buf; + for (vq = 0; vq < cpu->sve_max_vq; vq++) { + env->vfp.zregs[reg].d[vq * 2 + 1] = *p++; + env->vfp.zregs[reg].d[vq * 2] = *p++; + len += 16; + } + return len; + } + case 32: + vfp_set_fpsr(env, *(uint32_t *)buf); + return 4; + case 33: + vfp_set_fpcr(env, *(uint32_t *)buf); + return 4; + case 34 ... 50: + { + int preg = reg - 34; + int vq, len = 0; + uint64_t *p = (uint64_t *) buf; + for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { + env->vfp.pregs[preg].p[vq / 4] = *p++; + len += 8; + } + return len; + } + case 51: + /* cannot set vg via gdbstub */ + return 0; + default: + /* gdbstub asked for something out our range */ + break; + } + + return 0; +} +#endif /* TARGET_AARCH64 */ + static bool raw_accessors_invalid(const ARMCPRegInfo *ri) { /* Return true if the regdef would cause an assertion if you called @@ -6599,6 +6697,7 @@ static uint64_t id_pfr1_read(CPUARMState *env, const ARMCPRegInfo *ri) return pfr1; } +#ifndef CONFIG_USER_ONLY static uint64_t id_aa64pfr0_read(CPUARMState *env, const ARMCPRegInfo *ri) { ARMCPU *cpu = env_archcpu(env); @@ -6609,6 +6708,7 @@ static uint64_t id_aa64pfr0_read(CPUARMState *env, const ARMCPRegInfo *ri) } return pfr0; } +#endif /* Shared logic between LORID and the rest of the LOR* registers. * Secure state has already been delt with. @@ -7182,16 +7282,24 @@ void register_cp_regs_for_features(ARMCPU *cpu) * define new registers here. */ ARMCPRegInfo v8_idregs[] = { - /* ID_AA64PFR0_EL1 is not a plain ARM_CP_CONST because we don't - * know the right value for the GIC field until after we - * define these regs. + /* + * ID_AA64PFR0_EL1 is not a plain ARM_CP_CONST in system + * emulation because we don't know the right value for the + * GIC field until after we define these regs. */ { .name = "ID_AA64PFR0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 0, - .access = PL1_R, .type = ARM_CP_NO_RAW, + .access = PL1_R, +#ifdef CONFIG_USER_ONLY + .type = ARM_CP_CONST, + .resetvalue = cpu->isar.id_aa64pfr0 +#else + .type = ARM_CP_NO_RAW, .accessfn = access_aa64_tid3, .readfn = id_aa64pfr0_read, - .writefn = arm_cp_write_ignore }, + .writefn = arm_cp_write_ignore +#endif + }, { .name = "ID_AA64PFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, @@ -7966,9 +8074,22 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) CPUARMState *env = &cpu->env; if (arm_feature(env, ARM_FEATURE_AARCH64)) { - gdb_register_coprocessor(cs, aarch64_fpu_gdb_get_reg, - aarch64_fpu_gdb_set_reg, - 34, "aarch64-fpu.xml", 0); + /* + * The lower part of each SVE register aliases to the FPU + * registers so we don't need to include both. + */ +#ifdef TARGET_AARCH64 + if (isar_feature_aa64_sve(&cpu->isar)) { + gdb_register_coprocessor(cs, arm_gdb_get_svereg, arm_gdb_set_svereg, + arm_gen_dynamic_svereg_xml(cs, cs->gdb_num_regs), + "sve-registers.xml", 0); + } else +#endif + { + gdb_register_coprocessor(cs, aarch64_fpu_gdb_get_reg, + aarch64_fpu_gdb_set_reg, + 34, "aarch64-fpu.xml", 0); + } } else if (arm_feature(env, ARM_FEATURE_NEON)) { gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, 51, "arm-neon.xml", 0); @@ -7980,8 +8101,9 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) 19, "arm-vfp.xml", 0); } gdb_register_coprocessor(cs, arm_gdb_get_sysreg, arm_gdb_set_sysreg, - arm_gen_dynamic_xml(cs), + arm_gen_dynamic_sysreg_xml(cs, cs->gdb_num_regs), "system-registers.xml", 0); + } /* Sort alphabetically by type name, except for "any". */ @@ -11780,7 +11902,40 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, /* Definitely a real MMU, not an MPU */ if (regime_translation_disabled(env, mmu_idx)) { - /* MMU disabled. */ + /* + * MMU disabled. S1 addresses within aa64 translation regimes are + * still checked for bounds -- see AArch64.TranslateAddressS1Off. + */ + if (mmu_idx != ARMMMUIdx_Stage2) { + int r_el = regime_el(env, mmu_idx); + if (arm_el_is_aa64(env, r_el)) { + int pamax = arm_pamax(env_archcpu(env)); + uint64_t tcr = env->cp15.tcr_el[r_el].raw_tcr; + int addrtop, tbi; + + tbi = aa64_va_parameter_tbi(tcr, mmu_idx); + if (access_type == MMU_INST_FETCH) { + tbi &= ~aa64_va_parameter_tbid(tcr, mmu_idx); + } + tbi = (tbi >> extract64(address, 55, 1)) & 1; + addrtop = (tbi ? 55 : 63); + + if (extract64(address, pamax, addrtop - pamax + 1) != 0) { + fi->type = ARMFault_AddressSize; + fi->level = 0; + fi->stage2 = false; + return 1; + } + + /* + * When TBI is disabled, we've just validated that all of the + * bits above PAMax are zero, so logically we only need to + * clear the top byte for TBI. But it's clearer to follow + * the pseudocode set of addrdesc.paddress. + */ + address = extract64(address, 0, 52); + } + } *phys_ptr = address; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; *page_size = TARGET_PAGE_SIZE; @@ -12468,6 +12623,18 @@ void arm_rebuild_hflags(CPUARMState *env) env->hflags = rebuild_hflags_internal(env); } +/* + * If we have triggered a EL state change we can't rely on the + * translator having passed it to us, we need to recompute. + */ +void HELPER(rebuild_hflags_m32_newel)(CPUARMState *env) +{ + int el = arm_current_el(env); + int fp_el = fp_exception_el(env, el); + ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, el); + env->hflags = rebuild_hflags_m32(env, fp_el, mmu_idx); +} + void HELPER(rebuild_hflags_m32)(CPUARMState *env, int el) { int fp_el = fp_exception_el(env, el); @@ -12478,7 +12645,7 @@ void HELPER(rebuild_hflags_m32)(CPUARMState *env, int el) /* * If we have triggered a EL state change we can't rely on the - * translator having passed it too us, we need to recompute. + * translator having passed it to us, we need to recompute. */ void HELPER(rebuild_hflags_a32_newel)(CPUARMState *env) { diff --git a/target/arm/helper.h b/target/arm/helper.h index 72eb9e6a1a..f37b8670a5 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -90,6 +90,7 @@ DEF_HELPER_4(msr_banked, void, env, i32, i32, i32) DEF_HELPER_2(get_user_reg, i32, env, i32) DEF_HELPER_3(set_user_reg, void, env, i32, i32) +DEF_HELPER_FLAGS_1(rebuild_hflags_m32_newel, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(rebuild_hflags_m32, TCG_CALL_NO_RWG, void, env, int) DEF_HELPER_FLAGS_1(rebuild_hflags_a32_newel, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(rebuild_hflags_a32, TCG_CALL_NO_RWG, void, env, int) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 85860e6f95..390077c518 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -874,15 +874,17 @@ int kvm_arch_irqchip_create(KVMState *s) int kvm_arm_vgic_probe(void) { + int val = 0; + if (kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V3, true) == 0) { - return 3; - } else if (kvm_create_device(kvm_state, - KVM_DEV_TYPE_ARM_VGIC_V2, true) == 0) { - return 2; - } else { - return 0; + val |= KVM_ARM_VGIC_V3; + } + if (kvm_create_device(kvm_state, + KVM_DEV_TYPE_ARM_VGIC_V2, true) == 0) { + val |= KVM_ARM_VGIC_V2; } + return val; } int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level) diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c index f703c4fcad..f271181ab8 100644 --- a/target/arm/kvm32.c +++ b/target/arm/kvm32.c @@ -409,17 +409,22 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } - ret = kvm_put_vcpu_events(cpu); - if (ret) { - return ret; - } - write_cpustate_to_list(cpu, true); if (!write_list_to_kvmstate(cpu, level)) { return EINVAL; } + /* + * Setting VCPU events should be triggered after syncing the registers + * to avoid overwriting potential changes made by KVM upon calling + * KVM_SET_VCPU_EVENTS ioctl + */ + ret = kvm_put_vcpu_events(cpu); + if (ret) { + return ret; + } + kvm_arm_sync_mpstate_to_kvm(cpu); return ret; diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index 93ba1448da..be5b31c2b0 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -1094,17 +1094,22 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } - ret = kvm_put_vcpu_events(cpu); - if (ret) { - return ret; - } - write_cpustate_to_list(cpu, true); if (!write_list_to_kvmstate(cpu, level)) { return -EINVAL; } + /* + * Setting VCPU events should be triggered after syncing the registers + * to avoid overwriting potential changes made by KVM upon calling + * KVM_SET_VCPU_EVENTS ioctl + */ + ret = kvm_put_vcpu_events(cpu); + if (ret) { + return ret; + } + kvm_arm_sync_mpstate_to_kvm(cpu); return ret; diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index ae9e075d75..48bf5e16d5 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -15,6 +15,9 @@ #include "exec/memory.h" #include "qemu/error-report.h" +#define KVM_ARM_VGIC_V2 (1 << 0) +#define KVM_ARM_VGIC_V3 (1 << 1) + /** * kvm_arm_vcpu_init: * @cs: CPUState diff --git a/target/arm/monitor.c b/target/arm/monitor.c index c2dc7908de..ea6598c412 100644 --- a/target/arm/monitor.c +++ b/target/arm/monitor.c @@ -206,9 +206,7 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, return NULL; } } else { - Error *err = NULL; - arm_cpu_finalize_features(ARM_CPU(obj), &err); - assert(err == NULL); + arm_cpu_finalize_features(ARM_CPU(obj), &error_abort); } expansion_info = g_new0(CpuModelExpansionInfo, 1); @@ -221,12 +219,10 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, while ((name = cpu_model_advertised_features[i++]) != NULL) { ObjectProperty *prop = object_property_find(obj, name, NULL); if (prop) { - Error *err = NULL; QObject *value; assert(prop->get); - value = object_property_get_qobject(obj, name, &err); - assert(!err); + value = object_property_get_qobject(obj, name, &error_abort); qdict_put_obj(qdict_out, name, value); } diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index fefe8af7f5..8fffb52203 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -228,7 +228,18 @@ static void gen_a64_set_pc(DisasContext *s, TCGv_i64 src) static TCGv_i64 clean_data_tbi(DisasContext *s, TCGv_i64 addr) { TCGv_i64 clean = new_tmp_a64(s); + /* + * In order to get the correct value in the FAR_ELx register, + * we must present the memory subsystem with the "dirty" address + * including the TBI. In system mode we can make this work via + * the TLB, dropping the TBI during translation. But for user-only + * mode we don't have that option, and must remove the top byte now. + */ +#ifdef CONFIG_USER_ONLY gen_top_byte_ignore(s, clean, addr, s->tbid); +#else + tcg_gen_mov_i64(clean, addr); +#endif return clean; } diff --git a/target/arm/translate.c b/target/arm/translate.c index 6259064ea7..9f9f4e19e0 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -7296,7 +7296,7 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) { /* - * A write to any coprocessor regiser that ends a TB + * A write to any coprocessor register that ends a TB * must rebuild the hflags for the next TB. */ TCGv_i32 tcg_el = tcg_const_i32(s->current_el); @@ -8551,7 +8551,7 @@ static bool trans_MRS_v7m(DisasContext *s, arg_MRS_v7m *a) static bool trans_MSR_v7m(DisasContext *s, arg_MSR_v7m *a) { - TCGv_i32 addr, reg, el; + TCGv_i32 addr, reg; if (!arm_dc_feature(s, ARM_FEATURE_M)) { return false; @@ -8561,9 +8561,8 @@ static bool trans_MSR_v7m(DisasContext *s, arg_MSR_v7m *a) gen_helper_v7m_msr(cpu_env, addr, reg); tcg_temp_free_i32(addr); tcg_temp_free_i32(reg); - el = tcg_const_i32(s->current_el); - gen_helper_rebuild_hflags_m32(cpu_env, el); - tcg_temp_free_i32(el); + /* If we wrote to CONTROL, the EL might have changed */ + gen_helper_rebuild_hflags_m32_newel(cpu_env); gen_lookup_tb(s); return true; } @@ -10590,7 +10589,7 @@ static bool trans_CPS(DisasContext *s, arg_CPS *a) static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a) { - TCGv_i32 tmp, addr; + TCGv_i32 tmp, addr, el; if (!arm_dc_feature(s, ARM_FEATURE_M)) { return false; @@ -10613,6 +10612,9 @@ static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a) gen_helper_v7m_msr(cpu_env, addr, tmp); tcg_temp_free_i32(addr); } + el = tcg_const_i32(s->current_el); + gen_helper_rebuild_hflags_m32(cpu_env, el); + tcg_temp_free_i32(el); tcg_temp_free_i32(tmp); gen_lookup_tb(s); return true; diff --git a/target/cris/cpu-qom.h b/target/cris/cpu-qom.h index 308c1f95bd..f1de6041dc 100644 --- a/target/cris/cpu-qom.h +++ b/target/cris/cpu-qom.h @@ -45,7 +45,7 @@ typedef struct CRISCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; uint32_t vr; } CRISCPUClass; diff --git a/target/cris/cpu.c b/target/cris/cpu.c index 17c6712e29..cff6b9eabf 100644 --- a/target/cris/cpu.c +++ b/target/cris/cpu.c @@ -40,15 +40,15 @@ static bool cris_cpu_has_work(CPUState *cs) return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } -/* CPUClass::reset() */ -static void cris_cpu_reset(CPUState *s) +static void cris_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); CRISCPU *cpu = CRIS_CPU(s); CRISCPUClass *ccc = CRIS_CPU_GET_CLASS(cpu); CPUCRISState *env = &cpu->env; uint32_t vr; - ccc->parent_reset(s); + ccc->parent_reset(dev); vr = env->pregs[PR_VR]; memset(env, 0, offsetof(CPUCRISState, end_reset_fields)); @@ -264,7 +264,7 @@ static void cris_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, cris_cpu_realizefn, &ccc->parent_realize); - cpu_class_set_parent_reset(cc, cris_cpu_reset, &ccc->parent_reset); + device_class_set_parent_reset(dc, cris_cpu_reset, &ccc->parent_reset); cc->class_by_name = cris_cpu_class_by_name; cc->has_work = cris_cpu_has_work; diff --git a/target/cris/cpu.h b/target/cris/cpu.h index ca240bc788..8f08d7628b 100644 --- a/target/cris/cpu.h +++ b/target/cris/cpu.h @@ -195,8 +195,8 @@ void cris_cpu_dump_state(CPUState *cs, FILE *f, int flags); hwaddr cris_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int crisv10_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); -int cris_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int crisv10_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); +int cris_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int cris_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); /* you can call this signal handler from your SIGBUS and SIGSEGV diff --git a/target/cris/gdbstub.c b/target/cris/gdbstub.c index a3d76d2e8c..b01b2aa081 100644 --- a/target/cris/gdbstub.c +++ b/target/cris/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int crisv10_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int crisv10_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; @@ -53,7 +53,7 @@ int crisv10_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -int cris_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int cris_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; diff --git a/target/hppa/cpu-qom.h b/target/hppa/cpu-qom.h index 6367dc4793..b1f6045495 100644 --- a/target/hppa/cpu-qom.h +++ b/target/hppa/cpu-qom.h @@ -44,7 +44,7 @@ typedef struct HPPACPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } HPPACPUClass; typedef struct HPPACPU HPPACPU; diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 6713d04f11..801a4fb1ba 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -321,7 +321,7 @@ void cpu_hppa_change_prot_id(CPUHPPAState *env); int cpu_hppa_signal_handler(int host_signum, void *pinfo, void *puc); hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr); -int hppa_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int hppa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int hppa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void hppa_cpu_do_interrupt(CPUState *cpu); bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req); diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c index 341888a9da..a6428a2893 100644 --- a/target/hppa/gdbstub.c +++ b/target/hppa/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int hppa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int hppa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { HPPACPU *cpu = HPPA_CPU(cs); CPUHPPAState *env = &cpu->env; diff --git a/target/i386/cpu-qom.h b/target/i386/cpu-qom.h index 0efab2fc67..3e96f8d668 100644 --- a/target/i386/cpu-qom.h +++ b/target/i386/cpu-qom.h @@ -71,7 +71,7 @@ typedef struct X86CPUClass { DeviceRealize parent_realize; DeviceUnrealize parent_unrealize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } X86CPUClass; typedef struct X86CPU X86CPU; diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 92fafa2659..34b511f078 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1133,7 +1133,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "clzero", NULL, "xsaveerptr", NULL, NULL, NULL, NULL, NULL, NULL, "wbnoinvd", NULL, NULL, - "ibpb", NULL, NULL, NULL, + "ibpb", NULL, NULL, "amd-stibp", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "amd-ssbd", "virt-ssbd", "amd-no-ssb", NULL, @@ -1690,6 +1690,7 @@ typedef struct PropValue { typedef struct X86CPUVersionDefinition { X86CPUVersion version; const char *alias; + const char *note; PropValue *props; } X86CPUVersionDefinition; @@ -1720,6 +1721,7 @@ struct X86CPUModel { X86CPUDefinition *cpudef; /* CPU model version */ X86CPUVersion version; + const char *note; /* * If true, this is an alias CPU model. * This matters only for "-cpu help" and query-cpu-definitions @@ -1796,6 +1798,56 @@ static CPUCaches epyc_cache_info = { }, }; +static CPUCaches epyc_rome_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = 1, + .no_invd_sharing = true, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = 1, + .no_invd_sharing = true, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 1024, + .lines_per_tag = 1, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 16 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 16384, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = true, + }, +}; + /* The following VMX features are not supported by KVM and are left out in the * CPU definitions: * @@ -3592,6 +3644,18 @@ static X86CPUDefinition builtin_x86_defs[] = { .features[FEAT_VMX_VMFUNC] = MSR_VMX_VMFUNC_EPT_SWITCHING, .xlevel = 0x80000008, .model_id = "Intel Atom Processor (Denverton)", + .versions = (X86CPUVersionDefinition[]) { + { .version = 1 }, + { + .version = 2, + .props = (PropValue[]) { + { "monitor", "off" }, + { "mpx", "off" }, + { /* end of list */ }, + }, + }, + { /* end of list */ }, + }, }, { .name = "Snowridge", @@ -3928,10 +3992,6 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | CPUID_7_0_EBX_SHA_NI, - /* Missing: XSAVES (not supported by some Linux versions, - * including v4.1 to v4.12). - * KVM doesn't yet expose any XSAVES state save component. - */ .features[FEAT_XSAVE] = CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | CPUID_XSAVE_XGETBV1, @@ -3954,6 +4014,19 @@ static X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 3, + .props = (PropValue[]) { + { "ibpb", "on" }, + { "perfctr-core", "on" }, + { "clzero", "on" }, + { "xsaveerptr", "on" }, + { "xsaves", "on" }, + { "model-id", + "AMD EPYC Processor" }, + { /* end of list */ } + } + }, { /* end of list */ } } }, @@ -4007,6 +4080,56 @@ static X86CPUDefinition builtin_x86_defs[] = { .model_id = "Hygon Dhyana Processor", .cache_info = &epyc_cache_info, }, + { + .name = "EPYC-Rome", + .level = 0xd, + .vendor = CPUID_VENDOR_AMD, + .family = 23, + .model = 49, + .stepping = 0, + .features[FEAT_1_EDX] = + CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | + CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | + CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | + CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | CPUID_DE | + CPUID_VME | CPUID_FP87, + .features[FEAT_1_ECX] = + CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX | + CPUID_EXT_XSAVE | CPUID_EXT_AES | CPUID_EXT_POPCNT | + CPUID_EXT_MOVBE | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | + CPUID_EXT_CX16 | CPUID_EXT_FMA | CPUID_EXT_SSSE3 | + CPUID_EXT_MONITOR | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_FFXSR | CPUID_EXT2_MMXEXT | CPUID_EXT2_NX | + CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH | + CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | + CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM | + CPUID_EXT3_TOPOEXT | CPUID_EXT3_PERFCORE, + .features[FEAT_8000_0008_EBX] = + CPUID_8000_0008_EBX_CLZERO | CPUID_8000_0008_EBX_XSAVEERPTR | + CPUID_8000_0008_EBX_WBNOINVD | CPUID_8000_0008_EBX_IBPB | + CPUID_8000_0008_EBX_STIBP, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED | + CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | + CPUID_7_0_EBX_SHA_NI | CPUID_7_0_EBX_CLWB, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_RDPID, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .features[FEAT_SVM] = + CPUID_SVM_NPT | CPUID_SVM_NRIPSAVE, + .xlevel = 0x8000001E, + .model_id = "AMD EPYC-Rome Processor", + .cache_info = &epyc_rome_cache_info, + }, }; /* KVM-specific features that are automatically added/removed @@ -4849,6 +4972,7 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) g_autofree char *name = x86_cpu_class_get_model_name(cc); g_autofree char *desc = g_strdup(cc->model_description); g_autofree char *alias_of = x86_cpu_class_get_alias_of(cc); + g_autofree char *model_id = x86_cpu_class_get_model_id(cc); if (!desc && alias_of) { if (cc->model && cc->model->version == CPU_VERSION_AUTO) { @@ -4857,11 +4981,14 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) desc = g_strdup_printf("(alias of %s)", alias_of); } } + if (!desc && cc->model && cc->model->note) { + desc = g_strdup_printf("%s [%s]", model_id, cc->model->note); + } if (!desc) { - desc = x86_cpu_class_get_model_id(cc); + desc = g_strdup_printf("%s", model_id); } - qemu_printf("x86 %-20s %-48s\n", name, desc); + qemu_printf("x86 %-20s %-58s\n", name, desc); } /* list available CPU models and flags */ @@ -5338,6 +5465,7 @@ static void x86_register_cpudef_types(X86CPUDefinition *def) x86_cpu_versioned_model_name(def, vdef->version); m->cpudef = def; m->version = vdef->version; + m->note = vdef->note; x86_register_cpu_model_type(name, m); if (vdef->alias) { @@ -5369,6 +5497,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t die_offset; uint32_t limit; uint32_t signature[3]; + X86CPUTopoInfo topo_info; + + topo_info.dies_per_pkg = env->nr_dies; + topo_info.cores_per_die = cs->nr_cores; + topo_info.threads_per_core = cs->nr_threads; /* Calculate & apply limits for different index ranges */ if (index >= 0xC0000000) { @@ -5455,8 +5588,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ - die_offset = apicid_die_offset(env->nr_dies, - cs->nr_cores, cs->nr_threads); + die_offset = apicid_die_offset(&topo_info); if (cpu->enable_l3_cache) { encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache, (1 << die_offset), cs->nr_cores, @@ -5547,14 +5679,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: - *eax = apicid_core_offset(env->nr_dies, - cs->nr_cores, cs->nr_threads); + *eax = apicid_core_offset(&topo_info); *ebx = cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_SMT; break; case 1: - *eax = apicid_pkg_offset(env->nr_dies, - cs->nr_cores, cs->nr_threads); + *eax = apicid_pkg_offset(&topo_info); *ebx = cs->nr_cores * cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_CORE; break; @@ -5578,20 +5708,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = cpu->apic_id; switch (count) { case 0: - *eax = apicid_core_offset(env->nr_dies, cs->nr_cores, - cs->nr_threads); + *eax = apicid_core_offset(&topo_info); *ebx = cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_SMT; break; case 1: - *eax = apicid_die_offset(env->nr_dies, cs->nr_cores, - cs->nr_threads); + *eax = apicid_die_offset(&topo_info); *ebx = cs->nr_cores * cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_CORE; break; case 2: - *eax = apicid_pkg_offset(env->nr_dies, cs->nr_cores, - cs->nr_threads); + *eax = apicid_pkg_offset(&topo_info); *ebx = env->nr_dies * cs->nr_cores * cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_DIE; break; @@ -5855,9 +5982,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } -/* CPUClass::reset() */ -static void x86_cpu_reset(CPUState *s) +static void x86_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); X86CPU *cpu = X86_CPU(s); X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); CPUX86State *env = &cpu->env; @@ -5865,7 +5992,7 @@ static void x86_cpu_reset(CPUState *s) uint64_t xcr0; int i; - xcc->parent_reset(s); + xcc->parent_reset(dev); memset(env, 0, offsetof(CPUX86State, end_reset_fields)); @@ -6830,6 +6957,7 @@ static void x86_cpu_initfn(Object *obj) FeatureWord w; env->nr_dies = 1; + env->nr_nodes = 1; cpu_set_cpustate_pointers(cpu); object_property_add(obj, "family", "int", @@ -7169,7 +7297,7 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) &xcc->parent_unrealize); device_class_set_props(dc, x86_cpu_properties); - cpu_class_set_parent_reset(cc, x86_cpu_reset, &xcc->parent_reset); + device_class_set_parent_reset(dc, x86_cpu_reset, &xcc->parent_reset); cc->reset_dump_flags = CPU_DUMP_FPU | CPU_DUMP_CCOP; cc->class_by_name = x86_cpu_class_by_name; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 576f309bbf..60d797d594 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -792,6 +792,8 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_8000_0008_EBX_WBNOINVD (1U << 9) /* Indirect Branch Prediction Barrier */ #define CPUID_8000_0008_EBX_IBPB (1U << 12) +/* Single Thread Indirect Branch Predictors */ +#define CPUID_8000_0008_EBX_STIBP (1U << 15) #define CPUID_XSAVE_XSAVEOPT (1U << 0) #define CPUID_XSAVE_XSAVEC (1U << 1) @@ -1607,6 +1609,7 @@ typedef struct CPUX86State { TPRAccess tpr_access_type; unsigned nr_dies; + unsigned nr_nodes; } CPUX86State; struct kvm_msrs; @@ -1766,7 +1769,7 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags); hwaddr x86_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, MemTxAttrs *attrs); -int x86_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int x86_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int x86_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void x86_cpu_exec_enter(CPUState *cpu); diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c index 572ead641c..f3d23b614e 100644 --- a/target/i386/gdbstub.c +++ b/target/i386/gdbstub.c @@ -79,7 +79,7 @@ static const int gpr_map32[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; #endif -int x86_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int x86_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; @@ -98,26 +98,22 @@ int x86_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return gdb_get_reg64(mem_buf, env->regs[gpr_map[n]] & 0xffffffffUL); } else { - memset(mem_buf, 0, sizeof(target_ulong)); - return sizeof(target_ulong); + return gdb_get_regl(mem_buf, 0); } } else { return gdb_get_reg32(mem_buf, env->regs[gpr_map32[n]]); } } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) { -#ifdef USE_X86LDOUBLE - /* FIXME: byteswap float values - after fixing fpregs layout. */ - memcpy(mem_buf, &env->fpregs[n - IDX_FP_REGS], 10); -#else - memset(mem_buf, 0, 10); -#endif - return 10; + floatx80 *fp = (floatx80 *) &env->fpregs[n - IDX_FP_REGS]; + int len = gdb_get_reg64(mem_buf, cpu_to_le64(fp->low)); + len += gdb_get_reg16(mem_buf + len, cpu_to_le16(fp->high)); + return len; } else if (n >= IDX_XMM_REGS && n < IDX_XMM_REGS + CPU_NB_REGS) { n -= IDX_XMM_REGS; if (n < CPU_NB_REGS32 || TARGET_LONG_BITS == 64) { - stq_p(mem_buf, env->xmm_regs[n].ZMM_Q(0)); - stq_p(mem_buf + 8, env->xmm_regs[n].ZMM_Q(1)); - return 16; + return gdb_get_reg128(mem_buf, + env->xmm_regs[n].ZMM_Q(0), + env->xmm_regs[n].ZMM_Q(1)); } } else { switch (n) { @@ -290,10 +286,9 @@ int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 4; } } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) { -#ifdef USE_X86LDOUBLE - /* FIXME: byteswap float values - after fixing fpregs layout. */ - memcpy(&env->fpregs[n - IDX_FP_REGS], mem_buf, 10); -#endif + floatx80 *fp = (floatx80 *) &env->fpregs[n - IDX_FP_REGS]; + fp->low = le64_to_cpu(* (uint64_t *) mem_buf); + fp->high = le16_to_cpu(* (uint16_t *) (mem_buf + 8)); return 10; } else if (n >= IDX_XMM_REGS && n < IDX_XMM_REGS + CPU_NB_REGS) { n -= IDX_XMM_REGS; diff --git a/target/i386/hax-posix.c b/target/i386/hax-posix.c index a5426a6dac..3bad89f133 100644 --- a/target/i386/hax-posix.c +++ b/target/i386/hax-posix.c @@ -108,41 +108,12 @@ int hax_mod_version(struct hax_state *hax, struct hax_module_version *version) static char *hax_vm_devfs_string(int vm_id) { - char *name; - - if (vm_id > MAX_VM_ID) { - fprintf(stderr, "Too big VM id\n"); - return NULL; - } - -#define HAX_VM_DEVFS "/dev/hax_vm/vmxx" - name = g_strdup(HAX_VM_DEVFS); - if (!name) { - return NULL; - } - - snprintf(name, sizeof HAX_VM_DEVFS, "/dev/hax_vm/vm%02d", vm_id); - return name; + return g_strdup_printf("/dev/hax_vm/vm%02d", vm_id); } static char *hax_vcpu_devfs_string(int vm_id, int vcpu_id) { - char *name; - - if (vm_id > MAX_VM_ID || vcpu_id > MAX_VCPU_ID) { - fprintf(stderr, "Too big vm id %x or vcpu id %x\n", vm_id, vcpu_id); - return NULL; - } - -#define HAX_VCPU_DEVFS "/dev/hax_vmxx/vcpuxx" - name = g_strdup(HAX_VCPU_DEVFS); - if (!name) { - return NULL; - } - - snprintf(name, sizeof HAX_VCPU_DEVFS, "/dev/hax_vm%02d/vcpu%02d", - vm_id, vcpu_id); - return name; + return g_strdup_printf("/dev/hax_vm%02d/vcpu%02d", vm_id, vcpu_id); } int hax_host_create_vm(struct hax_state *hax, int *vmid) diff --git a/target/i386/hax-windows.c b/target/i386/hax-windows.c index 5729ad9b48..0ba488c468 100644 --- a/target/i386/hax-windows.c +++ b/target/i386/hax-windows.c @@ -185,41 +185,12 @@ int hax_mod_version(struct hax_state *hax, struct hax_module_version *version) static char *hax_vm_devfs_string(int vm_id) { - char *name; - - if (vm_id > MAX_VM_ID) { - fprintf(stderr, "Too big VM id\n"); - return NULL; - } - -#define HAX_VM_DEVFS "\\\\.\\hax_vmxx" - name = g_strdup(HAX_VM_DEVFS); - if (!name) { - return NULL; - } - - snprintf(name, sizeof HAX_VM_DEVFS, "\\\\.\\hax_vm%02d", vm_id); - return name; + return g_strdup_printf("/dev/hax_vm/vm%02d", vm_id); } static char *hax_vcpu_devfs_string(int vm_id, int vcpu_id) { - char *name; - - if (vm_id > MAX_VM_ID || vcpu_id > MAX_VCPU_ID) { - fprintf(stderr, "Too big vm id %x or vcpu id %x\n", vm_id, vcpu_id); - return NULL; - } - -#define HAX_VCPU_DEVFS "\\\\.\\hax_vmxx_vcpuxx" - name = g_strdup(HAX_VCPU_DEVFS); - if (!name) { - return NULL; - } - - snprintf(name, sizeof HAX_VCPU_DEVFS, "\\\\.\\hax_vm%02d_vcpu%02d", - vm_id, vcpu_id); - return name; + return g_strdup_printf("/dev/hax_vm%02d/vcpu%02d", vm_id, vcpu_id); } int hax_host_create_vm(struct hax_state *hax, int *vmid) diff --git a/target/i386/sev.c b/target/i386/sev.c index 024bb24e51..846018a12d 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -267,109 +267,21 @@ qsev_guest_class_init(ObjectClass *oc, void *data) } static void -qsev_guest_set_handle(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - uint32_t value; - - visit_type_uint32(v, name, &value, errp); - sev->handle = value; -} - -static void -qsev_guest_set_policy(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - uint32_t value; - - visit_type_uint32(v, name, &value, errp); - sev->policy = value; -} - -static void -qsev_guest_set_cbitpos(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - uint32_t value; - - visit_type_uint32(v, name, &value, errp); - sev->cbitpos = value; -} - -static void -qsev_guest_set_reduced_phys_bits(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - uint32_t value; - - visit_type_uint32(v, name, &value, errp); - sev->reduced_phys_bits = value; -} - -static void -qsev_guest_get_policy(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint32_t value; - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - - value = sev->policy; - visit_type_uint32(v, name, &value, errp); -} - -static void -qsev_guest_get_handle(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint32_t value; - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - - value = sev->handle; - visit_type_uint32(v, name, &value, errp); -} - -static void -qsev_guest_get_cbitpos(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint32_t value; - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - - value = sev->cbitpos; - visit_type_uint32(v, name, &value, errp); -} - -static void -qsev_guest_get_reduced_phys_bits(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint32_t value; - QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); - - value = sev->reduced_phys_bits; - visit_type_uint32(v, name, &value, errp); -} - -static void qsev_guest_init(Object *obj) { QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE); sev->policy = DEFAULT_GUEST_POLICY; - object_property_add(obj, "policy", "uint32", qsev_guest_get_policy, - qsev_guest_set_policy, NULL, NULL, NULL); - object_property_add(obj, "handle", "uint32", qsev_guest_get_handle, - qsev_guest_set_handle, NULL, NULL, NULL); - object_property_add(obj, "cbitpos", "uint32", qsev_guest_get_cbitpos, - qsev_guest_set_cbitpos, NULL, NULL, NULL); - object_property_add(obj, "reduced-phys-bits", "uint32", - qsev_guest_get_reduced_phys_bits, - qsev_guest_set_reduced_phys_bits, NULL, NULL, NULL); + object_property_add_uint32_ptr(obj, "policy", &sev->policy, + OBJ_PROP_FLAG_READWRITE, NULL); + object_property_add_uint32_ptr(obj, "handle", &sev->handle, + OBJ_PROP_FLAG_READWRITE, NULL); + object_property_add_uint32_ptr(obj, "cbitpos", &sev->cbitpos, + OBJ_PROP_FLAG_READWRITE, NULL); + object_property_add_uint32_ptr(obj, "reduced-phys-bits", + &sev->reduced_phys_bits, + OBJ_PROP_FLAG_READWRITE, NULL); } /* sev guest info */ diff --git a/target/i386/whp-dispatch.h b/target/i386/whp-dispatch.h index 87d049ceab..e4695c349f 100644 --- a/target/i386/whp-dispatch.h +++ b/target/i386/whp-dispatch.h @@ -23,6 +23,12 @@ X(HRESULT, WHvGetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, WHV_REGISTER_VALUE* RegisterValues)) \ X(HRESULT, WHvSetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, const WHV_REGISTER_VALUE* RegisterValues)) \ +/* + * These are supplemental functions that may not be present + * on all versions and are not critical for basic functionality. + */ +#define LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(X) \ + X(HRESULT, WHvSuspendPartitionTime, (WHV_PARTITION_HANDLE Partition)) \ #define LIST_WINHVEMULATION_FUNCTIONS(X) \ X(HRESULT, WHvEmulatorCreateEmulator, (const WHV_EMULATOR_CALLBACKS* Callbacks, WHV_EMULATOR_HANDLE* Emulator)) \ @@ -40,10 +46,12 @@ /* Define function typedef */ LIST_WINHVPLATFORM_FUNCTIONS(WHP_DEFINE_TYPE) LIST_WINHVEMULATION_FUNCTIONS(WHP_DEFINE_TYPE) +LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_DEFINE_TYPE) struct WHPDispatch { LIST_WINHVPLATFORM_FUNCTIONS(WHP_DECLARE_MEMBER) LIST_WINHVEMULATION_FUNCTIONS(WHP_DECLARE_MEMBER) + LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_DECLARE_MEMBER) }; extern struct WHPDispatch whp_dispatch; @@ -53,6 +61,7 @@ bool init_whp_dispatch(void); typedef enum WHPFunctionList { WINHV_PLATFORM_FNS_DEFAULT, WINHV_EMULATION_FNS_DEFAULT, + WINHV_PLATFORM_FNS_SUPPLEMENTAL } WHPFunctionList; #endif /* WHP_DISPATCH_H */ diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c index 683d49d217..c78baac6df 100644 --- a/target/i386/whpx-all.c +++ b/target/i386/whpx-all.c @@ -114,7 +114,6 @@ static const WHV_REGISTER_NAME whpx_register_names[] = { WHvX64RegisterXmmControlStatus, /* X64 MSRs */ - WHvX64RegisterTsc, WHvX64RegisterEfer, #ifdef TARGET_X86_64 WHvX64RegisterKernelGsBase, @@ -215,7 +214,44 @@ static SegmentCache whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER *hs) return qs; } -static void whpx_set_registers(CPUState *cpu) +static int whpx_set_tsc(CPUState *cpu) +{ + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc; + WHV_REGISTER_VALUE tsc_val; + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + + /* + * Suspend the partition prior to setting the TSC to reduce the variance + * in TSC across vCPUs. When the first vCPU runs post suspend, the + * partition is automatically resumed. + */ + if (whp_dispatch.WHvSuspendPartitionTime) { + + /* + * Unable to suspend partition while setting TSC is not a fatal + * error. It just increases the likelihood of TSC variance between + * vCPUs and some guest OS are able to handle that just fine. + */ + hr = whp_dispatch.WHvSuspendPartitionTime(whpx->partition); + if (FAILED(hr)) { + warn_report("WHPX: Failed to suspend partition, hr=%08lx", hr); + } + } + + tsc_val.Reg64 = env->tsc; + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, &tsc_reg, 1, &tsc_val); + if (FAILED(hr)) { + error_report("WHPX: Failed to set TSC, hr=%08lx", hr); + return -1; + } + + return 0; +} + +static void whpx_set_registers(CPUState *cpu, int level) { struct whpx_state *whpx = &whpx_global; struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); @@ -230,6 +266,14 @@ static void whpx_set_registers(CPUState *cpu) assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + /* + * Following MSRs have side effects on the guest or are too heavy for + * runtime. Limit them to full state update. + */ + if (level >= WHPX_SET_RESET_STATE) { + whpx_set_tsc(cpu); + } + memset(&vcxt, 0, sizeof(struct whpx_register_set)); v86 = (env->eflags & VM_MASK); @@ -330,8 +374,6 @@ static void whpx_set_registers(CPUState *cpu) idx += 1; /* MSRs */ - assert(whpx_register_names[idx] == WHvX64RegisterTsc); - vcxt.values[idx++].Reg64 = env->tsc; assert(whpx_register_names[idx] == WHvX64RegisterEfer); vcxt.values[idx++].Reg64 = env->efer; #ifdef TARGET_X86_64 @@ -379,6 +421,25 @@ static void whpx_set_registers(CPUState *cpu) return; } +static int whpx_get_tsc(CPUState *cpu) +{ + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); + WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc; + WHV_REGISTER_VALUE tsc_val; + HRESULT hr; + struct whpx_state *whpx = &whpx_global; + + hr = whp_dispatch.WHvGetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, &tsc_reg, 1, &tsc_val); + if (FAILED(hr)) { + error_report("WHPX: Failed to get TSC, hr=%08lx", hr); + return -1; + } + + env->tsc = tsc_val.Reg64; + return 0; +} + static void whpx_get_registers(CPUState *cpu) { struct whpx_state *whpx = &whpx_global; @@ -394,6 +455,11 @@ static void whpx_get_registers(CPUState *cpu) assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + if (!env->tsc_valid) { + whpx_get_tsc(cpu); + env->tsc_valid = !runstate_is_running(); + } + hr = whp_dispatch.WHvGetVirtualProcessorRegisters( whpx->partition, cpu->cpu_index, whpx_register_names, @@ -492,8 +558,6 @@ static void whpx_get_registers(CPUState *cpu) idx += 1; /* MSRs */ - assert(whpx_register_names[idx] == WHvX64RegisterTsc); - env->tsc = vcxt.values[idx++].Reg64; assert(whpx_register_names[idx] == WHvX64RegisterEfer); env->efer = vcxt.values[idx++].Reg64; #ifdef TARGET_X86_64 @@ -841,9 +905,8 @@ static void whpx_vcpu_process_async_events(CPUState *cpu) if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { - + whpx_cpu_synchronize_state(cpu); do_cpu_init(x86_cpu); - cpu->vcpu_dirty = true; vcpu->interruptable = true; } @@ -859,17 +922,13 @@ static void whpx_vcpu_process_async_events(CPUState *cpu) } if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) { - if (!cpu->vcpu_dirty) { - whpx_get_registers(cpu); - } + whpx_cpu_synchronize_state(cpu); do_cpu_sipi(x86_cpu); } if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; - if (!cpu->vcpu_dirty) { - whpx_get_registers(cpu); - } + whpx_cpu_synchronize_state(cpu); apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, env->tpr_access_type); } @@ -896,7 +955,7 @@ static int whpx_vcpu_run(CPUState *cpu) do { if (cpu->vcpu_dirty) { - whpx_set_registers(cpu); + whpx_set_registers(cpu, WHPX_SET_RUNTIME_STATE); cpu->vcpu_dirty = false; } @@ -980,38 +1039,32 @@ static int whpx_vcpu_run(CPUState *cpu) WHV_REGISTER_VALUE reg_values[5]; WHV_REGISTER_NAME reg_names[5]; UINT32 reg_count = 5; - UINT64 rip, rax, rcx, rdx, rbx; + UINT64 cpuid_fn, rip = 0, rax = 0, rcx = 0, rdx = 0, rbx = 0; + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; memset(reg_values, 0, sizeof(reg_values)); rip = vcpu->exit_ctx.VpContext.Rip + vcpu->exit_ctx.VpContext.InstructionLength; - switch (vcpu->exit_ctx.CpuidAccess.Rax) { - case 1: - rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax; - /* Advertise that we are running on a hypervisor */ - rcx = - vcpu->exit_ctx.CpuidAccess.DefaultResultRcx | - CPUID_EXT_HYPERVISOR; - - rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx; - rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx; - break; + cpuid_fn = vcpu->exit_ctx.CpuidAccess.Rax; + + /* + * Ideally, these should be supplied to the hypervisor during VCPU + * initialization and it should be able to satisfy this request. + * But, currently, WHPX doesn't support setting CPUID values in the + * hypervisor once the partition has been setup, which is too late + * since VCPUs are realized later. For now, use the values from + * QEMU to satisfy these requests, until WHPX adds support for + * being able to set these values in the hypervisor at runtime. + */ + cpu_x86_cpuid(env, cpuid_fn, 0, (UINT32 *)&rax, (UINT32 *)&rbx, + (UINT32 *)&rcx, (UINT32 *)&rdx); + switch (cpuid_fn) { case 0x80000001: - rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax; /* Remove any support of OSVW */ - rcx = - vcpu->exit_ctx.CpuidAccess.DefaultResultRcx & - ~CPUID_EXT3_OSVW; - - rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx; - rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx; + rcx &= ~CPUID_EXT3_OSVW; break; - default: - rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax; - rcx = vcpu->exit_ctx.CpuidAccess.DefaultResultRcx; - rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx; - rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx; } reg_names[0] = WHvX64RegisterRip; @@ -1067,21 +1120,23 @@ static int whpx_vcpu_run(CPUState *cpu) static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { - whpx_get_registers(cpu); - cpu->vcpu_dirty = true; + if (!cpu->vcpu_dirty) { + whpx_get_registers(cpu); + cpu->vcpu_dirty = true; + } } static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) { - whpx_set_registers(cpu); + whpx_set_registers(cpu, WHPX_SET_RESET_STATE); cpu->vcpu_dirty = false; } static void do_whpx_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { - whpx_set_registers(cpu); + whpx_set_registers(cpu, WHPX_SET_FULL_STATE); cpu->vcpu_dirty = false; } @@ -1123,6 +1178,15 @@ void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu) static Error *whpx_migration_blocker; +static void whpx_cpu_update_state(void *opaque, int running, RunState state) +{ + CPUX86State *env = opaque; + + if (running) { + env->tsc_valid = false; + } +} + int whpx_init_vcpu(CPUState *cpu) { HRESULT hr; @@ -1178,6 +1242,7 @@ int whpx_init_vcpu(CPUState *cpu) cpu->vcpu_dirty = true; cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu; + qemu_add_vm_change_state_handler(whpx_cpu_update_state, cpu->env_ptr); return 0; } @@ -1367,6 +1432,10 @@ static bool load_whp_dispatch_fns(HMODULE *handle, #define WINHV_PLATFORM_DLL "WinHvPlatform.dll" #define WINHV_EMULATION_DLL "WinHvEmulation.dll" + #define WHP_LOAD_FIELD_OPTIONAL(return_type, function_name, signature) \ + whp_dispatch.function_name = \ + (function_name ## _t)GetProcAddress(hLib, #function_name); \ + #define WHP_LOAD_FIELD(return_type, function_name, signature) \ whp_dispatch.function_name = \ (function_name ## _t)GetProcAddress(hLib, #function_name); \ @@ -1394,6 +1463,11 @@ static bool load_whp_dispatch_fns(HMODULE *handle, WHP_LOAD_LIB(WINHV_EMULATION_DLL, hLib) LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD) break; + + case WINHV_PLATFORM_FNS_SUPPLEMENTAL: + WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib) + LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_LOAD_FIELD_OPTIONAL) + break; } *handle = hLib; @@ -1554,6 +1628,8 @@ bool init_whp_dispatch(void) goto error; } + assert(load_whp_dispatch_fns(&hWinHvPlatform, + WINHV_PLATFORM_FNS_SUPPLEMENTAL)); whp_dispatch_initialized = true; return true; diff --git a/target/lm32/cpu-qom.h b/target/lm32/cpu-qom.h index dc9ac9ac9f..bdedb3759a 100644 --- a/target/lm32/cpu-qom.h +++ b/target/lm32/cpu-qom.h @@ -44,7 +44,7 @@ typedef struct LM32CPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } LM32CPUClass; typedef struct LM32CPU LM32CPU; diff --git a/target/lm32/cpu.c b/target/lm32/cpu.c index 687bf35e65..c50ad5fa15 100644 --- a/target/lm32/cpu.c +++ b/target/lm32/cpu.c @@ -99,14 +99,14 @@ static bool lm32_cpu_has_work(CPUState *cs) return cs->interrupt_request & CPU_INTERRUPT_HARD; } -/* CPUClass::reset() */ -static void lm32_cpu_reset(CPUState *s) +static void lm32_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); LM32CPU *cpu = LM32_CPU(s); LM32CPUClass *lcc = LM32_CPU_GET_CLASS(cpu); CPULM32State *env = &cpu->env; - lcc->parent_reset(s); + lcc->parent_reset(dev); /* reset cpu state */ memset(env, 0, offsetof(CPULM32State, end_reset_fields)); @@ -218,7 +218,7 @@ static void lm32_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, lm32_cpu_realizefn, &lcc->parent_realize); - cpu_class_set_parent_reset(cc, lm32_cpu_reset, &lcc->parent_reset); + device_class_set_parent_reset(dc, lm32_cpu_reset, &lcc->parent_reset); cc->class_by_name = lm32_cpu_class_by_name; cc->has_work = lm32_cpu_has_work; diff --git a/target/lm32/cpu.h b/target/lm32/cpu.h index 064c6b1267..01d408eb55 100644 --- a/target/lm32/cpu.h +++ b/target/lm32/cpu.h @@ -202,7 +202,7 @@ void lm32_cpu_do_interrupt(CPUState *cpu); bool lm32_cpu_exec_interrupt(CPUState *cs, int int_req); void lm32_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr lm32_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int lm32_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int lm32_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int lm32_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); typedef enum { diff --git a/target/lm32/gdbstub.c b/target/lm32/gdbstub.c index 82ede436e1..b6fe12e1d6 100644 --- a/target/lm32/gdbstub.c +++ b/target/lm32/gdbstub.c @@ -22,7 +22,7 @@ #include "exec/gdbstub.h" #include "hw/lm32/lm32_pic.h" -int lm32_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int lm32_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { LM32CPU *cpu = LM32_CPU(cs); CPULM32State *env = &cpu->env; diff --git a/target/m68k/cpu-qom.h b/target/m68k/cpu-qom.h index b56da8a213..88b11b60f1 100644 --- a/target/m68k/cpu-qom.h +++ b/target/m68k/cpu-qom.h @@ -44,7 +44,7 @@ typedef struct M68kCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } M68kCPUClass; typedef struct M68kCPU M68kCPU; diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index f0653cda2f..9445fcd6df 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -41,16 +41,16 @@ static void m68k_set_feature(CPUM68KState *env, int feature) env->features |= (1u << feature); } -/* CPUClass::reset() */ -static void m68k_cpu_reset(CPUState *s) +static void m68k_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); M68kCPU *cpu = M68K_CPU(s); M68kCPUClass *mcc = M68K_CPU_GET_CLASS(cpu); CPUM68KState *env = &cpu->env; floatx80 nan = floatx80_default_nan(NULL); int i; - mcc->parent_reset(s); + mcc->parent_reset(dev); memset(env, 0, offsetof(CPUM68KState, end_reset_fields)); #ifdef CONFIG_SOFTMMU @@ -273,7 +273,7 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) device_class_set_parent_realize(dc, m68k_cpu_realizefn, &mcc->parent_realize); - cpu_class_set_parent_reset(cc, m68k_cpu_reset, &mcc->parent_reset); + device_class_set_parent_reset(dc, m68k_cpu_reset, &mcc->parent_reset); cc->class_by_name = m68k_cpu_class_by_name; cc->has_work = m68k_cpu_has_work; diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 3de8e06dfe..521ac67cdd 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -168,7 +168,7 @@ void m68k_cpu_do_interrupt(CPUState *cpu); bool m68k_cpu_exec_interrupt(CPUState *cpu, int int_req); void m68k_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr m68k_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int m68k_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int m68k_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int m68k_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void m68k_tcg_init(void); diff --git a/target/m68k/gdbstub.c b/target/m68k/gdbstub.c index fdc96f57ff..eb2d030e14 100644 --- a/target/m68k/gdbstub.c +++ b/target/m68k/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int m68k_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int m68k_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { M68kCPU *cpu = M68K_CPU(cs); CPUM68KState *env = &cpu->env; diff --git a/target/m68k/helper.c b/target/m68k/helper.c index baf7729af0..014657c637 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -68,23 +68,19 @@ void m68k_cpu_list(void) g_slist_free(list); } -static int cf_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n) +static int cf_fpu_gdb_get_reg(CPUM68KState *env, GByteArray *mem_buf, int n) { if (n < 8) { float_status s; - stfq_p(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); - return 8; + return gdb_get_reg64(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); } switch (n) { case 8: /* fpcontrol */ - stl_be_p(mem_buf, env->fpcr); - return 4; + return gdb_get_reg32(mem_buf, env->fpcr); case 9: /* fpstatus */ - stl_be_p(mem_buf, env->fpsr); - return 4; + return gdb_get_reg32(mem_buf, env->fpsr); case 10: /* fpiar, not implemented */ - memset(mem_buf, 0, 4); - return 4; + return gdb_get_reg32(mem_buf, 0); } return 0; } @@ -109,24 +105,21 @@ static int cf_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) return 0; } -static int m68k_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n) +static int m68k_fpu_gdb_get_reg(CPUM68KState *env, GByteArray *mem_buf, int n) { if (n < 8) { - stw_be_p(mem_buf, env->fregs[n].l.upper); - memset(mem_buf + 2, 0, 2); - stq_be_p(mem_buf + 4, env->fregs[n].l.lower); - return 12; + int len = gdb_get_reg16(mem_buf, env->fregs[n].l.upper); + len += gdb_get_reg16(mem_buf + len, 0); + len += gdb_get_reg64(mem_buf + len, env->fregs[n].l.lower); + return len; } switch (n) { case 8: /* fpcontrol */ - stl_be_p(mem_buf, env->fpcr); - return 4; + return gdb_get_reg32(mem_buf, env->fpcr); case 9: /* fpstatus */ - stl_be_p(mem_buf, env->fpsr); - return 4; + return gdb_get_reg32(mem_buf, env->fpsr); case 10: /* fpiar, not implemented */ - memset(mem_buf, 0, 4); - return 4; + return gdb_get_reg32(mem_buf, 0); } return 0; } diff --git a/target/microblaze/cpu-qom.h b/target/microblaze/cpu-qom.h index 49b07cc697..053ba44ee8 100644 --- a/target/microblaze/cpu-qom.h +++ b/target/microblaze/cpu-qom.h @@ -44,7 +44,7 @@ typedef struct MicroBlazeCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } MicroBlazeCPUClass; typedef struct MicroBlazeCPU MicroBlazeCPU; diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 8c90110e52..a2c2f271df 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -102,14 +102,14 @@ static void microblaze_cpu_set_irq(void *opaque, int irq, int level) } #endif -/* CPUClass::reset() */ -static void mb_cpu_reset(CPUState *s) +static void mb_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); MicroBlazeCPU *cpu = MICROBLAZE_CPU(s); MicroBlazeCPUClass *mcc = MICROBLAZE_CPU_GET_CLASS(cpu); CPUMBState *env = &cpu->env; - mcc->parent_reset(s); + mcc->parent_reset(dev); memset(env, 0, offsetof(CPUMBState, end_reset_fields)); env->res_addr = RES_ADDR_NONE; @@ -292,7 +292,7 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, mb_cpu_realizefn, &mcc->parent_realize); - cpu_class_set_parent_reset(cc, mb_cpu_reset, &mcc->parent_reset); + device_class_set_parent_reset(dc, mb_cpu_reset, &mcc->parent_reset); cc->class_by_name = mb_cpu_class_by_name; cc->has_work = mb_cpu_has_work; diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 32522f606b..1a700a880c 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -313,7 +313,7 @@ void mb_cpu_do_interrupt(CPUState *cs); bool mb_cpu_exec_interrupt(CPUState *cs, int int_req); void mb_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr mb_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int mb_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int mb_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int mb_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void mb_tcg_init(void); diff --git a/target/microblaze/gdbstub.c b/target/microblaze/gdbstub.c index 30677b6d1f..f41ebf1f33 100644 --- a/target/microblaze/gdbstub.c +++ b/target/microblaze/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int mb_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int mb_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; diff --git a/target/mips/cpu-qom.h b/target/mips/cpu-qom.h index a430c0fe4b..9d0df6c034 100644 --- a/target/mips/cpu-qom.h +++ b/target/mips/cpu-qom.h @@ -48,7 +48,7 @@ typedef struct MIPSCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; const struct mips_def_t *cpu_def; } MIPSCPUClass; diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 6cd6b9650b..e86cd06548 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -96,14 +96,14 @@ static bool mips_cpu_has_work(CPUState *cs) return has_work; } -/* CPUClass::reset() */ -static void mips_cpu_reset(CPUState *s) +static void mips_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); MIPSCPU *cpu = MIPS_CPU(s); MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cpu); CPUMIPSState *env = &cpu->env; - mcc->parent_reset(s); + mcc->parent_reset(dev); memset(env, 0, offsetof(CPUMIPSState, end_reset_fields)); @@ -189,7 +189,7 @@ static void mips_cpu_class_init(ObjectClass *c, void *data) device_class_set_parent_realize(dc, mips_cpu_realizefn, &mcc->parent_realize); - cpu_class_set_parent_reset(cc, mips_cpu_reset, &mcc->parent_reset); + device_class_set_parent_reset(dc, mips_cpu_reset, &mcc->parent_reset); cc->class_by_name = mips_cpu_class_by_name; cc->has_work = mips_cpu_has_work; diff --git a/target/mips/gdbstub.c b/target/mips/gdbstub.c index bbb2544939..98f56e660d 100644 --- a/target/mips/gdbstub.c +++ b/target/mips/gdbstub.c @@ -22,7 +22,7 @@ #include "internal.h" #include "exec/gdbstub.h" -int mips_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int mips_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { MIPSCPU *cpu = MIPS_CPU(cs); CPUMIPSState *env = &cpu->env; diff --git a/target/mips/internal.h b/target/mips/internal.h index df55f845ba..1bf274b3ef 100644 --- a/target/mips/internal.h +++ b/target/mips/internal.h @@ -82,7 +82,7 @@ void mips_cpu_do_interrupt(CPUState *cpu); bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req); void mips_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr mips_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int mips_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int mips_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int mips_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, diff --git a/target/moxie/cpu.c b/target/moxie/cpu.c index cf47bc709b..6e0443ccb7 100644 --- a/target/moxie/cpu.c +++ b/target/moxie/cpu.c @@ -35,13 +35,14 @@ static bool moxie_cpu_has_work(CPUState *cs) return cs->interrupt_request & CPU_INTERRUPT_HARD; } -static void moxie_cpu_reset(CPUState *s) +static void moxie_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); MoxieCPU *cpu = MOXIE_CPU(s); MoxieCPUClass *mcc = MOXIE_CPU_GET_CLASS(cpu); CPUMoxieState *env = &cpu->env; - mcc->parent_reset(s); + mcc->parent_reset(dev); memset(env, 0, offsetof(CPUMoxieState, end_reset_fields)); env->pc = 0x1000; @@ -101,7 +102,7 @@ static void moxie_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, moxie_cpu_realizefn, &mcc->parent_realize); - cpu_class_set_parent_reset(cc, moxie_cpu_reset, &mcc->parent_reset); + device_class_set_parent_reset(dc, moxie_cpu_reset, &mcc->parent_reset); cc->class_by_name = moxie_cpu_class_by_name; diff --git a/target/moxie/cpu.h b/target/moxie/cpu.h index 01dca548e5..455553b794 100644 --- a/target/moxie/cpu.h +++ b/target/moxie/cpu.h @@ -69,7 +69,7 @@ typedef struct MoxieCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } MoxieCPUClass; /** diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c index 1c0c855a6f..8f7011fcb9 100644 --- a/target/nios2/cpu.c +++ b/target/nios2/cpu.c @@ -39,9 +39,9 @@ static bool nios2_cpu_has_work(CPUState *cs) return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } -/* CPUClass::reset() */ -static void nios2_cpu_reset(CPUState *cs) +static void nios2_cpu_reset(DeviceState *dev) { + CPUState *cs = CPU(dev); Nios2CPU *cpu = NIOS2_CPU(cs); Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(cpu); CPUNios2State *env = &cpu->env; @@ -51,7 +51,7 @@ static void nios2_cpu_reset(CPUState *cs) log_cpu_state(cs, 0); } - ncc->parent_reset(cs); + ncc->parent_reset(dev); memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS); env->regs[R_PC] = cpu->reset_addr; @@ -124,7 +124,7 @@ static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) #endif } -static int nios2_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { Nios2CPU *cpu = NIOS2_CPU(cs); CPUClass *cc = CPU_GET_CLASS(cs); @@ -188,7 +188,7 @@ static void nios2_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, nios2_cpu_realizefn, &ncc->parent_realize); device_class_set_props(dc, nios2_properties); - cpu_class_set_parent_reset(cc, nios2_cpu_reset, &ncc->parent_reset); + device_class_set_parent_reset(dc, nios2_cpu_reset, &ncc->parent_reset); cc->class_by_name = nios2_cpu_class_by_name; cc->has_work = nios2_cpu_has_work; diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h index 78f633f970..4dddf9c3a1 100644 --- a/target/nios2/cpu.h +++ b/target/nios2/cpu.h @@ -50,7 +50,7 @@ typedef struct Nios2CPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } Nios2CPUClass; #define TARGET_HAS_ICE 1 diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 5cd04dafab..5528c0918f 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -41,13 +41,13 @@ static void openrisc_disas_set_info(CPUState *cpu, disassemble_info *info) info->print_insn = print_insn_or1k; } -/* CPUClass::reset() */ -static void openrisc_cpu_reset(CPUState *s) +static void openrisc_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); OpenRISCCPU *cpu = OPENRISC_CPU(s); OpenRISCCPUClass *occ = OPENRISC_CPU_GET_CLASS(cpu); - occ->parent_reset(s); + occ->parent_reset(dev); memset(&cpu->env, 0, offsetof(CPUOpenRISCState, end_reset_fields)); @@ -150,7 +150,7 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, openrisc_cpu_realizefn, &occ->parent_realize); - cpu_class_set_parent_reset(cc, openrisc_cpu_reset, &occ->parent_reset); + device_class_set_parent_reset(dc, openrisc_cpu_reset, &occ->parent_reset); cc->class_by_name = openrisc_cpu_class_by_name; cc->has_work = openrisc_cpu_has_work; diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 0ad02eab79..f37a52e153 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -48,7 +48,7 @@ typedef struct OpenRISCCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } OpenRISCCPUClass; #define TARGET_INSN_START_EXTRA_WORDS 1 @@ -320,7 +320,7 @@ void openrisc_cpu_do_interrupt(CPUState *cpu); bool openrisc_cpu_exec_interrupt(CPUState *cpu, int int_req); void openrisc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int openrisc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int openrisc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int openrisc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void openrisc_translate_init(void); bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, diff --git a/target/openrisc/gdbstub.c b/target/openrisc/gdbstub.c index 0fcdb79668..095bf76c12 100644 --- a/target/openrisc/gdbstub.c +++ b/target/openrisc/gdbstub.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/gdbstub.h" -int openrisc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int openrisc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { OpenRISCCPU *cpu = OPENRISC_CPU(cs); CPUOpenRISCState *env = &cpu->env; diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index e499575dc8..000c7d405b 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -166,7 +166,7 @@ typedef struct PowerPCCPUClass { DeviceRealize parent_realize; DeviceUnrealize parent_unrealize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; void (*parent_parse_features)(const char *type, char *str, Error **errp); uint32_t pvr; @@ -177,6 +177,7 @@ typedef struct PowerPCCPUClass { uint64_t insns_flags; uint64_t insns_flags2; uint64_t msr_mask; + uint64_t lpcr_mask; /* Available bits in the LPCR */ uint64_t lpcr_pm; /* Power-saving mode Exit Cause Enable bits */ powerpc_mmu_t mmu_model; powerpc_excp_t excp_model; diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index b283042515..88d9449555 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -24,8 +24,6 @@ #include "exec/cpu-defs.h" #include "cpu-qom.h" -/* #define PPC_EMULATE_32BITS_HYPV */ - #define TCG_GUEST_DEFAULT_MO 0 #define TARGET_PAGE_BITS_64K 16 @@ -300,13 +298,12 @@ typedef struct ppc_v3_pate_t { #define MSR_SF 63 /* Sixty-four-bit mode hflags */ #define MSR_TAG 62 /* Tag-active mode (POWERx ?) */ #define MSR_ISF 61 /* Sixty-four-bit interrupt mode on 630 */ -#define MSR_SHV 60 /* hypervisor state hflags */ +#define MSR_HV 60 /* hypervisor state hflags */ #define MSR_TS0 34 /* Transactional state, 2 bits (Book3s) */ #define MSR_TS1 33 #define MSR_TM 32 /* Transactional Memory Available (Book3s) */ #define MSR_CM 31 /* Computation mode for BookE hflags */ #define MSR_ICM 30 /* Interrupt computation mode for BookE */ -#define MSR_THV 29 /* hypervisor state for 32 bits PowerPC hflags */ #define MSR_GS 28 /* guest state for BookE */ #define MSR_UCLE 26 /* User-mode cache lock enable for BookE */ #define MSR_VR 25 /* altivec available x hflags */ @@ -401,10 +398,13 @@ typedef struct ppc_v3_pate_t { #define msr_sf ((env->msr >> MSR_SF) & 1) #define msr_isf ((env->msr >> MSR_ISF) & 1) -#define msr_shv ((env->msr >> MSR_SHV) & 1) +#if defined(TARGET_PPC64) +#define msr_hv ((env->msr >> MSR_HV) & 1) +#else +#define msr_hv (0) +#endif #define msr_cm ((env->msr >> MSR_CM) & 1) #define msr_icm ((env->msr >> MSR_ICM) & 1) -#define msr_thv ((env->msr >> MSR_THV) & 1) #define msr_gs ((env->msr >> MSR_GS) & 1) #define msr_ucle ((env->msr >> MSR_UCLE) & 1) #define msr_vr ((env->msr >> MSR_VR) & 1) @@ -449,16 +449,9 @@ typedef struct ppc_v3_pate_t { /* Hypervisor bit is more specific */ #if defined(TARGET_PPC64) -#define MSR_HVB (1ULL << MSR_SHV) -#define msr_hv msr_shv -#else -#if defined(PPC_EMULATE_32BITS_HYPV) -#define MSR_HVB (1ULL << MSR_THV) -#define msr_hv msr_thv +#define MSR_HVB (1ULL << MSR_HV) #else #define MSR_HVB (0ULL) -#define msr_hv (0) -#endif #endif /* DSISR */ @@ -1051,10 +1044,6 @@ struct CPUPPCState { uint32_t flags; uint64_t insns_flags; uint64_t insns_flags2; -#if defined(TARGET_PPC64) - ppc_slb_t vrma_slb; - target_ulong rmls; -#endif int error_code; uint32_t pending_interrupts; @@ -1218,8 +1207,8 @@ bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req); void ppc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); void ppc_cpu_dump_statistics(CPUState *cpu, int flags); hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int ppc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); -int ppc_cpu_gdb_read_register_apple(CPUState *cpu, uint8_t *buf, int reg); +int ppc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); +int ppc_cpu_gdb_read_register_apple(CPUState *cpu, GByteArray *buf, int reg); int ppc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); int ppc_cpu_gdb_write_register_apple(CPUState *cpu, uint8_t *buf, int reg); #ifndef CONFIG_USER_ONLY @@ -1231,7 +1220,8 @@ int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); #ifndef CONFIG_USER_ONLY -void ppc_cpu_do_system_reset(CPUState *cs); +void ppc_cpu_do_system_reset(CPUState *cs, target_ulong vector); +void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector); extern const VMStateDescription vmstate_ppc_cpu; #endif diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 027f54c0ed..08bc885ca6 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -128,6 +128,37 @@ static uint64_t ppc_excp_vector_offset(CPUState *cs, int ail) return offset; } +static inline void powerpc_set_excp_state(PowerPCCPU *cpu, + target_ulong vector, target_ulong msr) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + + /* + * We don't use hreg_store_msr here as already have treated any + * special case that could occur. Just store MSR and update hflags + * + * Note: We *MUST* not use hreg_store_msr() as-is anyway because it + * will prevent setting of the HV bit which some exceptions might need + * to do. + */ + env->msr = msr & env->msr_mask; + hreg_compute_hflags(env); + env->nip = vector; + /* Reset exception state */ + cs->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + + /* Reset the reservation */ + env->reserve_addr = -1; + + /* + * Any interrupt is context synchronizing, check if TCG TLB needs + * a delayed flush on ppc64 + */ + check_tlb_flush(env, false); +} + /* * Note that this function should be greatly optimized when called * with a constant excp, from ppc_hw_interrupt @@ -768,29 +799,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } } #endif - /* - * We don't use hreg_store_msr here as already have treated any - * special case that could occur. Just store MSR and update hflags - * - * Note: We *MUST* not use hreg_store_msr() as-is anyway because it - * will prevent setting of the HV bit which some exceptions might need - * to do. - */ - env->msr = new_msr & env->msr_mask; - hreg_compute_hflags(env); - env->nip = vector; - /* Reset exception state */ - cs->exception_index = POWERPC_EXCP_NONE; - env->error_code = 0; - /* Reset the reservation */ - env->reserve_addr = -1; - - /* - * Any interrupt is context synchronizing, check if TCG TLB needs - * a delayed flush on ppc64 - */ - check_tlb_flush(env, false); + powerpc_set_excp_state(cpu, vector, new_msr); } void ppc_cpu_do_interrupt(CPUState *cs) @@ -951,12 +961,35 @@ static void ppc_hw_interrupt(CPUPPCState *env) } } -void ppc_cpu_do_system_reset(CPUState *cs) +void ppc_cpu_do_system_reset(CPUState *cs, target_ulong vector) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET); + if (vector != -1) { + env->nip = vector; + } +} + +void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + target_ulong msr = 0; + + /* + * Set MSR and NIP for the handler, SRR0/1, DAR and DSISR have already + * been set by KVM. + */ + msr = (1ULL << MSR_ME); + msr |= env->msr & (1ULL << MSR_SF); + if (!(*pcc->interrupts_big_endian)(cpu)) { + msr |= (1ULL << MSR_LE); + } + + powerpc_set_excp_state(cpu, vector, msr); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c index 823759c92e..eb362dd9ae 100644 --- a/target/ppc/gdbstub.c +++ b/target/ppc/gdbstub.c @@ -114,10 +114,11 @@ void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len) * the FP regs zero size when talking to a newer gdb. */ -int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int ppc_cpu_gdb_read_register(CPUState *cs, GByteArray *buf, int n) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; + uint8_t *mem_buf; int r = ppc_gdb_register_len(n); if (!r) { @@ -126,17 +127,17 @@ int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) if (n < 32) { /* gprs */ - gdb_get_regl(mem_buf, env->gpr[n]); + gdb_get_regl(buf, env->gpr[n]); } else if (n < 64) { /* fprs */ - stfq_p(mem_buf, *cpu_fpr_ptr(env, n - 32)); + gdb_get_reg64(buf, *cpu_fpr_ptr(env, n - 32)); } else { switch (n) { case 64: - gdb_get_regl(mem_buf, env->nip); + gdb_get_regl(buf, env->nip); break; case 65: - gdb_get_regl(mem_buf, env->msr); + gdb_get_regl(buf, env->msr); break; case 66: { @@ -145,31 +146,33 @@ int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) for (i = 0; i < 8; i++) { cr |= env->crf[i] << (32 - ((i + 1) * 4)); } - gdb_get_reg32(mem_buf, cr); + gdb_get_reg32(buf, cr); break; } case 67: - gdb_get_regl(mem_buf, env->lr); + gdb_get_regl(buf, env->lr); break; case 68: - gdb_get_regl(mem_buf, env->ctr); + gdb_get_regl(buf, env->ctr); break; case 69: - gdb_get_reg32(mem_buf, env->xer); + gdb_get_reg32(buf, env->xer); break; case 70: - gdb_get_reg32(mem_buf, env->fpscr); + gdb_get_reg32(buf, env->fpscr); break; } } + mem_buf = buf->data + buf->len - r; ppc_maybe_bswap_register(env, mem_buf, r); return r; } -int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) +int ppc_cpu_gdb_read_register_apple(CPUState *cs, GByteArray *buf, int n) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; + uint8_t *mem_buf; int r = ppc_gdb_register_len_apple(n); if (!r) { @@ -178,21 +181,21 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) if (n < 32) { /* gprs */ - gdb_get_reg64(mem_buf, env->gpr[n]); + gdb_get_reg64(buf, env->gpr[n]); } else if (n < 64) { /* fprs */ - stfq_p(mem_buf, *cpu_fpr_ptr(env, n - 32)); + gdb_get_reg64(buf, *cpu_fpr_ptr(env, n - 32)); } else if (n < 96) { /* Altivec */ - stq_p(mem_buf, n - 64); - stq_p(mem_buf + 8, 0); + gdb_get_reg64(buf, n - 64); + gdb_get_reg64(buf, 0); } else { switch (n) { case 64 + 32: - gdb_get_reg64(mem_buf, env->nip); + gdb_get_reg64(buf, env->nip); break; case 65 + 32: - gdb_get_reg64(mem_buf, env->msr); + gdb_get_reg64(buf, env->msr); break; case 66 + 32: { @@ -201,23 +204,24 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) for (i = 0; i < 8; i++) { cr |= env->crf[i] << (32 - ((i + 1) * 4)); } - gdb_get_reg32(mem_buf, cr); + gdb_get_reg32(buf, cr); break; } case 67 + 32: - gdb_get_reg64(mem_buf, env->lr); + gdb_get_reg64(buf, env->lr); break; case 68 + 32: - gdb_get_reg64(mem_buf, env->ctr); + gdb_get_reg64(buf, env->ctr); break; case 69 + 32: - gdb_get_reg32(mem_buf, env->xer); + gdb_get_reg32(buf, env->xer); break; case 70 + 32: - gdb_get_reg64(mem_buf, env->fpscr); + gdb_get_reg64(buf, env->fpscr); break; } } + mem_buf = buf->data + buf->len - r; ppc_maybe_bswap_register(env, mem_buf, r); return r; } diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 7f44b1aa1a..597f72be1b 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -2113,7 +2113,7 @@ void kvmppc_error_append_smt_possible_hint(Error *const *errp) #ifdef TARGET_PPC64 -uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift) +uint64_t kvmppc_vrma_limit(unsigned int hash_shift) { struct kvm_ppc_smmu_info info; long rampagesize, best_page_shift; @@ -2140,8 +2140,7 @@ uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift) } } - return MIN(current_size, - 1ULL << (best_page_shift + hash_shift - 7)); + return 1ULL << (best_page_shift + hash_shift - 7); } #endif diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 9e4f2357cc..332fa0aa1c 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -47,7 +47,7 @@ void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift, int *pfd, bool need_vfio); 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); +uint64_t kvmppc_vrma_limit(unsigned int hash_shift); bool kvmppc_has_cap_spapr_vfio(void); #endif /* !CONFIG_USER_ONLY */ bool kvmppc_has_cap_epr(void); @@ -255,10 +255,9 @@ static inline int kvmppc_reset_htab(int shift_hint) return 0; } -static inline uint64_t kvmppc_rma_size(uint64_t current_size, - unsigned int hash_shift) +static inline uint64_t kvmppc_vrma_limit(unsigned int hash_shift) { - return ram_size; + g_assert_not_reached(); } static inline bool kvmppc_hpt_needs_host_contiguous_pages(void) diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index da8966ccf5..34f6009b1e 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -18,6 +18,7 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" @@ -668,6 +669,21 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, return 0; } +static bool ppc_hash64_use_vrma(CPUPPCState *env) +{ + switch (env->mmu_model) { + case POWERPC_MMU_3_00: + /* + * ISAv3.0 (POWER9) always uses VRMA, the VPM0 field and RMOR + * register no longer exist + */ + return true; + + default: + return !!(env->spr[SPR_LPCR] & LPCR_VPM0); + } +} + static void ppc_hash64_set_isi(CPUState *cs, uint64_t error_code) { CPUPPCState *env = &POWERPC_CPU(cs)->env; @@ -676,15 +692,7 @@ static void ppc_hash64_set_isi(CPUState *cs, uint64_t error_code) if (msr_ir) { vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1); } else { - switch (env->mmu_model) { - case POWERPC_MMU_3_00: - /* Field deprecated in ISAv3.00 - interrupts always go to hyperv */ - vpm = true; - break; - default: - vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0); - break; - } + vpm = ppc_hash64_use_vrma(env); } if (vpm && !msr_hv) { cs->exception_index = POWERPC_EXCP_HISI; @@ -702,15 +710,7 @@ static void ppc_hash64_set_dsi(CPUState *cs, uint64_t dar, uint64_t dsisr) if (msr_dr) { vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM1); } else { - switch (env->mmu_model) { - case POWERPC_MMU_3_00: - /* Field deprecated in ISAv3.00 - interrupts always go to hyperv */ - vpm = true; - break; - default: - vpm = !!(env->spr[SPR_LPCR] & LPCR_VPM0); - break; - } + vpm = ppc_hash64_use_vrma(env); } if (vpm && !msr_hv) { cs->exception_index = POWERPC_EXCP_HDSI; @@ -758,11 +758,67 @@ static void ppc_hash64_set_c(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte1) stb_phys(CPU(cpu)->as, base + offset, (pte1 & 0xff) | 0x80); } +static target_ulong rmls_limit(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + /* + * In theory the meanings of RMLS values are implementation + * dependent. In practice, this seems to have been the set from + * POWER4+..POWER8, and RMLS is no longer supported in POWER9. + * + * Unsupported values mean the OS has shot itself in the + * foot. Return a 0-sized RMA in this case, which we expect + * to trigger an immediate DSI or ISI + */ + static const target_ulong rma_sizes[16] = { + [0] = 256 * GiB, + [1] = 16 * GiB, + [2] = 1 * GiB, + [3] = 64 * MiB, + [4] = 256 * MiB, + [7] = 128 * MiB, + [8] = 32 * MiB, + }; + target_ulong rmls = (env->spr[SPR_LPCR] & LPCR_RMLS) >> LPCR_RMLS_SHIFT; + + return rma_sizes[rmls]; +} + +static int build_vrma_slbe(PowerPCCPU *cpu, ppc_slb_t *slb) +{ + CPUPPCState *env = &cpu->env; + target_ulong lpcr = env->spr[SPR_LPCR]; + uint32_t vrmasd = (lpcr & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT; + target_ulong vsid = SLB_VSID_VRMA | ((vrmasd << 4) & SLB_VSID_LLP_MASK); + int i; + + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + const PPCHash64SegmentPageSizes *sps = &cpu->hash64_opts->sps[i]; + + if (!sps->page_shift) { + break; + } + + if ((vsid & SLB_VSID_LLP_MASK) == sps->slb_enc) { + slb->esid = SLB_ESID_V; + slb->vsid = vsid; + slb->sps = sps; + return 0; + } + } + + error_report("Bad page size encoding in LPCR[VRMASD]; LPCR=0x" + TARGET_FMT_lx"\n", lpcr); + + return -1; +} + int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; + ppc_slb_t vrma_slbe; ppc_slb_t *slb; unsigned apshift; hwaddr ptex; @@ -789,27 +845,32 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, */ raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL; - /* In HV mode, add HRMOR if top EA bit is clear */ - if (msr_hv || !env->has_hv_mode) { + if (cpu->vhyp) { + /* + * In virtual hypervisor mode, there's nothing to do: + * EA == GPA == qemu guest address + */ + } else if (msr_hv || !env->has_hv_mode) { + /* In HV mode, add HRMOR if top EA bit is clear */ if (!(eaddr >> 63)) { raddr |= env->spr[SPR_HRMOR]; } - } else { - /* Otherwise, check VPM for RMA vs VRMA */ - if (env->spr[SPR_LPCR] & LPCR_VPM0) { - slb = &env->vrma_slb; - if (slb->sps) { - goto skip_slb_search; - } - /* Not much else to do here */ + } else if (ppc_hash64_use_vrma(env)) { + /* Emulated VRMA mode */ + slb = &vrma_slbe; + if (build_vrma_slbe(cpu, slb) != 0) { + /* Invalid VRMA setup, machine check */ cs->exception_index = POWERPC_EXCP_MCHECK; env->error_code = 0; return 1; - } else if (raddr < env->rmls) { - /* RMA. Check bounds in RMLS */ - raddr |= env->spr[SPR_RMOR]; - } else { - /* The access failed, generate the approriate interrupt */ + } + + goto skip_slb_search; + } else { + target_ulong limit = rmls_limit(cpu); + + /* Emulated old-style RMO mode, bounds check against RMLS */ + if (raddr >= limit) { if (rwx == 2) { ppc_hash64_set_isi(cs, SRR1_PROTFAULT); } else { @@ -821,6 +882,8 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, } return 1; } + + raddr |= env->spr[SPR_RMOR]; } tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx, @@ -943,6 +1006,7 @@ skip_slb_search: hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr) { CPUPPCState *env = &cpu->env; + ppc_slb_t vrma_slbe; ppc_slb_t *slb; hwaddr ptex, raddr; ppc_hash_pte64_t pte; @@ -953,22 +1017,29 @@ hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr) /* In real mode the top 4 effective address bits are ignored */ raddr = addr & 0x0FFFFFFFFFFFFFFFULL; - /* In HV mode, add HRMOR if top EA bit is clear */ - if ((msr_hv || !env->has_hv_mode) && !(addr >> 63)) { + if (cpu->vhyp) { + /* + * In virtual hypervisor mode, there's nothing to do: + * EA == GPA == qemu guest address + */ + return raddr; + } else if ((msr_hv || !env->has_hv_mode) && !(addr >> 63)) { + /* In HV mode, add HRMOR if top EA bit is clear */ return raddr | env->spr[SPR_HRMOR]; - } + } else if (ppc_hash64_use_vrma(env)) { + /* Emulated VRMA mode */ + slb = &vrma_slbe; + if (build_vrma_slbe(cpu, slb) != 0) { + return -1; + } + } else { + target_ulong limit = rmls_limit(cpu); - /* Otherwise, check VPM for RMA vs VRMA */ - if (env->spr[SPR_LPCR] & LPCR_VPM0) { - slb = &env->vrma_slb; - if (!slb->sps) { + /* Emulated old-style RMO mode, bounds check against RMLS */ + if (raddr >= limit) { return -1; } - } else if (raddr < env->rmls) { - /* RMA. Check bounds in RMLS */ return raddr | env->spr[SPR_RMOR]; - } else { - return -1; } } else { slb = slb_lookup(cpu, addr); @@ -997,168 +1068,12 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong ptex, cpu->env.tlb_need_flush = TLB_NEED_GLOBAL_FLUSH | TLB_NEED_LOCAL_FLUSH; } -static void ppc_hash64_update_rmls(PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - uint64_t lpcr = env->spr[SPR_LPCR]; - - /* - * This is the full 4 bits encoding of POWER8. Previous - * CPUs only support a subset of these but the filtering - * is done when writing LPCR - */ - switch ((lpcr & LPCR_RMLS) >> LPCR_RMLS_SHIFT) { - case 0x8: /* 32MB */ - env->rmls = 0x2000000ull; - break; - case 0x3: /* 64MB */ - env->rmls = 0x4000000ull; - break; - case 0x7: /* 128MB */ - env->rmls = 0x8000000ull; - break; - case 0x4: /* 256MB */ - env->rmls = 0x10000000ull; - break; - case 0x2: /* 1GB */ - env->rmls = 0x40000000ull; - break; - case 0x1: /* 16GB */ - env->rmls = 0x400000000ull; - break; - default: - /* What to do here ??? */ - env->rmls = 0; - } -} - -static void ppc_hash64_update_vrma(PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - const PPCHash64SegmentPageSizes *sps = NULL; - target_ulong esid, vsid, lpcr; - ppc_slb_t *slb = &env->vrma_slb; - uint32_t vrmasd; - int i; - - /* First clear it */ - slb->esid = slb->vsid = 0; - slb->sps = NULL; - - /* Is VRMA enabled ? */ - lpcr = env->spr[SPR_LPCR]; - if (!(lpcr & LPCR_VPM0)) { - return; - } - - /* - * Make one up. Mostly ignore the ESID which will not be needed - * for translation - */ - vsid = SLB_VSID_VRMA; - vrmasd = (lpcr & LPCR_VRMASD) >> LPCR_VRMASD_SHIFT; - vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP); - esid = SLB_ESID_V; - - for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - const PPCHash64SegmentPageSizes *sps1 = &cpu->hash64_opts->sps[i]; - - if (!sps1->page_shift) { - break; - } - - if ((vsid & SLB_VSID_LLP_MASK) == sps1->slb_enc) { - sps = sps1; - break; - } - } - - if (!sps) { - error_report("Bad page size encoding esid 0x"TARGET_FMT_lx - " vsid 0x"TARGET_FMT_lx, esid, vsid); - return; - } - - slb->vsid = vsid; - slb->esid = esid; - slb->sps = sps; -} - void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val) { + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; - uint64_t lpcr = 0; - - /* Filter out bits */ - switch (env->mmu_model) { - case POWERPC_MMU_64B: /* 970 */ - if (val & 0x40) { - lpcr |= LPCR_LPES0; - } - if (val & 0x8000000000000000ull) { - lpcr |= LPCR_LPES1; - } - if (val & 0x20) { - lpcr |= (0x4ull << LPCR_RMLS_SHIFT); - } - if (val & 0x4000000000000000ull) { - lpcr |= (0x2ull << LPCR_RMLS_SHIFT); - } - if (val & 0x2000000000000000ull) { - lpcr |= (0x1ull << LPCR_RMLS_SHIFT); - } - env->spr[SPR_RMOR] = ((lpcr >> 41) & 0xffffull) << 26; - /* - * XXX We could also write LPID from HID4 here - * but since we don't tag any translation on it - * it doesn't actually matter - * - * XXX For proper emulation of 970 we also need - * to dig HRMOR out of HID5 - */ - break; - case POWERPC_MMU_2_03: /* P5p */ - lpcr = val & (LPCR_RMLS | LPCR_ILE | - LPCR_LPES0 | LPCR_LPES1 | - LPCR_RMI | LPCR_HDICE); - break; - case POWERPC_MMU_2_06: /* P7 */ - lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD | - LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | - LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 | - LPCR_MER | LPCR_TC | - LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE); - break; - case POWERPC_MMU_2_07: /* P8 */ - lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | - LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | - LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 | - LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 | - LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE); - break; - case POWERPC_MMU_3_00: /* P9 */ - lpcr = val & (LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | - (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | - LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD | - (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE | - LPCR_DEE | LPCR_OEE)) | LPCR_MER | LPCR_GTSE | LPCR_TC | - LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE); - /* - * If we have a virtual hypervisor, we need to bring back RMLS. It - * doesn't exist on an actual P9 but that's all we know how to - * configure with softmmu at the moment - */ - if (cpu->vhyp) { - lpcr |= (val & LPCR_RMLS); - } - break; - default: - ; - } - env->spr[SPR_LPCR] = lpcr; - ppc_hash64_update_rmls(cpu); - ppc_hash64_update_vrma(cpu); + env->spr[SPR_LPCR] = val & pcc->lpcr_mask; } void helper_store_lpcr(CPUPPCState *env, target_ulong val) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 36fa27367c..127c82a24e 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1938,15 +1938,17 @@ static void gen_rlwinm(DisasContext *ctx) me += 32; #endif mask = MASK(mb, me); - if (sh == 0) { - tcg_gen_andi_tl(t_ra, t_rs, mask); - } else if (mask <= 0xffffffffu) { - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(t0, t_rs); - tcg_gen_rotli_i32(t0, t0, sh); - tcg_gen_andi_i32(t0, t0, mask); - tcg_gen_extu_i32_tl(t_ra, t0); - tcg_temp_free_i32(t0); + if (mask <= 0xffffffffu) { + if (sh == 0) { + tcg_gen_andi_tl(t_ra, t_rs, mask); + } else { + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t0, t_rs); + tcg_gen_rotli_i32(t0, t0, sh); + tcg_gen_andi_i32(t0, t0, mask); + tcg_gen_extu_i32_tl(t_ra, t0); + tcg_temp_free_i32(t0); + } } else { #if defined(TARGET_PPC64) tcg_gen_deposit_i64(t_ra, t_rs, t_rs, 32, 32); diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c index 53995f62ea..e853164a86 100644 --- a/target/ppc/translate_init.inc.c +++ b/target/ppc/translate_init.inc.c @@ -7895,25 +7895,21 @@ static void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) { gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); } - -static void spr_write_970_hid4(DisasContext *ctx, int sprn, int gprn) -{ -#if defined(TARGET_PPC64) - spr_write_generic(ctx, sprn, gprn); - gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); -#endif -} - #endif /* !defined(CONFIG_USER_ONLY) */ static void gen_spr_970_lpar(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) - /* Logical partitionning */ - /* PPC970: HID4 is effectively the LPCR */ + /* + * PPC970: HID4 covers things later controlled by the LPCR and + * RMOR in later CPUs, but with a different encoding. We only + * support the 970 in "Apple mode" which has all hypervisor + * facilities disabled by strapping, so we can basically just + * ignore it + */ spr_register(env, SPR_970_HID4, "HID4", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_970_hid4, + &spr_read_generic, &spr_write_generic, 0x00000000); #endif } @@ -8019,12 +8015,16 @@ static void gen_spr_book3s_ids(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); - spr_register_hv(env, SPR_RMOR, "RMOR", + spr_register_hv(env, SPR_HRMOR, "HRMOR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); - spr_register_hv(env, SPR_HRMOR, "HRMOR", +} + +static void gen_spr_rmor(CPUPPCState *env) +{ + spr_register_hv(env, SPR_RMOR, "RMOR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, @@ -8476,6 +8476,8 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) (1ull << MSR_DR) | (1ull << MSR_PMM) | (1ull << MSR_RI); + pcc->lpcr_mask = LPCR_RMLS | LPCR_ILE | LPCR_LPES0 | LPCR_LPES1 | + LPCR_RMI | LPCR_HDICE; pcc->mmu_model = POWERPC_MMU_2_03; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; @@ -8492,44 +8494,6 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x10000; } -/* - * The CPU used to have a "compat" property which set the - * compatibility mode PVR. However, this was conceptually broken - it - * only makes sense on the pseries machine type (otherwise the guest - * owns the PCR and can control the compatibility mode itself). It's - * been replaced with the 'max-cpu-compat' property on the pseries - * machine type. For backwards compatibility, pseries specially - * parses the -cpu parameter and converts old compat= parameters into - * the appropriate machine parameters. This stub implementation of - * the parameter catches any uses on explicitly created CPUs. - */ -static void getset_compat_deprecated(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - QNull *null = NULL; - - if (!qtest_enabled()) { - warn_report("CPU 'compat' property is deprecated and has no effect; " - "use max-cpu-compat machine property instead"); - } - visit_type_null(v, name, &null, NULL); - qobject_unref(null); -} - -static const PropertyInfo ppc_compat_deprecated_propinfo = { - .name = "str", - .description = "compatibility mode (deprecated)", - .get = getset_compat_deprecated, - .set = getset_compat_deprecated, -}; -static Property powerpc_servercpu_properties[] = { - { - .name = "compat", - .info = &ppc_compat_deprecated_propinfo, - }, - DEFINE_PROP_END_OF_LIST(), -}; - static void init_proc_POWER7(CPUPPCState *env) { /* Common Registers */ @@ -8539,6 +8503,7 @@ static void init_proc_POWER7(CPUPPCState *env) /* POWER7 Specific Registers */ gen_spr_book3s_ids(env); + gen_spr_rmor(env); gen_spr_amr(env); gen_spr_book3s_purr(env); gen_spr_power5p_common(env); @@ -8611,7 +8576,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER7"; dc->desc = "POWER7"; - device_class_set_props(dc, powerpc_servercpu_properties); pcc->pvr_match = ppc_pvr_match_power7; pcc->pcr_mask = PCR_VEC_DIS | PCR_VSX_DIS | PCR_COMPAT_2_05; pcc->pcr_supported = PCR_COMPAT_2_06 | PCR_COMPAT_2_05; @@ -8652,6 +8616,12 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) (1ull << MSR_PMM) | (1ull << MSR_RI) | (1ull << MSR_LE); + pcc->lpcr_mask = LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD | + LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | + LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 | + LPCR_MER | LPCR_TC | + LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE; + pcc->lpcr_pm = LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2; pcc->mmu_model = POWERPC_MMU_2_06; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; @@ -8668,7 +8638,6 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; - pcc->lpcr_pm = LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2; } static void init_proc_POWER8(CPUPPCState *env) @@ -8680,6 +8649,7 @@ static void init_proc_POWER8(CPUPPCState *env) /* POWER8 Specific Registers */ gen_spr_book3s_ids(env); + gen_spr_rmor(env); gen_spr_amr(env); gen_spr_iamr(env); gen_spr_book3s_purr(env); @@ -8776,7 +8746,6 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER8"; dc->desc = "POWER8"; - device_class_set_props(dc, powerpc_servercpu_properties); pcc->pvr_match = ppc_pvr_match_power8; pcc->pcr_mask = PCR_TM_DIS | PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->pcr_supported = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05; @@ -8804,7 +8773,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | PPC2_TM | PPC2_PM_ISA206; pcc->msr_mask = (1ull << MSR_SF) | - (1ull << MSR_SHV) | + (1ull << MSR_HV) | (1ull << MSR_TM) | (1ull << MSR_VR) | (1ull << MSR_VSX) | @@ -8823,6 +8792,13 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) (1ull << MSR_TS0) | (1ull << MSR_TS1) | (1ull << MSR_LE); + pcc->lpcr_mask = LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | + LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | + LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 | + LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 | + LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE; + pcc->lpcr_pm = LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 | + LPCR_P8_PECE3 | LPCR_P8_PECE4; pcc->mmu_model = POWERPC_MMU_2_07; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; @@ -8840,8 +8816,6 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; - pcc->lpcr_pm = LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 | - LPCR_P8_PECE3 | LPCR_P8_PECE4; } #ifdef CONFIG_SOFTMMU @@ -8988,7 +8962,6 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER9"; dc->desc = "POWER9"; - device_class_set_props(dc, powerpc_servercpu_properties); pcc->pvr_match = ppc_pvr_match_power9; pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07; pcc->pcr_supported = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | @@ -9017,7 +8990,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL; pcc->msr_mask = (1ull << MSR_SF) | - (1ull << MSR_SHV) | + (1ull << MSR_HV) | (1ull << MSR_TM) | (1ull << MSR_VR) | (1ull << MSR_VSX) | @@ -9034,6 +9007,14 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) (1ull << MSR_PMM) | (1ull << MSR_RI) | (1ull << MSR_LE); + pcc->lpcr_mask = LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | + (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | + LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD | + (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE | + LPCR_DEE | LPCR_OEE)) + | LPCR_MER | LPCR_GTSE | LPCR_TC | + LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE; + pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; pcc->mmu_model = POWERPC_MMU_3_00; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault; @@ -9053,7 +9034,6 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; - pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; } #ifdef CONFIG_SOFTMMU @@ -9198,7 +9178,6 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER10"; dc->desc = "POWER10"; - device_class_set_props(dc, powerpc_servercpu_properties); pcc->pvr_match = ppc_pvr_match_power10; pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07 | PCR_COMPAT_3_00; @@ -9228,7 +9207,7 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL; pcc->msr_mask = (1ull << MSR_SF) | - (1ull << MSR_SHV) | + (1ull << MSR_HV) | (1ull << MSR_TM) | (1ull << MSR_VR) | (1ull << MSR_VSX) | @@ -9245,6 +9224,14 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) (1ull << MSR_PMM) | (1ull << MSR_RI) | (1ull << MSR_LE); + pcc->lpcr_mask = LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | + (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | + LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD | + (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE | + LPCR_DEE | LPCR_OEE)) + | LPCR_MER | LPCR_GTSE | LPCR_TC | + LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE; + pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; pcc->mmu_model = POWERPC_MMU_3_00; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault; @@ -9263,7 +9250,6 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr; - pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; } #if !defined(CONFIG_USER_ONLY) @@ -9857,7 +9843,7 @@ static int gdb_find_spr_idx(CPUPPCState *env, int n) return -1; } -static int gdb_get_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_spr_reg(CPUPPCState *env, GByteArray *buf, int n) { int reg; int len; @@ -9868,8 +9854,8 @@ static int gdb_get_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) } len = TARGET_LONG_SIZE; - stn_p(mem_buf, len, env->spr[reg]); - ppc_maybe_bswap_register(env, mem_buf, len); + gdb_get_regl(buf, env->spr[reg]); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, len), len); return len; } @@ -9891,15 +9877,18 @@ static int gdb_set_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) } #endif -static int gdb_get_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_float_reg(CPUPPCState *env, GByteArray *buf, int n) { + uint8_t *mem_buf; if (n < 32) { - stfq_p(mem_buf, *cpu_fpr_ptr(env, n)); + gdb_get_reg64(buf, *cpu_fpr_ptr(env, n)); + mem_buf = gdb_get_reg_ptr(buf, 8); ppc_maybe_bswap_register(env, mem_buf, 8); return 8; } if (n == 32) { - stl_p(mem_buf, env->fpscr); + gdb_get_reg32(buf, env->fpscr); + mem_buf = gdb_get_reg_ptr(buf, 4); ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } @@ -9921,28 +9910,31 @@ static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 0; } -static int gdb_get_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_avr_reg(CPUPPCState *env, GByteArray *buf, int n) { + uint8_t *mem_buf; + if (n < 32) { ppc_avr_t *avr = cpu_avr_ptr(env, n); if (!avr_need_swap(env)) { - stq_p(mem_buf, avr->u64[0]); - stq_p(mem_buf + 8, avr->u64[1]); + gdb_get_reg128(buf, avr->u64[0] , avr->u64[1]); } else { - stq_p(mem_buf, avr->u64[1]); - stq_p(mem_buf + 8, avr->u64[0]); + gdb_get_reg128(buf, avr->u64[1] , avr->u64[0]); } + mem_buf = gdb_get_reg_ptr(buf, 16); ppc_maybe_bswap_register(env, mem_buf, 8); ppc_maybe_bswap_register(env, mem_buf + 8, 8); return 16; } if (n == 32) { - stl_p(mem_buf, helper_mfvscr(env)); + gdb_get_reg32(buf, helper_mfvscr(env)); + mem_buf = gdb_get_reg_ptr(buf, 4); ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } if (n == 33) { - stl_p(mem_buf, (uint32_t)env->spr[SPR_VRSAVE]); + gdb_get_reg32(buf, (uint32_t)env->spr[SPR_VRSAVE]); + mem_buf = gdb_get_reg_ptr(buf, 4); ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } @@ -9977,25 +9969,25 @@ static int gdb_set_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 0; } -static int gdb_get_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_spe_reg(CPUPPCState *env, GByteArray *buf, int n) { if (n < 32) { #if defined(TARGET_PPC64) - stl_p(mem_buf, env->gpr[n] >> 32); - ppc_maybe_bswap_register(env, mem_buf, 4); + gdb_get_reg32(buf, env->gpr[n] >> 32); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4); #else - stl_p(mem_buf, env->gprh[n]); + gdb_get_reg32(buf, env->gprh[n]); #endif return 4; } if (n == 32) { - stq_p(mem_buf, env->spe_acc); - ppc_maybe_bswap_register(env, mem_buf, 8); + gdb_get_reg64(buf, env->spe_acc); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8); return 8; } if (n == 33) { - stl_p(mem_buf, env->spe_fscr); - ppc_maybe_bswap_register(env, mem_buf, 4); + gdb_get_reg32(buf, env->spe_fscr); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4); return 4; } return 0; @@ -10030,11 +10022,11 @@ static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) return 0; } -static int gdb_get_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +static int gdb_get_vsx_reg(CPUPPCState *env, GByteArray *buf, int n) { if (n < 32) { - stq_p(mem_buf, *cpu_vsrl_ptr(env, n)); - ppc_maybe_bswap_register(env, mem_buf, 8); + gdb_get_reg64(buf, *cpu_vsrl_ptr(env, n)); + ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8); return 8; } return 0; @@ -10486,6 +10478,8 @@ static void ppc_cpu_parse_featurestr(const char *type, char *features, *s = '\0'; for (i = 0; inpieces[i]; i++) { if (g_str_has_prefix(inpieces[i], "compat=")) { + warn_report_once("CPU 'compat' property is deprecated; " + "use max-cpu-compat machine property instead"); compat_str = inpieces[i]; continue; } @@ -10669,16 +10663,16 @@ static bool ppc_cpu_has_work(CPUState *cs) return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD); } -/* CPUClass::reset() */ -static void ppc_cpu_reset(CPUState *s) +static void ppc_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); PowerPCCPU *cpu = POWERPC_CPU(s); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; target_ulong msr; int i; - pcc->parent_reset(s); + pcc->parent_reset(dev); msr = (target_ulong)0; msr |= (target_ulong)MSR_HVB; @@ -10885,7 +10879,7 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_always; device_class_set_props(dc, ppc_cpu_properties); - cpu_class_set_parent_reset(cc, ppc_cpu_reset, &pcc->parent_reset); + device_class_set_parent_reset(dc, ppc_cpu_reset, &pcc->parent_reset); cc->class_by_name = ppc_cpu_class_by_name; pcc->parent_parse_features = cc->parse_features; diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index c0b7023100..4e578239d3 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -330,13 +330,14 @@ void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb, env->pc = data[0]; } -static void riscv_cpu_reset(CPUState *cs) +static void riscv_cpu_reset(DeviceState *dev) { + CPUState *cs = CPU(dev); RISCVCPU *cpu = RISCV_CPU(cs); RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); CPURISCVState *env = &cpu->env; - mcc->parent_reset(cs); + mcc->parent_reset(dev); #ifndef CONFIG_USER_ONLY env->priv = PRV_M; env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV); @@ -511,7 +512,7 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) device_class_set_parent_realize(dc, riscv_cpu_realize, &mcc->parent_realize); - cpu_class_set_parent_reset(cc, riscv_cpu_reset, &mcc->parent_reset); + device_class_set_parent_reset(dc, riscv_cpu_reset, &mcc->parent_reset); cc->class_by_name = riscv_cpu_class_by_name; cc->has_work = riscv_cpu_has_work; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 3dcdf92227..7d21addbab 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -234,7 +234,7 @@ typedef struct RISCVCPUClass { CPUClass parent_class; /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } RISCVCPUClass; /** @@ -293,7 +293,7 @@ extern const char * const riscv_excp_names[]; extern const char * const riscv_intr_names[]; void riscv_cpu_do_interrupt(CPUState *cpu); -int riscv_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int riscv_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request); bool riscv_cpu_fp_enabled(CPURISCVState *env); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 5ea5d133aa..d3ba9efb02 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -46,7 +46,7 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env) target_ulong pending = env->mip & env->mie & ~(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); target_ulong vspending = (env->mip & env->mie & - (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) >> 1; + (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)); target_ulong mie = env->priv < PRV_M || (env->priv == PRV_M && mstatus_mie); @@ -907,6 +907,13 @@ void riscv_cpu_do_interrupt(CPUState *cs) if (riscv_cpu_virt_enabled(env) && ((hdeleg >> cause) & 1) && !force_hs_execp) { + /* + * See if we need to adjust cause. Yes if its VS mode interrupt + * no if hypervisor has delegated one of hs mode's interrupt + */ + if (cause == IRQ_VS_TIMER || cause == IRQ_VS_SOFT || + cause == IRQ_VS_EXT) + cause = cause - 1; /* Trap to VS mode */ } else if (riscv_cpu_virt_enabled(env)) { /* Trap into HS mode, from virt */ diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index 2f32750f2f..eba12a86f2 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -270,7 +270,7 @@ static int csr_register_map[] = { CSR_MHCOUNTEREN, }; -int riscv_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int riscv_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; @@ -301,14 +301,14 @@ int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return 0; } -static int riscv_gdb_get_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) +static int riscv_gdb_get_fpu(CPURISCVState *env, GByteArray *buf, int n) { if (n < 32) { if (env->misa & RVD) { - return gdb_get_reg64(mem_buf, env->fpr[n]); + return gdb_get_reg64(buf, env->fpr[n]); } if (env->misa & RVF) { - return gdb_get_reg32(mem_buf, env->fpr[n]); + return gdb_get_reg32(buf, env->fpr[n]); } /* there is hole between ft11 and fflags in fpu.xml */ } else if (n < 36 && n > 32) { @@ -322,7 +322,7 @@ static int riscv_gdb_get_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) result = riscv_csrrw_debug(env, n - 33 + csr_register_map[8], &val, 0, 0); if (result == 0) { - return gdb_get_regl(mem_buf, val); + return gdb_get_regl(buf, val); } } return 0; @@ -351,7 +351,7 @@ static int riscv_gdb_set_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) return 0; } -static int riscv_gdb_get_csr(CPURISCVState *env, uint8_t *mem_buf, int n) +static int riscv_gdb_get_csr(CPURISCVState *env, GByteArray *buf, int n) { if (n < ARRAY_SIZE(csr_register_map)) { target_ulong val = 0; @@ -359,7 +359,7 @@ static int riscv_gdb_get_csr(CPURISCVState *env, uint8_t *mem_buf, int n) result = riscv_csrrw_debug(env, csr_register_map[n], &val, 0, 0); if (result == 0) { - return gdb_get_regl(mem_buf, val); + return gdb_get_regl(buf, val); } } return 0; @@ -379,13 +379,13 @@ static int riscv_gdb_set_csr(CPURISCVState *env, uint8_t *mem_buf, int n) return 0; } -static int riscv_gdb_get_virtual(CPURISCVState *cs, uint8_t *mem_buf, int n) +static int riscv_gdb_get_virtual(CPURISCVState *cs, GByteArray *buf, int n) { if (n == 0) { #ifdef CONFIG_USER_ONLY - return gdb_get_regl(mem_buf, 0); + return gdb_get_regl(buf, 0); #else - return gdb_get_regl(mem_buf, cs->priv); + return gdb_get_regl(buf, cs->priv); #endif } return 0; diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 8736f689c2..c6412f680c 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -85,7 +85,7 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) } if (env->priv_ver >= PRIV_VERSION_1_10_0 && - get_field(env->mstatus, MSTATUS_TSR)) { + get_field(env->mstatus, MSTATUS_TSR) && !(env->priv >= PRV_M)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } diff --git a/target/rx/Makefile.objs b/target/rx/Makefile.objs new file mode 100644 index 0000000000..a0018d5bc5 --- /dev/null +++ b/target/rx/Makefile.objs @@ -0,0 +1,11 @@ +obj-y += translate.o op_helper.o helper.o cpu.o gdbstub.o disas.o + +DECODETREE = $(SRC_PATH)/scripts/decodetree.py + +target/rx/decode.inc.c: \ + $(SRC_PATH)/target/rx/insns.decode $(DECODETREE) + $(call quiet-command,\ + $(PYTHON) $(DECODETREE) --varinsnwidth 32 -o $@ $<, "GEN", $(TARGET_DIR)$@) + +target/rx/translate.o: target/rx/decode.inc.c +target/rx/disas.o: target/rx/decode.inc.c diff --git a/target/rx/cpu-param.h b/target/rx/cpu-param.h new file mode 100644 index 0000000000..b156ad1ca0 --- /dev/null +++ b/target/rx/cpu-param.h @@ -0,0 +1,30 @@ +/* + * RX cpu parameters + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef RX_CPU_PARAM_H +#define RX_CPU_PARAM_H + +#define TARGET_LONG_BITS 32 +#define TARGET_PAGE_BITS 12 + +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + +#define NB_MMU_MODES 1 + +#endif diff --git a/target/rx/cpu-qom.h b/target/rx/cpu-qom.h new file mode 100644 index 0000000000..3e81856ef5 --- /dev/null +++ b/target/rx/cpu-qom.h @@ -0,0 +1,53 @@ +/* + * RX CPU + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef RX_CPU_QOM_H +#define RX_CPU_QOM_H + +#include "hw/core/cpu.h" + +#define TYPE_RX_CPU "rx-cpu" + +#define TYPE_RX62N_CPU RX_CPU_TYPE_NAME("rx62n") + +#define RXCPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(RXCPUClass, (klass), TYPE_RX_CPU) +#define RXCPU(obj) \ + OBJECT_CHECK(RXCPU, (obj), TYPE_RX_CPU) +#define RXCPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(RXCPUClass, (obj), TYPE_RX_CPU) + +/* + * RXCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * A RX CPU model. + */ +typedef struct RXCPUClass { + /*< private >*/ + CPUClass parent_class; + /*< public >*/ + + DeviceRealize parent_realize; + DeviceReset parent_reset; +} RXCPUClass; + +#define CPUArchState struct CPURXState + +#endif diff --git a/target/rx/cpu.c b/target/rx/cpu.c new file mode 100644 index 0000000000..219e05397b --- /dev/null +++ b/target/rx/cpu.c @@ -0,0 +1,225 @@ +/* + * QEMU RX CPU + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/qemu-print.h" +#include "qapi/error.h" +#include "cpu.h" +#include "qemu-common.h" +#include "migration/vmstate.h" +#include "exec/exec-all.h" +#include "hw/loader.h" +#include "fpu/softfloat.h" + +static void rx_cpu_set_pc(CPUState *cs, vaddr value) +{ + RXCPU *cpu = RXCPU(cs); + + cpu->env.pc = value; +} + +static void rx_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ + RXCPU *cpu = RXCPU(cs); + + cpu->env.pc = tb->pc; +} + +static bool rx_cpu_has_work(CPUState *cs) +{ + return cs->interrupt_request & + (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR); +} + +static void rx_cpu_reset(DeviceState *dev) +{ + RXCPU *cpu = RXCPU(dev); + RXCPUClass *rcc = RXCPU_GET_CLASS(cpu); + CPURXState *env = &cpu->env; + uint32_t *resetvec; + + rcc->parent_reset(dev); + + memset(env, 0, offsetof(CPURXState, end_reset_fields)); + + resetvec = rom_ptr(0xfffffffc, 4); + if (resetvec) { + /* In the case of kernel, it is ignored because it is not set. */ + env->pc = ldl_p(resetvec); + } + rx_cpu_unpack_psw(env, 0, 1); + env->regs[0] = env->isp = env->usp = 0; + env->fpsw = 0; + set_flush_to_zero(1, &env->fp_status); + set_flush_inputs_to_zero(1, &env->fp_status); +} + +static void rx_cpu_list_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + + qemu_printf(" %s\n", object_class_get_name(oc)); +} + +void rx_cpu_list(void) +{ + GSList *list; + list = object_class_get_list_sorted(TYPE_RX_CPU, false); + qemu_printf("Available CPUs:\n"); + g_slist_foreach(list, rx_cpu_list_entry, NULL); + g_slist_free(list); +} + +static ObjectClass *rx_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + + oc = object_class_by_name(cpu_model); + if (oc != NULL && object_class_dynamic_cast(oc, TYPE_RX_CPU) != NULL && + !object_class_is_abstract(oc)) { + return oc; + } + typename = g_strdup_printf(RX_CPU_TYPE_NAME("%s"), cpu_model); + oc = object_class_by_name(typename); + g_free(typename); + if (oc != NULL && object_class_is_abstract(oc)) { + oc = NULL; + } + + return oc; +} + +static void rx_cpu_realize(DeviceState *dev, Error **errp) +{ + CPUState *cs = CPU(dev); + RXCPUClass *rcc = RXCPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + qemu_init_vcpu(cs); + cpu_reset(cs); + + rcc->parent_realize(dev, errp); +} + +static void rx_cpu_set_irq(void *opaque, int no, int request) +{ + RXCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + int irq = request & 0xff; + + static const int mask[] = { + [RX_CPU_IRQ] = CPU_INTERRUPT_HARD, + [RX_CPU_FIR] = CPU_INTERRUPT_FIR, + }; + if (irq) { + cpu->env.req_irq = irq; + cpu->env.req_ipl = (request >> 8) & 0x0f; + cpu_interrupt(cs, mask[no]); + } else { + cpu_reset_interrupt(cs, mask[no]); + } +} + +static void rx_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->mach = bfd_mach_rx; + info->print_insn = print_insn_rx; +} + +static bool rx_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr) +{ + uint32_t address, physical, prot; + + /* Linear mapping */ + address = physical = addr & TARGET_PAGE_MASK; + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + tlb_set_page(cs, address, physical, prot, mmu_idx, TARGET_PAGE_SIZE); + return true; +} + +static void rx_cpu_init(Object *obj) +{ + CPUState *cs = CPU(obj); + RXCPU *cpu = RXCPU(obj); + CPURXState *env = &cpu->env; + + cpu_set_cpustate_pointers(cpu); + cs->env_ptr = env; + qdev_init_gpio_in(DEVICE(cpu), rx_cpu_set_irq, 2); +} + +static void rx_cpu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + CPUClass *cc = CPU_CLASS(klass); + RXCPUClass *rcc = RXCPU_CLASS(klass); + + device_class_set_parent_realize(dc, rx_cpu_realize, + &rcc->parent_realize); + device_class_set_parent_reset(dc, rx_cpu_reset, + &rcc->parent_reset); + + cc->class_by_name = rx_cpu_class_by_name; + cc->has_work = rx_cpu_has_work; + cc->do_interrupt = rx_cpu_do_interrupt; + cc->cpu_exec_interrupt = rx_cpu_exec_interrupt; + cc->dump_state = rx_cpu_dump_state; + cc->set_pc = rx_cpu_set_pc; + cc->synchronize_from_tb = rx_cpu_synchronize_from_tb; + cc->gdb_read_register = rx_cpu_gdb_read_register; + cc->gdb_write_register = rx_cpu_gdb_write_register; + cc->get_phys_page_debug = rx_cpu_get_phys_page_debug; + cc->disas_set_info = rx_cpu_disas_set_info; + cc->tcg_initialize = rx_translate_init; + cc->tlb_fill = rx_cpu_tlb_fill; + + cc->gdb_num_core_regs = 26; + cc->gdb_core_xml_file = "rx-core.xml"; +} + +static const TypeInfo rx_cpu_info = { + .name = TYPE_RX_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(RXCPU), + .instance_init = rx_cpu_init, + .abstract = true, + .class_size = sizeof(RXCPUClass), + .class_init = rx_cpu_class_init, +}; + +static const TypeInfo rx62n_rx_cpu_info = { + .name = TYPE_RX62N_CPU, + .parent = TYPE_RX_CPU, +}; + +static void rx_cpu_register_types(void) +{ + type_register_static(&rx_cpu_info); + type_register_static(&rx62n_rx_cpu_info); +} + +type_init(rx_cpu_register_types) diff --git a/target/rx/cpu.h b/target/rx/cpu.h new file mode 100644 index 0000000000..d1fb1ef3ca --- /dev/null +++ b/target/rx/cpu.h @@ -0,0 +1,180 @@ +/* + * RX emulation definition + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef RX_CPU_H +#define RX_CPU_H + +#include "qemu/bitops.h" +#include "qemu-common.h" +#include "hw/registerfields.h" +#include "cpu-qom.h" + +#include "exec/cpu-defs.h" + +/* PSW define */ +REG32(PSW, 0) +FIELD(PSW, C, 0, 1) +FIELD(PSW, Z, 1, 1) +FIELD(PSW, S, 2, 1) +FIELD(PSW, O, 3, 1) +FIELD(PSW, I, 16, 1) +FIELD(PSW, U, 17, 1) +FIELD(PSW, PM, 20, 1) +FIELD(PSW, IPL, 24, 4) + +/* FPSW define */ +REG32(FPSW, 0) +FIELD(FPSW, RM, 0, 2) +FIELD(FPSW, CV, 2, 1) +FIELD(FPSW, CO, 3, 1) +FIELD(FPSW, CZ, 4, 1) +FIELD(FPSW, CU, 5, 1) +FIELD(FPSW, CX, 6, 1) +FIELD(FPSW, CE, 7, 1) +FIELD(FPSW, CAUSE, 2, 6) +FIELD(FPSW, DN, 8, 1) +FIELD(FPSW, EV, 10, 1) +FIELD(FPSW, EO, 11, 1) +FIELD(FPSW, EZ, 12, 1) +FIELD(FPSW, EU, 13, 1) +FIELD(FPSW, EX, 14, 1) +FIELD(FPSW, ENABLE, 10, 5) +FIELD(FPSW, FV, 26, 1) +FIELD(FPSW, FO, 27, 1) +FIELD(FPSW, FZ, 28, 1) +FIELD(FPSW, FU, 29, 1) +FIELD(FPSW, FX, 30, 1) +FIELD(FPSW, FLAGS, 26, 4) +FIELD(FPSW, FS, 31, 1) + +enum { + NUM_REGS = 16, +}; + +typedef struct CPURXState { + /* CPU registers */ + uint32_t regs[NUM_REGS]; /* general registers */ + uint32_t psw_o; /* O bit of status register */ + uint32_t psw_s; /* S bit of status register */ + uint32_t psw_z; /* Z bit of status register */ + uint32_t psw_c; /* C bit of status register */ + uint32_t psw_u; + uint32_t psw_i; + uint32_t psw_pm; + uint32_t psw_ipl; + uint32_t bpsw; /* backup status */ + uint32_t bpc; /* backup pc */ + uint32_t isp; /* global base register */ + uint32_t usp; /* vector base register */ + uint32_t pc; /* program counter */ + uint32_t intb; /* interrupt vector */ + uint32_t fintv; + uint32_t fpsw; + uint64_t acc; + + /* Fields up to this point are cleared by a CPU reset */ + struct {} end_reset_fields; + + /* Internal use */ + uint32_t in_sleep; + uint32_t req_irq; /* Requested interrupt no (hard) */ + uint32_t req_ipl; /* Requested interrupt level */ + uint32_t ack_irq; /* execute irq */ + uint32_t ack_ipl; /* execute ipl */ + float_status fp_status; + qemu_irq ack; /* Interrupt acknowledge */ +} CPURXState; + +/* + * RXCPU: + * @env: #CPURXState + * + * A RX CPU + */ +struct RXCPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPUNegativeOffsetState neg; + CPURXState env; +}; + +typedef struct RXCPU RXCPU; +typedef RXCPU ArchCPU; + +#define ENV_OFFSET offsetof(RXCPU, env) + +#define RX_CPU_TYPE_SUFFIX "-" TYPE_RX_CPU +#define RX_CPU_TYPE_NAME(model) model RX_CPU_TYPE_SUFFIX +#define CPU_RESOLVING_TYPE TYPE_RX_CPU + +const char *rx_crname(uint8_t cr); +void rx_cpu_do_interrupt(CPUState *cpu); +bool rx_cpu_exec_interrupt(CPUState *cpu, int int_req); +void rx_cpu_dump_state(CPUState *cpu, FILE *f, int flags); +int rx_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); +int rx_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +hwaddr rx_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); + +void rx_translate_init(void); +int cpu_rx_signal_handler(int host_signum, void *pinfo, + void *puc); + +void rx_cpu_list(void); +void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte); + +#define cpu_signal_handler cpu_rx_signal_handler +#define cpu_list rx_cpu_list + +#include "exec/cpu-all.h" + +#define CPU_INTERRUPT_SOFT CPU_INTERRUPT_TGT_INT_0 +#define CPU_INTERRUPT_FIR CPU_INTERRUPT_TGT_INT_1 + +#define RX_CPU_IRQ 0 +#define RX_CPU_FIR 1 + +static inline void cpu_get_tb_cpu_state(CPURXState *env, target_ulong *pc, + target_ulong *cs_base, uint32_t *flags) +{ + *pc = env->pc; + *cs_base = 0; + *flags = FIELD_DP32(0, PSW, PM, env->psw_pm); +} + +static inline int cpu_mmu_index(CPURXState *env, bool ifetch) +{ + return 0; +} + +static inline uint32_t rx_cpu_pack_psw(CPURXState *env) +{ + uint32_t psw = 0; + psw = FIELD_DP32(psw, PSW, IPL, env->psw_ipl); + psw = FIELD_DP32(psw, PSW, PM, env->psw_pm); + psw = FIELD_DP32(psw, PSW, U, env->psw_u); + psw = FIELD_DP32(psw, PSW, I, env->psw_i); + psw = FIELD_DP32(psw, PSW, O, env->psw_o >> 31); + psw = FIELD_DP32(psw, PSW, S, env->psw_s >> 31); + psw = FIELD_DP32(psw, PSW, Z, env->psw_z == 0); + psw = FIELD_DP32(psw, PSW, C, env->psw_c); + return psw; +} + +#endif /* RX_CPU_H */ diff --git a/target/rx/disas.c b/target/rx/disas.c new file mode 100644 index 0000000000..6dee7a0342 --- /dev/null +++ b/target/rx/disas.c @@ -0,0 +1,1446 @@ +/* + * Renesas RX Disassembler + * + * Copyright (c) 2019 Yoshinori Sato <ysato@users.sourceforge.jp> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "disas/dis-asm.h" +#include "qemu/bitops.h" +#include "cpu.h" + +typedef struct DisasContext { + disassemble_info *dis; + uint32_t addr; + uint32_t pc; + uint8_t len; + uint8_t bytes[8]; +} DisasContext; + + +static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn, + int i, int n) +{ + uint32_t addr = ctx->addr; + + g_assert(ctx->len == i); + g_assert(n <= ARRAY_SIZE(ctx->bytes)); + + while (++i <= n) { + ctx->dis->read_memory_func(addr++, &ctx->bytes[i - 1], 1, ctx->dis); + insn |= ctx->bytes[i - 1] << (32 - i * 8); + } + ctx->addr = addr; + ctx->len = n; + + return insn; +} + +static int32_t li(DisasContext *ctx, int sz) +{ + uint32_t addr = ctx->addr; + uintptr_t len = ctx->len; + + switch (sz) { + case 1: + g_assert(len + 1 <= ARRAY_SIZE(ctx->bytes)); + ctx->addr += 1; + ctx->len += 1; + ctx->dis->read_memory_func(addr, ctx->bytes + len, 1, ctx->dis); + return (int8_t)ctx->bytes[len]; + case 2: + g_assert(len + 2 <= ARRAY_SIZE(ctx->bytes)); + ctx->addr += 2; + ctx->len += 2; + ctx->dis->read_memory_func(addr, ctx->bytes + len, 2, ctx->dis); + return ldsw_le_p(ctx->bytes + len); + case 3: + g_assert(len + 3 <= ARRAY_SIZE(ctx->bytes)); + ctx->addr += 3; + ctx->len += 3; + ctx->dis->read_memory_func(addr, ctx->bytes + len, 3, ctx->dis); + return (int8_t)ctx->bytes[len + 2] << 16 | lduw_le_p(ctx->bytes + len); + case 0: + g_assert(len + 4 <= ARRAY_SIZE(ctx->bytes)); + ctx->addr += 4; + ctx->len += 4; + ctx->dis->read_memory_func(addr, ctx->bytes + len, 4, ctx->dis); + return ldl_le_p(ctx->bytes + len); + default: + g_assert_not_reached(); + } +} + +static int bdsp_s(DisasContext *ctx, int d) +{ + /* + * 0 -> 8 + * 1 -> 9 + * 2 -> 10 + * 3 -> 3 + * : + * 7 -> 7 + */ + if (d < 3) { + d += 8; + } + return d; +} + +/* Include the auto-generated decoder. */ +#include "decode.inc.c" + +static void dump_bytes(DisasContext *ctx) +{ + int i, len = ctx->len; + + for (i = 0; i < len; ++i) { + ctx->dis->fprintf_func(ctx->dis->stream, "%02x ", ctx->bytes[i]); + } + ctx->dis->fprintf_func(ctx->dis->stream, "%*c", (8 - i) * 3, '\t'); +} + +#define prt(...) \ + do { \ + dump_bytes(ctx); \ + ctx->dis->fprintf_func(ctx->dis->stream, __VA_ARGS__); \ + } while (0) + +#define RX_MEMORY_BYTE 0 +#define RX_MEMORY_WORD 1 +#define RX_MEMORY_LONG 2 + +#define RX_IM_BYTE 0 +#define RX_IM_WORD 1 +#define RX_IM_LONG 2 +#define RX_IM_UWORD 3 + +static const char size[] = {'b', 'w', 'l'}; +static const char cond[][4] = { + "eq", "ne", "c", "nc", "gtu", "leu", "pz", "n", + "ge", "lt", "gt", "le", "o", "no", "ra", "f" +}; +static const char psw[] = { + 'c', 'z', 's', 'o', 0, 0, 0, 0, + 'i', 'u', 0, 0, 0, 0, 0, 0, +}; + +static void rx_index_addr(DisasContext *ctx, char out[8], int ld, int mi) +{ + uint32_t addr = ctx->addr; + uintptr_t len = ctx->len; + uint16_t dsp; + + switch (ld) { + case 0: + /* No index; return empty string. */ + out[0] = '\0'; + return; + case 1: + g_assert(len + 1 <= ARRAY_SIZE(ctx->bytes)); + ctx->addr += 1; + ctx->len += 1; + ctx->dis->read_memory_func(addr, ctx->bytes + len, 1, ctx->dis); + dsp = ctx->bytes[len]; + break; + case 2: + g_assert(len + 2 <= ARRAY_SIZE(ctx->bytes)); + ctx->addr += 2; + ctx->len += 2; + ctx->dis->read_memory_func(addr, ctx->bytes + len, 2, ctx->dis); + dsp = lduw_le_p(ctx->bytes + len); + break; + default: + g_assert_not_reached(); + } + + sprintf(out, "%u", dsp << (mi < 3 ? mi : 4 - mi)); +} + +static void prt_ldmi(DisasContext *ctx, const char *insn, + int ld, int mi, int rs, int rd) +{ + static const char sizes[][4] = {".b", ".w", ".l", ".uw", ".ub"}; + char dsp[8]; + + if (ld < 3) { + rx_index_addr(ctx, dsp, ld, mi); + prt("%s\t%s[r%d]%s, r%d", insn, dsp, rs, sizes[mi], rd); + } else { + prt("%s\tr%d, r%d", insn, rs, rd); + } +} + +static void prt_ir(DisasContext *ctx, const char *insn, int imm, int rd) +{ + if (imm < 0x100) { + prt("%s\t#%d, r%d", insn, imm, rd); + } else { + prt("%s\t#0x%08x, r%d", insn, imm, rd); + } +} + +/* mov.[bwl] rs,dsp:[rd] */ +static bool trans_MOV_rm(DisasContext *ctx, arg_MOV_rm *a) +{ + if (a->dsp > 0) { + prt("mov.%c\tr%d,%d[r%d]", + size[a->sz], a->rs, a->dsp << a->sz, a->rd); + } else { + prt("mov.%c\tr%d,[r%d]", + size[a->sz], a->rs, a->rd); + } + return true; +} + +/* mov.[bwl] dsp:[rs],rd */ +static bool trans_MOV_mr(DisasContext *ctx, arg_MOV_mr *a) +{ + if (a->dsp > 0) { + prt("mov.%c\t%d[r%d], r%d", + size[a->sz], a->dsp << a->sz, a->rs, a->rd); + } else { + prt("mov.%c\t[r%d], r%d", + size[a->sz], a->rs, a->rd); + } + return true; +} + +/* mov.l #uimm4,rd */ +/* mov.l #uimm8,rd */ +/* mov.l #imm,rd */ +static bool trans_MOV_ir(DisasContext *ctx, arg_MOV_ir *a) +{ + prt_ir(ctx, "mov.l", a->imm, a->rd); + return true; +} + +/* mov.[bwl] #uimm8,dsp:[rd] */ +/* mov #imm, dsp:[rd] */ +static bool trans_MOV_im(DisasContext *ctx, arg_MOV_im *a) +{ + if (a->dsp > 0) { + prt("mov.%c\t#%d,%d[r%d]", + size[a->sz], a->imm, a->dsp << a->sz, a->rd); + } else { + prt("mov.%c\t#%d,[r%d]", + size[a->sz], a->imm, a->rd); + } + return true; +} + +/* mov.[bwl] [ri,rb],rd */ +static bool trans_MOV_ar(DisasContext *ctx, arg_MOV_ar *a) +{ + prt("mov.%c\t[r%d,r%d], r%d", size[a->sz], a->ri, a->rb, a->rd); + return true; +} + +/* mov.[bwl] rd,[ri,rb] */ +static bool trans_MOV_ra(DisasContext *ctx, arg_MOV_ra *a) +{ + prt("mov.%c\tr%d, [r%d, r%d]", size[a->sz], a->rs, a->ri, a->rb); + return true; +} + + +/* mov.[bwl] dsp:[rs],dsp:[rd] */ +/* mov.[bwl] rs,dsp:[rd] */ +/* mov.[bwl] dsp:[rs],rd */ +/* mov.[bwl] rs,rd */ +static bool trans_MOV_mm(DisasContext *ctx, arg_MOV_mm *a) +{ + char dspd[8], dsps[8], szc = size[a->sz]; + + if (a->lds == 3 && a->ldd == 3) { + /* mov.[bwl] rs,rd */ + prt("mov.%c\tr%d, r%d", szc, a->rs, a->rd); + } else if (a->lds == 3) { + rx_index_addr(ctx, dspd, a->ldd, a->sz); + prt("mov.%c\tr%d, %s[r%d]", szc, a->rs, dspd, a->rd); + } else if (a->ldd == 3) { + rx_index_addr(ctx, dsps, a->lds, a->sz); + prt("mov.%c\t%s[r%d], r%d", szc, dsps, a->rs, a->rd); + } else { + rx_index_addr(ctx, dsps, a->lds, a->sz); + rx_index_addr(ctx, dspd, a->ldd, a->sz); + prt("mov.%c\t%s[r%d], %s[r%d]", szc, dsps, a->rs, dspd, a->rd); + } + return true; +} + +/* mov.[bwl] rs,[rd+] */ +/* mov.[bwl] rs,[-rd] */ +static bool trans_MOV_rp(DisasContext *ctx, arg_MOV_rp *a) +{ + if (a->ad) { + prt("mov.%c\tr%d, [-r%d]", size[a->sz], a->rs, a->rd); + } else { + prt("mov.%c\tr%d, [r%d+]", size[a->sz], a->rs, a->rd); + } + return true; +} + +/* mov.[bwl] [rd+],rs */ +/* mov.[bwl] [-rd],rs */ +static bool trans_MOV_pr(DisasContext *ctx, arg_MOV_pr *a) +{ + if (a->ad) { + prt("mov.%c\t[-r%d], r%d", size[a->sz], a->rd, a->rs); + } else { + prt("mov.%c\t[r%d+], r%d", size[a->sz], a->rd, a->rs); + } + return true; +} + +/* movu.[bw] dsp5:[rs],rd */ +static bool trans_MOVU_mr(DisasContext *ctx, arg_MOVU_mr *a) +{ + if (a->dsp > 0) { + prt("movu.%c\t%d[r%d], r%d", size[a->sz], + a->dsp << a->sz, a->rs, a->rd); + } else { + prt("movu.%c\t[r%d], r%d", size[a->sz], a->rs, a->rd); + } + return true; +} + +/* movu.[bw] rs,rd */ +static bool trans_MOVU_rr(DisasContext *ctx, arg_MOVU_rr *a) +{ + prt("movu.%c\tr%d, r%d", size[a->sz], a->rs, a->rd); + return true; +} + +/* movu.[bw] [ri,rb],rd */ +static bool trans_MOVU_ar(DisasContext *ctx, arg_MOVU_ar *a) +{ + prt("mov.%c\t[r%d,r%d], r%d", size[a->sz], a->ri, a->rb, a->rd); + return true; +} + +/* movu.[bw] [rs+],rd */ +/* movu.[bw] [-rs],rd */ +static bool trans_MOVU_pr(DisasContext *ctx, arg_MOVU_pr *a) +{ + if (a->ad) { + prt("movu.%c\t[-r%d], r%d", size[a->sz], a->rd, a->rs); + } else { + prt("movu.%c\t[r%d+], r%d", size[a->sz], a->rd, a->rs); + } + return true; +} + +/* pop rd */ +static bool trans_POP(DisasContext *ctx, arg_POP *a) +{ + prt("pop\tr%d", a->rd); + return true; +} + +/* popc rx */ +static bool trans_POPC(DisasContext *ctx, arg_POPC *a) +{ + prt("pop\tr%s", rx_crname(a->cr)); + return true; +} + +/* popm rd-rd2 */ +static bool trans_POPM(DisasContext *ctx, arg_POPM *a) +{ + prt("popm\tr%d-r%d", a->rd, a->rd2); + return true; +} + +/* push rs */ +static bool trans_PUSH_r(DisasContext *ctx, arg_PUSH_r *a) +{ + prt("push\tr%d", a->rs); + return true; +} + +/* push dsp[rs] */ +static bool trans_PUSH_m(DisasContext *ctx, arg_PUSH_m *a) +{ + char dsp[8]; + + rx_index_addr(ctx, dsp, a->ld, a->sz); + prt("push\t%s[r%d]", dsp, a->rs); + return true; +} + +/* pushc rx */ +static bool trans_PUSHC(DisasContext *ctx, arg_PUSHC *a) +{ + prt("push\t%s", rx_crname(a->cr)); + return true; +} + +/* pushm rs-rs2*/ +static bool trans_PUSHM(DisasContext *ctx, arg_PUSHM *a) +{ + prt("pushm\tr%d-r%d", a->rs, a->rs2); + return true; +} + +/* xchg rs,rd */ +static bool trans_XCHG_rr(DisasContext *ctx, arg_XCHG_rr *a) +{ + prt("xchg\tr%d, r%d", a->rs, a->rd); + return true; +} +/* xchg dsp[rs].<mi>,rd */ +static bool trans_XCHG_mr(DisasContext *ctx, arg_XCHG_mr *a) +{ + prt_ldmi(ctx, "xchg", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* stz #imm,rd */ +static bool trans_STZ(DisasContext *ctx, arg_STZ *a) +{ + prt_ir(ctx, "stz", a->imm, a->rd); + return true; +} + +/* stnz #imm,rd */ +static bool trans_STNZ(DisasContext *ctx, arg_STNZ *a) +{ + prt_ir(ctx, "stnz", a->imm, a->rd); + return true; +} + +/* rtsd #imm */ +static bool trans_RTSD_i(DisasContext *ctx, arg_RTSD_i *a) +{ + prt("rtsd\t#%d", a->imm << 2); + return true; +} + +/* rtsd #imm, rd-rd2 */ +static bool trans_RTSD_irr(DisasContext *ctx, arg_RTSD_irr *a) +{ + prt("rtsd\t#%d, r%d - r%d", a->imm << 2, a->rd, a->rd2); + return true; +} + +/* and #uimm:4, rd */ +/* and #imm, rd */ +static bool trans_AND_ir(DisasContext *ctx, arg_AND_ir *a) +{ + prt_ir(ctx, "and", a->imm, a->rd); + return true; +} + +/* and dsp[rs], rd */ +/* and rs,rd */ +static bool trans_AND_mr(DisasContext *ctx, arg_AND_mr *a) +{ + prt_ldmi(ctx, "and", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* and rs,rs2,rd */ +static bool trans_AND_rrr(DisasContext *ctx, arg_AND_rrr *a) +{ + prt("and\tr%d,r%d, r%d", a->rs, a->rs2, a->rd); + return true; +} + +/* or #uimm:4, rd */ +/* or #imm, rd */ +static bool trans_OR_ir(DisasContext *ctx, arg_OR_ir *a) +{ + prt_ir(ctx, "or", a->imm, a->rd); + return true; +} + +/* or dsp[rs], rd */ +/* or rs,rd */ +static bool trans_OR_mr(DisasContext *ctx, arg_OR_mr *a) +{ + prt_ldmi(ctx, "or", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* or rs,rs2,rd */ +static bool trans_OR_rrr(DisasContext *ctx, arg_OR_rrr *a) +{ + prt("or\tr%d, r%d, r%d", a->rs, a->rs2, a->rd); + return true; +} + +/* xor #imm, rd */ +static bool trans_XOR_ir(DisasContext *ctx, arg_XOR_ir *a) +{ + prt_ir(ctx, "xor", a->imm, a->rd); + return true; +} + +/* xor dsp[rs], rd */ +/* xor rs,rd */ +static bool trans_XOR_mr(DisasContext *ctx, arg_XOR_mr *a) +{ + prt_ldmi(ctx, "xor", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* tst #imm, rd */ +static bool trans_TST_ir(DisasContext *ctx, arg_TST_ir *a) +{ + prt_ir(ctx, "tst", a->imm, a->rd); + return true; +} + +/* tst dsp[rs], rd */ +/* tst rs, rd */ +static bool trans_TST_mr(DisasContext *ctx, arg_TST_mr *a) +{ + prt_ldmi(ctx, "tst", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* not rd */ +/* not rs, rd */ +static bool trans_NOT_rr(DisasContext *ctx, arg_NOT_rr *a) +{ + if (a->rs != a->rd) { + prt("not\tr%d, r%d", a->rs, a->rd); + } else { + prt("not\tr%d", a->rs); + } + return true; +} + +/* neg rd */ +/* neg rs, rd */ +static bool trans_NEG_rr(DisasContext *ctx, arg_NEG_rr *a) +{ + if (a->rs != a->rd) { + prt("neg\tr%d, r%d", a->rs, a->rd); + } else { + prt("neg\tr%d", a->rs); + } + return true; +} + +/* adc #imm, rd */ +static bool trans_ADC_ir(DisasContext *ctx, arg_ADC_ir *a) +{ + prt_ir(ctx, "adc", a->imm, a->rd); + return true; +} + +/* adc rs, rd */ +static bool trans_ADC_rr(DisasContext *ctx, arg_ADC_rr *a) +{ + prt("adc\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* adc dsp[rs], rd */ +static bool trans_ADC_mr(DisasContext *ctx, arg_ADC_mr *a) +{ + char dsp[8]; + + rx_index_addr(ctx, dsp, a->ld, 2); + prt("adc\t%s[r%d], r%d", dsp, a->rs, a->rd); + return true; +} + +/* add #uimm4, rd */ +/* add #imm, rs, rd */ +static bool trans_ADD_irr(DisasContext *ctx, arg_ADD_irr *a) +{ + if (a->imm < 0x10 && a->rs2 == a->rd) { + prt("add\t#%d, r%d", a->imm, a->rd); + } else { + prt("add\t#0x%08x, r%d, r%d", a->imm, a->rs2, a->rd); + } + return true; +} + +/* add rs, rd */ +/* add dsp[rs], rd */ +static bool trans_ADD_mr(DisasContext *ctx, arg_ADD_mr *a) +{ + prt_ldmi(ctx, "add", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* add rs, rs2, rd */ +static bool trans_ADD_rrr(DisasContext *ctx, arg_ADD_rrr *a) +{ + prt("add\tr%d, r%d, r%d", a->rs, a->rs2, a->rd); + return true; +} + +/* cmp #imm4, rd */ +/* cmp #imm8, rd */ +/* cmp #imm, rs2 */ +static bool trans_CMP_ir(DisasContext *ctx, arg_CMP_ir *a) +{ + prt_ir(ctx, "cmp", a->imm, a->rs2); + return true; +} + +/* cmp rs, rs2 */ +/* cmp dsp[rs], rs2 */ +static bool trans_CMP_mr(DisasContext *ctx, arg_CMP_mr *a) +{ + prt_ldmi(ctx, "cmp", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* sub #imm4, rd */ +static bool trans_SUB_ir(DisasContext *ctx, arg_SUB_ir *a) +{ + prt("sub\t#%d, r%d", a->imm, a->rd); + return true; +} + +/* sub rs, rd */ +/* sub dsp[rs], rd */ +static bool trans_SUB_mr(DisasContext *ctx, arg_SUB_mr *a) +{ + prt_ldmi(ctx, "sub", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* sub rs, rs2, rd */ +static bool trans_SUB_rrr(DisasContext *ctx, arg_SUB_rrr *a) +{ + prt("sub\tr%d, r%d, r%d", a->rs, a->rs2, a->rd); + return true; +} + +/* sbb rs, rd */ +static bool trans_SBB_rr(DisasContext *ctx, arg_SBB_rr *a) +{ + prt("sbb\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* sbb dsp[rs], rd */ +static bool trans_SBB_mr(DisasContext *ctx, arg_SBB_mr *a) +{ + prt_ldmi(ctx, "sbb", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* abs rd */ +/* abs rs, rd */ +static bool trans_ABS_rr(DisasContext *ctx, arg_ABS_rr *a) +{ + if (a->rs != a->rd) { + prt("abs\tr%d, r%d", a->rs, a->rd); + } else { + prt("abs\tr%d", a->rs); + } + return true; +} + +/* max #imm, rd */ +static bool trans_MAX_ir(DisasContext *ctx, arg_MAX_ir *a) +{ + prt_ir(ctx, "max", a->imm, a->rd); + return true; +} + +/* max rs, rd */ +/* max dsp[rs], rd */ +static bool trans_MAX_mr(DisasContext *ctx, arg_MAX_mr *a) +{ + prt_ldmi(ctx, "max", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* min #imm, rd */ +static bool trans_MIN_ir(DisasContext *ctx, arg_MIN_ir *a) +{ + prt_ir(ctx, "min", a->imm, a->rd); + return true; +} + +/* min rs, rd */ +/* min dsp[rs], rd */ +static bool trans_MIN_mr(DisasContext *ctx, arg_MIN_mr *a) +{ + prt_ldmi(ctx, "min", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* mul #uimm4, rd */ +/* mul #imm, rd */ +static bool trans_MUL_ir(DisasContext *ctx, arg_MUL_ir *a) +{ + prt_ir(ctx, "mul", a->imm, a->rd); + return true; +} + +/* mul rs, rd */ +/* mul dsp[rs], rd */ +static bool trans_MUL_mr(DisasContext *ctx, arg_MUL_mr *a) +{ + prt_ldmi(ctx, "mul", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* mul rs, rs2, rd */ +static bool trans_MUL_rrr(DisasContext *ctx, arg_MUL_rrr *a) +{ + prt("mul\tr%d,r%d,r%d", a->rs, a->rs2, a->rd); + return true; +} + +/* emul #imm, rd */ +static bool trans_EMUL_ir(DisasContext *ctx, arg_EMUL_ir *a) +{ + prt_ir(ctx, "emul", a->imm, a->rd); + return true; +} + +/* emul rs, rd */ +/* emul dsp[rs], rd */ +static bool trans_EMUL_mr(DisasContext *ctx, arg_EMUL_mr *a) +{ + prt_ldmi(ctx, "emul", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* emulu #imm, rd */ +static bool trans_EMULU_ir(DisasContext *ctx, arg_EMULU_ir *a) +{ + prt_ir(ctx, "emulu", a->imm, a->rd); + return true; +} + +/* emulu rs, rd */ +/* emulu dsp[rs], rd */ +static bool trans_EMULU_mr(DisasContext *ctx, arg_EMULU_mr *a) +{ + prt_ldmi(ctx, "emulu", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* div #imm, rd */ +static bool trans_DIV_ir(DisasContext *ctx, arg_DIV_ir *a) +{ + prt_ir(ctx, "div", a->imm, a->rd); + return true; +} + +/* div rs, rd */ +/* div dsp[rs], rd */ +static bool trans_DIV_mr(DisasContext *ctx, arg_DIV_mr *a) +{ + prt_ldmi(ctx, "div", a->ld, a->mi, a->rs, a->rd); + return true; +} + +/* divu #imm, rd */ +static bool trans_DIVU_ir(DisasContext *ctx, arg_DIVU_ir *a) +{ + prt_ir(ctx, "divu", a->imm, a->rd); + return true; +} + +/* divu rs, rd */ +/* divu dsp[rs], rd */ +static bool trans_DIVU_mr(DisasContext *ctx, arg_DIVU_mr *a) +{ + prt_ldmi(ctx, "divu", a->ld, a->mi, a->rs, a->rd); + return true; +} + + +/* shll #imm:5, rd */ +/* shll #imm:5, rs, rd */ +static bool trans_SHLL_irr(DisasContext *ctx, arg_SHLL_irr *a) +{ + if (a->rs2 != a->rd) { + prt("shll\t#%d, r%d, r%d", a->imm, a->rs2, a->rd); + } else { + prt("shll\t#%d, r%d", a->imm, a->rd); + } + return true; +} + +/* shll rs, rd */ +static bool trans_SHLL_rr(DisasContext *ctx, arg_SHLL_rr *a) +{ + prt("shll\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* shar #imm:5, rd */ +/* shar #imm:5, rs, rd */ +static bool trans_SHAR_irr(DisasContext *ctx, arg_SHAR_irr *a) +{ + if (a->rs2 != a->rd) { + prt("shar\t#%d, r%d, r%d", a->imm, a->rs2, a->rd); + } else { + prt("shar\t#%d, r%d", a->imm, a->rd); + } + return true; +} + +/* shar rs, rd */ +static bool trans_SHAR_rr(DisasContext *ctx, arg_SHAR_rr *a) +{ + prt("shar\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* shlr #imm:5, rd */ +/* shlr #imm:5, rs, rd */ +static bool trans_SHLR_irr(DisasContext *ctx, arg_SHLR_irr *a) +{ + if (a->rs2 != a->rd) { + prt("shlr\t#%d, r%d, r%d", a->imm, a->rs2, a->rd); + } else { + prt("shlr\t#%d, r%d", a->imm, a->rd); + } + return true; +} + +/* shlr rs, rd */ +static bool trans_SHLR_rr(DisasContext *ctx, arg_SHLR_rr *a) +{ + prt("shlr\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* rolc rd */ +static bool trans_ROLC(DisasContext *ctx, arg_ROLC *a) +{ + prt("rorc\tr%d", a->rd); + return true; +} + +/* rorc rd */ +static bool trans_RORC(DisasContext *ctx, arg_RORC *a) +{ + prt("rorc\tr%d", a->rd); + return true; +} + +/* rotl #imm, rd */ +static bool trans_ROTL_ir(DisasContext *ctx, arg_ROTL_ir *a) +{ + prt("rotl\t#%d, r%d", a->imm, a->rd); + return true; +} + +/* rotl rs, rd */ +static bool trans_ROTL_rr(DisasContext *ctx, arg_ROTL_rr *a) +{ + prt("rotl\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* rotr #imm, rd */ +static bool trans_ROTR_ir(DisasContext *ctx, arg_ROTR_ir *a) +{ + prt("rotr\t#%d, r%d", a->imm, a->rd); + return true; +} + +/* rotr rs, rd */ +static bool trans_ROTR_rr(DisasContext *ctx, arg_ROTR_rr *a) +{ + prt("rotr\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* revl rs, rd */ +static bool trans_REVL(DisasContext *ctx, arg_REVL *a) +{ + prt("revl\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* revw rs, rd */ +static bool trans_REVW(DisasContext *ctx, arg_REVW *a) +{ + prt("revw\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* conditional branch helper */ +static void rx_bcnd_main(DisasContext *ctx, int cd, int len, int dst) +{ + static const char sz[] = {'s', 'b', 'w', 'a'}; + prt("b%s.%c\t%08x", cond[cd], sz[len - 1], ctx->pc + dst); +} + +/* beq dsp:3 / bne dsp:3 */ +/* beq dsp:8 / bne dsp:8 */ +/* bc dsp:8 / bnc dsp:8 */ +/* bgtu dsp:8 / bleu dsp:8 */ +/* bpz dsp:8 / bn dsp:8 */ +/* bge dsp:8 / blt dsp:8 */ +/* bgt dsp:8 / ble dsp:8 */ +/* bo dsp:8 / bno dsp:8 */ +/* beq dsp:16 / bne dsp:16 */ +static bool trans_BCnd(DisasContext *ctx, arg_BCnd *a) +{ + rx_bcnd_main(ctx, a->cd, a->sz, a->dsp); + return true; +} + +/* bra dsp:3 */ +/* bra dsp:8 */ +/* bra dsp:16 */ +/* bra dsp:24 */ +static bool trans_BRA(DisasContext *ctx, arg_BRA *a) +{ + rx_bcnd_main(ctx, 14, a->sz, a->dsp); + return true; +} + +/* bra rs */ +static bool trans_BRA_l(DisasContext *ctx, arg_BRA_l *a) +{ + prt("bra.l\tr%d", a->rd); + return true; +} + +/* jmp rs */ +static bool trans_JMP(DisasContext *ctx, arg_JMP *a) +{ + prt("jmp\tr%d", a->rs); + return true; +} + +/* jsr rs */ +static bool trans_JSR(DisasContext *ctx, arg_JSR *a) +{ + prt("jsr\tr%d", a->rs); + return true; +} + +/* bsr dsp:16 */ +/* bsr dsp:24 */ +static bool trans_BSR(DisasContext *ctx, arg_BSR *a) +{ + static const char sz[] = {'w', 'a'}; + prt("bsr.%c\t%08x", sz[a->sz - 3], ctx->pc + a->dsp); + return true; +} + +/* bsr rs */ +static bool trans_BSR_l(DisasContext *ctx, arg_BSR_l *a) +{ + prt("bsr.l\tr%d", a->rd); + return true; +} + +/* rts */ +static bool trans_RTS(DisasContext *ctx, arg_RTS *a) +{ + prt("rts"); + return true; +} + +/* nop */ +static bool trans_NOP(DisasContext *ctx, arg_NOP *a) +{ + prt("nop"); + return true; +} + +/* scmpu */ +static bool trans_SCMPU(DisasContext *ctx, arg_SCMPU *a) +{ + prt("scmpu"); + return true; +} + +/* smovu */ +static bool trans_SMOVU(DisasContext *ctx, arg_SMOVU *a) +{ + prt("smovu"); + return true; +} + +/* smovf */ +static bool trans_SMOVF(DisasContext *ctx, arg_SMOVF *a) +{ + prt("smovf"); + return true; +} + +/* smovb */ +static bool trans_SMOVB(DisasContext *ctx, arg_SMOVB *a) +{ + prt("smovb"); + return true; +} + +/* suntile */ +static bool trans_SUNTIL(DisasContext *ctx, arg_SUNTIL *a) +{ + prt("suntil.%c", size[a->sz]); + return true; +} + +/* swhile */ +static bool trans_SWHILE(DisasContext *ctx, arg_SWHILE *a) +{ + prt("swhile.%c", size[a->sz]); + return true; +} +/* sstr */ +static bool trans_SSTR(DisasContext *ctx, arg_SSTR *a) +{ + prt("sstr.%c", size[a->sz]); + return true; +} + +/* rmpa */ +static bool trans_RMPA(DisasContext *ctx, arg_RMPA *a) +{ + prt("rmpa.%c", size[a->sz]); + return true; +} + +/* mulhi rs,rs2 */ +static bool trans_MULHI(DisasContext *ctx, arg_MULHI *a) +{ + prt("mulhi\tr%d,r%d", a->rs, a->rs2); + return true; +} + +/* mullo rs,rs2 */ +static bool trans_MULLO(DisasContext *ctx, arg_MULLO *a) +{ + prt("mullo\tr%d, r%d", a->rs, a->rs2); + return true; +} + +/* machi rs,rs2 */ +static bool trans_MACHI(DisasContext *ctx, arg_MACHI *a) +{ + prt("machi\tr%d, r%d", a->rs, a->rs2); + return true; +} + +/* maclo rs,rs2 */ +static bool trans_MACLO(DisasContext *ctx, arg_MACLO *a) +{ + prt("maclo\tr%d, r%d", a->rs, a->rs2); + return true; +} + +/* mvfachi rd */ +static bool trans_MVFACHI(DisasContext *ctx, arg_MVFACHI *a) +{ + prt("mvfachi\tr%d", a->rd); + return true; +} + +/* mvfacmi rd */ +static bool trans_MVFACMI(DisasContext *ctx, arg_MVFACMI *a) +{ + prt("mvfacmi\tr%d", a->rd); + return true; +} + +/* mvtachi rs */ +static bool trans_MVTACHI(DisasContext *ctx, arg_MVTACHI *a) +{ + prt("mvtachi\tr%d", a->rs); + return true; +} + +/* mvtaclo rs */ +static bool trans_MVTACLO(DisasContext *ctx, arg_MVTACLO *a) +{ + prt("mvtaclo\tr%d", a->rs); + return true; +} + +/* racw #imm */ +static bool trans_RACW(DisasContext *ctx, arg_RACW *a) +{ + prt("racw\t#%d", a->imm + 1); + return true; +} + +/* sat rd */ +static bool trans_SAT(DisasContext *ctx, arg_SAT *a) +{ + prt("sat\tr%d", a->rd); + return true; +} + +/* satr */ +static bool trans_SATR(DisasContext *ctx, arg_SATR *a) +{ + prt("satr"); + return true; +} + +/* fadd #imm, rd */ +static bool trans_FADD_ir(DisasContext *ctx, arg_FADD_ir *a) +{ + prt("fadd\t#%d,r%d", li(ctx, 0), a->rd); + return true; +} + +/* fadd dsp[rs], rd */ +/* fadd rs, rd */ +static bool trans_FADD_mr(DisasContext *ctx, arg_FADD_mr *a) +{ + prt_ldmi(ctx, "fadd", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* fcmp #imm, rd */ +static bool trans_FCMP_ir(DisasContext *ctx, arg_FCMP_ir *a) +{ + prt("fadd\t#%d,r%d", li(ctx, 0), a->rd); + return true; +} + +/* fcmp dsp[rs], rd */ +/* fcmp rs, rd */ +static bool trans_FCMP_mr(DisasContext *ctx, arg_FCMP_mr *a) +{ + prt_ldmi(ctx, "fcmp", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* fsub #imm, rd */ +static bool trans_FSUB_ir(DisasContext *ctx, arg_FSUB_ir *a) +{ + prt("fsub\t#%d,r%d", li(ctx, 0), a->rd); + return true; +} + +/* fsub dsp[rs], rd */ +/* fsub rs, rd */ +static bool trans_FSUB_mr(DisasContext *ctx, arg_FSUB_mr *a) +{ + prt_ldmi(ctx, "fsub", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* ftoi dsp[rs], rd */ +/* ftoi rs, rd */ +static bool trans_FTOI(DisasContext *ctx, arg_FTOI *a) +{ + prt_ldmi(ctx, "ftoi", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* fmul #imm, rd */ +static bool trans_FMUL_ir(DisasContext *ctx, arg_FMUL_ir *a) +{ + prt("fmul\t#%d,r%d", li(ctx, 0), a->rd); + return true; +} + +/* fmul dsp[rs], rd */ +/* fmul rs, rd */ +static bool trans_FMUL_mr(DisasContext *ctx, arg_FMUL_mr *a) +{ + prt_ldmi(ctx, "fmul", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* fdiv #imm, rd */ +static bool trans_FDIV_ir(DisasContext *ctx, arg_FDIV_ir *a) +{ + prt("fdiv\t#%d,r%d", li(ctx, 0), a->rd); + return true; +} + +/* fdiv dsp[rs], rd */ +/* fdiv rs, rd */ +static bool trans_FDIV_mr(DisasContext *ctx, arg_FDIV_mr *a) +{ + prt_ldmi(ctx, "fdiv", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* round dsp[rs], rd */ +/* round rs, rd */ +static bool trans_ROUND(DisasContext *ctx, arg_ROUND *a) +{ + prt_ldmi(ctx, "round", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +/* itof rs, rd */ +/* itof dsp[rs], rd */ +static bool trans_ITOF(DisasContext *ctx, arg_ITOF *a) +{ + prt_ldmi(ctx, "itof", a->ld, RX_IM_LONG, a->rs, a->rd); + return true; +} + +#define BOP_IM(name, reg) \ + do { \ + char dsp[8]; \ + rx_index_addr(ctx, dsp, a->ld, RX_MEMORY_BYTE); \ + prt("b%s\t#%d, %s[r%d]", #name, a->imm, dsp, reg); \ + return true; \ + } while (0) + +#define BOP_RM(name) \ + do { \ + char dsp[8]; \ + rx_index_addr(ctx, dsp, a->ld, RX_MEMORY_BYTE); \ + prt("b%s\tr%d, %s[r%d]", #name, a->rd, dsp, a->rs); \ + return true; \ + } while (0) + +/* bset #imm, dsp[rd] */ +static bool trans_BSET_im(DisasContext *ctx, arg_BSET_im *a) +{ + BOP_IM(bset, a->rs); +} + +/* bset rs, dsp[rd] */ +static bool trans_BSET_rm(DisasContext *ctx, arg_BSET_rm *a) +{ + BOP_RM(set); +} + +/* bset rs, rd */ +static bool trans_BSET_rr(DisasContext *ctx, arg_BSET_rr *a) +{ + prt("bset\tr%d,r%d", a->rs, a->rd); + return true; +} + +/* bset #imm, rd */ +static bool trans_BSET_ir(DisasContext *ctx, arg_BSET_ir *a) +{ + prt("bset\t#%d, r%d", a->imm, a->rd); + return true; +} + +/* bclr #imm, dsp[rd] */ +static bool trans_BCLR_im(DisasContext *ctx, arg_BCLR_im *a) +{ + BOP_IM(clr, a->rs); +} + +/* bclr rs, dsp[rd] */ +static bool trans_BCLR_rm(DisasContext *ctx, arg_BCLR_rm *a) +{ + BOP_RM(clr); +} + +/* bclr rs, rd */ +static bool trans_BCLR_rr(DisasContext *ctx, arg_BCLR_rr *a) +{ + prt("bclr\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* bclr #imm, rd */ +static bool trans_BCLR_ir(DisasContext *ctx, arg_BCLR_ir *a) +{ + prt("bclr\t#%d,r%d", a->imm, a->rd); + return true; +} + +/* btst #imm, dsp[rd] */ +static bool trans_BTST_im(DisasContext *ctx, arg_BTST_im *a) +{ + BOP_IM(tst, a->rs); +} + +/* btst rs, dsp[rd] */ +static bool trans_BTST_rm(DisasContext *ctx, arg_BTST_rm *a) +{ + BOP_RM(tst); +} + +/* btst rs, rd */ +static bool trans_BTST_rr(DisasContext *ctx, arg_BTST_rr *a) +{ + prt("btst\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* btst #imm, rd */ +static bool trans_BTST_ir(DisasContext *ctx, arg_BTST_ir *a) +{ + prt("btst\t#%d, r%d", a->imm, a->rd); + return true; +} + +/* bnot rs, dsp[rd] */ +static bool trans_BNOT_rm(DisasContext *ctx, arg_BNOT_rm *a) +{ + BOP_RM(not); +} + +/* bnot rs, rd */ +static bool trans_BNOT_rr(DisasContext *ctx, arg_BNOT_rr *a) +{ + prt("bnot\tr%d, r%d", a->rs, a->rd); + return true; +} + +/* bnot #imm, dsp[rd] */ +static bool trans_BNOT_im(DisasContext *ctx, arg_BNOT_im *a) +{ + BOP_IM(not, a->rs); +} + +/* bnot #imm, rd */ +static bool trans_BNOT_ir(DisasContext *ctx, arg_BNOT_ir *a) +{ + prt("bnot\t#%d, r%d", a->imm, a->rd); + return true; +} + +/* bmcond #imm, dsp[rd] */ +static bool trans_BMCnd_im(DisasContext *ctx, arg_BMCnd_im *a) +{ + char dsp[8]; + + rx_index_addr(ctx, dsp, a->ld, RX_MEMORY_BYTE); + prt("bm%s\t#%d, %s[r%d]", cond[a->cd], a->imm, dsp, a->rd); + return true; +} + +/* bmcond #imm, rd */ +static bool trans_BMCnd_ir(DisasContext *ctx, arg_BMCnd_ir *a) +{ + prt("bm%s\t#%d, r%d", cond[a->cd], a->imm, a->rd); + return true; +} + +/* clrpsw psw */ +static bool trans_CLRPSW(DisasContext *ctx, arg_CLRPSW *a) +{ + prt("clrpsw\t%c", psw[a->cb]); + return true; +} + +/* setpsw psw */ +static bool trans_SETPSW(DisasContext *ctx, arg_SETPSW *a) +{ + prt("setpsw\t%c", psw[a->cb]); + return true; +} + +/* mvtipl #imm */ +static bool trans_MVTIPL(DisasContext *ctx, arg_MVTIPL *a) +{ + prt("movtipl\t#%d", a->imm); + return true; +} + +/* mvtc #imm, rd */ +static bool trans_MVTC_i(DisasContext *ctx, arg_MVTC_i *a) +{ + prt("mvtc\t#0x%08x, %s", a->imm, rx_crname(a->cr)); + return true; +} + +/* mvtc rs, rd */ +static bool trans_MVTC_r(DisasContext *ctx, arg_MVTC_r *a) +{ + prt("mvtc\tr%d, %s", a->rs, rx_crname(a->cr)); + return true; +} + +/* mvfc rs, rd */ +static bool trans_MVFC(DisasContext *ctx, arg_MVFC *a) +{ + prt("mvfc\t%s, r%d", rx_crname(a->cr), a->rd); + return true; +} + +/* rtfi */ +static bool trans_RTFI(DisasContext *ctx, arg_RTFI *a) +{ + prt("rtfi"); + return true; +} + +/* rte */ +static bool trans_RTE(DisasContext *ctx, arg_RTE *a) +{ + prt("rte"); + return true; +} + +/* brk */ +static bool trans_BRK(DisasContext *ctx, arg_BRK *a) +{ + prt("brk"); + return true; +} + +/* int #imm */ +static bool trans_INT(DisasContext *ctx, arg_INT *a) +{ + prt("int\t#%d", a->imm); + return true; +} + +/* wait */ +static bool trans_WAIT(DisasContext *ctx, arg_WAIT *a) +{ + prt("wait"); + return true; +} + +/* sccnd.[bwl] rd */ +/* sccnd.[bwl] dsp:[rd] */ +static bool trans_SCCnd(DisasContext *ctx, arg_SCCnd *a) +{ + if (a->ld < 3) { + char dsp[8]; + rx_index_addr(ctx, dsp, a->sz, a->ld); + prt("sc%s.%c\t%s[r%d]", cond[a->cd], size[a->sz], dsp, a->rd); + } else { + prt("sc%s.%c\tr%d", cond[a->cd], size[a->sz], a->rd); + } + return true; +} + +int print_insn_rx(bfd_vma addr, disassemble_info *dis) +{ + DisasContext ctx; + uint32_t insn; + int i; + + ctx.dis = dis; + ctx.pc = ctx.addr = addr; + ctx.len = 0; + + insn = decode_load(&ctx); + if (!decode(&ctx, insn)) { + ctx.dis->fprintf_func(ctx.dis->stream, ".byte\t"); + for (i = 0; i < ctx.addr - addr; i++) { + if (i > 0) { + ctx.dis->fprintf_func(ctx.dis->stream, ","); + } + ctx.dis->fprintf_func(ctx.dis->stream, "0x%02x", insn >> 24); + insn <<= 8; + } + } + return ctx.addr - addr; +} diff --git a/target/rx/gdbstub.c b/target/rx/gdbstub.c new file mode 100644 index 0000000000..9391e8151e --- /dev/null +++ b/target/rx/gdbstub.c @@ -0,0 +1,112 @@ +/* + * RX gdb server stub + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" +#include "exec/gdbstub.h" + +int rx_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) +{ + RXCPU *cpu = RXCPU(cs); + CPURXState *env = &cpu->env; + + switch (n) { + case 0 ... 15: + return gdb_get_regl(mem_buf, env->regs[n]); + case 16: + return gdb_get_regl(mem_buf, (env->psw_u) ? env->regs[0] : env->usp); + case 17: + return gdb_get_regl(mem_buf, (!env->psw_u) ? env->regs[0] : env->isp); + case 18: + return gdb_get_regl(mem_buf, rx_cpu_pack_psw(env)); + case 19: + return gdb_get_regl(mem_buf, env->pc); + case 20: + return gdb_get_regl(mem_buf, env->intb); + case 21: + return gdb_get_regl(mem_buf, env->bpsw); + case 22: + return gdb_get_regl(mem_buf, env->bpc); + case 23: + return gdb_get_regl(mem_buf, env->fintv); + case 24: + return gdb_get_regl(mem_buf, env->fpsw); + case 25: + return 0; + } + return 0; +} + +int rx_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + RXCPU *cpu = RXCPU(cs); + CPURXState *env = &cpu->env; + uint32_t psw; + switch (n) { + case 0 ... 15: + env->regs[n] = ldl_p(mem_buf); + if (n == 0) { + if (env->psw_u) { + env->usp = env->regs[0]; + } else { + env->isp = env->regs[0]; + } + } + break; + case 16: + env->usp = ldl_p(mem_buf); + if (env->psw_u) { + env->regs[0] = ldl_p(mem_buf); + } + break; + case 17: + env->isp = ldl_p(mem_buf); + if (!env->psw_u) { + env->regs[0] = ldl_p(mem_buf); + } + break; + case 18: + psw = ldl_p(mem_buf); + rx_cpu_unpack_psw(env, psw, 1); + break; + case 19: + env->pc = ldl_p(mem_buf); + break; + case 20: + env->intb = ldl_p(mem_buf); + break; + case 21: + env->bpsw = ldl_p(mem_buf); + break; + case 22: + env->bpc = ldl_p(mem_buf); + break; + case 23: + env->fintv = ldl_p(mem_buf); + break; + case 24: + env->fpsw = ldl_p(mem_buf); + break; + case 25: + return 8; + default: + return 0; + } + + return 4; +} diff --git a/target/rx/helper.c b/target/rx/helper.c new file mode 100644 index 0000000000..a6a337a311 --- /dev/null +++ b/target/rx/helper.c @@ -0,0 +1,149 @@ +/* + * RX emulation + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "cpu.h" +#include "exec/log.h" +#include "exec/cpu_ldst.h" +#include "sysemu/sysemu.h" +#include "hw/irq.h" + +void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte) +{ + if (env->psw_pm == 0) { + env->psw_ipl = FIELD_EX32(psw, PSW, IPL); + if (rte) { + /* PSW.PM can write RTE and RTFI */ + env->psw_pm = FIELD_EX32(psw, PSW, PM); + } + env->psw_u = FIELD_EX32(psw, PSW, U); + env->psw_i = FIELD_EX32(psw, PSW, I); + } + env->psw_o = FIELD_EX32(psw, PSW, O) << 31; + env->psw_s = FIELD_EX32(psw, PSW, S) << 31; + env->psw_z = 1 - FIELD_EX32(psw, PSW, Z); + env->psw_c = FIELD_EX32(psw, PSW, C); +} + +#define INT_FLAGS (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR) +void rx_cpu_do_interrupt(CPUState *cs) +{ + RXCPU *cpu = RXCPU(cs); + CPURXState *env = &cpu->env; + int do_irq = cs->interrupt_request & INT_FLAGS; + uint32_t save_psw; + + env->in_sleep = 0; + + if (env->psw_u) { + env->usp = env->regs[0]; + } else { + env->isp = env->regs[0]; + } + save_psw = rx_cpu_pack_psw(env); + env->psw_pm = env->psw_i = env->psw_u = 0; + + if (do_irq) { + if (do_irq & CPU_INTERRUPT_FIR) { + env->bpc = env->pc; + env->bpsw = save_psw; + env->pc = env->fintv; + env->psw_ipl = 15; + cs->interrupt_request &= ~CPU_INTERRUPT_FIR; + qemu_set_irq(env->ack, env->ack_irq); + qemu_log_mask(CPU_LOG_INT, "fast interrupt raised\n"); + } else if (do_irq & CPU_INTERRUPT_HARD) { + env->isp -= 4; + cpu_stl_data(env, env->isp, save_psw); + env->isp -= 4; + cpu_stl_data(env, env->isp, env->pc); + env->pc = cpu_ldl_data(env, env->intb + env->ack_irq * 4); + env->psw_ipl = env->ack_ipl; + cs->interrupt_request &= ~CPU_INTERRUPT_HARD; + qemu_set_irq(env->ack, env->ack_irq); + qemu_log_mask(CPU_LOG_INT, + "interrupt 0x%02x raised\n", env->ack_irq); + } + } else { + uint32_t vec = cs->exception_index; + const char *expname = "unknown exception"; + + env->isp -= 4; + cpu_stl_data(env, env->isp, save_psw); + env->isp -= 4; + cpu_stl_data(env, env->isp, env->pc); + + if (vec < 0x100) { + env->pc = cpu_ldl_data(env, 0xffffffc0 + vec * 4); + } else { + env->pc = cpu_ldl_data(env, env->intb + (vec & 0xff) * 4); + } + switch (vec) { + case 20: + expname = "privilege violation"; + break; + case 21: + expname = "access exception"; + break; + case 23: + expname = "illegal instruction"; + break; + case 25: + expname = "fpu exception"; + break; + case 30: + expname = "non-maskable interrupt"; + break; + case 0x100 ... 0x1ff: + expname = "unconditional trap"; + } + qemu_log_mask(CPU_LOG_INT, "exception 0x%02x [%s] raised\n", + (vec & 0xff), expname); + } + env->regs[0] = env->isp; +} + +bool rx_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + RXCPU *cpu = RXCPU(cs); + CPURXState *env = &cpu->env; + int accept = 0; + /* hardware interrupt (Normal) */ + if ((interrupt_request & CPU_INTERRUPT_HARD) && + env->psw_i && (env->psw_ipl < env->req_ipl)) { + env->ack_irq = env->req_irq; + env->ack_ipl = env->req_ipl; + accept = 1; + } + /* hardware interrupt (FIR) */ + if ((interrupt_request & CPU_INTERRUPT_FIR) && + env->psw_i && (env->psw_ipl < 15)) { + accept = 1; + } + if (accept) { + rx_cpu_do_interrupt(cs); + return true; + } + return false; +} + +hwaddr rx_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ + return addr; +} diff --git a/target/rx/helper.h b/target/rx/helper.h new file mode 100644 index 0000000000..f0b7ebbbf7 --- /dev/null +++ b/target/rx/helper.h @@ -0,0 +1,31 @@ +DEF_HELPER_1(raise_illegal_instruction, noreturn, env) +DEF_HELPER_1(raise_access_fault, noreturn, env) +DEF_HELPER_1(raise_privilege_violation, noreturn, env) +DEF_HELPER_1(wait, noreturn, env) +DEF_HELPER_1(debug, noreturn, env) +DEF_HELPER_2(rxint, noreturn, env, i32) +DEF_HELPER_1(rxbrk, noreturn, env) +DEF_HELPER_FLAGS_3(fadd, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fsub, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fmul, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fdiv, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fcmp, TCG_CALL_NO_WG, void, env, f32, f32) +DEF_HELPER_FLAGS_2(ftoi, TCG_CALL_NO_WG, i32, env, f32) +DEF_HELPER_FLAGS_2(round, TCG_CALL_NO_WG, i32, env, f32) +DEF_HELPER_FLAGS_2(itof, TCG_CALL_NO_WG, f32, env, i32) +DEF_HELPER_2(set_fpsw, void, env, i32) +DEF_HELPER_FLAGS_2(racw, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_2(set_psw_rte, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_2(set_psw, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_1(pack_psw, i32, env) +DEF_HELPER_FLAGS_3(div, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_1(scmpu, TCG_CALL_NO_WG, void, env) +DEF_HELPER_1(smovu, void, env) +DEF_HELPER_1(smovf, void, env) +DEF_HELPER_1(smovb, void, env) +DEF_HELPER_2(sstr, void, env, i32) +DEF_HELPER_FLAGS_2(swhile, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_2(suntil, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_2(rmpa, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_1(satr, void, env) diff --git a/target/rx/insns.decode b/target/rx/insns.decode new file mode 100644 index 0000000000..232a61fc8e --- /dev/null +++ b/target/rx/insns.decode @@ -0,0 +1,621 @@ +# +# Renesas RX instruction decode definitions. +# +# Copyright (c) 2019 Richard Henderson <richard.henderson@linaro.org> +# Copyright (c) 2019 Yoshinori Sato <ysato@users.sourceforge.jp> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see <http://www.gnu.org/licenses/>. +# + +&bcnd cd dsp sz +&jdsp dsp sz +&jreg rs +&rr rd rs +&ri rd imm +&rrr rd rs rs2 +&rri rd imm rs2 +&rm rd rs ld mi +&mi rs ld mi imm +&mr rs ld mi rs2 +&mcnd ld sz rd cd +######## +%b1_bdsp 24:3 !function=bdsp_s + +@b1_bcnd_s .... cd:1 ... &bcnd dsp=%b1_bdsp sz=1 +@b1_bra_s .... .... &jdsp dsp=%b1_bdsp sz=1 + +%b2_r_0 16:4 +%b2_li_2 18:2 !function=li +%b2_li_8 24:2 !function=li +%b2_dsp5_3 23:4 19:1 + +@b2_rds .... .... .... rd:4 &rr rs=%b2_r_0 +@b2_rds_li .... .... .... rd:4 &rri rs2=%b2_r_0 imm=%b2_li_8 +@b2_rds_uimm4 .... .... imm:4 rd:4 &rri rs2=%b2_r_0 +@b2_rs2_uimm4 .... .... imm:4 rs2:4 &rri rd=0 +@b2_rds_imm5 .... ... imm:5 rd:4 &rri rs2=%b2_r_0 +@b2_rd_rs_li .... .... rs2:4 rd:4 &rri imm=%b2_li_8 +@b2_rd_ld_ub .... .. ld:2 rs:4 rd:4 &rm mi=4 +@b2_ld_imm3 .... .. ld:2 rs:4 . imm:3 &mi mi=4 +@b2_bcnd_b .... cd:4 dsp:s8 &bcnd sz=2 +@b2_bra_b .... .... dsp:s8 &jdsp sz=2 + +######## + +%b3_r_0 8:4 +%b3_li_10 18:2 !function=li +%b3_dsp5_8 23:1 16:4 +%b3_bdsp 8:s8 16:8 + +@b3_rd_rs .... .... .... .... rs:4 rd:4 &rr +@b3_rs_rd .... .... .... .... rd:4 rs:4 &rr +@b3_rd_li .... .... .... .... .... rd:4 \ + &rri rs2=%b3_r_0 imm=%b3_li_10 +@b3_rd_ld .... .... mi:2 .... ld:2 rs:4 rd:4 &rm +@b3_rd_ld_ub .... .... .... .. ld:2 rs:4 rd:4 &rm mi=4 +@b3_rd_ld_ul .... .... .... .. ld:2 rs:4 rd:4 &rm mi=2 +@b3_rd_rs_rs2 .... .... .... rd:4 rs:4 rs2:4 &rrr +@b3_rds_imm5 .... .... ....... imm:5 rd:4 &rri rs2=%b3_r_0 +@b3_rd_rs_imm5 .... .... ... imm:5 rs2:4 rd:4 &rri +@b3_bcnd_w .... ... cd:1 .... .... .... .... &bcnd dsp=%b3_bdsp sz=3 +@b3_bra_w .... .... .... .... .... .... &jdsp dsp=%b3_bdsp sz=3 +@b3_ld_rd_rs .... .... .... .. ld:2 rs:4 rd:4 &rm mi=0 +@b3_sz_ld_rd_cd .... .... .... sz:2 ld:2 rd:4 cd:4 &mcnd + +######## + +%b4_li_18 18:2 !function=li +%b4_dsp_16 0:s8 8:8 +%b4_bdsp 0:s8 8:8 16:8 + +@b4_rd_ldmi .... .... mi:2 .... ld:2 .... .... rs:4 rd:4 &rm +@b4_bra_a .... .... .... .... .... .... .... .... \ + &jdsp dsp=%b4_bdsp sz=4 +######## +# ABS rd +ABS_rr 0111 1110 0010 .... @b2_rds +# ABS rs, rd +ABS_rr 1111 1100 0000 1111 .... .... @b3_rd_rs + +# ADC #imm, rd +ADC_ir 1111 1101 0111 ..00 0010 .... @b3_rd_li +# ADC rs, rd +ADC_rr 1111 1100 0000 1011 .... .... @b3_rd_rs +# ADC dsp[rs].l, rd +# Note only mi==2 allowed. +ADC_mr 0000 0110 ..10 00.. 0000 0010 .... .... @b4_rd_ldmi + +# ADD #uimm4, rd +ADD_irr 0110 0010 .... .... @b2_rds_uimm4 +# ADD #imm, rs, rd +ADD_irr 0111 00.. .... .... @b2_rd_rs_li +# ADD dsp[rs].ub, rd +# ADD rs, rd +ADD_mr 0100 10.. .... .... @b2_rd_ld_ub +# ADD dsp[rs], rd +ADD_mr 0000 0110 ..00 10.. .... .... @b3_rd_ld +# ADD rs, rs2, rd +ADD_rrr 1111 1111 0010 .... .... .... @b3_rd_rs_rs2 + +# AND #uimm4, rd +AND_ir 0110 0100 .... .... @b2_rds_uimm4 +# AND #imm, rd +AND_ir 0111 01.. 0010 .... @b2_rds_li +# AND dsp[rs].ub, rd +# AND rs, rd +AND_mr 0101 00.. .... .... @b2_rd_ld_ub +# AND dsp[rs], rd +AND_mr 0000 0110 ..01 00.. .... .... @b3_rd_ld +# AND rs, rs2, rd +AND_rrr 1111 1111 0100 .... .... .... @b3_rd_rs_rs2 + +# BCLR #imm, dsp[rd] +BCLR_im 1111 00.. .... 1... @b2_ld_imm3 +# BCLR #imm, rs +BCLR_ir 0111 101. .... .... @b2_rds_imm5 +# BCLR rs, rd +# BCLR rs, dsp[rd] +{ + BCLR_rr 1111 1100 0110 0111 .... .... @b3_rs_rd + BCLR_rm 1111 1100 0110 01.. .... .... @b3_rd_ld_ub +} + +# BCnd.s dsp +BCnd 0001 .... @b1_bcnd_s +# BRA.b dsp +# BCnd.b dsp +{ + BRA 0010 1110 .... .... @b2_bra_b + BCnd 0010 .... .... .... @b2_bcnd_b +} + +# BCnd.w dsp +BCnd 0011 101 . .... .... .... .... @b3_bcnd_w + +# BNOT #imm, dsp[rd] +# BMCnd #imm, dsp[rd] +{ + BNOT_im 1111 1100 111 imm:3 ld:2 rs:4 1111 + BMCnd_im 1111 1100 111 imm:3 ld:2 rd:4 cd:4 +} + +# BNOT #imm, rd +# BMCnd #imm, rd +{ + BNOT_ir 1111 1101 111 imm:5 1111 rd:4 + BMCnd_ir 1111 1101 111 imm:5 cd:4 rd:4 +} + +# BNOT rs, rd +# BNOT rs, dsp[rd] +{ + BNOT_rr 1111 1100 0110 1111 .... .... @b3_rs_rd + BNOT_rm 1111 1100 0110 11.. .... .... @b3_rd_ld_ub +} + +# BRA.s dsp +BRA 0000 1 ... @b1_bra_s +# BRA.w dsp +BRA 0011 1000 .... .... .... .... @b3_bra_w +# BRA.a dsp +BRA 0000 0100 .... .... .... .... .... .... @b4_bra_a +# BRA.l rs +BRA_l 0111 1111 0100 rd:4 + +BRK 0000 0000 + +# BSET #imm, dsp[rd] +BSET_im 1111 00.. .... 0... @b2_ld_imm3 +# BSET #imm, rd +BSET_ir 0111 100. .... .... @b2_rds_imm5 +# BSET rs, rd +# BSET rs, dsp[rd] +{ + BSET_rr 1111 1100 0110 0011 .... .... @b3_rs_rd + BSET_rm 1111 1100 0110 00.. .... .... @b3_rd_ld_ub +} + +# BSR.w dsp +BSR 0011 1001 .... .... .... .... @b3_bra_w +# BSR.a dsp +BSR 0000 0101 .... .... .... .... .... .... @b4_bra_a +# BSR.l rs +BSR_l 0111 1111 0101 rd:4 + +# BSET #imm, dsp[rd] +BTST_im 1111 01.. .... 0... @b2_ld_imm3 +# BSET #imm, rd +BTST_ir 0111 110. .... .... @b2_rds_imm5 +# BSET rs, rd +# BSET rs, dsp[rd] +{ + BTST_rr 1111 1100 0110 1011 .... .... @b3_rs_rd + BTST_rm 1111 1100 0110 10.. .... .... @b3_rd_ld_ub +} + +# CLRSPW psw +CLRPSW 0111 1111 1011 cb:4 + +# CMP #uimm4, rs2 +CMP_ir 0110 0001 .... .... @b2_rs2_uimm4 +# CMP #uimm8, rs2 +CMP_ir 0111 0101 0101 rs2:4 imm:8 &rri rd=0 +# CMP #imm, rs2 +CMP_ir 0111 01.. 0000 rs2:4 &rri imm=%b2_li_8 rd=0 +# CMP dsp[rs].ub, rs2 +# CMP rs, rs2 +CMP_mr 0100 01.. .... .... @b2_rd_ld_ub +# CMP dsp[rs], rs2 +CMP_mr 0000 0110 ..00 01.. .... .... @b3_rd_ld + +# DIV #imm, rd +DIV_ir 1111 1101 0111 ..00 1000 .... @b3_rd_li +# DIV dsp[rs].ub, rd +# DIV rs, rd +DIV_mr 1111 1100 0010 00.. .... .... @b3_rd_ld_ub +# DIV dsp[rs], rd +DIV_mr 0000 0110 ..10 00.. 0000 1000 .... .... @b4_rd_ldmi + +# DIVU #imm, rd +DIVU_ir 1111 1101 0111 ..00 1001 .... @b3_rd_li +# DIVU dsp[rs].ub, rd +# DIVU rs, rd +DIVU_mr 1111 1100 0010 01.. .... .... @b3_rd_ld_ub +# DIVU dsp[rs], rd +DIVU_mr 0000 0110 ..10 00.. 0000 1001 .... .... @b4_rd_ldmi + +# EMUL #imm, rd +EMUL_ir 1111 1101 0111 ..00 0110 .... @b3_rd_li +# EMUL dsp[rs].ub, rd +# EMUL rs, rd +EMUL_mr 1111 1100 0001 10.. .... .... @b3_rd_ld_ub +# EMUL dsp[rs], rd +EMUL_mr 0000 0110 ..10 00.. 0000 0110 .... .... @b4_rd_ldmi + +# EMULU #imm, rd +EMULU_ir 1111 1101 0111 ..00 0111 .... @b3_rd_li +# EMULU dsp[rs].ub, rd +# EMULU rs, rd +EMULU_mr 1111 1100 0001 11.. .... .... @b3_rd_ld_ub +# EMULU dsp[rs], rd +EMULU_mr 0000 0110 ..10 00.. 0000 0111 .... .... @b4_rd_ldmi + +# FADD #imm, rd +FADD_ir 1111 1101 0111 0010 0010 rd:4 +# FADD rs, rd +# FADD dsp[rs], rd +FADD_mr 1111 1100 1000 10.. .... .... @b3_rd_ld_ul + +# FCMP #imm, rd +FCMP_ir 1111 1101 0111 0010 0001 rd:4 +# FCMP rs, rd +# FCMP dsp[rs], rd +FCMP_mr 1111 1100 1000 01.. .... .... @b3_rd_ld_ul + +# FDIV #imm, rd +FDIV_ir 1111 1101 0111 0010 0100 rd:4 +# FDIV rs, rd +# FDIV dsp[rs], rd +FDIV_mr 1111 1100 1001 00.. .... .... @b3_rd_ld_ul + +# FMUL #imm, rd +FMUL_ir 1111 1101 0111 0010 0011 rd:4 +# FMUL rs, rd +# FMUL dsp[rs], rd +FMUL_mr 1111 1100 1000 11.. .... .... @b3_rd_ld_ul + +# FSUB #imm, rd +FSUB_ir 1111 1101 0111 0010 0000 rd:4 +# FSUB rs, rd +# FSUB dsp[rs], rd +FSUB_mr 1111 1100 1000 00.. .... .... @b3_rd_ld_ul + +# FTOI rs, rd +# FTOI dsp[rs], rd +FTOI 1111 1100 1001 01.. .... .... @b3_rd_ld_ul + +# INT #uimm8 +INT 0111 0101 0110 0000 imm:8 + +# ITOF dsp[rs].ub, rd +# ITOF rs, rd +ITOF 1111 1100 0100 01.. .... .... @b3_rd_ld_ub +# ITOF dsp[rs], rd +ITOF 0000 0110 ..10 00.. 0001 0001 .... .... @b4_rd_ldmi + +# JMP rs +JMP 0111 1111 0000 rs:4 &jreg +# JSR rs +JSR 0111 1111 0001 rs:4 &jreg + +# MACHI rs, rs2 +MACHI 1111 1101 0000 0100 rs:4 rs2:4 +# MACLO rs, rs2 +MACLO 1111 1101 0000 0101 rs:4 rs2:4 + +# MAX #imm, rd +MAX_ir 1111 1101 0111 ..00 0100 .... @b3_rd_li +# MAX dsp[rs].ub, rd +# MAX rs, rd +MAX_mr 1111 1100 0001 00.. .... .... @b3_rd_ld_ub +# MAX dsp[rs], rd +MAX_mr 0000 0110 ..10 00.. 0000 0100 .... .... @b4_rd_ldmi + +# MIN #imm, rd +MIN_ir 1111 1101 0111 ..00 0101 .... @b3_rd_li +# MIN dsp[rs].ub, rd +# MIN rs, rd +MIN_mr 1111 1100 0001 01.. .... .... @b3_rd_ld_ub +# MIN dsp[rs], rd +MIN_mr 0000 0110 ..10 00.. 0000 0101 .... .... @b4_rd_ldmi + +# MOV.b rs, dsp5[rd] +MOV_rm 1000 0 .... rd:3 . rs:3 dsp=%b2_dsp5_3 sz=0 +# MOV.w rs, dsp5[rd] +MOV_rm 1001 0 .... rd:3 . rs:3 dsp=%b2_dsp5_3 sz=1 +# MOV.l rs, dsp5[rd] +MOV_rm 1010 0 .... rd:3 . rs:3 dsp=%b2_dsp5_3 sz=2 +# MOV.b dsp5[rs], rd +MOV_mr 1000 1 .... rs:3 . rd:3 dsp=%b2_dsp5_3 sz=0 +# MOV.w dsp5[rs], rd +MOV_mr 1001 1 .... rs:3 . rd:3 dsp=%b2_dsp5_3 sz=1 +# MOV.l dsp5[rs], rd +MOV_mr 1010 1 .... rs:3 . rd:3 dsp=%b2_dsp5_3 sz=2 +# MOV.l #uimm4, rd +MOV_ir 0110 0110 imm:4 rd:4 +# MOV.b #imm8, dsp5[rd] +MOV_im 0011 1100 . rd:3 .... imm:8 sz=0 dsp=%b3_dsp5_8 +# MOV.w #imm8, dsp5[rd] +MOV_im 0011 1101 . rd:3 .... imm:8 sz=1 dsp=%b3_dsp5_8 +# MOV.l #imm8, dsp5[rd] +MOV_im 0011 1110 . rd:3 .... imm:8 sz=2 dsp=%b3_dsp5_8 +# MOV.l #imm8, rd +MOV_ir 0111 0101 0100 rd:4 imm:8 +# MOV.l #mm8, rd +MOV_ir 1111 1011 rd:4 .. 10 imm=%b2_li_2 +# MOV.<bwl> #imm, [rd] +MOV_im 1111 1000 rd:4 .. sz:2 dsp=0 imm=%b2_li_2 +# MOV.<bwl> #imm, dsp8[rd] +MOV_im 1111 1001 rd:4 .. sz:2 dsp:8 imm=%b3_li_10 +# MOV.<bwl> #imm, dsp16[rd] +MOV_im 1111 1010 rd:4 .. sz:2 .... .... .... .... \ + imm=%b4_li_18 dsp=%b4_dsp_16 +# MOV.<bwl> [ri,rb], rd +MOV_ar 1111 1110 01 sz:2 ri:4 rb:4 rd:4 +# MOV.<bwl> rs, [ri,rb] +MOV_ra 1111 1110 00 sz:2 ri:4 rb:4 rs:4 +# Note ldd=3 and lds=3 indicate register src or dst +# MOV.b rs, rd +# MOV.b rs, dsp[rd] +# MOV.b dsp[rs], rd +# MOV.b dsp[rs], dsp[rd] +MOV_mm 1100 ldd:2 lds:2 rs:4 rd:4 sz=0 +# MOV.w rs, rd +# MOV.w rs, dsp[rd] +# MOV.w dsp[rs], rd +# MOV.w dsp[rs], dsp[rd] +MOV_mm 1101 ldd:2 lds:2 rs:4 rd:4 sz=1 +# MOV.l rs, rd +# MOV.l rs, dsp[rd] +# MOV.l dsp[rs], rd +# MOV.l dsp[rs], dsp[rd] +MOV_mm 1110 ldd:2 lds:2 rs:4 rd:4 sz=2 +# MOV.l rs, [rd+] +# MOV.l rs, [-rd] +MOV_rp 1111 1101 0010 0 ad:1 sz:2 rd:4 rs:4 +# MOV.l [rs+], rd +# MOV.l [-rs], rd +MOV_pr 1111 1101 0010 1 ad:1 sz:2 rd:4 rs:4 + +# MOVU.<bw> dsp5[rs], rd +MOVU_mr 1011 sz:1 ... . rs:3 . rd:3 dsp=%b2_dsp5_3 +# MOVU.<bw> [rs], rd +MOVU_mr 0101 1 sz:1 00 rs:4 rd:4 dsp=0 +# MOVU.<bw> dsp8[rs], rd +MOVU_mr 0101 1 sz:1 01 rs:4 rd:4 dsp:8 +# MOVU.<bw> dsp16[rs], rd +MOVU_mr 0101 1 sz:1 10 rs:4 rd:4 .... .... .... .... dsp=%b4_dsp_16 +# MOVU.<bw> rs, rd +MOVU_rr 0101 1 sz:1 11 rs:4 rd:4 +# MOVU.<bw> [ri, rb], rd +MOVU_ar 1111 1110 110 sz:1 ri:4 rb:4 rd:4 +# MOVU.<bw> [rs+], rd +MOVU_pr 1111 1101 0011 1 ad:1 0 sz:1 rd:4 rs:4 + +# MUL #uimm4, rd +MUL_ir 0110 0011 .... .... @b2_rds_uimm4 +# MUL #imm4, rd +MUL_ir 0111 01.. 0001 .... @b2_rds_li +# MUL dsp[rs].ub, rd +# MUL rs, rd +MUL_mr 0100 11.. .... .... @b2_rd_ld_ub +# MUL dsp[rs], rd +MUL_mr 0000 0110 ..00 11.. .... .... @b3_rd_ld +# MOV rs, rs2, rd +MUL_rrr 1111 1111 0011 .... .... .... @b3_rd_rs_rs2 + +# MULHI rs, rs2 +MULHI 1111 1101 0000 0000 rs:4 rs2:4 +# MULLO rs, rs2 +MULLO 1111 1101 0000 0001 rs:4 rs2:4 + +# MVFACHI rd +MVFACHI 1111 1101 0001 1111 0000 rd:4 +# MVFACMI rd +MVFACMI 1111 1101 0001 1111 0010 rd:4 + +# MVFC cr, rd +MVFC 1111 1101 0110 1010 cr:4 rd:4 + +# MVTACHI rs +MVTACHI 1111 1101 0001 0111 0000 rs:4 +# MVTACLO rs +MVTACLO 1111 1101 0001 0111 0001 rs:4 + +# MVTC #imm, cr +MVTC_i 1111 1101 0111 ..11 0000 cr:4 imm=%b3_li_10 +# MVTC rs, cr +MVTC_r 1111 1101 0110 1000 rs:4 cr:4 + +# MVTIPL #imm +MVTIPL 0111 0101 0111 0000 0000 imm:4 + +# NEG rd +NEG_rr 0111 1110 0001 .... @b2_rds +# NEG rs, rd +NEG_rr 1111 1100 0000 0111 .... .... @b3_rd_rs + +NOP 0000 0011 + +# NOT rd +NOT_rr 0111 1110 0000 .... @b2_rds +# NOT rs, rd +NOT_rr 1111 1100 0011 1011 .... .... @b3_rd_rs + +# OR #uimm4, rd +OR_ir 0110 0101 .... .... @b2_rds_uimm4 +# OR #imm, rd +OR_ir 0111 01.. 0011 .... @b2_rds_li +# OR dsp[rs].ub, rd +# OR rs, rd +OR_mr 0101 01.. .... .... @b2_rd_ld_ub +# OR dsp[rs], rd +OR_mr 0000 0110 .. 0101 .. .... .... @b3_rd_ld +# OR rs, rs2, rd +OR_rrr 1111 1111 0101 .... .... .... @b3_rd_rs_rs2 + +# POP cr +POPC 0111 1110 1110 cr:4 +# POP rd-rd2 +POPM 0110 1111 rd:4 rd2:4 + +# POP rd +# PUSH.<bwl> rs +{ + POP 0111 1110 1011 rd:4 + PUSH_r 0111 1110 10 sz:2 rs:4 +} +# PUSH.<bwl> dsp[rs] +PUSH_m 1111 01 ld:2 rs:4 10 sz:2 +# PUSH cr +PUSHC 0111 1110 1100 cr:4 +# PUSHM rs-rs2 +PUSHM 0110 1110 rs:4 rs2:4 + +# RACW #imm +RACW 1111 1101 0001 1000 000 imm:1 0000 + +# REVL rs,rd +REVL 1111 1101 0110 0111 .... .... @b3_rd_rs +# REVW rs,rd +REVW 1111 1101 0110 0101 .... .... @b3_rd_rs + +# SMOVF +# RPMA.<bwl> +{ + SMOVF 0111 1111 1000 1111 + RMPA 0111 1111 1000 11 sz:2 +} + +# ROLC rd +ROLC 0111 1110 0101 .... @b2_rds +# RORC rd +RORC 0111 1110 0100 .... @b2_rds + +# ROTL #imm, rd +ROTL_ir 1111 1101 0110 111. .... .... @b3_rds_imm5 +# ROTL rs, rd +ROTL_rr 1111 1101 0110 0110 .... .... @b3_rd_rs + +# ROTR #imm, rd +ROTR_ir 1111 1101 0110 110. .... .... @b3_rds_imm5 +# ROTR #imm, rd +ROTR_rr 1111 1101 0110 0100 .... .... @b3_rd_rs + +# ROUND rs,rd +# ROUND dsp[rs],rd +ROUND 1111 1100 1001 10 .. .... .... @b3_ld_rd_rs + +RTE 0111 1111 1001 0101 + +RTFI 0111 1111 1001 0100 + +RTS 0000 0010 + +# RTSD #imm +RTSD_i 0110 0111 imm:8 +# RTSD #imm, rd-rd2 +RTSD_irr 0011 1111 rd:4 rd2:4 imm:8 + +# SAT rd +SAT 0111 1110 0011 .... @b2_rds +# SATR +SATR 0111 1111 1001 0011 + +# SBB rs, rd +SBB_rr 1111 1100 0000 0011 .... .... @b3_rd_rs +# SBB dsp[rs].l, rd +# Note only mi==2 allowed. +SBB_mr 0000 0110 ..10 00.. 0000 0000 .... .... @b4_rd_ldmi + +# SCCnd dsp[rd] +# SCCnd rd +SCCnd 1111 1100 1101 .... .... .... @b3_sz_ld_rd_cd + +# SETPSW psw +SETPSW 0111 1111 1010 cb:4 + +# SHAR #imm, rd +SHAR_irr 0110 101. .... .... @b2_rds_imm5 +# SHAR #imm, rs, rd +SHAR_irr 1111 1101 101. .... .... .... @b3_rd_rs_imm5 +# SHAR rs, rd +SHAR_rr 1111 1101 0110 0001 .... .... @b3_rd_rs + +# SHLL #imm, rd +SHLL_irr 0110 110. .... .... @b2_rds_imm5 +# SHLL #imm, rs, rd +SHLL_irr 1111 1101 110. .... .... .... @b3_rd_rs_imm5 +# SHLL rs, rd +SHLL_rr 1111 1101 0110 0010 .... .... @b3_rd_rs + +# SHLR #imm, rd +SHLR_irr 0110 100. .... .... @b2_rds_imm5 +# SHLR #imm, rs, rd +SHLR_irr 1111 1101 100. .... .... .... @b3_rd_rs_imm5 +# SHLR rs, rd +SHLR_rr 1111 1101 0110 0000 .... .... @b3_rd_rs + +# SMOVB +# SSTR.<bwl> +{ + SMOVB 0111 1111 1000 1011 + SSTR 0111 1111 1000 10 sz:2 +} + +# STNZ #imm, rd +STNZ 1111 1101 0111 ..00 1111 .... @b3_rd_li +# STZ #imm, rd +STZ 1111 1101 0111 ..00 1110 .... @b3_rd_li + +# SUB #uimm4, rd +SUB_ir 0110 0000 .... .... @b2_rds_uimm4 +# SUB dsp[rs].ub, rd +# SUB rs, rd +SUB_mr 0100 00.. .... .... @b2_rd_ld_ub +# SUB dsp[rs], rd +SUB_mr 0000 0110 ..00 00.. .... .... @b3_rd_ld +# SUB rs, rs2, rd +SUB_rrr 1111 1111 0000 .... .... .... @b3_rd_rs_rs2 + +# SCMPU +# SUNTIL.<bwl> +{ + SCMPU 0111 1111 1000 0011 + SUNTIL 0111 1111 1000 00 sz:2 +} + +# SMOVU +# SWHILE.<bwl> +{ + SMOVU 0111 1111 1000 0111 + SWHILE 0111 1111 1000 01 sz:2 +} + +# TST #imm, rd +TST_ir 1111 1101 0111 ..00 1100 .... @b3_rd_li +# TST dsp[rs].ub, rd +# TST rs, rd +TST_mr 1111 1100 0011 00.. .... .... @b3_rd_ld_ub +# TST dsp[rs], rd +TST_mr 0000 0110 ..10 00.. 0000 1100 .... .... @b4_rd_ldmi + +WAIT 0111 1111 1001 0110 + +# XCHG rs, rd +# XCHG dsp[rs].ub, rd +{ + XCHG_rr 1111 1100 0100 0011 .... .... @b3_rd_rs + XCHG_mr 1111 1100 0100 00.. .... .... @b3_rd_ld_ub +} +# XCHG dsp[rs], rd +XCHG_mr 0000 0110 ..10 00.. 0001 0000 .... .... @b4_rd_ldmi + +# XOR #imm, rd +XOR_ir 1111 1101 0111 ..00 1101 .... @b3_rd_li +# XOR dsp[rs].ub, rd +# XOR rs, rd +XOR_mr 1111 1100 0011 01.. .... .... @b3_rd_ld_ub +# XOR dsp[rs], rd +XOR_mr 0000 0110 ..10 00.. 0000 1101 .... .... @b4_rd_ldmi diff --git a/target/rx/op_helper.c b/target/rx/op_helper.c new file mode 100644 index 0000000000..f89d294f2b --- /dev/null +++ b/target/rx/op_helper.c @@ -0,0 +1,470 @@ +/* + * RX helper functions + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" +#include "fpu/softfloat.h" + +static inline void QEMU_NORETURN raise_exception(CPURXState *env, int index, + uintptr_t retaddr); + +static void _set_psw(CPURXState *env, uint32_t psw, uint32_t rte) +{ + uint32_t prev_u; + prev_u = env->psw_u; + rx_cpu_unpack_psw(env, psw, rte); + if (prev_u != env->psw_u) { + /* switch r0 */ + if (env->psw_u) { + env->isp = env->regs[0]; + env->regs[0] = env->usp; + } else { + env->usp = env->regs[0]; + env->regs[0] = env->isp; + } + } +} + +void helper_set_psw(CPURXState *env, uint32_t psw) +{ + _set_psw(env, psw, 0); +} + +void helper_set_psw_rte(CPURXState *env, uint32_t psw) +{ + _set_psw(env, psw, 1); +} + +uint32_t helper_pack_psw(CPURXState *env) +{ + return rx_cpu_pack_psw(env); +} + +#define SET_FPSW(b) \ + do { \ + env->fpsw = FIELD_DP32(env->fpsw, FPSW, C ## b, 1); \ + if (!FIELD_EX32(env->fpsw, FPSW, E ## b)) { \ + env->fpsw = FIELD_DP32(env->fpsw, FPSW, F ## b, 1); \ + } \ + } while (0) + +/* fp operations */ +static void update_fpsw(CPURXState *env, float32 ret, uintptr_t retaddr) +{ + int xcpt, cause, enable; + + env->psw_z = ret & ~(1 << 31); /* mask sign bit */ + env->psw_s = ret; + + xcpt = get_float_exception_flags(&env->fp_status); + + /* Clear the cause entries */ + env->fpsw = FIELD_DP32(env->fpsw, FPSW, CAUSE, 0); + + /* set FPSW */ + if (unlikely(xcpt)) { + if (xcpt & float_flag_invalid) { + SET_FPSW(V); + } + if (xcpt & float_flag_divbyzero) { + SET_FPSW(Z); + } + if (xcpt & float_flag_overflow) { + SET_FPSW(O); + } + if (xcpt & float_flag_underflow) { + SET_FPSW(U); + } + if (xcpt & float_flag_inexact) { + SET_FPSW(X); + } + if ((xcpt & (float_flag_input_denormal + | float_flag_output_denormal)) + && !FIELD_EX32(env->fpsw, FPSW, DN)) { + env->fpsw = FIELD_DP32(env->fpsw, FPSW, CE, 1); + } + + /* update FPSW_FLAG_S */ + if (FIELD_EX32(env->fpsw, FPSW, FLAGS) != 0) { + env->fpsw = FIELD_DP32(env->fpsw, FPSW, FS, 1); + } + + /* Generate an exception if enabled */ + cause = FIELD_EX32(env->fpsw, FPSW, CAUSE); + enable = FIELD_EX32(env->fpsw, FPSW, ENABLE); + enable |= 1 << 5; /* CE always enabled */ + if (cause & enable) { + raise_exception(env, 21, retaddr); + } + } +} + +void helper_set_fpsw(CPURXState *env, uint32_t val) +{ + static const int roundmode[] = { + float_round_nearest_even, + float_round_to_zero, + float_round_up, + float_round_down, + }; + uint32_t fpsw = env->fpsw; + fpsw |= 0x7fffff03; + val &= ~0x80000000; + fpsw &= val; + FIELD_DP32(fpsw, FPSW, FS, FIELD_EX32(fpsw, FPSW, FLAGS) != 0); + env->fpsw = fpsw; + set_float_rounding_mode(roundmode[FIELD_EX32(env->fpsw, FPSW, RM)], + &env->fp_status); +} + +#define FLOATOP(op, func) \ + float32 helper_##op(CPURXState *env, float32 t0, float32 t1) \ + { \ + float32 ret; \ + ret = func(t0, t1, &env->fp_status); \ + update_fpsw(env, *(uint32_t *)&ret, GETPC()); \ + return ret; \ + } + +FLOATOP(fadd, float32_add) +FLOATOP(fsub, float32_sub) +FLOATOP(fmul, float32_mul) +FLOATOP(fdiv, float32_div) + +void helper_fcmp(CPURXState *env, float32 t0, float32 t1) +{ + int st; + st = float32_compare(t0, t1, &env->fp_status); + update_fpsw(env, 0, GETPC()); + env->psw_z = 1; + env->psw_s = env->psw_o = 0; + switch (st) { + case float_relation_equal: + env->psw_z = 0; + break; + case float_relation_less: + env->psw_s = -1; + break; + case float_relation_unordered: + env->psw_o = -1; + break; + } +} + +uint32_t helper_ftoi(CPURXState *env, float32 t0) +{ + uint32_t ret; + ret = float32_to_int32_round_to_zero(t0, &env->fp_status); + update_fpsw(env, ret, GETPC()); + return ret; +} + +uint32_t helper_round(CPURXState *env, float32 t0) +{ + uint32_t ret; + ret = float32_to_int32(t0, &env->fp_status); + update_fpsw(env, ret, GETPC()); + return ret; +} + +float32 helper_itof(CPURXState *env, uint32_t t0) +{ + float32 ret; + ret = int32_to_float32(t0, &env->fp_status); + update_fpsw(env, ret, GETPC()); + return ret; +} + +/* string operations */ +void helper_scmpu(CPURXState *env) +{ + uint8_t tmp0, tmp1; + if (env->regs[3] == 0) { + return; + } + while (env->regs[3] != 0) { + tmp0 = cpu_ldub_data_ra(env, env->regs[1]++, GETPC()); + tmp1 = cpu_ldub_data_ra(env, env->regs[2]++, GETPC()); + env->regs[3]--; + if (tmp0 != tmp1 || tmp0 == '\0') { + break; + } + } + env->psw_z = tmp0 - tmp1; + env->psw_c = (tmp0 >= tmp1); +} + +static uint32_t (* const cpu_ldufn[])(CPUArchState *env, + target_ulong ptr, + uintptr_t retaddr) = { + cpu_ldub_data_ra, cpu_lduw_data_ra, cpu_ldl_data_ra, +}; + +static uint32_t (* const cpu_ldfn[])(CPUArchState *env, + target_ulong ptr, + uintptr_t retaddr) = { + cpu_ldub_data_ra, cpu_lduw_data_ra, cpu_ldl_data_ra, +}; + +static void (* const cpu_stfn[])(CPUArchState *env, + target_ulong ptr, + uint32_t val, + uintptr_t retaddr) = { + cpu_stb_data_ra, cpu_stw_data_ra, cpu_stl_data_ra, +}; + +void helper_sstr(CPURXState *env, uint32_t sz) +{ + tcg_debug_assert(sz < 3); + while (env->regs[3] != 0) { + cpu_stfn[sz](env, env->regs[1], env->regs[2], GETPC()); + env->regs[1] += 1 << sz; + env->regs[3]--; + } +} + +#define OP_SMOVU 1 +#define OP_SMOVF 0 +#define OP_SMOVB 2 + +static void smov(uint32_t mode, CPURXState *env) +{ + uint8_t tmp; + int dir; + + dir = (mode & OP_SMOVB) ? -1 : 1; + while (env->regs[3] != 0) { + tmp = cpu_ldub_data_ra(env, env->regs[2], GETPC()); + cpu_stb_data_ra(env, env->regs[1], tmp, GETPC()); + env->regs[1] += dir; + env->regs[2] += dir; + env->regs[3]--; + if ((mode & OP_SMOVU) && tmp == 0) { + break; + } + } +} + +void helper_smovu(CPURXState *env) +{ + smov(OP_SMOVU, env); +} + +void helper_smovf(CPURXState *env) +{ + smov(OP_SMOVF, env); +} + +void helper_smovb(CPURXState *env) +{ + smov(OP_SMOVB, env); +} + + +void helper_suntil(CPURXState *env, uint32_t sz) +{ + uint32_t tmp; + tcg_debug_assert(sz < 3); + if (env->regs[3] == 0) { + return ; + } + while (env->regs[3] != 0) { + tmp = cpu_ldufn[sz](env, env->regs[1], GETPC()); + env->regs[1] += 1 << sz; + env->regs[3]--; + if (tmp == env->regs[2]) { + break; + } + } + env->psw_z = tmp - env->regs[2]; + env->psw_c = (tmp <= env->regs[2]); +} + +void helper_swhile(CPURXState *env, uint32_t sz) +{ + uint32_t tmp; + tcg_debug_assert(sz < 3); + if (env->regs[3] == 0) { + return ; + } + while (env->regs[3] != 0) { + tmp = cpu_ldufn[sz](env, env->regs[1], GETPC()); + env->regs[1] += 1 << sz; + env->regs[3]--; + if (tmp != env->regs[2]) { + break; + } + } + env->psw_z = env->regs[3]; + env->psw_c = (tmp <= env->regs[2]); +} + +/* accumlator operations */ +void helper_rmpa(CPURXState *env, uint32_t sz) +{ + uint64_t result_l, prev; + int32_t result_h; + int64_t tmp0, tmp1; + + if (env->regs[3] == 0) { + return; + } + result_l = env->regs[5]; + result_l <<= 32; + result_l |= env->regs[4]; + result_h = env->regs[6]; + env->psw_o = 0; + + while (env->regs[3] != 0) { + tmp0 = cpu_ldfn[sz](env, env->regs[1], GETPC()); + tmp1 = cpu_ldfn[sz](env, env->regs[2], GETPC()); + tmp0 *= tmp1; + prev = result_l; + result_l += tmp0; + /* carry / bollow */ + if (tmp0 < 0) { + if (prev > result_l) { + result_h--; + } + } else { + if (prev < result_l) { + result_h++; + } + } + + env->regs[1] += 1 << sz; + env->regs[2] += 1 << sz; + } + env->psw_s = result_h; + env->psw_o = (result_h != 0 && result_h != -1) << 31; + env->regs[6] = result_h; + env->regs[5] = result_l >> 32; + env->regs[4] = result_l & 0xffffffff; +} + +void helper_racw(CPURXState *env, uint32_t imm) +{ + int64_t acc; + acc = env->acc; + acc <<= (imm + 1); + acc += 0x0000000080000000LL; + if (acc > 0x00007fff00000000LL) { + acc = 0x00007fff00000000LL; + } else if (acc < -0x800000000000LL) { + acc = -0x800000000000LL; + } else { + acc &= 0xffffffff00000000LL; + } + env->acc = acc; +} + +void helper_satr(CPURXState *env) +{ + if (env->psw_o >> 31) { + if ((int)env->psw_s < 0) { + env->regs[6] = 0x00000000; + env->regs[5] = 0x7fffffff; + env->regs[4] = 0xffffffff; + } else { + env->regs[6] = 0xffffffff; + env->regs[5] = 0x80000000; + env->regs[4] = 0x00000000; + } + } +} + +/* div */ +uint32_t helper_div(CPURXState *env, uint32_t num, uint32_t den) +{ + uint32_t ret = num; + if (!((num == INT_MIN && den == -1) || den == 0)) { + ret = (int32_t)num / (int32_t)den; + env->psw_o = 0; + } else { + env->psw_o = -1; + } + return ret; +} + +uint32_t helper_divu(CPURXState *env, uint32_t num, uint32_t den) +{ + uint32_t ret = num; + if (den != 0) { + ret = num / den; + env->psw_o = 0; + } else { + env->psw_o = -1; + } + return ret; +} + +/* exception */ +static inline void QEMU_NORETURN raise_exception(CPURXState *env, int index, + uintptr_t retaddr) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = index; + cpu_loop_exit_restore(cs, retaddr); +} + +void QEMU_NORETURN helper_raise_privilege_violation(CPURXState *env) +{ + raise_exception(env, 20, GETPC()); +} + +void QEMU_NORETURN helper_raise_access_fault(CPURXState *env) +{ + raise_exception(env, 21, GETPC()); +} + +void QEMU_NORETURN helper_raise_illegal_instruction(CPURXState *env) +{ + raise_exception(env, 23, GETPC()); +} + +void QEMU_NORETURN helper_wait(CPURXState *env) +{ + CPUState *cs = env_cpu(env); + + cs->halted = 1; + env->in_sleep = 1; + raise_exception(env, EXCP_HLT, 0); +} + +void QEMU_NORETURN helper_debug(CPURXState *env) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = EXCP_DEBUG; + cpu_loop_exit(cs); +} + +void QEMU_NORETURN helper_rxint(CPURXState *env, uint32_t vec) +{ + raise_exception(env, 0x100 + vec, 0); +} + +void QEMU_NORETURN helper_rxbrk(CPURXState *env) +{ + raise_exception(env, 0x100, 0); +} diff --git a/target/rx/translate.c b/target/rx/translate.c new file mode 100644 index 0000000000..b3d7305f23 --- /dev/null +++ b/target/rx/translate.c @@ -0,0 +1,2439 @@ +/* + * RX translation + * + * Copyright (c) 2019 Yoshinori Sato + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "qemu/qemu-print.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "tcg/tcg-op.h" +#include "exec/cpu_ldst.h" +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" +#include "exec/translator.h" +#include "trace-tcg.h" +#include "exec/log.h" + +typedef struct DisasContext { + DisasContextBase base; + CPURXState *env; + uint32_t pc; +} DisasContext; + +typedef struct DisasCompare { + TCGv value; + TCGv temp; + TCGCond cond; +} DisasCompare; + +const char *rx_crname(uint8_t cr) +{ + static const char *cr_names[] = { + "psw", "pc", "usp", "fpsw", "", "", "", "", + "bpsw", "bpc", "isp", "fintv", "intb", "", "", "" + }; + if (cr >= ARRAY_SIZE(cr_names)) { + return "illegal"; + } + return cr_names[cr]; +} + +/* Target-specific values for dc->base.is_jmp. */ +#define DISAS_JUMP DISAS_TARGET_0 +#define DISAS_UPDATE DISAS_TARGET_1 +#define DISAS_EXIT DISAS_TARGET_2 + +/* global register indexes */ +static TCGv cpu_regs[16]; +static TCGv cpu_psw_o, cpu_psw_s, cpu_psw_z, cpu_psw_c; +static TCGv cpu_psw_i, cpu_psw_pm, cpu_psw_u, cpu_psw_ipl; +static TCGv cpu_usp, cpu_fpsw, cpu_bpsw, cpu_bpc, cpu_isp; +static TCGv cpu_fintv, cpu_intb, cpu_pc; +static TCGv_i64 cpu_acc; + +#define cpu_sp cpu_regs[0] + +#include "exec/gen-icount.h" + +/* decoder helper */ +static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn, + int i, int n) +{ + while (++i <= n) { + uint8_t b = cpu_ldub_code(ctx->env, ctx->base.pc_next++); + insn |= b << (32 - i * 8); + } + return insn; +} + +static uint32_t li(DisasContext *ctx, int sz) +{ + int32_t tmp, addr; + CPURXState *env = ctx->env; + addr = ctx->base.pc_next; + + tcg_debug_assert(sz < 4); + switch (sz) { + case 1: + ctx->base.pc_next += 1; + return cpu_ldsb_code(env, addr); + case 2: + ctx->base.pc_next += 2; + return cpu_ldsw_code(env, addr); + case 3: + ctx->base.pc_next += 3; + tmp = cpu_ldsb_code(env, addr + 2) << 16; + tmp |= cpu_lduw_code(env, addr) & 0xffff; + return tmp; + case 0: + ctx->base.pc_next += 4; + return cpu_ldl_code(env, addr); + } + return 0; +} + +static int bdsp_s(DisasContext *ctx, int d) +{ + /* + * 0 -> 8 + * 1 -> 9 + * 2 -> 10 + * 3 -> 3 + * : + * 7 -> 7 + */ + if (d < 3) { + d += 8; + } + return d; +} + +/* Include the auto-generated decoder. */ +#include "decode.inc.c" + +void rx_cpu_dump_state(CPUState *cs, FILE *f, int flags) +{ + RXCPU *cpu = RXCPU(cs); + CPURXState *env = &cpu->env; + int i; + uint32_t psw; + + psw = rx_cpu_pack_psw(env); + qemu_fprintf(f, "pc=0x%08x psw=0x%08x\n", + env->pc, psw); + for (i = 0; i < 16; i += 4) { + qemu_fprintf(f, "r%d=0x%08x r%d=0x%08x r%d=0x%08x r%d=0x%08x\n", + i, env->regs[i], i + 1, env->regs[i + 1], + i + 2, env->regs[i + 2], i + 3, env->regs[i + 3]); + } +} + +static bool use_goto_tb(DisasContext *dc, target_ulong dest) +{ + if (unlikely(dc->base.singlestep_enabled)) { + return false; + } else { + return true; + } +} + +static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) +{ + if (use_goto_tb(dc, dest)) { + tcg_gen_goto_tb(n); + tcg_gen_movi_i32(cpu_pc, dest); + tcg_gen_exit_tb(dc->base.tb, n); + } else { + tcg_gen_movi_i32(cpu_pc, dest); + if (dc->base.singlestep_enabled) { + gen_helper_debug(cpu_env); + } else { + tcg_gen_lookup_and_goto_ptr(); + } + } + dc->base.is_jmp = DISAS_NORETURN; +} + +/* generic load wrapper */ +static inline void rx_gen_ld(unsigned int size, TCGv reg, TCGv mem) +{ + tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_SIGN | MO_TE); +} + +/* unsigned load wrapper */ +static inline void rx_gen_ldu(unsigned int size, TCGv reg, TCGv mem) +{ + tcg_gen_qemu_ld_i32(reg, mem, 0, size | MO_TE); +} + +/* generic store wrapper */ +static inline void rx_gen_st(unsigned int size, TCGv reg, TCGv mem) +{ + tcg_gen_qemu_st_i32(reg, mem, 0, size | MO_TE); +} + +/* [ri, rb] */ +static inline void rx_gen_regindex(DisasContext *ctx, TCGv mem, + int size, int ri, int rb) +{ + tcg_gen_shli_i32(mem, cpu_regs[ri], size); + tcg_gen_add_i32(mem, mem, cpu_regs[rb]); +} + +/* dsp[reg] */ +static inline TCGv rx_index_addr(DisasContext *ctx, TCGv mem, + int ld, int size, int reg) +{ + uint32_t dsp; + + tcg_debug_assert(ld < 3); + switch (ld) { + case 0: + return cpu_regs[reg]; + case 1: + dsp = cpu_ldub_code(ctx->env, ctx->base.pc_next) << size; + tcg_gen_addi_i32(mem, cpu_regs[reg], dsp); + ctx->base.pc_next += 1; + return mem; + case 2: + dsp = cpu_lduw_code(ctx->env, ctx->base.pc_next) << size; + tcg_gen_addi_i32(mem, cpu_regs[reg], dsp); + ctx->base.pc_next += 2; + return mem; + } + return NULL; +} + +static inline MemOp mi_to_mop(unsigned mi) +{ + static const MemOp mop[5] = { MO_SB, MO_SW, MO_UL, MO_UW, MO_UB }; + tcg_debug_assert(mi < 5); + return mop[mi]; +} + +/* load source operand */ +static inline TCGv rx_load_source(DisasContext *ctx, TCGv mem, + int ld, int mi, int rs) +{ + TCGv addr; + MemOp mop; + if (ld < 3) { + mop = mi_to_mop(mi); + addr = rx_index_addr(ctx, mem, ld, mop & MO_SIZE, rs); + tcg_gen_qemu_ld_i32(mem, addr, 0, mop | MO_TE); + return mem; + } else { + return cpu_regs[rs]; + } +} + +/* Processor mode check */ +static int is_privileged(DisasContext *ctx, int is_exception) +{ + if (FIELD_EX32(ctx->base.tb->flags, PSW, PM)) { + if (is_exception) { + gen_helper_raise_privilege_violation(cpu_env); + } + return 0; + } else { + return 1; + } +} + +/* generate QEMU condition */ +static void psw_cond(DisasCompare *dc, uint32_t cond) +{ + tcg_debug_assert(cond < 16); + switch (cond) { + case 0: /* z */ + dc->cond = TCG_COND_EQ; + dc->value = cpu_psw_z; + break; + case 1: /* nz */ + dc->cond = TCG_COND_NE; + dc->value = cpu_psw_z; + break; + case 2: /* c */ + dc->cond = TCG_COND_NE; + dc->value = cpu_psw_c; + break; + case 3: /* nc */ + dc->cond = TCG_COND_EQ; + dc->value = cpu_psw_c; + break; + case 4: /* gtu (C& ~Z) == 1 */ + case 5: /* leu (C& ~Z) == 0 */ + tcg_gen_setcondi_i32(TCG_COND_NE, dc->temp, cpu_psw_z, 0); + tcg_gen_and_i32(dc->temp, dc->temp, cpu_psw_c); + dc->cond = (cond == 4) ? TCG_COND_NE : TCG_COND_EQ; + dc->value = dc->temp; + break; + case 6: /* pz (S == 0) */ + dc->cond = TCG_COND_GE; + dc->value = cpu_psw_s; + break; + case 7: /* n (S == 1) */ + dc->cond = TCG_COND_LT; + dc->value = cpu_psw_s; + break; + case 8: /* ge (S^O)==0 */ + case 9: /* lt (S^O)==1 */ + tcg_gen_xor_i32(dc->temp, cpu_psw_o, cpu_psw_s); + dc->cond = (cond == 8) ? TCG_COND_GE : TCG_COND_LT; + dc->value = dc->temp; + break; + case 10: /* gt ((S^O)|Z)==0 */ + case 11: /* le ((S^O)|Z)==1 */ + tcg_gen_xor_i32(dc->temp, cpu_psw_o, cpu_psw_s); + tcg_gen_sari_i32(dc->temp, dc->temp, 31); + tcg_gen_andc_i32(dc->temp, cpu_psw_z, dc->temp); + dc->cond = (cond == 10) ? TCG_COND_NE : TCG_COND_EQ; + dc->value = dc->temp; + break; + case 12: /* o */ + dc->cond = TCG_COND_LT; + dc->value = cpu_psw_o; + break; + case 13: /* no */ + dc->cond = TCG_COND_GE; + dc->value = cpu_psw_o; + break; + case 14: /* always true */ + dc->cond = TCG_COND_ALWAYS; + dc->value = dc->temp; + break; + case 15: /* always false */ + dc->cond = TCG_COND_NEVER; + dc->value = dc->temp; + break; + } +} + +static void move_from_cr(TCGv ret, int cr, uint32_t pc) +{ + TCGv z = tcg_const_i32(0); + switch (cr) { + case 0: /* PSW */ + gen_helper_pack_psw(ret, cpu_env); + break; + case 1: /* PC */ + tcg_gen_movi_i32(ret, pc); + break; + case 2: /* USP */ + tcg_gen_movcond_i32(TCG_COND_NE, ret, + cpu_psw_u, z, cpu_sp, cpu_usp); + break; + case 3: /* FPSW */ + tcg_gen_mov_i32(ret, cpu_fpsw); + break; + case 8: /* BPSW */ + tcg_gen_mov_i32(ret, cpu_bpsw); + break; + case 9: /* BPC */ + tcg_gen_mov_i32(ret, cpu_bpc); + break; + case 10: /* ISP */ + tcg_gen_movcond_i32(TCG_COND_EQ, ret, + cpu_psw_u, z, cpu_sp, cpu_isp); + break; + case 11: /* FINTV */ + tcg_gen_mov_i32(ret, cpu_fintv); + break; + case 12: /* INTB */ + tcg_gen_mov_i32(ret, cpu_intb); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Unimplement control register %d", cr); + /* Unimplement registers return 0 */ + tcg_gen_movi_i32(ret, 0); + break; + } + tcg_temp_free(z); +} + +static void move_to_cr(DisasContext *ctx, TCGv val, int cr) +{ + TCGv z; + if (cr >= 8 && !is_privileged(ctx, 0)) { + /* Some control registers can only be written in privileged mode. */ + qemu_log_mask(LOG_GUEST_ERROR, + "disallow control register write %s", rx_crname(cr)); + return; + } + z = tcg_const_i32(0); + switch (cr) { + case 0: /* PSW */ + gen_helper_set_psw(cpu_env, val); + break; + /* case 1: to PC not supported */ + case 2: /* USP */ + tcg_gen_mov_i32(cpu_usp, val); + tcg_gen_movcond_i32(TCG_COND_NE, cpu_sp, + cpu_psw_u, z, cpu_usp, cpu_sp); + break; + case 3: /* FPSW */ + gen_helper_set_fpsw(cpu_env, val); + break; + case 8: /* BPSW */ + tcg_gen_mov_i32(cpu_bpsw, val); + break; + case 9: /* BPC */ + tcg_gen_mov_i32(cpu_bpc, val); + break; + case 10: /* ISP */ + tcg_gen_mov_i32(cpu_isp, val); + /* if PSW.U is 0, copy isp to r0 */ + tcg_gen_movcond_i32(TCG_COND_EQ, cpu_sp, + cpu_psw_u, z, cpu_isp, cpu_sp); + break; + case 11: /* FINTV */ + tcg_gen_mov_i32(cpu_fintv, val); + break; + case 12: /* INTB */ + tcg_gen_mov_i32(cpu_intb, val); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "Unimplement control register %d", cr); + break; + } + tcg_temp_free(z); +} + +static void push(TCGv val) +{ + tcg_gen_subi_i32(cpu_sp, cpu_sp, 4); + rx_gen_st(MO_32, val, cpu_sp); +} + +static void pop(TCGv ret) +{ + rx_gen_ld(MO_32, ret, cpu_sp); + tcg_gen_addi_i32(cpu_sp, cpu_sp, 4); +} + +/* mov.<bwl> rs,dsp5[rd] */ +static bool trans_MOV_rm(DisasContext *ctx, arg_MOV_rm *a) +{ + TCGv mem; + mem = tcg_temp_new(); + tcg_gen_addi_i32(mem, cpu_regs[a->rd], a->dsp << a->sz); + rx_gen_st(a->sz, cpu_regs[a->rs], mem); + tcg_temp_free(mem); + return true; +} + +/* mov.<bwl> dsp5[rs],rd */ +static bool trans_MOV_mr(DisasContext *ctx, arg_MOV_mr *a) +{ + TCGv mem; + mem = tcg_temp_new(); + tcg_gen_addi_i32(mem, cpu_regs[a->rs], a->dsp << a->sz); + rx_gen_ld(a->sz, cpu_regs[a->rd], mem); + tcg_temp_free(mem); + return true; +} + +/* mov.l #uimm4,rd */ +/* mov.l #uimm8,rd */ +/* mov.l #imm,rd */ +static bool trans_MOV_ir(DisasContext *ctx, arg_MOV_ir *a) +{ + tcg_gen_movi_i32(cpu_regs[a->rd], a->imm); + return true; +} + +/* mov.<bwl> #uimm8,dsp[rd] */ +/* mov.<bwl> #imm, dsp[rd] */ +static bool trans_MOV_im(DisasContext *ctx, arg_MOV_im *a) +{ + TCGv imm, mem; + imm = tcg_const_i32(a->imm); + mem = tcg_temp_new(); + tcg_gen_addi_i32(mem, cpu_regs[a->rd], a->dsp << a->sz); + rx_gen_st(a->sz, imm, mem); + tcg_temp_free(imm); + tcg_temp_free(mem); + return true; +} + +/* mov.<bwl> [ri,rb],rd */ +static bool trans_MOV_ar(DisasContext *ctx, arg_MOV_ar *a) +{ + TCGv mem; + mem = tcg_temp_new(); + rx_gen_regindex(ctx, mem, a->sz, a->ri, a->rb); + rx_gen_ld(a->sz, cpu_regs[a->rd], mem); + tcg_temp_free(mem); + return true; +} + +/* mov.<bwl> rd,[ri,rb] */ +static bool trans_MOV_ra(DisasContext *ctx, arg_MOV_ra *a) +{ + TCGv mem; + mem = tcg_temp_new(); + rx_gen_regindex(ctx, mem, a->sz, a->ri, a->rb); + rx_gen_st(a->sz, cpu_regs[a->rs], mem); + tcg_temp_free(mem); + return true; +} + +/* mov.<bwl> dsp[rs],dsp[rd] */ +/* mov.<bwl> rs,dsp[rd] */ +/* mov.<bwl> dsp[rs],rd */ +/* mov.<bwl> rs,rd */ +static bool trans_MOV_mm(DisasContext *ctx, arg_MOV_mm *a) +{ + static void (* const mov[])(TCGv ret, TCGv arg) = { + tcg_gen_ext8s_i32, tcg_gen_ext16s_i32, tcg_gen_mov_i32, + }; + TCGv tmp, mem, addr; + if (a->lds == 3 && a->ldd == 3) { + /* mov.<bwl> rs,rd */ + mov[a->sz](cpu_regs[a->rd], cpu_regs[a->rs]); + return true; + } + + mem = tcg_temp_new(); + if (a->lds == 3) { + /* mov.<bwl> rs,dsp[rd] */ + addr = rx_index_addr(ctx, mem, a->ldd, a->sz, a->rs); + rx_gen_st(a->sz, cpu_regs[a->rd], addr); + } else if (a->ldd == 3) { + /* mov.<bwl> dsp[rs],rd */ + addr = rx_index_addr(ctx, mem, a->lds, a->sz, a->rs); + rx_gen_ld(a->sz, cpu_regs[a->rd], addr); + } else { + /* mov.<bwl> dsp[rs],dsp[rd] */ + tmp = tcg_temp_new(); + addr = rx_index_addr(ctx, mem, a->lds, a->sz, a->rs); + rx_gen_ld(a->sz, tmp, addr); + addr = rx_index_addr(ctx, mem, a->ldd, a->sz, a->rd); + rx_gen_st(a->sz, tmp, addr); + tcg_temp_free(tmp); + } + tcg_temp_free(mem); + return true; +} + +/* mov.<bwl> rs,[rd+] */ +/* mov.<bwl> rs,[-rd] */ +static bool trans_MOV_rp(DisasContext *ctx, arg_MOV_rp *a) +{ + TCGv val; + val = tcg_temp_new(); + tcg_gen_mov_i32(val, cpu_regs[a->rs]); + if (a->ad == 1) { + tcg_gen_subi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); + } + rx_gen_st(a->sz, val, cpu_regs[a->rd]); + if (a->ad == 0) { + tcg_gen_addi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); + } + tcg_temp_free(val); + return true; +} + +/* mov.<bwl> [rd+],rs */ +/* mov.<bwl> [-rd],rs */ +static bool trans_MOV_pr(DisasContext *ctx, arg_MOV_pr *a) +{ + TCGv val; + val = tcg_temp_new(); + if (a->ad == 1) { + tcg_gen_subi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); + } + rx_gen_ld(a->sz, val, cpu_regs[a->rd]); + if (a->ad == 0) { + tcg_gen_addi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); + } + tcg_gen_mov_i32(cpu_regs[a->rs], val); + tcg_temp_free(val); + return true; +} + +/* movu.<bw> dsp5[rs],rd */ +/* movu.<bw> dsp[rs],rd */ +static bool trans_MOVU_mr(DisasContext *ctx, arg_MOVU_mr *a) +{ + TCGv mem; + mem = tcg_temp_new(); + tcg_gen_addi_i32(mem, cpu_regs[a->rs], a->dsp << a->sz); + rx_gen_ldu(a->sz, cpu_regs[a->rd], mem); + tcg_temp_free(mem); + return true; +} + +/* movu.<bw> rs,rd */ +static bool trans_MOVU_rr(DisasContext *ctx, arg_MOVU_rr *a) +{ + static void (* const ext[])(TCGv ret, TCGv arg) = { + tcg_gen_ext8u_i32, tcg_gen_ext16u_i32, + }; + ext[a->sz](cpu_regs[a->rd], cpu_regs[a->rs]); + return true; +} + +/* movu.<bw> [ri,rb],rd */ +static bool trans_MOVU_ar(DisasContext *ctx, arg_MOVU_ar *a) +{ + TCGv mem; + mem = tcg_temp_new(); + rx_gen_regindex(ctx, mem, a->sz, a->ri, a->rb); + rx_gen_ldu(a->sz, cpu_regs[a->rd], mem); + tcg_temp_free(mem); + return true; +} + +/* movu.<bw> [rd+],rs */ +/* mov.<bw> [-rd],rs */ +static bool trans_MOVU_pr(DisasContext *ctx, arg_MOVU_pr *a) +{ + TCGv val; + val = tcg_temp_new(); + if (a->ad == 1) { + tcg_gen_subi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); + } + rx_gen_ldu(a->sz, val, cpu_regs[a->rd]); + if (a->ad == 0) { + tcg_gen_addi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1 << a->sz); + } + tcg_gen_mov_i32(cpu_regs[a->rs], val); + tcg_temp_free(val); + return true; +} + + +/* pop rd */ +static bool trans_POP(DisasContext *ctx, arg_POP *a) +{ + /* mov.l [r0+], rd */ + arg_MOV_rp mov_a; + mov_a.rd = 0; + mov_a.rs = a->rd; + mov_a.ad = 0; + mov_a.sz = MO_32; + trans_MOV_pr(ctx, &mov_a); + return true; +} + +/* popc cr */ +static bool trans_POPC(DisasContext *ctx, arg_POPC *a) +{ + TCGv val; + val = tcg_temp_new(); + pop(val); + move_to_cr(ctx, val, a->cr); + if (a->cr == 0 && is_privileged(ctx, 0)) { + /* PSW.I may be updated here. exit TB. */ + ctx->base.is_jmp = DISAS_UPDATE; + } + tcg_temp_free(val); + return true; +} + +/* popm rd-rd2 */ +static bool trans_POPM(DisasContext *ctx, arg_POPM *a) +{ + int r; + if (a->rd == 0 || a->rd >= a->rd2) { + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid register ranges r%d-r%d", a->rd, a->rd2); + } + r = a->rd; + while (r <= a->rd2 && r < 16) { + pop(cpu_regs[r++]); + } + return true; +} + + +/* push.<bwl> rs */ +static bool trans_PUSH_r(DisasContext *ctx, arg_PUSH_r *a) +{ + TCGv val; + val = tcg_temp_new(); + tcg_gen_mov_i32(val, cpu_regs[a->rs]); + tcg_gen_subi_i32(cpu_sp, cpu_sp, 4); + rx_gen_st(a->sz, val, cpu_sp); + tcg_temp_free(val); + return true; +} + +/* push.<bwl> dsp[rs] */ +static bool trans_PUSH_m(DisasContext *ctx, arg_PUSH_m *a) +{ + TCGv mem, val, addr; + mem = tcg_temp_new(); + val = tcg_temp_new(); + addr = rx_index_addr(ctx, mem, a->ld, a->sz, a->rs); + rx_gen_ld(a->sz, val, addr); + tcg_gen_subi_i32(cpu_sp, cpu_sp, 4); + rx_gen_st(a->sz, val, cpu_sp); + tcg_temp_free(mem); + tcg_temp_free(val); + return true; +} + +/* pushc rx */ +static bool trans_PUSHC(DisasContext *ctx, arg_PUSHC *a) +{ + TCGv val; + val = tcg_temp_new(); + move_from_cr(val, a->cr, ctx->pc); + push(val); + tcg_temp_free(val); + return true; +} + +/* pushm rs-rs2 */ +static bool trans_PUSHM(DisasContext *ctx, arg_PUSHM *a) +{ + int r; + + if (a->rs == 0 || a->rs >= a->rs2) { + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid register ranges r%d-r%d", a->rs, a->rs2); + } + r = a->rs2; + while (r >= a->rs && r >= 0) { + push(cpu_regs[r--]); + } + return true; +} + +/* xchg rs,rd */ +static bool trans_XCHG_rr(DisasContext *ctx, arg_XCHG_rr *a) +{ + TCGv tmp; + tmp = tcg_temp_new(); + tcg_gen_mov_i32(tmp, cpu_regs[a->rs]); + tcg_gen_mov_i32(cpu_regs[a->rs], cpu_regs[a->rd]); + tcg_gen_mov_i32(cpu_regs[a->rd], tmp); + tcg_temp_free(tmp); + return true; +} + +/* xchg dsp[rs].<mi>,rd */ +static bool trans_XCHG_mr(DisasContext *ctx, arg_XCHG_mr *a) +{ + TCGv mem, addr; + mem = tcg_temp_new(); + switch (a->mi) { + case 0: /* dsp[rs].b */ + case 1: /* dsp[rs].w */ + case 2: /* dsp[rs].l */ + addr = rx_index_addr(ctx, mem, a->ld, a->mi, a->rs); + break; + case 3: /* dsp[rs].uw */ + case 4: /* dsp[rs].ub */ + addr = rx_index_addr(ctx, mem, a->ld, 4 - a->mi, a->rs); + break; + default: + g_assert_not_reached(); + } + tcg_gen_atomic_xchg_i32(cpu_regs[a->rd], addr, cpu_regs[a->rd], + 0, mi_to_mop(a->mi)); + tcg_temp_free(mem); + return true; +} + +static inline void stcond(TCGCond cond, int rd, int imm) +{ + TCGv z; + TCGv _imm; + z = tcg_const_i32(0); + _imm = tcg_const_i32(imm); + tcg_gen_movcond_i32(cond, cpu_regs[rd], cpu_psw_z, z, + _imm, cpu_regs[rd]); + tcg_temp_free(z); + tcg_temp_free(_imm); +} + +/* stz #imm,rd */ +static bool trans_STZ(DisasContext *ctx, arg_STZ *a) +{ + stcond(TCG_COND_EQ, a->rd, a->imm); + return true; +} + +/* stnz #imm,rd */ +static bool trans_STNZ(DisasContext *ctx, arg_STNZ *a) +{ + stcond(TCG_COND_NE, a->rd, a->imm); + return true; +} + +/* sccnd.<bwl> rd */ +/* sccnd.<bwl> dsp:[rd] */ +static bool trans_SCCnd(DisasContext *ctx, arg_SCCnd *a) +{ + DisasCompare dc; + TCGv val, mem, addr; + dc.temp = tcg_temp_new(); + psw_cond(&dc, a->cd); + if (a->ld < 3) { + val = tcg_temp_new(); + mem = tcg_temp_new(); + tcg_gen_setcondi_i32(dc.cond, val, dc.value, 0); + addr = rx_index_addr(ctx, mem, a->sz, a->ld, a->rd); + rx_gen_st(a->sz, val, addr); + tcg_temp_free(val); + tcg_temp_free(mem); + } else { + tcg_gen_setcondi_i32(dc.cond, cpu_regs[a->rd], dc.value, 0); + } + tcg_temp_free(dc.temp); + return true; +} + +/* rtsd #imm */ +static bool trans_RTSD_i(DisasContext *ctx, arg_RTSD_i *a) +{ + tcg_gen_addi_i32(cpu_sp, cpu_sp, a->imm << 2); + pop(cpu_pc); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +/* rtsd #imm, rd-rd2 */ +static bool trans_RTSD_irr(DisasContext *ctx, arg_RTSD_irr *a) +{ + int dst; + int adj; + + if (a->rd2 >= a->rd) { + adj = a->imm - (a->rd2 - a->rd + 1); + } else { + adj = a->imm - (15 - a->rd + 1); + } + + tcg_gen_addi_i32(cpu_sp, cpu_sp, adj << 2); + dst = a->rd; + while (dst <= a->rd2 && dst < 16) { + pop(cpu_regs[dst++]); + } + pop(cpu_pc); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +typedef void (*op2fn)(TCGv ret, TCGv arg1); +typedef void (*op3fn)(TCGv ret, TCGv arg1, TCGv arg2); + +static inline void rx_gen_op_rr(op2fn opr, int dst, int src) +{ + opr(cpu_regs[dst], cpu_regs[src]); +} + +static inline void rx_gen_op_rrr(op3fn opr, int dst, int src, int src2) +{ + opr(cpu_regs[dst], cpu_regs[src], cpu_regs[src2]); +} + +static inline void rx_gen_op_irr(op3fn opr, int dst, int src, uint32_t src2) +{ + TCGv imm = tcg_const_i32(src2); + opr(cpu_regs[dst], cpu_regs[src], imm); + tcg_temp_free(imm); +} + +static inline void rx_gen_op_mr(op3fn opr, DisasContext *ctx, + int dst, int src, int ld, int mi) +{ + TCGv val, mem; + mem = tcg_temp_new(); + val = rx_load_source(ctx, mem, ld, mi, src); + opr(cpu_regs[dst], cpu_regs[dst], val); + tcg_temp_free(mem); +} + +static void rx_and(TCGv ret, TCGv arg1, TCGv arg2) +{ + tcg_gen_and_i32(cpu_psw_s, arg1, arg2); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); + tcg_gen_mov_i32(ret, cpu_psw_s); +} + +/* and #uimm:4, rd */ +/* and #imm, rd */ +static bool trans_AND_ir(DisasContext *ctx, arg_AND_ir *a) +{ + rx_gen_op_irr(rx_and, a->rd, a->rd, a->imm); + return true; +} + +/* and dsp[rs], rd */ +/* and rs,rd */ +static bool trans_AND_mr(DisasContext *ctx, arg_AND_mr *a) +{ + rx_gen_op_mr(rx_and, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* and rs,rs2,rd */ +static bool trans_AND_rrr(DisasContext *ctx, arg_AND_rrr *a) +{ + rx_gen_op_rrr(rx_and, a->rd, a->rs, a->rs2); + return true; +} + +static void rx_or(TCGv ret, TCGv arg1, TCGv arg2) +{ + tcg_gen_or_i32(cpu_psw_s, arg1, arg2); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); + tcg_gen_mov_i32(ret, cpu_psw_s); +} + +/* or #uimm:4, rd */ +/* or #imm, rd */ +static bool trans_OR_ir(DisasContext *ctx, arg_OR_ir *a) +{ + rx_gen_op_irr(rx_or, a->rd, a->rd, a->imm); + return true; +} + +/* or dsp[rs], rd */ +/* or rs,rd */ +static bool trans_OR_mr(DisasContext *ctx, arg_OR_mr *a) +{ + rx_gen_op_mr(rx_or, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* or rs,rs2,rd */ +static bool trans_OR_rrr(DisasContext *ctx, arg_OR_rrr *a) +{ + rx_gen_op_rrr(rx_or, a->rd, a->rs, a->rs2); + return true; +} + +static void rx_xor(TCGv ret, TCGv arg1, TCGv arg2) +{ + tcg_gen_xor_i32(cpu_psw_s, arg1, arg2); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); + tcg_gen_mov_i32(ret, cpu_psw_s); +} + +/* xor #imm, rd */ +static bool trans_XOR_ir(DisasContext *ctx, arg_XOR_ir *a) +{ + rx_gen_op_irr(rx_xor, a->rd, a->rd, a->imm); + return true; +} + +/* xor dsp[rs], rd */ +/* xor rs,rd */ +static bool trans_XOR_mr(DisasContext *ctx, arg_XOR_mr *a) +{ + rx_gen_op_mr(rx_xor, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +static void rx_tst(TCGv ret, TCGv arg1, TCGv arg2) +{ + tcg_gen_and_i32(cpu_psw_s, arg1, arg2); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); +} + +/* tst #imm, rd */ +static bool trans_TST_ir(DisasContext *ctx, arg_TST_ir *a) +{ + rx_gen_op_irr(rx_tst, a->rd, a->rd, a->imm); + return true; +} + +/* tst dsp[rs], rd */ +/* tst rs, rd */ +static bool trans_TST_mr(DisasContext *ctx, arg_TST_mr *a) +{ + rx_gen_op_mr(rx_tst, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +static void rx_not(TCGv ret, TCGv arg1) +{ + tcg_gen_not_i32(ret, arg1); + tcg_gen_mov_i32(cpu_psw_z, ret); + tcg_gen_mov_i32(cpu_psw_s, ret); +} + +/* not rd */ +/* not rs, rd */ +static bool trans_NOT_rr(DisasContext *ctx, arg_NOT_rr *a) +{ + rx_gen_op_rr(rx_not, a->rd, a->rs); + return true; +} + +static void rx_neg(TCGv ret, TCGv arg1) +{ + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_o, arg1, 0x80000000); + tcg_gen_neg_i32(ret, arg1); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_c, ret, 0); + tcg_gen_mov_i32(cpu_psw_z, ret); + tcg_gen_mov_i32(cpu_psw_s, ret); +} + + +/* neg rd */ +/* neg rs, rd */ +static bool trans_NEG_rr(DisasContext *ctx, arg_NEG_rr *a) +{ + rx_gen_op_rr(rx_neg, a->rd, a->rs); + return true; +} + +/* ret = arg1 + arg2 + psw_c */ +static void rx_adc(TCGv ret, TCGv arg1, TCGv arg2) +{ + TCGv z; + z = tcg_const_i32(0); + tcg_gen_add2_i32(cpu_psw_s, cpu_psw_c, arg1, z, cpu_psw_c, z); + tcg_gen_add2_i32(cpu_psw_s, cpu_psw_c, cpu_psw_s, cpu_psw_c, arg2, z); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); + tcg_gen_xor_i32(cpu_psw_o, cpu_psw_s, arg1); + tcg_gen_xor_i32(z, arg1, arg2); + tcg_gen_andc_i32(cpu_psw_o, cpu_psw_o, z); + tcg_gen_mov_i32(ret, cpu_psw_s); + tcg_temp_free(z); +} + +/* adc #imm, rd */ +static bool trans_ADC_ir(DisasContext *ctx, arg_ADC_ir *a) +{ + rx_gen_op_irr(rx_adc, a->rd, a->rd, a->imm); + return true; +} + +/* adc rs, rd */ +static bool trans_ADC_rr(DisasContext *ctx, arg_ADC_rr *a) +{ + rx_gen_op_rrr(rx_adc, a->rd, a->rd, a->rs); + return true; +} + +/* adc dsp[rs], rd */ +static bool trans_ADC_mr(DisasContext *ctx, arg_ADC_mr *a) +{ + /* mi only 2 */ + if (a->mi != 2) { + return false; + } + rx_gen_op_mr(rx_adc, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* ret = arg1 + arg2 */ +static void rx_add(TCGv ret, TCGv arg1, TCGv arg2) +{ + TCGv z; + z = tcg_const_i32(0); + tcg_gen_add2_i32(cpu_psw_s, cpu_psw_c, arg1, z, arg2, z); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); + tcg_gen_xor_i32(cpu_psw_o, cpu_psw_s, arg1); + tcg_gen_xor_i32(z, arg1, arg2); + tcg_gen_andc_i32(cpu_psw_o, cpu_psw_o, z); + tcg_gen_mov_i32(ret, cpu_psw_s); + tcg_temp_free(z); +} + +/* add #uimm4, rd */ +/* add #imm, rs, rd */ +static bool trans_ADD_irr(DisasContext *ctx, arg_ADD_irr *a) +{ + rx_gen_op_irr(rx_add, a->rd, a->rs2, a->imm); + return true; +} + +/* add rs, rd */ +/* add dsp[rs], rd */ +static bool trans_ADD_mr(DisasContext *ctx, arg_ADD_mr *a) +{ + rx_gen_op_mr(rx_add, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* add rs, rs2, rd */ +static bool trans_ADD_rrr(DisasContext *ctx, arg_ADD_rrr *a) +{ + rx_gen_op_rrr(rx_add, a->rd, a->rs, a->rs2); + return true; +} + +/* ret = arg1 - arg2 */ +static void rx_sub(TCGv ret, TCGv arg1, TCGv arg2) +{ + TCGv temp; + tcg_gen_sub_i32(cpu_psw_s, arg1, arg2); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_s); + tcg_gen_setcond_i32(TCG_COND_GEU, cpu_psw_c, arg1, arg2); + tcg_gen_xor_i32(cpu_psw_o, cpu_psw_s, arg1); + temp = tcg_temp_new_i32(); + tcg_gen_xor_i32(temp, arg1, arg2); + tcg_gen_and_i32(cpu_psw_o, cpu_psw_o, temp); + tcg_temp_free_i32(temp); + /* CMP not requred return */ + if (ret) { + tcg_gen_mov_i32(ret, cpu_psw_s); + } +} +static void rx_cmp(TCGv dummy, TCGv arg1, TCGv arg2) +{ + rx_sub(NULL, arg1, arg2); +} +/* ret = arg1 - arg2 - !psw_c */ +/* -> ret = arg1 + ~arg2 + psw_c */ +static void rx_sbb(TCGv ret, TCGv arg1, TCGv arg2) +{ + TCGv temp; + temp = tcg_temp_new(); + tcg_gen_not_i32(temp, arg2); + rx_adc(ret, arg1, temp); + tcg_temp_free(temp); +} + +/* cmp #imm4, rs2 */ +/* cmp #imm8, rs2 */ +/* cmp #imm, rs2 */ +static bool trans_CMP_ir(DisasContext *ctx, arg_CMP_ir *a) +{ + rx_gen_op_irr(rx_cmp, 0, a->rs2, a->imm); + return true; +} + +/* cmp rs, rs2 */ +/* cmp dsp[rs], rs2 */ +static bool trans_CMP_mr(DisasContext *ctx, arg_CMP_mr *a) +{ + rx_gen_op_mr(rx_cmp, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* sub #imm4, rd */ +static bool trans_SUB_ir(DisasContext *ctx, arg_SUB_ir *a) +{ + rx_gen_op_irr(rx_sub, a->rd, a->rd, a->imm); + return true; +} + +/* sub rs, rd */ +/* sub dsp[rs], rd */ +static bool trans_SUB_mr(DisasContext *ctx, arg_SUB_mr *a) +{ + rx_gen_op_mr(rx_sub, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* sub rs2, rs, rd */ +static bool trans_SUB_rrr(DisasContext *ctx, arg_SUB_rrr *a) +{ + rx_gen_op_rrr(rx_sub, a->rd, a->rs2, a->rs); + return true; +} + +/* sbb rs, rd */ +static bool trans_SBB_rr(DisasContext *ctx, arg_SBB_rr *a) +{ + rx_gen_op_rrr(rx_sbb, a->rd, a->rd, a->rs); + return true; +} + +/* sbb dsp[rs], rd */ +static bool trans_SBB_mr(DisasContext *ctx, arg_SBB_mr *a) +{ + /* mi only 2 */ + if (a->mi != 2) { + return false; + } + rx_gen_op_mr(rx_sbb, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +static void rx_abs(TCGv ret, TCGv arg1) +{ + TCGv neg; + TCGv zero; + neg = tcg_temp_new(); + zero = tcg_const_i32(0); + tcg_gen_neg_i32(neg, arg1); + tcg_gen_movcond_i32(TCG_COND_LT, ret, arg1, zero, neg, arg1); + tcg_temp_free(neg); + tcg_temp_free(zero); +} + +/* abs rd */ +/* abs rs, rd */ +static bool trans_ABS_rr(DisasContext *ctx, arg_ABS_rr *a) +{ + rx_gen_op_rr(rx_abs, a->rd, a->rs); + return true; +} + +/* max #imm, rd */ +static bool trans_MAX_ir(DisasContext *ctx, arg_MAX_ir *a) +{ + rx_gen_op_irr(tcg_gen_smax_i32, a->rd, a->rd, a->imm); + return true; +} + +/* max rs, rd */ +/* max dsp[rs], rd */ +static bool trans_MAX_mr(DisasContext *ctx, arg_MAX_mr *a) +{ + rx_gen_op_mr(tcg_gen_smax_i32, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* min #imm, rd */ +static bool trans_MIN_ir(DisasContext *ctx, arg_MIN_ir *a) +{ + rx_gen_op_irr(tcg_gen_smin_i32, a->rd, a->rd, a->imm); + return true; +} + +/* min rs, rd */ +/* min dsp[rs], rd */ +static bool trans_MIN_mr(DisasContext *ctx, arg_MIN_mr *a) +{ + rx_gen_op_mr(tcg_gen_smin_i32, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* mul #uimm4, rd */ +/* mul #imm, rd */ +static bool trans_MUL_ir(DisasContext *ctx, arg_MUL_ir *a) +{ + rx_gen_op_irr(tcg_gen_mul_i32, a->rd, a->rd, a->imm); + return true; +} + +/* mul rs, rd */ +/* mul dsp[rs], rd */ +static bool trans_MUL_mr(DisasContext *ctx, arg_MUL_mr *a) +{ + rx_gen_op_mr(tcg_gen_mul_i32, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* mul rs, rs2, rd */ +static bool trans_MUL_rrr(DisasContext *ctx, arg_MUL_rrr *a) +{ + rx_gen_op_rrr(tcg_gen_mul_i32, a->rd, a->rs, a->rs2); + return true; +} + +/* emul #imm, rd */ +static bool trans_EMUL_ir(DisasContext *ctx, arg_EMUL_ir *a) +{ + TCGv imm = tcg_const_i32(a->imm); + if (a->rd > 14) { + qemu_log_mask(LOG_GUEST_ERROR, "rd too large %d", a->rd); + } + tcg_gen_muls2_i32(cpu_regs[a->rd], cpu_regs[(a->rd + 1) & 15], + cpu_regs[a->rd], imm); + tcg_temp_free(imm); + return true; +} + +/* emul rs, rd */ +/* emul dsp[rs], rd */ +static bool trans_EMUL_mr(DisasContext *ctx, arg_EMUL_mr *a) +{ + TCGv val, mem; + if (a->rd > 14) { + qemu_log_mask(LOG_GUEST_ERROR, "rd too large %d", a->rd); + } + mem = tcg_temp_new(); + val = rx_load_source(ctx, mem, a->ld, a->mi, a->rs); + tcg_gen_muls2_i32(cpu_regs[a->rd], cpu_regs[(a->rd + 1) & 15], + cpu_regs[a->rd], val); + tcg_temp_free(mem); + return true; +} + +/* emulu #imm, rd */ +static bool trans_EMULU_ir(DisasContext *ctx, arg_EMULU_ir *a) +{ + TCGv imm = tcg_const_i32(a->imm); + if (a->rd > 14) { + qemu_log_mask(LOG_GUEST_ERROR, "rd too large %d", a->rd); + } + tcg_gen_mulu2_i32(cpu_regs[a->rd], cpu_regs[(a->rd + 1) & 15], + cpu_regs[a->rd], imm); + tcg_temp_free(imm); + return true; +} + +/* emulu rs, rd */ +/* emulu dsp[rs], rd */ +static bool trans_EMULU_mr(DisasContext *ctx, arg_EMULU_mr *a) +{ + TCGv val, mem; + if (a->rd > 14) { + qemu_log_mask(LOG_GUEST_ERROR, "rd too large %d", a->rd); + } + mem = tcg_temp_new(); + val = rx_load_source(ctx, mem, a->ld, a->mi, a->rs); + tcg_gen_mulu2_i32(cpu_regs[a->rd], cpu_regs[(a->rd + 1) & 15], + cpu_regs[a->rd], val); + tcg_temp_free(mem); + return true; +} + +static void rx_div(TCGv ret, TCGv arg1, TCGv arg2) +{ + gen_helper_div(ret, cpu_env, arg1, arg2); +} + +static void rx_divu(TCGv ret, TCGv arg1, TCGv arg2) +{ + gen_helper_divu(ret, cpu_env, arg1, arg2); +} + +/* div #imm, rd */ +static bool trans_DIV_ir(DisasContext *ctx, arg_DIV_ir *a) +{ + rx_gen_op_irr(rx_div, a->rd, a->rd, a->imm); + return true; +} + +/* div rs, rd */ +/* div dsp[rs], rd */ +static bool trans_DIV_mr(DisasContext *ctx, arg_DIV_mr *a) +{ + rx_gen_op_mr(rx_div, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + +/* divu #imm, rd */ +static bool trans_DIVU_ir(DisasContext *ctx, arg_DIVU_ir *a) +{ + rx_gen_op_irr(rx_divu, a->rd, a->rd, a->imm); + return true; +} + +/* divu rs, rd */ +/* divu dsp[rs], rd */ +static bool trans_DIVU_mr(DisasContext *ctx, arg_DIVU_mr *a) +{ + rx_gen_op_mr(rx_divu, ctx, a->rd, a->rs, a->ld, a->mi); + return true; +} + + +/* shll #imm:5, rd */ +/* shll #imm:5, rs2, rd */ +static bool trans_SHLL_irr(DisasContext *ctx, arg_SHLL_irr *a) +{ + TCGv tmp; + tmp = tcg_temp_new(); + if (a->imm) { + tcg_gen_sari_i32(cpu_psw_c, cpu_regs[a->rs2], 32 - a->imm); + tcg_gen_shli_i32(cpu_regs[a->rd], cpu_regs[a->rs2], a->imm); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_o, cpu_psw_c, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, tmp, cpu_psw_c, 0xffffffff); + tcg_gen_or_i32(cpu_psw_o, cpu_psw_o, tmp); + tcg_gen_setcondi_i32(TCG_COND_NE, cpu_psw_c, cpu_psw_c, 0); + } else { + tcg_gen_mov_i32(cpu_regs[a->rd], cpu_regs[a->rs2]); + tcg_gen_movi_i32(cpu_psw_c, 0); + tcg_gen_movi_i32(cpu_psw_o, 0); + } + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[a->rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[a->rd]); + return true; +} + +/* shll rs, rd */ +static bool trans_SHLL_rr(DisasContext *ctx, arg_SHLL_rr *a) +{ + TCGLabel *noshift, *done; + TCGv count, tmp; + + noshift = gen_new_label(); + done = gen_new_label(); + /* if (cpu_regs[a->rs]) { */ + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_regs[a->rs], 0, noshift); + count = tcg_const_i32(32); + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, cpu_regs[a->rs], 31); + tcg_gen_sub_i32(count, count, tmp); + tcg_gen_sar_i32(cpu_psw_c, cpu_regs[a->rd], count); + tcg_gen_shl_i32(cpu_regs[a->rd], cpu_regs[a->rd], tmp); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_psw_o, cpu_psw_c, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, tmp, cpu_psw_c, 0xffffffff); + tcg_gen_or_i32(cpu_psw_o, cpu_psw_o, tmp); + tcg_gen_setcondi_i32(TCG_COND_NE, cpu_psw_c, cpu_psw_c, 0); + tcg_gen_br(done); + /* } else { */ + gen_set_label(noshift); + tcg_gen_movi_i32(cpu_psw_c, 0); + tcg_gen_movi_i32(cpu_psw_o, 0); + /* } */ + gen_set_label(done); + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[a->rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[a->rd]); + tcg_temp_free(count); + tcg_temp_free(tmp); + return true; +} + +static inline void shiftr_imm(uint32_t rd, uint32_t rs, uint32_t imm, + unsigned int alith) +{ + static void (* const gen_sXri[])(TCGv ret, TCGv arg1, int arg2) = { + tcg_gen_shri_i32, tcg_gen_sari_i32, + }; + tcg_debug_assert(alith < 2); + if (imm) { + gen_sXri[alith](cpu_regs[rd], cpu_regs[rs], imm - 1); + tcg_gen_andi_i32(cpu_psw_c, cpu_regs[rd], 0x00000001); + gen_sXri[alith](cpu_regs[rd], cpu_regs[rd], 1); + } else { + tcg_gen_mov_i32(cpu_regs[rd], cpu_regs[rs]); + tcg_gen_movi_i32(cpu_psw_c, 0); + } + tcg_gen_movi_i32(cpu_psw_o, 0); + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[rd]); +} + +static inline void shiftr_reg(uint32_t rd, uint32_t rs, unsigned int alith) +{ + TCGLabel *noshift, *done; + TCGv count; + static void (* const gen_sXri[])(TCGv ret, TCGv arg1, int arg2) = { + tcg_gen_shri_i32, tcg_gen_sari_i32, + }; + static void (* const gen_sXr[])(TCGv ret, TCGv arg1, TCGv arg2) = { + tcg_gen_shr_i32, tcg_gen_sar_i32, + }; + tcg_debug_assert(alith < 2); + noshift = gen_new_label(); + done = gen_new_label(); + count = tcg_temp_new(); + /* if (cpu_regs[rs]) { */ + tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_regs[rs], 0, noshift); + tcg_gen_andi_i32(count, cpu_regs[rs], 31); + tcg_gen_subi_i32(count, count, 1); + gen_sXr[alith](cpu_regs[rd], cpu_regs[rd], count); + tcg_gen_andi_i32(cpu_psw_c, cpu_regs[rd], 0x00000001); + gen_sXri[alith](cpu_regs[rd], cpu_regs[rd], 1); + tcg_gen_br(done); + /* } else { */ + gen_set_label(noshift); + tcg_gen_movi_i32(cpu_psw_c, 0); + /* } */ + gen_set_label(done); + tcg_gen_movi_i32(cpu_psw_o, 0); + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[rd]); + tcg_temp_free(count); +} + +/* shar #imm:5, rd */ +/* shar #imm:5, rs2, rd */ +static bool trans_SHAR_irr(DisasContext *ctx, arg_SHAR_irr *a) +{ + shiftr_imm(a->rd, a->rs2, a->imm, 1); + return true; +} + +/* shar rs, rd */ +static bool trans_SHAR_rr(DisasContext *ctx, arg_SHAR_rr *a) +{ + shiftr_reg(a->rd, a->rs, 1); + return true; +} + +/* shlr #imm:5, rd */ +/* shlr #imm:5, rs2, rd */ +static bool trans_SHLR_irr(DisasContext *ctx, arg_SHLR_irr *a) +{ + shiftr_imm(a->rd, a->rs2, a->imm, 0); + return true; +} + +/* shlr rs, rd */ +static bool trans_SHLR_rr(DisasContext *ctx, arg_SHLR_rr *a) +{ + shiftr_reg(a->rd, a->rs, 0); + return true; +} + +/* rolc rd */ +static bool trans_ROLC(DisasContext *ctx, arg_ROLC *a) +{ + TCGv tmp; + tmp = tcg_temp_new(); + tcg_gen_shri_i32(tmp, cpu_regs[a->rd], 31); + tcg_gen_shli_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1); + tcg_gen_or_i32(cpu_regs[a->rd], cpu_regs[a->rd], cpu_psw_c); + tcg_gen_mov_i32(cpu_psw_c, tmp); + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[a->rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[a->rd]); + tcg_temp_free(tmp); + return true; +} + +/* rorc rd */ +static bool trans_RORC(DisasContext *ctx, arg_RORC *a) +{ + TCGv tmp; + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, cpu_regs[a->rd], 0x00000001); + tcg_gen_shri_i32(cpu_regs[a->rd], cpu_regs[a->rd], 1); + tcg_gen_shli_i32(cpu_psw_c, cpu_psw_c, 31); + tcg_gen_or_i32(cpu_regs[a->rd], cpu_regs[a->rd], cpu_psw_c); + tcg_gen_mov_i32(cpu_psw_c, tmp); + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[a->rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[a->rd]); + return true; +} + +enum {ROTR = 0, ROTL = 1}; +enum {ROT_IMM = 0, ROT_REG = 1}; +static inline void rx_rot(int ir, int dir, int rd, int src) +{ + switch (dir) { + case ROTL: + if (ir == ROT_IMM) { + tcg_gen_rotli_i32(cpu_regs[rd], cpu_regs[rd], src); + } else { + tcg_gen_rotl_i32(cpu_regs[rd], cpu_regs[rd], cpu_regs[src]); + } + tcg_gen_andi_i32(cpu_psw_c, cpu_regs[rd], 0x00000001); + break; + case ROTR: + if (ir == ROT_IMM) { + tcg_gen_rotri_i32(cpu_regs[rd], cpu_regs[rd], src); + } else { + tcg_gen_rotr_i32(cpu_regs[rd], cpu_regs[rd], cpu_regs[src]); + } + tcg_gen_shri_i32(cpu_psw_c, cpu_regs[rd], 31); + break; + } + tcg_gen_mov_i32(cpu_psw_z, cpu_regs[rd]); + tcg_gen_mov_i32(cpu_psw_s, cpu_regs[rd]); +} + +/* rotl #imm, rd */ +static bool trans_ROTL_ir(DisasContext *ctx, arg_ROTL_ir *a) +{ + rx_rot(ROT_IMM, ROTL, a->rd, a->imm); + return true; +} + +/* rotl rs, rd */ +static bool trans_ROTL_rr(DisasContext *ctx, arg_ROTL_rr *a) +{ + rx_rot(ROT_REG, ROTL, a->rd, a->rs); + return true; +} + +/* rotr #imm, rd */ +static bool trans_ROTR_ir(DisasContext *ctx, arg_ROTR_ir *a) +{ + rx_rot(ROT_IMM, ROTR, a->rd, a->imm); + return true; +} + +/* rotr rs, rd */ +static bool trans_ROTR_rr(DisasContext *ctx, arg_ROTR_rr *a) +{ + rx_rot(ROT_REG, ROTR, a->rd, a->rs); + return true; +} + +/* revl rs, rd */ +static bool trans_REVL(DisasContext *ctx, arg_REVL *a) +{ + tcg_gen_bswap32_i32(cpu_regs[a->rd], cpu_regs[a->rs]); + return true; +} + +/* revw rs, rd */ +static bool trans_REVW(DisasContext *ctx, arg_REVW *a) +{ + TCGv tmp; + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, cpu_regs[a->rs], 0x00ff00ff); + tcg_gen_shli_i32(tmp, tmp, 8); + tcg_gen_shri_i32(cpu_regs[a->rd], cpu_regs[a->rs], 8); + tcg_gen_andi_i32(cpu_regs[a->rd], cpu_regs[a->rd], 0x00ff00ff); + tcg_gen_or_i32(cpu_regs[a->rd], cpu_regs[a->rd], tmp); + tcg_temp_free(tmp); + return true; +} + +/* conditional branch helper */ +static void rx_bcnd_main(DisasContext *ctx, int cd, int dst) +{ + DisasCompare dc; + TCGLabel *t, *done; + + switch (cd) { + case 0 ... 13: + dc.temp = tcg_temp_new(); + psw_cond(&dc, cd); + t = gen_new_label(); + done = gen_new_label(); + tcg_gen_brcondi_i32(dc.cond, dc.value, 0, t); + gen_goto_tb(ctx, 0, ctx->base.pc_next); + tcg_gen_br(done); + gen_set_label(t); + gen_goto_tb(ctx, 1, ctx->pc + dst); + gen_set_label(done); + tcg_temp_free(dc.temp); + break; + case 14: + /* always true case */ + gen_goto_tb(ctx, 0, ctx->pc + dst); + break; + case 15: + /* always false case */ + /* Nothing do */ + break; + } +} + +/* beq dsp:3 / bne dsp:3 */ +/* beq dsp:8 / bne dsp:8 */ +/* bc dsp:8 / bnc dsp:8 */ +/* bgtu dsp:8 / bleu dsp:8 */ +/* bpz dsp:8 / bn dsp:8 */ +/* bge dsp:8 / blt dsp:8 */ +/* bgt dsp:8 / ble dsp:8 */ +/* bo dsp:8 / bno dsp:8 */ +/* beq dsp:16 / bne dsp:16 */ +static bool trans_BCnd(DisasContext *ctx, arg_BCnd *a) +{ + rx_bcnd_main(ctx, a->cd, a->dsp); + return true; +} + +/* bra dsp:3 */ +/* bra dsp:8 */ +/* bra dsp:16 */ +/* bra dsp:24 */ +static bool trans_BRA(DisasContext *ctx, arg_BRA *a) +{ + rx_bcnd_main(ctx, 14, a->dsp); + return true; +} + +/* bra rs */ +static bool trans_BRA_l(DisasContext *ctx, arg_BRA_l *a) +{ + tcg_gen_addi_i32(cpu_pc, cpu_regs[a->rd], ctx->pc); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +static inline void rx_save_pc(DisasContext *ctx) +{ + TCGv pc = tcg_const_i32(ctx->base.pc_next); + push(pc); + tcg_temp_free(pc); +} + +/* jmp rs */ +static bool trans_JMP(DisasContext *ctx, arg_JMP *a) +{ + tcg_gen_mov_i32(cpu_pc, cpu_regs[a->rs]); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +/* jsr rs */ +static bool trans_JSR(DisasContext *ctx, arg_JSR *a) +{ + rx_save_pc(ctx); + tcg_gen_mov_i32(cpu_pc, cpu_regs[a->rs]); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +/* bsr dsp:16 */ +/* bsr dsp:24 */ +static bool trans_BSR(DisasContext *ctx, arg_BSR *a) +{ + rx_save_pc(ctx); + rx_bcnd_main(ctx, 14, a->dsp); + return true; +} + +/* bsr rs */ +static bool trans_BSR_l(DisasContext *ctx, arg_BSR_l *a) +{ + rx_save_pc(ctx); + tcg_gen_addi_i32(cpu_pc, cpu_regs[a->rd], ctx->pc); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +/* rts */ +static bool trans_RTS(DisasContext *ctx, arg_RTS *a) +{ + pop(cpu_pc); + ctx->base.is_jmp = DISAS_JUMP; + return true; +} + +/* nop */ +static bool trans_NOP(DisasContext *ctx, arg_NOP *a) +{ + return true; +} + +/* scmpu */ +static bool trans_SCMPU(DisasContext *ctx, arg_SCMPU *a) +{ + gen_helper_scmpu(cpu_env); + return true; +} + +/* smovu */ +static bool trans_SMOVU(DisasContext *ctx, arg_SMOVU *a) +{ + gen_helper_smovu(cpu_env); + return true; +} + +/* smovf */ +static bool trans_SMOVF(DisasContext *ctx, arg_SMOVF *a) +{ + gen_helper_smovf(cpu_env); + return true; +} + +/* smovb */ +static bool trans_SMOVB(DisasContext *ctx, arg_SMOVB *a) +{ + gen_helper_smovb(cpu_env); + return true; +} + +#define STRING(op) \ + do { \ + TCGv size = tcg_const_i32(a->sz); \ + gen_helper_##op(cpu_env, size); \ + tcg_temp_free(size); \ + } while (0) + +/* suntile.<bwl> */ +static bool trans_SUNTIL(DisasContext *ctx, arg_SUNTIL *a) +{ + STRING(suntil); + return true; +} + +/* swhile.<bwl> */ +static bool trans_SWHILE(DisasContext *ctx, arg_SWHILE *a) +{ + STRING(swhile); + return true; +} +/* sstr.<bwl> */ +static bool trans_SSTR(DisasContext *ctx, arg_SSTR *a) +{ + STRING(sstr); + return true; +} + +/* rmpa.<bwl> */ +static bool trans_RMPA(DisasContext *ctx, arg_RMPA *a) +{ + STRING(rmpa); + return true; +} + +static void rx_mul64hi(TCGv_i64 ret, int rs, int rs2) +{ + TCGv_i64 tmp0, tmp1; + tmp0 = tcg_temp_new_i64(); + tmp1 = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(tmp0, cpu_regs[rs]); + tcg_gen_sari_i64(tmp0, tmp0, 16); + tcg_gen_ext_i32_i64(tmp1, cpu_regs[rs2]); + tcg_gen_sari_i64(tmp1, tmp1, 16); + tcg_gen_mul_i64(ret, tmp0, tmp1); + tcg_gen_shli_i64(ret, ret, 16); + tcg_temp_free_i64(tmp0); + tcg_temp_free_i64(tmp1); +} + +static void rx_mul64lo(TCGv_i64 ret, int rs, int rs2) +{ + TCGv_i64 tmp0, tmp1; + tmp0 = tcg_temp_new_i64(); + tmp1 = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(tmp0, cpu_regs[rs]); + tcg_gen_ext16s_i64(tmp0, tmp0); + tcg_gen_ext_i32_i64(tmp1, cpu_regs[rs2]); + tcg_gen_ext16s_i64(tmp1, tmp1); + tcg_gen_mul_i64(ret, tmp0, tmp1); + tcg_gen_shli_i64(ret, ret, 16); + tcg_temp_free_i64(tmp0); + tcg_temp_free_i64(tmp1); +} + +/* mulhi rs,rs2 */ +static bool trans_MULHI(DisasContext *ctx, arg_MULHI *a) +{ + rx_mul64hi(cpu_acc, a->rs, a->rs2); + return true; +} + +/* mullo rs,rs2 */ +static bool trans_MULLO(DisasContext *ctx, arg_MULLO *a) +{ + rx_mul64lo(cpu_acc, a->rs, a->rs2); + return true; +} + +/* machi rs,rs2 */ +static bool trans_MACHI(DisasContext *ctx, arg_MACHI *a) +{ + TCGv_i64 tmp; + tmp = tcg_temp_new_i64(); + rx_mul64hi(tmp, a->rs, a->rs2); + tcg_gen_add_i64(cpu_acc, cpu_acc, tmp); + tcg_temp_free_i64(tmp); + return true; +} + +/* maclo rs,rs2 */ +static bool trans_MACLO(DisasContext *ctx, arg_MACLO *a) +{ + TCGv_i64 tmp; + tmp = tcg_temp_new_i64(); + rx_mul64lo(tmp, a->rs, a->rs2); + tcg_gen_add_i64(cpu_acc, cpu_acc, tmp); + tcg_temp_free_i64(tmp); + return true; +} + +/* mvfachi rd */ +static bool trans_MVFACHI(DisasContext *ctx, arg_MVFACHI *a) +{ + tcg_gen_extrh_i64_i32(cpu_regs[a->rd], cpu_acc); + return true; +} + +/* mvfacmi rd */ +static bool trans_MVFACMI(DisasContext *ctx, arg_MVFACMI *a) +{ + TCGv_i64 rd64; + rd64 = tcg_temp_new_i64(); + tcg_gen_extract_i64(rd64, cpu_acc, 16, 32); + tcg_gen_extrl_i64_i32(cpu_regs[a->rd], rd64); + tcg_temp_free_i64(rd64); + return true; +} + +/* mvtachi rs */ +static bool trans_MVTACHI(DisasContext *ctx, arg_MVTACHI *a) +{ + TCGv_i64 rs64; + rs64 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(rs64, cpu_regs[a->rs]); + tcg_gen_deposit_i64(cpu_acc, cpu_acc, rs64, 32, 32); + tcg_temp_free_i64(rs64); + return true; +} + +/* mvtaclo rs */ +static bool trans_MVTACLO(DisasContext *ctx, arg_MVTACLO *a) +{ + TCGv_i64 rs64; + rs64 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(rs64, cpu_regs[a->rs]); + tcg_gen_deposit_i64(cpu_acc, cpu_acc, rs64, 0, 32); + tcg_temp_free_i64(rs64); + return true; +} + +/* racw #imm */ +static bool trans_RACW(DisasContext *ctx, arg_RACW *a) +{ + TCGv imm = tcg_const_i32(a->imm + 1); + gen_helper_racw(cpu_env, imm); + tcg_temp_free(imm); + return true; +} + +/* sat rd */ +static bool trans_SAT(DisasContext *ctx, arg_SAT *a) +{ + TCGv tmp, z; + tmp = tcg_temp_new(); + z = tcg_const_i32(0); + /* S == 1 -> 0xffffffff / S == 0 -> 0x00000000 */ + tcg_gen_sari_i32(tmp, cpu_psw_s, 31); + /* S == 1 -> 0x7fffffff / S == 0 -> 0x80000000 */ + tcg_gen_xori_i32(tmp, tmp, 0x80000000); + tcg_gen_movcond_i32(TCG_COND_LT, cpu_regs[a->rd], + cpu_psw_o, z, tmp, cpu_regs[a->rd]); + tcg_temp_free(tmp); + tcg_temp_free(z); + return true; +} + +/* satr */ +static bool trans_SATR(DisasContext *ctx, arg_SATR *a) +{ + gen_helper_satr(cpu_env); + return true; +} + +#define cat3(a, b, c) a##b##c +#define FOP(name, op) \ + static bool cat3(trans_, name, _ir)(DisasContext *ctx, \ + cat3(arg_, name, _ir) * a) \ + { \ + TCGv imm = tcg_const_i32(li(ctx, 0)); \ + gen_helper_##op(cpu_regs[a->rd], cpu_env, \ + cpu_regs[a->rd], imm); \ + tcg_temp_free(imm); \ + return true; \ + } \ + static bool cat3(trans_, name, _mr)(DisasContext *ctx, \ + cat3(arg_, name, _mr) * a) \ + { \ + TCGv val, mem; \ + mem = tcg_temp_new(); \ + val = rx_load_source(ctx, mem, a->ld, MO_32, a->rs); \ + gen_helper_##op(cpu_regs[a->rd], cpu_env, \ + cpu_regs[a->rd], val); \ + tcg_temp_free(mem); \ + return true; \ + } + +#define FCONVOP(name, op) \ + static bool trans_##name(DisasContext *ctx, arg_##name * a) \ + { \ + TCGv val, mem; \ + mem = tcg_temp_new(); \ + val = rx_load_source(ctx, mem, a->ld, MO_32, a->rs); \ + gen_helper_##op(cpu_regs[a->rd], cpu_env, val); \ + tcg_temp_free(mem); \ + return true; \ + } + +FOP(FADD, fadd) +FOP(FSUB, fsub) +FOP(FMUL, fmul) +FOP(FDIV, fdiv) + +/* fcmp #imm, rd */ +static bool trans_FCMP_ir(DisasContext *ctx, arg_FCMP_ir * a) +{ + TCGv imm = tcg_const_i32(li(ctx, 0)); + gen_helper_fcmp(cpu_env, cpu_regs[a->rd], imm); + tcg_temp_free(imm); + return true; +} + +/* fcmp dsp[rs], rd */ +/* fcmp rs, rd */ +static bool trans_FCMP_mr(DisasContext *ctx, arg_FCMP_mr *a) +{ + TCGv val, mem; + mem = tcg_temp_new(); + val = rx_load_source(ctx, mem, a->ld, MO_32, a->rs); + gen_helper_fcmp(cpu_env, cpu_regs[a->rd], val); + tcg_temp_free(mem); + return true; +} + +FCONVOP(FTOI, ftoi) +FCONVOP(ROUND, round) + +/* itof rs, rd */ +/* itof dsp[rs], rd */ +static bool trans_ITOF(DisasContext *ctx, arg_ITOF * a) +{ + TCGv val, mem; + mem = tcg_temp_new(); + val = rx_load_source(ctx, mem, a->ld, a->mi, a->rs); + gen_helper_itof(cpu_regs[a->rd], cpu_env, val); + tcg_temp_free(mem); + return true; +} + +static void rx_bsetm(TCGv mem, TCGv mask) +{ + TCGv val; + val = tcg_temp_new(); + rx_gen_ld(MO_8, val, mem); + tcg_gen_or_i32(val, val, mask); + rx_gen_st(MO_8, val, mem); + tcg_temp_free(val); +} + +static void rx_bclrm(TCGv mem, TCGv mask) +{ + TCGv val; + val = tcg_temp_new(); + rx_gen_ld(MO_8, val, mem); + tcg_gen_andc_i32(val, val, mask); + rx_gen_st(MO_8, val, mem); + tcg_temp_free(val); +} + +static void rx_btstm(TCGv mem, TCGv mask) +{ + TCGv val; + val = tcg_temp_new(); + rx_gen_ld(MO_8, val, mem); + tcg_gen_and_i32(val, val, mask); + tcg_gen_setcondi_i32(TCG_COND_NE, cpu_psw_c, val, 0); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_c); + tcg_temp_free(val); +} + +static void rx_bnotm(TCGv mem, TCGv mask) +{ + TCGv val; + val = tcg_temp_new(); + rx_gen_ld(MO_8, val, mem); + tcg_gen_xor_i32(val, val, mask); + rx_gen_st(MO_8, val, mem); + tcg_temp_free(val); +} + +static void rx_bsetr(TCGv reg, TCGv mask) +{ + tcg_gen_or_i32(reg, reg, mask); +} + +static void rx_bclrr(TCGv reg, TCGv mask) +{ + tcg_gen_andc_i32(reg, reg, mask); +} + +static inline void rx_btstr(TCGv reg, TCGv mask) +{ + TCGv t0; + t0 = tcg_temp_new(); + tcg_gen_and_i32(t0, reg, mask); + tcg_gen_setcondi_i32(TCG_COND_NE, cpu_psw_c, t0, 0); + tcg_gen_mov_i32(cpu_psw_z, cpu_psw_c); + tcg_temp_free(t0); +} + +static inline void rx_bnotr(TCGv reg, TCGv mask) +{ + tcg_gen_xor_i32(reg, reg, mask); +} + +#define BITOP(name, op) \ + static bool cat3(trans_, name, _im)(DisasContext *ctx, \ + cat3(arg_, name, _im) * a) \ + { \ + TCGv mask, mem, addr; \ + mem = tcg_temp_new(); \ + mask = tcg_const_i32(1 << a->imm); \ + addr = rx_index_addr(ctx, mem, a->ld, MO_8, a->rs); \ + cat3(rx_, op, m)(addr, mask); \ + tcg_temp_free(mask); \ + tcg_temp_free(mem); \ + return true; \ + } \ + static bool cat3(trans_, name, _ir)(DisasContext *ctx, \ + cat3(arg_, name, _ir) * a) \ + { \ + TCGv mask; \ + mask = tcg_const_i32(1 << a->imm); \ + cat3(rx_, op, r)(cpu_regs[a->rd], mask); \ + tcg_temp_free(mask); \ + return true; \ + } \ + static bool cat3(trans_, name, _rr)(DisasContext *ctx, \ + cat3(arg_, name, _rr) * a) \ + { \ + TCGv mask, b; \ + mask = tcg_const_i32(1); \ + b = tcg_temp_new(); \ + tcg_gen_andi_i32(b, cpu_regs[a->rs], 31); \ + tcg_gen_shl_i32(mask, mask, b); \ + cat3(rx_, op, r)(cpu_regs[a->rd], mask); \ + tcg_temp_free(mask); \ + tcg_temp_free(b); \ + return true; \ + } \ + static bool cat3(trans_, name, _rm)(DisasContext *ctx, \ + cat3(arg_, name, _rm) * a) \ + { \ + TCGv mask, mem, addr, b; \ + mask = tcg_const_i32(1); \ + b = tcg_temp_new(); \ + tcg_gen_andi_i32(b, cpu_regs[a->rd], 7); \ + tcg_gen_shl_i32(mask, mask, b); \ + mem = tcg_temp_new(); \ + addr = rx_index_addr(ctx, mem, a->ld, MO_8, a->rs); \ + cat3(rx_, op, m)(addr, mask); \ + tcg_temp_free(mem); \ + tcg_temp_free(mask); \ + tcg_temp_free(b); \ + return true; \ + } + +BITOP(BSET, bset) +BITOP(BCLR, bclr) +BITOP(BTST, btst) +BITOP(BNOT, bnot) + +static inline void bmcnd_op(TCGv val, TCGCond cond, int pos) +{ + TCGv bit; + DisasCompare dc; + dc.temp = tcg_temp_new(); + bit = tcg_temp_new(); + psw_cond(&dc, cond); + tcg_gen_andi_i32(val, val, ~(1 << pos)); + tcg_gen_setcondi_i32(dc.cond, bit, dc.value, 0); + tcg_gen_deposit_i32(val, val, bit, pos, 1); + tcg_temp_free(bit); + tcg_temp_free(dc.temp); + } + +/* bmcnd #imm, dsp[rd] */ +static bool trans_BMCnd_im(DisasContext *ctx, arg_BMCnd_im *a) +{ + TCGv val, mem, addr; + val = tcg_temp_new(); + mem = tcg_temp_new(); + addr = rx_index_addr(ctx, mem, a->ld, MO_8, a->rd); + rx_gen_ld(MO_8, val, addr); + bmcnd_op(val, a->cd, a->imm); + rx_gen_st(MO_8, val, addr); + tcg_temp_free(val); + tcg_temp_free(mem); + return true; +} + +/* bmcond #imm, rd */ +static bool trans_BMCnd_ir(DisasContext *ctx, arg_BMCnd_ir *a) +{ + bmcnd_op(cpu_regs[a->rd], a->cd, a->imm); + return true; +} + +enum { + PSW_C = 0, + PSW_Z = 1, + PSW_S = 2, + PSW_O = 3, + PSW_I = 8, + PSW_U = 9, +}; + +static inline void clrsetpsw(DisasContext *ctx, int cb, int val) +{ + if (cb < 8) { + switch (cb) { + case PSW_C: + tcg_gen_movi_i32(cpu_psw_c, val); + break; + case PSW_Z: + tcg_gen_movi_i32(cpu_psw_z, val == 0); + break; + case PSW_S: + tcg_gen_movi_i32(cpu_psw_s, val ? -1 : 0); + break; + case PSW_O: + tcg_gen_movi_i32(cpu_psw_o, val << 31); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Invalid distination %d", cb); + break; + } + } else if (is_privileged(ctx, 0)) { + switch (cb) { + case PSW_I: + tcg_gen_movi_i32(cpu_psw_i, val); + ctx->base.is_jmp = DISAS_UPDATE; + break; + case PSW_U: + tcg_gen_movi_i32(cpu_psw_u, val); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Invalid distination %d", cb); + break; + } + } +} + +/* clrpsw psw */ +static bool trans_CLRPSW(DisasContext *ctx, arg_CLRPSW *a) +{ + clrsetpsw(ctx, a->cb, 0); + return true; +} + +/* setpsw psw */ +static bool trans_SETPSW(DisasContext *ctx, arg_SETPSW *a) +{ + clrsetpsw(ctx, a->cb, 1); + return true; +} + +/* mvtipl #imm */ +static bool trans_MVTIPL(DisasContext *ctx, arg_MVTIPL *a) +{ + if (is_privileged(ctx, 1)) { + tcg_gen_movi_i32(cpu_psw_ipl, a->imm); + ctx->base.is_jmp = DISAS_UPDATE; + } + return true; +} + +/* mvtc #imm, rd */ +static bool trans_MVTC_i(DisasContext *ctx, arg_MVTC_i *a) +{ + TCGv imm; + + imm = tcg_const_i32(a->imm); + move_to_cr(ctx, imm, a->cr); + if (a->cr == 0 && is_privileged(ctx, 0)) { + ctx->base.is_jmp = DISAS_UPDATE; + } + tcg_temp_free(imm); + return true; +} + +/* mvtc rs, rd */ +static bool trans_MVTC_r(DisasContext *ctx, arg_MVTC_r *a) +{ + move_to_cr(ctx, cpu_regs[a->rs], a->cr); + if (a->cr == 0 && is_privileged(ctx, 0)) { + ctx->base.is_jmp = DISAS_UPDATE; + } + return true; +} + +/* mvfc rs, rd */ +static bool trans_MVFC(DisasContext *ctx, arg_MVFC *a) +{ + move_from_cr(cpu_regs[a->rd], a->cr, ctx->pc); + return true; +} + +/* rtfi */ +static bool trans_RTFI(DisasContext *ctx, arg_RTFI *a) +{ + TCGv psw; + if (is_privileged(ctx, 1)) { + psw = tcg_temp_new(); + tcg_gen_mov_i32(cpu_pc, cpu_bpc); + tcg_gen_mov_i32(psw, cpu_bpsw); + gen_helper_set_psw_rte(cpu_env, psw); + ctx->base.is_jmp = DISAS_EXIT; + tcg_temp_free(psw); + } + return true; +} + +/* rte */ +static bool trans_RTE(DisasContext *ctx, arg_RTE *a) +{ + TCGv psw; + if (is_privileged(ctx, 1)) { + psw = tcg_temp_new(); + pop(cpu_pc); + pop(psw); + gen_helper_set_psw_rte(cpu_env, psw); + ctx->base.is_jmp = DISAS_EXIT; + tcg_temp_free(psw); + } + return true; +} + +/* brk */ +static bool trans_BRK(DisasContext *ctx, arg_BRK *a) +{ + tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); + gen_helper_rxbrk(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + return true; +} + +/* int #imm */ +static bool trans_INT(DisasContext *ctx, arg_INT *a) +{ + TCGv vec; + + tcg_debug_assert(a->imm < 0x100); + vec = tcg_const_i32(a->imm); + tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); + gen_helper_rxint(cpu_env, vec); + tcg_temp_free(vec); + ctx->base.is_jmp = DISAS_NORETURN; + return true; +} + +/* wait */ +static bool trans_WAIT(DisasContext *ctx, arg_WAIT *a) +{ + if (is_privileged(ctx, 1)) { + tcg_gen_addi_i32(cpu_pc, cpu_pc, 2); + gen_helper_wait(cpu_env); + } + return true; +} + +static void rx_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) +{ + CPURXState *env = cs->env_ptr; + DisasContext *ctx = container_of(dcbase, DisasContext, base); + ctx->env = env; +} + +static void rx_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) +{ +} + +static void rx_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + tcg_gen_insn_start(ctx->base.pc_next); +} + +static bool rx_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, + const CPUBreakpoint *bp) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + /* We have hit a breakpoint - make sure PC is up-to-date */ + tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); + gen_helper_debug(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + ctx->base.pc_next += 1; + return true; +} + +static void rx_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + uint32_t insn; + + ctx->pc = ctx->base.pc_next; + insn = decode_load(ctx); + if (!decode(ctx, insn)) { + gen_helper_raise_illegal_instruction(cpu_env); + } +} + +static void rx_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + switch (ctx->base.is_jmp) { + case DISAS_NEXT: + case DISAS_TOO_MANY: + gen_goto_tb(ctx, 0, dcbase->pc_next); + break; + case DISAS_JUMP: + if (ctx->base.singlestep_enabled) { + gen_helper_debug(cpu_env); + } else { + tcg_gen_lookup_and_goto_ptr(); + } + break; + case DISAS_UPDATE: + tcg_gen_movi_i32(cpu_pc, ctx->base.pc_next); + case DISAS_EXIT: + tcg_gen_exit_tb(NULL, 0); + break; + case DISAS_NORETURN: + break; + default: + g_assert_not_reached(); + } +} + +static void rx_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +{ + qemu_log("IN:\n"); /* , lookup_symbol(dcbase->pc_first)); */ + log_target_disas(cs, dcbase->pc_first, dcbase->tb->size); +} + +static const TranslatorOps rx_tr_ops = { + .init_disas_context = rx_tr_init_disas_context, + .tb_start = rx_tr_tb_start, + .insn_start = rx_tr_insn_start, + .breakpoint_check = rx_tr_breakpoint_check, + .translate_insn = rx_tr_translate_insn, + .tb_stop = rx_tr_tb_stop, + .disas_log = rx_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +{ + DisasContext dc; + + translator_loop(&rx_tr_ops, &dc.base, cs, tb, max_insns); +} + +void restore_state_to_opc(CPURXState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->pc = data[0]; +} + +#define ALLOC_REGISTER(sym, name) \ + cpu_##sym = tcg_global_mem_new_i32(cpu_env, \ + offsetof(CPURXState, sym), name) + +void rx_translate_init(void) +{ + static const char * const regnames[NUM_REGS] = { + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", + "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15" + }; + int i; + + for (i = 0; i < NUM_REGS; i++) { + cpu_regs[i] = tcg_global_mem_new_i32(cpu_env, + offsetof(CPURXState, regs[i]), + regnames[i]); + } + ALLOC_REGISTER(pc, "PC"); + ALLOC_REGISTER(psw_o, "PSW(O)"); + ALLOC_REGISTER(psw_s, "PSW(S)"); + ALLOC_REGISTER(psw_z, "PSW(Z)"); + ALLOC_REGISTER(psw_c, "PSW(C)"); + ALLOC_REGISTER(psw_u, "PSW(U)"); + ALLOC_REGISTER(psw_i, "PSW(I)"); + ALLOC_REGISTER(psw_pm, "PSW(PM)"); + ALLOC_REGISTER(psw_ipl, "PSW(IPL)"); + ALLOC_REGISTER(usp, "USP"); + ALLOC_REGISTER(fpsw, "FPSW"); + ALLOC_REGISTER(bpsw, "BPSW"); + ALLOC_REGISTER(bpc, "BPC"); + ALLOC_REGISTER(isp, "ISP"); + ALLOC_REGISTER(fintv, "FINTV"); + ALLOC_REGISTER(intb, "INTB"); + cpu_acc = tcg_global_mem_new_i64(cpu_env, + offsetof(CPURXState, acc), "ACC"); +} diff --git a/target/s390x/cpu-qom.h b/target/s390x/cpu-qom.h index dbe5346ec9..1630818c28 100644 --- a/target/s390x/cpu-qom.h +++ b/target/s390x/cpu-qom.h @@ -61,7 +61,7 @@ typedef struct S390CPUClass { const char *desc; DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; void (*load_normal)(CPUState *cpu); void (*reset)(CPUState *cpu, cpu_reset_type type); } S390CPUClass; diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 3dd396e870..427a46e3e1 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -96,8 +96,9 @@ static void s390_cpu_reset(CPUState *s, cpu_reset_type type) S390CPU *cpu = S390_CPU(s); S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); CPUS390XState *env = &cpu->env; + DeviceState *dev = DEVICE(s); - scc->parent_reset(s); + scc->parent_reset(dev); cpu->env.sigp_order = 0; s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); @@ -450,8 +451,9 @@ static Property s390x_cpu_properties[] = { DEFINE_PROP_END_OF_LIST() }; -static void s390_cpu_reset_full(CPUState *s) +static void s390_cpu_reset_full(DeviceState *dev) { + CPUState *s = CPU(dev); return s390_cpu_reset(s, S390_CPU_RESET_CLEAR); } @@ -466,7 +468,7 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) device_class_set_props(dc, s390x_cpu_properties); dc->user_creatable = true; - cpu_class_set_parent_reset(cc, s390_cpu_reset_full, &scc->parent_reset); + device_class_set_parent_reset(dc, s390_cpu_reset_full, &scc->parent_reset); #if !defined(CONFIG_USER_ONLY) scc->load_normal = s390_cpu_load_normal; #endif diff --git a/target/s390x/diag.c b/target/s390x/diag.c index b5aec06d6b..54e5670b3f 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -117,7 +117,7 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len)); - if (!iplb_valid_ccw(iplb) && !iplb_valid_fcp(iplb)) { + if (!iplb_valid(iplb)) { env->regs[r1 + 1] = DIAG_308_RC_INVALID; goto out; } diff --git a/target/s390x/gdbstub.c b/target/s390x/gdbstub.c index e24a49f4a9..d6fce5ff1e 100644 --- a/target/s390x/gdbstub.c +++ b/target/s390x/gdbstub.c @@ -27,7 +27,7 @@ #include "sysemu/hw_accel.h" #include "sysemu/tcg.h" -int s390_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int s390_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { S390CPU *cpu = S390_CPU(cs); CPUS390XState *env = &cpu->env; @@ -82,11 +82,11 @@ int s390_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) /* total number of registers in s390-acr.xml */ #define S390_NUM_AC_REGS 16 -static int cpu_read_ac_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_ac_reg(CPUS390XState *env, GByteArray *buf, int n) { switch (n) { case S390_A0_REGNUM ... S390_A15_REGNUM: - return gdb_get_reg32(mem_buf, env->aregs[n]); + return gdb_get_reg32(buf, env->aregs[n]); default: return 0; } @@ -111,13 +111,13 @@ static int cpu_write_ac_reg(CPUS390XState *env, uint8_t *mem_buf, int n) /* total number of registers in s390-fpr.xml */ #define S390_NUM_FP_REGS 17 -static int cpu_read_fp_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_fp_reg(CPUS390XState *env, GByteArray *buf, int n) { switch (n) { case S390_FPC_REGNUM: - return gdb_get_reg32(mem_buf, env->fpc); + return gdb_get_reg32(buf, env->fpc); case S390_F0_REGNUM ... S390_F15_REGNUM: - return gdb_get_reg64(mem_buf, *get_freg(env, n - S390_F0_REGNUM)); + return gdb_get_reg64(buf, *get_freg(env, n - S390_F0_REGNUM)); default: return 0; } @@ -145,17 +145,17 @@ static int cpu_write_fp_reg(CPUS390XState *env, uint8_t *mem_buf, int n) /* total number of registers in s390-vx.xml */ #define S390_NUM_VREGS 32 -static int cpu_read_vreg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_vreg(CPUS390XState *env, GByteArray *buf, int n) { int ret; switch (n) { case S390_V0L_REGNUM ... S390_V15L_REGNUM: - ret = gdb_get_reg64(mem_buf, env->vregs[n][1]); + ret = gdb_get_reg64(buf, env->vregs[n][1]); break; case S390_V16_REGNUM ... S390_V31_REGNUM: - ret = gdb_get_reg64(mem_buf, env->vregs[n][0]); - ret += gdb_get_reg64(mem_buf + 8, env->vregs[n][1]); + ret = gdb_get_reg64(buf, env->vregs[n][0]); + ret += gdb_get_reg64(buf, env->vregs[n][1]); break; default: ret = 0; @@ -186,11 +186,11 @@ static int cpu_write_vreg(CPUS390XState *env, uint8_t *mem_buf, int n) #define S390_NUM_C_REGS 16 #ifndef CONFIG_USER_ONLY -static int cpu_read_c_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_c_reg(CPUS390XState *env, GByteArray *buf, int n) { switch (n) { case S390_C0_REGNUM ... S390_C15_REGNUM: - return gdb_get_regl(mem_buf, env->cregs[n]); + return gdb_get_regl(buf, env->cregs[n]); default: return 0; } @@ -223,7 +223,7 @@ static int cpu_write_c_reg(CPUS390XState *env, uint8_t *mem_buf, int n) /* total number of registers in s390-virt.xml */ #define S390_NUM_VIRT_REGS 8 -static int cpu_read_virt_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_virt_reg(CPUS390XState *env, GByteArray *mem_buf, int n) { switch (n) { case S390_VIRT_CKC_REGNUM: @@ -296,9 +296,9 @@ static int cpu_write_virt_reg(CPUS390XState *env, uint8_t *mem_buf, int n) /* total number of registers in s390-gs.xml */ #define S390_NUM_GS_REGS 4 -static int cpu_read_gs_reg(CPUS390XState *env, uint8_t *mem_buf, int n) +static int cpu_read_gs_reg(CPUS390XState *env, GByteArray *buf, int n) { - return gdb_get_regl(mem_buf, env->gscb[n]); + return gdb_get_regl(buf, env->gscb[n]); } static int cpu_write_gs_reg(CPUS390XState *env, uint8_t *mem_buf, int n) diff --git a/target/s390x/internal.h b/target/s390x/internal.h index d37816104d..8c95c734db 100644 --- a/target/s390x/internal.h +++ b/target/s390x/internal.h @@ -292,7 +292,7 @@ uint16_t float128_dcmask(CPUS390XState *env, float128 f1); /* gdbstub.c */ -int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int s390_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int s390_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void s390_cpu_gdb_init(CPUState *cs); diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c index c437a1d8c6..0e840cc579 100644 --- a/target/s390x/ioinst.c +++ b/target/s390x/ioinst.c @@ -347,7 +347,7 @@ typedef struct ChscResp { uint16_t len; uint16_t code; uint32_t param; - char data[0]; + char data[]; } QEMU_PACKED ChscResp; #define CHSC_MIN_RESP_LEN 0x0008 diff --git a/target/sh4/cpu-qom.h b/target/sh4/cpu-qom.h index 0c56d055ba..72a63f3fd3 100644 --- a/target/sh4/cpu-qom.h +++ b/target/sh4/cpu-qom.h @@ -51,7 +51,7 @@ typedef struct SuperHCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; uint32_t pvr; uint32_t prr; diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 70c8d8170f..3c68021c56 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -47,14 +47,14 @@ static bool superh_cpu_has_work(CPUState *cs) return cs->interrupt_request & CPU_INTERRUPT_HARD; } -/* CPUClass::reset() */ -static void superh_cpu_reset(CPUState *s) +static void superh_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); SuperHCPU *cpu = SUPERH_CPU(s); SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(cpu); CPUSH4State *env = &cpu->env; - scc->parent_reset(s); + scc->parent_reset(dev); memset(env, 0, offsetof(CPUSH4State, end_reset_fields)); @@ -214,7 +214,7 @@ static void superh_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, superh_cpu_realizefn, &scc->parent_realize); - cpu_class_set_parent_reset(cc, superh_cpu_reset, &scc->parent_reset); + device_class_set_parent_reset(dc, superh_cpu_reset, &scc->parent_reset); cc->class_by_name = superh_cpu_class_by_name; cc->has_work = superh_cpu_has_work; diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index 452a596e67..dbe58c7888 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -208,7 +208,7 @@ void superh_cpu_do_interrupt(CPUState *cpu); bool superh_cpu_exec_interrupt(CPUState *cpu, int int_req); void superh_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int superh_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int superh_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int superh_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void superh_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, diff --git a/target/sh4/gdbstub.c b/target/sh4/gdbstub.c index 44c1679e9d..49fc4a0cc6 100644 --- a/target/sh4/gdbstub.c +++ b/target/sh4/gdbstub.c @@ -24,7 +24,7 @@ /* Hint: Use "set architecture sh4" in GDB to see fpu registers */ /* FIXME: We should use XML for this. */ -int superh_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int superh_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { SuperHCPU *cpu = SUPERH_CPU(cs); CPUSH4State *env = &cpu->env; diff --git a/target/sparc/cpu-qom.h b/target/sparc/cpu-qom.h index 7442e2768e..8b4d33c21e 100644 --- a/target/sparc/cpu-qom.h +++ b/target/sparc/cpu-qom.h @@ -49,7 +49,7 @@ typedef struct SPARCCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; sparc_def_t *cpu_def; } SPARCCPUClass; diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index eeaecbd8d6..3f05aba9d6 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -28,14 +28,14 @@ //#define DEBUG_FEATURES -/* CPUClass::reset() */ -static void sparc_cpu_reset(CPUState *s) +static void sparc_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); SPARCCPU *cpu = SPARC_CPU(s); SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(cpu); CPUSPARCState *env = &cpu->env; - scc->parent_reset(s); + scc->parent_reset(dev); memset(env, 0, offsetof(CPUSPARCState, end_reset_fields)); env->cwp = 0; @@ -859,7 +859,7 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) &scc->parent_realize); device_class_set_props(dc, sparc_cpu_properties); - cpu_class_set_parent_reset(cc, sparc_cpu_reset, &scc->parent_reset); + device_class_set_parent_reset(dc, sparc_cpu_reset, &scc->parent_reset); cc->class_by_name = sparc_cpu_class_by_name; cc->parse_features = sparc_cpu_parse_features; diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index ae97c7d9f7..b9369398f2 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -571,7 +571,7 @@ extern const VMStateDescription vmstate_sparc_cpu; void sparc_cpu_do_interrupt(CPUState *cpu); void sparc_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr sparc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -int sparc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int sparc_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int sparc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, diff --git a/target/sparc/gdbstub.c b/target/sparc/gdbstub.c index 8be742b5a3..78dc8dcc98 100644 --- a/target/sparc/gdbstub.c +++ b/target/sparc/gdbstub.c @@ -27,7 +27,7 @@ #define gdb_get_rega(buf, val) gdb_get_regl(buf, val) #endif -int sparc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int sparc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { SPARCCPU *cpu = SPARC_CPU(cs); CPUSPARCState *env = &cpu->env; diff --git a/target/tilegx/cpu.c b/target/tilegx/cpu.c index cd422a0467..1fee87c094 100644 --- a/target/tilegx/cpu.c +++ b/target/tilegx/cpu.c @@ -68,13 +68,14 @@ static bool tilegx_cpu_has_work(CPUState *cs) return true; } -static void tilegx_cpu_reset(CPUState *s) +static void tilegx_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); TileGXCPU *cpu = TILEGX_CPU(s); TileGXCPUClass *tcc = TILEGX_CPU_GET_CLASS(cpu); CPUTLGState *env = &cpu->env; - tcc->parent_reset(s); + tcc->parent_reset(dev); memset(env, 0, offsetof(CPUTLGState, end_reset_fields)); } @@ -142,7 +143,7 @@ static void tilegx_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, tilegx_cpu_realizefn, &tcc->parent_realize); - cpu_class_set_parent_reset(cc, tilegx_cpu_reset, &tcc->parent_reset); + device_class_set_parent_reset(dc, tilegx_cpu_reset, &tcc->parent_reset); cc->class_by_name = tilegx_cpu_class_by_name; cc->has_work = tilegx_cpu_has_work; diff --git a/target/tilegx/cpu.h b/target/tilegx/cpu.h index 9cbec247d2..193b6bbccb 100644 --- a/target/tilegx/cpu.h +++ b/target/tilegx/cpu.h @@ -118,7 +118,7 @@ typedef struct TileGXCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } TileGXCPUClass; /** diff --git a/target/tricore/cpu-qom.h b/target/tricore/cpu-qom.h index 7c1e130b4e..cd819e6f24 100644 --- a/target/tricore/cpu-qom.h +++ b/target/tricore/cpu-qom.h @@ -36,7 +36,7 @@ typedef struct TriCoreCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; } TriCoreCPUClass; typedef struct TriCoreCPU TriCoreCPU; diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 85bc9f03a1..743b404a95 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -53,13 +53,14 @@ static void tricore_cpu_synchronize_from_tb(CPUState *cs, env->PC = tb->pc; } -static void tricore_cpu_reset(CPUState *s) +static void tricore_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); TriCoreCPU *cpu = TRICORE_CPU(s); TriCoreCPUClass *tcc = TRICORE_CPU_GET_CLASS(cpu); CPUTriCoreState *env = &cpu->env; - tcc->parent_reset(s); + tcc->parent_reset(dev); cpu_state_reset(env); } @@ -153,7 +154,7 @@ static void tricore_cpu_class_init(ObjectClass *c, void *data) device_class_set_parent_realize(dc, tricore_cpu_realizefn, &mcc->parent_realize); - cpu_class_set_parent_reset(cc, tricore_cpu_reset, &mcc->parent_reset); + device_class_set_parent_reset(dc, tricore_cpu_reset, &mcc->parent_reset); cc->class_by_name = tricore_cpu_class_by_name; cc->has_work = tricore_cpu_has_work; diff --git a/target/xtensa/cpu-qom.h b/target/xtensa/cpu-qom.h index 9ac54241bd..3ea93ce1f9 100644 --- a/target/xtensa/cpu-qom.h +++ b/target/xtensa/cpu-qom.h @@ -56,7 +56,7 @@ typedef struct XtensaCPUClass { /*< public >*/ DeviceRealize parent_realize; - void (*parent_reset)(CPUState *cpu); + DeviceReset parent_reset; const XtensaConfig *config; } XtensaCPUClass; diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 4856aee8ec..82c2ee0679 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -67,14 +67,14 @@ bool xtensa_abi_call0(void) } #endif -/* CPUClass::reset() */ -static void xtensa_cpu_reset(CPUState *s) +static void xtensa_cpu_reset(DeviceState *dev) { + CPUState *s = CPU(dev); XtensaCPU *cpu = XTENSA_CPU(s); XtensaCPUClass *xcc = XTENSA_CPU_GET_CLASS(cpu); CPUXtensaState *env = &cpu->env; - xcc->parent_reset(s); + xcc->parent_reset(dev); env->exception_taken = 0; env->pc = env->config->exception_vector[EXC_RESET0 + env->static_vectors]; @@ -184,7 +184,7 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data) device_class_set_parent_realize(dc, xtensa_cpu_realizefn, &xcc->parent_realize); - cpu_class_set_parent_reset(cc, xtensa_cpu_reset, &xcc->parent_reset); + device_class_set_parent_reset(dc, xtensa_cpu_reset, &xcc->parent_reset); cc->class_by_name = xtensa_cpu_class_by_name; cc->has_work = xtensa_cpu_has_work; diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index 493f4fc80c..c0d69fad96 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -569,7 +569,7 @@ void xtensa_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); void xtensa_count_regs(const XtensaConfig *config, unsigned *n_regs, unsigned *n_core_regs); -int xtensa_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int xtensa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int xtensa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, MMUAccessType access_type, diff --git a/target/xtensa/gdbstub.c b/target/xtensa/gdbstub.c index 54727881f3..0ee3feabe5 100644 --- a/target/xtensa/gdbstub.c +++ b/target/xtensa/gdbstub.c @@ -63,7 +63,7 @@ void xtensa_count_regs(const XtensaConfig *config, } } -int xtensa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +int xtensa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { XtensaCPU *cpu = XTENSA_CPU(cs); CPUXtensaState *env = &cpu->env; diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c index cdedcb2b25..223dba9c8c 100644 --- a/tcg/i386/tcg-target.inc.c +++ b/tcg/i386/tcg-target.inc.c @@ -3391,12 +3391,15 @@ static void expand_vec_sari(TCGType type, unsigned vece, case MO_64: if (imm <= 32) { - /* We can emulate a small sign extend by performing an arithmetic + /* + * We can emulate a small sign extend by performing an arithmetic * 32-bit shift and overwriting the high half of a 64-bit logical - * shift (note that the ISA says shift of 32 is valid). + * shift. Note that the ISA says shift of 32 is valid, but TCG + * does not, so we have to bound the smaller shift -- we get the + * same result in the high half either way. */ t1 = tcg_temp_new_vec(type); - tcg_gen_sari_vec(MO_32, t1, v1, imm); + tcg_gen_sari_vec(MO_32, t1, v1, MIN(imm, 31)); tcg_gen_shri_vec(MO_64, v0, v1, imm); vec_gen_4(INDEX_op_x86_blend_vec, type, MO_32, tcgv_vec_arg(v0), tcgv_vec_arg(v0), diff --git a/tests/.gitignore b/tests/.gitignore index 7306866f21..d03c037d77 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -10,6 +10,7 @@ qht-bench rcutorture test-* !test-*.c +!test-*.py !docker/test-* test-qapi-commands.[ch] test-qapi-init-commands.[ch] diff --git a/tests/Makefile.include b/tests/Makefile.include index edcbd475aa..51de676298 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -20,6 +20,8 @@ check-help: @echo " $(MAKE) check-venv Creates a Python venv for tests" @echo " $(MAKE) check-clean Clean the tests and related data" @echo + @echo " $(MAKE) get-vm-images Downloads all images used by acceptance tests, according to configured targets (~350 MB each, 1.5 GB max)" + @echo @echo @echo "The variable SPEED can be set to control the gtester speed setting." @echo "Default options are -k and (for $(MAKE) V=1) --verbose; they can be" @@ -242,6 +244,7 @@ qapi-schema += event-case.json qapi-schema += event-member-invalid-dict.json qapi-schema += event-nest-struct.json qapi-schema += features-bad-type.json +qapi-schema += features-deprecated-type.json qapi-schema += features-duplicate-name.json qapi-schema += features-if-invalid.json qapi-schema += features-missing-name.json @@ -589,6 +592,7 @@ include $(SRC_PATH)/tests/qtest/Makefile.include tests/test-qga$(EXESUF): qemu-ga$(EXESUF) tests/test-qga$(EXESUF): tests/test-qga.o $(qtest-obj-y) tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a +tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o SPEED = quick @@ -888,7 +892,21 @@ $(TESTS_RESULTS_DIR): check-venv: $(TESTS_VENV_DIR) -check-acceptance: check-venv $(TESTS_RESULTS_DIR) +FEDORA_31_ARCHES_CANDIDATES=$(patsubst ppc64,ppc64le,$(TARGETS)) +FEDORA_31_ARCHES := x86_64 aarch64 ppc64le s390x +FEDORA_31_DOWNLOAD=$(filter $(FEDORA_31_ARCHES),$(FEDORA_31_ARCHES_CANDIDATES)) + +# download one specific Fedora 31 image +get-vm-image-fedora-31-%: check-venv + $(call quiet-command, \ + $(TESTS_VENV_DIR)/bin/python -m avocado vmimage get \ + --distro=fedora --distro-version=31 --arch=$*, \ + "AVOCADO", "Downloading acceptance tests VM image for $*") + +# download all vm images, according to defined targets +get-vm-images: check-venv $(patsubst %,get-vm-image-fedora-31-%, $(FEDORA_31_DOWNLOAD)) + +check-acceptance: check-venv $(TESTS_RESULTS_DIR) get-vm-images $(call quiet-command, \ $(TESTS_VENV_DIR)/bin/python -m avocado \ --show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \ @@ -899,7 +917,7 @@ check-acceptance: check-venv $(TESTS_RESULTS_DIR) # Consolidated targets -.PHONY: check-block check-qapi-schema check-qtest check-unit check check-clean +.PHONY: check-block check-qapi-schema check-qtest check-unit check check-clean get-vm-images check-qapi-schema: check-tests/qapi-schema/frontend check-tests/qapi-schema/doc-good.texi check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS)) ifeq ($(CONFIG_TOOLS),y) diff --git a/tests/acceptance/avocado_qemu/__init__.py b/tests/acceptance/avocado_qemu/__init__.py index d4358eb431..59e7b4f763 100644 --- a/tests/acceptance/avocado_qemu/__init__.py +++ b/tests/acceptance/avocado_qemu/__init__.py @@ -16,8 +16,21 @@ import tempfile import avocado -SRC_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..', '..', '..') -sys.path.append(os.path.join(SRC_ROOT_DIR, 'python')) +#: The QEMU build root directory. It may also be the source directory +#: if building from the source dir, but it's safer to use BUILD_DIR for +#: that purpose. Be aware that if this code is moved outside of a source +#: and build tree, it will not be accurate. +BUILD_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) + +if os.path.islink(os.path.dirname(os.path.dirname(__file__))): + # The link to the acceptance tests dir in the source code directory + lnk = os.path.dirname(os.path.dirname(__file__)) + #: The QEMU root source directory + SOURCE_DIR = os.path.dirname(os.path.dirname(os.readlink(lnk))) +else: + SOURCE_DIR = BUILD_DIR + +sys.path.append(os.path.join(SOURCE_DIR, 'python')) from qemu.machine import QEMUMachine @@ -49,10 +62,10 @@ def pick_default_qemu_bin(arch=None): if is_readable_executable_file(qemu_bin_relative_path): return qemu_bin_relative_path - qemu_bin_from_src_dir_path = os.path.join(SRC_ROOT_DIR, + qemu_bin_from_bld_dir_path = os.path.join(BUILD_DIR, qemu_bin_relative_path) - if is_readable_executable_file(qemu_bin_from_src_dir_path): - return qemu_bin_from_src_dir_path + if is_readable_executable_file(qemu_bin_from_bld_dir_path): + return qemu_bin_from_bld_dir_path def _console_interaction(test, success_message, failure_message, @@ -153,7 +166,7 @@ class Test(avocado.Test): self.qemu_bin = self.params.get('qemu_bin', default=default_qemu_bin) if self.qemu_bin is None: - self.cancel("No QEMU binary defined or found in the source tree") + self.cancel("No QEMU binary defined or found in the build tree") def _new_vm(self, *args): vm = QEMUMachine(self.qemu_bin, sock_dir=tempfile.mkdtemp()) diff --git a/tests/acceptance/boot_linux.py b/tests/acceptance/boot_linux.py new file mode 100644 index 0000000000..075a386300 --- /dev/null +++ b/tests/acceptance/boot_linux.py @@ -0,0 +1,222 @@ +# Functional test that boots a complete Linux system via a cloud image +# +# Copyright (c) 2018-2020 Red Hat, Inc. +# +# Author: +# Cleber Rosa <crosa@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os + +from avocado_qemu import Test, BUILD_DIR + +from qemu.accel import kvm_available +from qemu.accel import tcg_available + +from avocado.utils import cloudinit +from avocado.utils import network +from avocado.utils import vmimage +from avocado.utils import datadrainer +from avocado.utils.path import find_command + +ACCEL_NOT_AVAILABLE_FMT = "%s accelerator does not seem to be available" +KVM_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "KVM" +TCG_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "TCG" + + +class BootLinux(Test): + """ + Boots a Linux system, checking for a successful initialization + """ + + timeout = 900 + chksum = None + + def setUp(self): + super(BootLinux, self).setUp() + self.vm.add_args('-smp', '2') + self.vm.add_args('-m', '1024') + self.prepare_boot() + self.prepare_cloudinit() + + def prepare_boot(self): + self.log.debug('Looking for and selecting a qemu-img binary to be ' + 'used to create the bootable snapshot image') + # If qemu-img has been built, use it, otherwise the system wide one + # will be used. If none is available, the test will cancel. + qemu_img = os.path.join(BUILD_DIR, 'qemu-img') + if not os.path.exists(qemu_img): + qemu_img = find_command('qemu-img', False) + if qemu_img is False: + self.cancel('Could not find "qemu-img", which is required to ' + 'create the bootable image') + vmimage.QEMU_IMG = qemu_img + + self.log.info('Downloading/preparing boot image') + # Fedora 31 only provides ppc64le images + image_arch = self.arch + if image_arch == 'ppc64': + image_arch = 'ppc64le' + try: + self.boot = vmimage.get( + 'fedora', arch=image_arch, version='31', + checksum=self.chksum, + algorithm='sha256', + cache_dir=self.cache_dirs[0], + snapshot_dir=self.workdir) + self.vm.add_args('-drive', 'file=%s' % self.boot.path) + except: + self.cancel('Failed to download/prepare boot image') + + def prepare_cloudinit(self): + self.log.info('Preparing cloudinit image') + try: + cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') + self.phone_home_port = network.find_free_port() + cloudinit.iso(cloudinit_iso, self.name, + username='root', + password='password', + # QEMU's hard coded usermode router address + phone_home_host='10.0.2.2', + phone_home_port=self.phone_home_port) + self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso) + except Exception: + self.cancel('Failed to prepared cloudinit image') + + def launch_and_wait(self): + self.vm.set_console() + self.vm.launch() + console_drainer = datadrainer.LineLogger(self.vm.console_socket.fileno(), + logger=self.log.getChild('console')) + console_drainer.start() + self.log.info('VM launched, waiting for boot confirmation from guest') + cloudinit.wait_for_phone_home(('0.0.0.0', self.phone_home_port), self.name) + + +class BootLinuxX8664(BootLinux): + """ + :avocado: tags=arch:x86_64 + """ + + chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0' + + def test_pc_i440fx_tcg(self): + """ + :avocado: tags=machine:pc + :avocado: tags=accel:tcg + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.launch_and_wait() + + def test_pc_i440fx_kvm(self): + """ + :avocado: tags=machine:pc + :avocado: tags=accel:kvm + """ + if not kvm_available(self.arch, self.qemu_bin): + self.cancel(KVM_NOT_AVAILABLE) + self.vm.add_args("-accel", "kvm") + self.launch_and_wait() + + def test_pc_q35_tcg(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=accel:tcg + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.launch_and_wait() + + def test_pc_q35_kvm(self): + """ + :avocado: tags=machine:q35 + :avocado: tags=accel:kvm + """ + if not kvm_available(self.arch, self.qemu_bin): + self.cancel(KVM_NOT_AVAILABLE) + self.vm.add_args("-accel", "kvm") + self.launch_and_wait() + + +class BootLinuxAarch64(BootLinux): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:virt + :avocado: tags=machine:gic-version=2 + """ + + chksum = '1e18d9c0cf734940c4b5d5ec592facaed2af0ad0329383d5639c997fdf16fe49' + + def add_common_args(self): + self.vm.add_args('-bios', + os.path.join(BUILD_DIR, 'pc-bios', + 'edk2-aarch64-code.fd')) + self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') + self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom') + + def test_virt_tcg(self): + """ + :avocado: tags=accel:tcg + :avocado: tags=cpu:max + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.vm.add_args("-cpu", "max") + self.vm.add_args("-machine", "virt,gic-version=2") + self.add_common_args() + self.launch_and_wait() + + def test_virt_kvm(self): + """ + :avocado: tags=accel:kvm + :avocado: tags=cpu:host + """ + if not kvm_available(self.arch, self.qemu_bin): + self.cancel(KVM_NOT_AVAILABLE) + self.vm.add_args("-accel", "kvm") + self.vm.add_args("-cpu", "host") + self.vm.add_args("-machine", "virt,gic-version=2") + self.add_common_args() + self.launch_and_wait() + + +class BootLinuxPPC64(BootLinux): + """ + :avocado: tags=arch:ppc64 + """ + + chksum = '7c3528b85a3df4b2306e892199a9e1e43f991c506f2cc390dc4efa2026ad2f58' + + def test_pseries_tcg(self): + """ + :avocado: tags=machine:pseries + :avocado: tags=accel:tcg + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.launch_and_wait() + + +class BootLinuxS390X(BootLinux): + """ + :avocado: tags=arch:s390x + """ + + chksum = '4caaab5a434fd4d1079149a072fdc7891e354f834d355069ca982fdcaf5a122d' + + def test_s390_ccw_virtio_tcg(self): + """ + :avocado: tags=machine:s390-ccw-virtio + :avocado: tags=accel:tcg + """ + if not tcg_available(self.qemu_bin): + self.cancel(TCG_NOT_AVAILABLE) + self.vm.add_args("-accel", "tcg") + self.launch_and_wait() diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py index 34d37eba3b..f825cd9ef5 100644 --- a/tests/acceptance/boot_linux_console.py +++ b/tests/acceptance/boot_linux_console.py @@ -16,10 +16,17 @@ import shutil from avocado import skipUnless from avocado_qemu import Test from avocado_qemu import exec_command_and_wait_for_pattern +from avocado_qemu import interrupt_interactive_console_until_pattern from avocado_qemu import wait_for_console_pattern from avocado.utils import process from avocado.utils import archive +from avocado.utils.path import find_command, CmdNotFoundError +P7ZIP_AVAILABLE = True +try: + find_command('7z') +except CmdNotFoundError: + P7ZIP_AVAILABLE = False class BootLinuxConsole(Test): """ @@ -507,6 +514,229 @@ class BootLinuxConsole(Test): exec_command_and_wait_for_pattern(self, 'reboot', 'reboot: Restarting system') + def test_arm_orangepi(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:orangepi-pc + """ + deb_url = ('https://apt.armbian.com/pool/main/l/' + 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') + deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-4.20.7-sunxi') + dtb_path = '/usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200n8 ' + 'earlycon=uart,mmio32,0x1c28000') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + + def test_arm_orangepi_initrd(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:orangepi-pc + """ + deb_url = ('https://apt.armbian.com/pool/main/l/' + 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') + deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-4.20.7-sunxi') + dtb_path = '/usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + initrd_url = ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv7a.cpio.gz') + initrd_hash = '604b2e45cdf35045846b8bbfbf2129b1891bdc9c' + initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + archive.gzip_uncompress(initrd_path_gz, initrd_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun8i Family') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'system-control@1c00000') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + + def test_arm_orangepi_sd(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:orangepi-pc + """ + deb_url = ('https://apt.armbian.com/pool/main/l/' + 'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb') + deb_hash = '1334c29c44d984ffa05ed10de8c3361f33d78315' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-4.20.7-sunxi') + dtb_path = '/usr/lib/linux-image-dev-sunxi/sun8i-h3-orangepi-pc.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/' + 'kci-2019.02/armel/base/rootfs.ext2.xz') + rootfs_hash = '692510cb625efda31640d1de0a8d60e26040f061' + rootfs_path_xz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash) + rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') + archive.lzma_uncompress(rootfs_path_xz, rootfs_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'root=/dev/mmcblk0 rootwait rw ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-drive', 'file=' + rootfs_path + ',if=sd,format=raw', + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + shell_ready = "/bin/sh: can't access tty; job control turned off" + self.wait_for_console_pattern(shell_ready) + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun8i Family') + exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', + 'mmcblk0') + exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up', + 'eth0: Link is Up') + exec_command_and_wait_for_pattern(self, 'udhcpc eth0', + 'udhcpc: lease of 10.0.2.15 obtained') + exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', + '3 packets transmitted, 3 packets received, 0% packet loss') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + + @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') + @skipUnless(P7ZIP_AVAILABLE, '7z not installed') + def test_arm_orangepi_bionic(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:orangepi-pc + """ + + # This test download a 196MB compressed image and expand it to 932MB... + image_url = ('https://dl.armbian.com/orangepipc/archive/' + 'Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.7z') + image_hash = '196a8ffb72b0123d92cea4a070894813d305c71e' + image_path_7z = self.fetch_asset(image_url, asset_hash=image_hash) + image_name = 'Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.img' + image_path = os.path.join(self.workdir, image_name) + process.run("7z e -o%s %s" % (self.workdir, image_path_7z)) + + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', + '-nic', 'user', + '-no-reboot') + self.vm.launch() + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'loglevel=7 ' + 'nosmp ' + 'systemd.default_timeout_start_sec=9000 ' + 'systemd.mask=armbian-zram-config.service ' + 'systemd.mask=armbian-ramlog.service') + + self.wait_for_console_pattern('U-Boot SPL') + self.wait_for_console_pattern('Autoboot in ') + exec_command_and_wait_for_pattern(self, ' ', '=>') + exec_command_and_wait_for_pattern(self, "setenv extraargs '" + + kernel_command_line + "'", '=>') + exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); + + self.wait_for_console_pattern('systemd[1]: Set hostname ' + + 'to <orangepipc>') + self.wait_for_console_pattern('Starting Load Kernel Modules...') + + @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') + def test_arm_orangepi_uboot_netbsd9(self): + """ + :avocado: tags=arch:arm + :avocado: tags=machine:orangepi-pc + """ + # This test download a 304MB compressed image and expand it to 1.3GB... + deb_url = ('http://snapshot.debian.org/archive/debian/' + '20200108T145233Z/pool/main/u/u-boot/' + 'u-boot-sunxi_2020.01%2Bdfsg-1_armhf.deb') + deb_hash = 'f67f404a80753ca3d1258f13e38f2b060e13db99' + deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) + # We use the common OrangePi PC 'plus' build of U-Boot for our secondary + # program loader (SPL). We will then set the path to the more specific + # OrangePi "PC" device tree blob with 'setenv fdtfile' in U-Boot prompt, + # before to boot NetBSD. + uboot_path = '/usr/lib/u-boot/orangepi_plus/u-boot-sunxi-with-spl.bin' + uboot_path = self.extract_from_deb(deb_path, uboot_path) + image_url = ('https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/' + 'evbarm-earmv7hf/binary/gzimg/armv7.img.gz') + image_hash = '2babb29d36d8360adcb39c09e31060945259917a' + image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash) + image_path = os.path.join(self.workdir, 'armv7.img') + image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path + archive.gzip_uncompress(image_path_gz, image_path) + + # dd if=u-boot-sunxi-with-spl.bin of=armv7.img bs=1K seek=8 conv=notrunc + with open(uboot_path, 'rb') as f_in: + with open(image_path, 'r+b') as f_out: + f_out.seek(8 * 1024) + shutil.copyfileobj(f_in, f_out) + + # Extend image, to avoid that NetBSD thinks the partition + # inside the image is larger than device size itself + f_out.seek(0, 2) + f_out.seek(64 * 1024 * 1024, 1) + f_out.write(bytearray([0x00])) + + self.vm.set_console() + self.vm.add_args('-nic', 'user', + '-drive', image_drive_args, + '-global', 'allwinner-rtc.base-year=2000', + '-no-reboot') + self.vm.launch() + wait_for_console_pattern(self, 'U-Boot 2020.01+dfsg-1') + interrupt_interactive_console_until_pattern(self, + 'Hit any key to stop autoboot:', + 'switch to partitions #0, OK') + + exec_command_and_wait_for_pattern(self, '', '=>') + cmd = 'setenv bootargs root=ld0a' + exec_command_and_wait_for_pattern(self, cmd, '=>') + cmd = 'setenv kernel netbsd-GENERIC.ub' + exec_command_and_wait_for_pattern(self, cmd, '=>') + cmd = 'setenv fdtfile dtb/sun8i-h3-orangepi-pc.dtb' + exec_command_and_wait_for_pattern(self, cmd, '=>') + cmd = ("setenv bootcmd 'fatload mmc 0:1 ${kernel_addr_r} ${kernel}; " + "fatload mmc 0:1 ${fdt_addr_r} ${fdtfile}; " + "fdt addr ${fdt_addr_r}; " + "bootm ${kernel_addr_r} - ${fdt_addr_r}'") + exec_command_and_wait_for_pattern(self, cmd, '=>') + + exec_command_and_wait_for_pattern(self, 'boot', + 'Booting kernel from Legacy Image') + wait_for_console_pattern(self, 'Starting kernel ...') + wait_for_console_pattern(self, 'NetBSD 9.0 (GENERIC)') + # Wait for user-space + wait_for_console_pattern(self, 'Starting root file system check') + def test_s390x_s390_ccw_virtio(self): """ :avocado: tags=arch:s390x diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker new file mode 100644 index 0000000000..bfa0d33c9c --- /dev/null +++ b/tests/docker/dockerfiles/centos8.docker @@ -0,0 +1,32 @@ +FROM centos:8.1.1911 + +RUN dnf -y update +ENV PACKAGES \ + SDL-devel \ + bison \ + bzip2 \ + bzip2-devel \ + dbus-daemon \ + flex \ + gcc \ + gcc-c++ \ + gettext \ + git \ + glib2-devel \ + libaio-devel \ + libepoxy-devel \ + lzo-devel \ + make \ + mesa-libEGL-devel \ + nettle-devel \ + perl-Test-Harness \ + pixman-devel \ + python36 \ + rdma-core-devel \ + spice-glib-devel \ + spice-server \ + tar \ + zlib-devel + +RUN dnf install -y $PACKAGES +RUN rpm -q $PACKAGES | sort > /packages.txt diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker index 3b860af106..d4849f509f 100644 --- a/tests/docker/dockerfiles/debian-amd64.docker +++ b/tests/docker/dockerfiles/debian-amd64.docker @@ -17,6 +17,7 @@ RUN apt update && \ libbz2-dev \ liblzo2-dev \ librdmacm-dev \ + libsasl2-dev \ libsnappy-dev \ libvte-dev @@ -27,9 +28,9 @@ RUN apt update && \ libegl1-mesa-dev \ libepoxy-dev \ libgbm-dev -RUN git clone https://anongit.freedesktop.org/git/virglrenderer.git /usr/src/virglrenderer && \ - cd /usr/src/virglrenderer && git checkout virglrenderer-0.7.0 -RUN cd /usr/src/virglrenderer && ./autogen.sh && ./configure --with-glx --disable-tests && make install +RUN git clone https://gitlab.freedesktop.org/virgl/virglrenderer.git /usr/src/virglrenderer && \ + cd /usr/src/virglrenderer && git checkout virglrenderer-0.8.0 +RUN cd /usr/src/virglrenderer && ./autogen.sh && ./configure --disable-tests && make install # netmap RUN apt update && \ diff --git a/tests/docker/dockerfiles/debian-win32-cross.docker b/tests/docker/dockerfiles/debian-win32-cross.docker index 9d7053e59d..d16d6431bc 100644 --- a/tests/docker/dockerfiles/debian-win32-cross.docker +++ b/tests/docker/dockerfiles/debian-win32-cross.docker @@ -9,7 +9,7 @@ MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org> ENV TARGET i686 -ENV PATH $PATH:/usr/lib/mxe/usr/$TARGET-w64-mingw32.shared/bin +ENV PATH $PATH:/usr/lib/mxe/usr/bin:/usr/lib/mxe/usr/$TARGET-w64-mingw32.shared/bin ENV PKG_CONFIG_PATH \ $PKG_CONFIG_PATH:/usr/lib/mxe/usr/$TARGET-w64-mingw32.shared/lib/pkgconfig diff --git a/tests/docker/dockerfiles/debian10.docker b/tests/docker/dockerfiles/debian10.docker index 5de79ae552..2fcdc406e8 100644 --- a/tests/docker/dockerfiles/debian10.docker +++ b/tests/docker/dockerfiles/debian10.docker @@ -17,14 +17,17 @@ RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ + bc \ bison \ build-essential \ ca-certificates \ clang \ dbus \ flex \ + gdb-multiarch \ gettext \ git \ + libncurses5-dev \ pkg-config \ psmisc \ python3 \ diff --git a/tests/docker/dockerfiles/debian9.docker b/tests/docker/dockerfiles/debian9.docker index 8cbd742bb5..92edbbf0f4 100644 --- a/tests/docker/dockerfiles/debian9.docker +++ b/tests/docker/dockerfiles/debian9.docker @@ -17,13 +17,16 @@ RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ + bc \ bison \ build-essential \ ca-certificates \ clang \ flex \ + gdb-multiarch \ gettext \ git \ + libncurses5-dev \ pkg-config \ psmisc \ python3 \ diff --git a/tests/guest-debug/run-test.py b/tests/guest-debug/run-test.py new file mode 100755 index 0000000000..8c49ee2f22 --- /dev/null +++ b/tests/guest-debug/run-test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# +# Run a gdbstub test case +# +# Copyright (c) 2019 Linaro +# +# Author: Alex Bennée <alex.bennee@linaro.org> +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import subprocess +import shutil +import shlex + +def get_args(): + parser = argparse.ArgumentParser(description="A gdbstub test runner") + parser.add_argument("--qemu", help="Qemu binary for test", + required=True) + parser.add_argument("--qargs", help="Qemu arguments for test") + parser.add_argument("--binary", help="Binary to debug", + required=True) + parser.add_argument("--test", help="GDB test script", + required=True) + parser.add_argument("--gdb", help="The gdb binary to use", default=None) + + return parser.parse_args() + +if __name__ == '__main__': + args = get_args() + + # Search for a gdb we can use + if not args.gdb: + args.gdb = shutil.which("gdb-multiarch") + if not args.gdb: + args.gdb = shutil.which("gdb") + if not args.gdb: + print("We need gdb to run the test") + exit(-1) + + # Launch QEMU with binary + if "system" in args.qemu: + cmd = "%s %s %s -s -S" % (args.qemu, args.qargs, args.binary) + else: + cmd = "%s %s -g 1234 %s" % (args.qemu, args.qargs, args.binary) + + inferior = subprocess.Popen(shlex.split(cmd)) + + # Now launch gdb with our test and collect the result + gdb_cmd = "%s %s -ex 'target remote localhost:1234' -x %s" % (args.gdb, args.binary, args.test) + + result = subprocess.call(gdb_cmd, shell=True); + + exit(result) diff --git a/tests/qapi-schema/alternate-base.err b/tests/qapi-schema/alternate-base.err index 31ebe56bbf..970a08ab26 100644 --- a/tests/qapi-schema/alternate-base.err +++ b/tests/qapi-schema/alternate-base.err @@ -1,3 +1,3 @@ alternate-base.json: In alternate 'Alt': alternate-base.json:4: alternate has unknown key 'base' -Valid keys are 'alternate', 'data', 'if'. +Valid keys are 'alternate', 'data', 'features', 'if'. diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index d992e713d9..ddd89d1233 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -53,10 +53,14 @@ # @Enum: # @one: The _one_ {and only} # +# Features: +# @enum-feat: Also _one_ {and only} +# # @two is undocumented ## { 'enum': 'Enum', 'data': [ { 'name': 'one', 'if': 'defined(IFONE)' }, 'two' ], + 'features': [ 'enum-feat' ], 'if': 'defined(IFCOND)' } ## @@ -74,10 +78,13 @@ # # Features: # @variant1-feat: a feature +# @member-feat: a member feature ## { 'struct': 'Variant1', 'features': [ 'variant1-feat' ], - 'data': { 'var1': { 'type': 'str', 'if': 'defined(IFSTR)' } } } + 'data': { 'var1': { 'type': 'str', + 'features': [ 'member-feat' ], + 'if': 'defined(IFSTR)' } } } ## # @Variant2: @@ -86,24 +93,34 @@ ## # @Object: +# Features: +# @union-feat1: a feature ## { 'union': 'Object', + 'features': [ 'union-feat1' ], 'base': 'Base', 'discriminator': 'base1', 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } ## # @SugaredUnion: +# Features: +# @union-feat2: a feature ## { 'union': 'SugaredUnion', + 'features': [ 'union-feat2' ], 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } ## # @Alternate: # @i: an integer # @b is undocumented +# +# Features: +# @alt-feat: a feature ## { 'alternate': 'Alternate', + 'features': [ 'alt-feat' ], 'data': { 'i': 'int', 'b': 'bool' } } ## @@ -160,6 +177,9 @@ ## # @EVT-BOXED: +# Features: +# @feat3: a feature ## { 'event': 'EVT-BOXED', 'boxed': true, + 'features': [ 'feat3' ], 'data': 'Object' } diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 4c9406a464..6757dd26a2 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -15,11 +15,13 @@ enum Enum if ['defined(IFONE)'] member two if ['defined(IFCOND)'] + feature enum-feat object Base member base1: Enum optional=False object Variant1 member var1: str optional=False if ['defined(IFSTR)'] + feature member-feat feature variant1-feat object Variant2 object Object @@ -28,6 +30,7 @@ object Object case one: Variant1 case two: Variant2 if ['IFTWO'] + feature union-feat1 object q_obj_Variant1-wrapper member data: Variant1 optional=False object q_obj_Variant2-wrapper @@ -42,10 +45,12 @@ object SugaredUnion case one: q_obj_Variant1-wrapper case two: q_obj_Variant2-wrapper if ['IFTWO'] + feature union-feat2 alternate Alternate tag type case i: int case b: bool + feature alt-feat object q_obj_cmd-arg member arg1: int optional=False member arg2: str optional=True @@ -60,6 +65,7 @@ command cmd-boxed Object -> None feature cmd-feat2 event EVT-BOXED Object boxed=True + feature feat3 doc freeform body= = Section @@ -112,6 +118,8 @@ doc symbol=Enum The _one_ {and only} arg=two + feature=enum-feat +Also _one_ {and only} section=None @two is undocumented doc symbol=Base @@ -128,17 +136,23 @@ Another paragraph (but no @var: line) feature=variant1-feat a feature + feature=member-feat +a member feature doc symbol=Variant2 body= doc symbol=Object body= + feature=union-feat1 +a feature doc symbol=SugaredUnion body= arg=type + feature=union-feat2 +a feature doc symbol=Alternate body= @@ -147,6 +161,8 @@ an integer @b is undocumented arg=b + feature=alt-feat +a feature doc freeform body= == Another subsection @@ -197,3 +213,5 @@ another feature doc symbol=EVT-BOXED body= + feature=feat3 +a feature diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi index d4b15dabf0..7f28fb7a0f 100644 --- a/tests/qapi-schema/doc-good.texi +++ b/tests/qapi-schema/doc-good.texi @@ -88,6 +88,12 @@ The @emph{one} @{and only@} @item @code{two} Not documented @end table + +@b{Features:} +@table @asis +@item @code{enum-feat} +Also @emph{one} @{and only@} +@end table @code{two} is undocumented @b{If:} @code{defined(IFCOND)} @@ -126,6 +132,8 @@ Not documented @table @asis @item @code{variant1-feat} a feature +@item @code{member-feat} +a member feature @end table @end deftp @@ -151,6 +159,12 @@ a feature @item The members of @code{Variant2} when @code{base1} is @t{"two"} (@b{If:} @code{IFTWO}) @end table +@b{Features:} +@table @asis +@item @code{union-feat1} +a feature +@end table + @end deftp @@ -167,6 +181,12 @@ One of @t{"one"}, @t{"two"} @item @code{data: Variant2} when @code{type} is @t{"two"} (@b{If:} @code{IFTWO}) @end table +@b{Features:} +@table @asis +@item @code{union-feat2} +a feature +@end table + @end deftp @@ -184,6 +204,12 @@ an integer Not documented @end table +@b{Features:} +@table @asis +@item @code{alt-feat} +a feature +@end table + @end deftp @@ -283,5 +309,11 @@ another feature @b{Arguments:} the members of @code{Object} +@b{Features:} +@table @asis +@item @code{feat3} +a feature +@end table + @end deftypefn diff --git a/tests/qapi-schema/features-deprecated-type.err b/tests/qapi-schema/features-deprecated-type.err new file mode 100644 index 0000000000..af4ffe20aa --- /dev/null +++ b/tests/qapi-schema/features-deprecated-type.err @@ -0,0 +1,2 @@ +features-deprecated-type.json: In struct 'S': +features-deprecated-type.json:2: feature 'deprecated' is not supported for types diff --git a/tests/qapi-schema/features-deprecated-type.json b/tests/qapi-schema/features-deprecated-type.json new file mode 100644 index 0000000000..4b5bf5b86e --- /dev/null +++ b/tests/qapi-schema/features-deprecated-type.json @@ -0,0 +1,3 @@ +# Feature 'deprecated' is not supported for types +{ 'struct': 'S', 'data': {}, + 'features': [ 'deprecated' ] } diff --git a/tests/qapi-schema/features-deprecated-type.out b/tests/qapi-schema/features-deprecated-type.out new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/qapi-schema/features-deprecated-type.out diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 9abf175fe0..6b1f05afa7 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -252,13 +252,13 @@ 'bar': { 'type': ['TestIfEnum'], 'if': 'defined(TEST_IF_EVT_BAR)' } }, 'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' } -# test 'features' for structs +# test 'features' { 'struct': 'FeatureStruct0', 'data': { 'foo': 'int' }, 'features': [] } { 'struct': 'FeatureStruct1', - 'data': { 'foo': 'int' }, + 'data': { 'foo': { 'type': 'int', 'features': [ 'deprecated' ] } }, 'features': [ 'feature1' ] } { 'struct': 'FeatureStruct2', 'data': { 'foo': 'int' }, @@ -281,7 +281,22 @@ 'data': { 'foo': 'int' }, 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] } ] } -{ 'command': 'test-features', + +{ 'enum': 'FeatureEnum1', + 'data': [ 'eins', 'zwei', 'drei' ], + 'features': [ 'feature1' ] } + +{ 'union': 'FeatureUnion1', + 'base': { 'tag': 'FeatureEnum1' }, + 'discriminator': 'tag', + 'data': { 'eins': 'FeatureStruct1' }, + 'features': [ 'feature1' ] } + +{ 'alternate': 'FeatureAlternate1', + 'data': { 'eins': 'FeatureStruct1' }, + 'features': [ 'feature1' ] } + +{ 'command': 'test-features0', 'data': { 'fs0': 'FeatureStruct0', 'fs1': 'FeatureStruct1', 'fs2': 'FeatureStruct2', @@ -289,14 +304,11 @@ 'fs4': 'FeatureStruct4', 'cfs1': 'CondFeatureStruct1', 'cfs2': 'CondFeatureStruct2', - 'cfs3': 'CondFeatureStruct3' } } - -# test 'features' for command - -{ 'command': 'test-command-features0', + 'cfs3': 'CondFeatureStruct3' }, 'features': [] } + { 'command': 'test-command-features1', - 'features': [ 'feature1' ] } + 'features': [ 'deprecated' ] } { 'command': 'test-command-features3', 'features': [ 'feature1', 'feature2' ] } @@ -308,3 +320,6 @@ { 'command': 'test-command-cond-features3', 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] } ] } + +{ 'event': 'TEST-EVENT-FEATURES1', + 'features': [ 'deprecated' ] } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 9bd3c4a490..891b4101e0 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -359,6 +359,7 @@ object FeatureStruct0 member foo: int optional=False object FeatureStruct1 member foo: int optional=False + feature deprecated feature feature1 object FeatureStruct2 member foo: int optional=False @@ -387,7 +388,25 @@ object CondFeatureStruct3 member foo: int optional=False feature feature1 if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] -object q_obj_test-features-arg +enum FeatureEnum1 + member eins + member zwei + member drei + feature feature1 +object q_obj_FeatureUnion1-base + member tag: FeatureEnum1 optional=False +object FeatureUnion1 + base q_obj_FeatureUnion1-base + tag tag + case eins: FeatureStruct1 + case zwei: q_empty + case drei: q_empty + feature feature1 +alternate FeatureAlternate1 + tag type + case eins: FeatureStruct1 + feature feature1 +object q_obj_test-features0-arg member fs0: FeatureStruct0 optional=False member fs1: FeatureStruct1 optional=False member fs2: FeatureStruct2 optional=False @@ -396,13 +415,11 @@ object q_obj_test-features-arg member cfs1: CondFeatureStruct1 optional=False member cfs2: CondFeatureStruct2 optional=False member cfs3: CondFeatureStruct3 optional=False -command test-features q_obj_test-features-arg -> None - gen=True success_response=True boxed=False oob=False preconfig=False -command test-command-features0 None -> None +command test-features0 q_obj_test-features0-arg -> None gen=True success_response=True boxed=False oob=False preconfig=False command test-command-features1 None -> None gen=True success_response=True boxed=False oob=False preconfig=False - feature feature1 + feature deprecated command test-command-features3 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 @@ -421,6 +438,9 @@ command test-command-cond-features3 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] +event TEST-EVENT-FEATURES1 None + boxed=False + feature deprecated module include/sub-module.json include sub-sub-module.json object SecondArrayRef diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index bee18ee344..f396b471eb 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -30,7 +30,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): def visit_include(self, name, info): print('include %s' % name) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): print('enum %s' % name) if prefix: print(' prefix %s' % prefix) @@ -38,6 +38,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print(' member %s' % m.name) self._print_if(m.ifcond, indent=8) self._print_if(ifcond) + self._print_features(features) def visit_array_type(self, name, info, ifcond, element_type): if not info: @@ -45,8 +46,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print('array %s %s' % (name, element_type.name)) self._print_if(ifcond) - def visit_object_type(self, name, info, ifcond, base, members, variants, - features): + def visit_object_type(self, name, info, ifcond, features, + base, members, variants): print('object %s' % name) if base: print(' base %s' % base.name) @@ -54,18 +55,20 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print(' member %s: %s optional=%s' % (m.name, m.type.name, m.optional)) self._print_if(m.ifcond, 8) + self._print_features(m.features, indent=8) self._print_variants(variants) self._print_if(ifcond) self._print_features(features) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): print('alternate %s' % name) self._print_variants(variants) self._print_if(ifcond) + self._print_features(features) - def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, - success_response, boxed, allow_oob, allow_preconfig, - features): + def visit_command(self, name, info, ifcond, features, + arg_type, ret_type, gen, success_response, boxed, + allow_oob, allow_preconfig): print('command %s %s -> %s' % (name, arg_type and arg_type.name, ret_type and ret_type.name)) @@ -74,10 +77,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): self._print_if(ifcond) self._print_features(features) - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): print('event %s %s' % (name, arg_type and arg_type.name)) print(' boxed=%s' % boxed) self._print_if(ifcond) + self._print_features(features) @staticmethod def _print_variants(variants): @@ -93,11 +97,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print('%sif %s' % (' ' * indent, ifcond)) @classmethod - def _print_features(cls, features): + def _print_features(cls, features, indent=4): if features: for f in features: - print(' feature %s' % f.name) - cls._print_if(f.ifcond, 8) + print('%sfeature %s' % (' ' * indent, f.name)) + cls._print_if(f.ifcond, indent + 4) def test_frontend(fname): diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out index d94ad22f70..fd11aae678 100644 --- a/tests/qemu-iotests/085.out +++ b/tests/qemu-iotests/085.out @@ -82,7 +82,7 @@ Formatting 'TEST_DIR/12-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_f === Invalid command - cannot create a snapshot using a file BDS === { 'execute': 'blockdev-snapshot', 'arguments': { 'node':'virtio0', 'overlay':'file_12' } } -{"error": {"class": "GenericError", "desc": "The overlay does not support backing images"}} +{"error": {"class": "GenericError", "desc": "The overlay is already in use"}} === Invalid command - snapshot node used as active layer === @@ -96,7 +96,7 @@ Formatting 'TEST_DIR/12-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_f === Invalid command - snapshot node used as backing hd === { 'execute': 'blockdev-snapshot', 'arguments': { 'node': 'virtio0', 'overlay':'snap_11' } } -{"error": {"class": "GenericError", "desc": "Node 'snap_11' is busy: node is used as backing hd of 'snap_12'"}} +{"error": {"class": "GenericError", "desc": "The overlay is already in use"}} === Invalid command - snapshot node has a backing image === diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 index f237868710..571bce9de4 100755 --- a/tests/qemu-iotests/155 +++ b/tests/qemu-iotests/155 @@ -45,10 +45,18 @@ target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) # image during runtime, only makes sense if # target_blockdev_backing is not None # (None: same as target_backing) +# target_open_with_backing: If True, the target image is added with its backing +# chain opened right away. If False, blockdev-add +# opens it without a backing file and job completion +# is supposed to open the backing chain. +# use_iothread: If True, an iothread is configured for the virtio-blk device +# that uses the image being mirrored class BaseClass(iotests.QMPTestCase): target_blockdev_backing = None target_real_backing = None + target_open_with_backing = True + use_iothread = False def setUp(self): qemu_img('create', '-f', iotests.imgfmt, back0_img, '1440K') @@ -64,7 +72,16 @@ class BaseClass(iotests.QMPTestCase): 'file': {'driver': 'file', 'filename': source_img}} self.vm.add_blockdev(self.vm.qmp_to_opts(blockdev)) - self.vm.add_device('virtio-blk,id=qdev0,drive=source') + + if self.use_iothread: + self.vm.add_object('iothread,id=iothread0') + iothread = ",iothread=iothread0" + else: + iothread = "" + + self.vm.add_device('virtio-scsi%s' % iothread) + self.vm.add_device('scsi-hd,id=qdev0,drive=source') + self.vm.launch() self.assertIntactSourceBackingChain() @@ -80,9 +97,13 @@ class BaseClass(iotests.QMPTestCase): options = { 'node-name': 'target', 'driver': iotests.imgfmt, 'file': { 'driver': 'file', + 'node-name': 'target-file', 'filename': target_img } } - if self.target_blockdev_backing: - options['backing'] = self.target_blockdev_backing + + if not self.target_open_with_backing: + options['backing'] = None + elif self.target_blockdev_backing: + options['backing'] = self.target_blockdev_backing result = self.vm.qmp('blockdev-add', **options) self.assert_qmp(result, 'return', {}) @@ -147,10 +168,14 @@ class BaseClass(iotests.QMPTestCase): # cmd: Mirroring command to execute, either drive-mirror or blockdev-mirror class MirrorBaseClass(BaseClass): + def openBacking(self): + pass + def runMirror(self, sync): if self.cmd == 'blockdev-mirror': result = self.vm.qmp(self.cmd, job_id='mirror-job', device='source', - sync=sync, target='target') + sync=sync, target='target', + auto_finalize=False) else: if self.existing: mode = 'existing' @@ -159,33 +184,31 @@ class MirrorBaseClass(BaseClass): result = self.vm.qmp(self.cmd, job_id='mirror-job', device='source', sync=sync, target=target_img, format=iotests.imgfmt, mode=mode, - node_name='target') + node_name='target', auto_finalize=False) self.assert_qmp(result, 'return', {}) - self.complete_and_wait('mirror-job') + self.vm.run_job('mirror-job', use_log=False, auto_finalize=False, + pre_finalize=self.openBacking, auto_dismiss=True) def testFull(self): self.runMirror('full') - node = self.findBlockNode('target', - '/machine/peripheral/qdev0/virtio-backend') + node = self.findBlockNode('target', 'qdev0') self.assertCorrectBackingImage(node, None) self.assertIntactSourceBackingChain() def testTop(self): self.runMirror('top') - node = self.findBlockNode('target', - '/machine/peripheral/qdev0/virtio-backend') + node = self.findBlockNode('target', 'qdev0') self.assertCorrectBackingImage(node, back2_img) self.assertIntactSourceBackingChain() def testNone(self): self.runMirror('none') - node = self.findBlockNode('target', - '/machine/peripheral/qdev0/virtio-backend') + node = self.findBlockNode('target', 'qdev0') self.assertCorrectBackingImage(node, source_img) self.assertIntactSourceBackingChain() @@ -221,6 +244,44 @@ class TestBlockdevMirrorForcedBacking(MirrorBaseClass): target_blockdev_backing = { 'driver': 'null-co' } target_real_backing = 'null-co://' +# Attach the backing chain only during completion, with blockdev-reopen +class TestBlockdevMirrorReopen(MirrorBaseClass): + cmd = 'blockdev-mirror' + existing = True + target_backing = 'null-co://' + target_open_with_backing = False + + def openBacking(self): + if not self.target_open_with_backing: + result = self.vm.qmp('blockdev-add', node_name="backing", + driver="null-co") + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('x-blockdev-reopen', node_name="target", + driver=iotests.imgfmt, file="target-file", + backing="backing") + self.assert_qmp(result, 'return', {}) + +class TestBlockdevMirrorReopenIothread(TestBlockdevMirrorReopen): + use_iothread = True + +# Attach the backing chain only during completion, with blockdev-snapshot +class TestBlockdevMirrorSnapshot(MirrorBaseClass): + cmd = 'blockdev-mirror' + existing = True + target_backing = 'null-co://' + target_open_with_backing = False + + def openBacking(self): + if not self.target_open_with_backing: + result = self.vm.qmp('blockdev-add', node_name="backing", + driver="null-co") + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('blockdev-snapshot', node="backing", + overlay="target") + self.assert_qmp(result, 'return', {}) + +class TestBlockdevMirrorSnapshotIothread(TestBlockdevMirrorSnapshot): + use_iothread = True class TestCommit(BaseClass): existing = False @@ -237,8 +298,7 @@ class TestCommit(BaseClass): self.vm.event_wait('BLOCK_JOB_COMPLETED') - node = self.findBlockNode(None, - '/machine/peripheral/qdev0/virtio-backend') + node = self.findBlockNode(None, 'qdev0') self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename', back1_img) self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename', diff --git a/tests/qemu-iotests/155.out b/tests/qemu-iotests/155.out index 4176bb9402..ed714d5263 100644 --- a/tests/qemu-iotests/155.out +++ b/tests/qemu-iotests/155.out @@ -1,5 +1,5 @@ -................... +............................... ---------------------------------------------------------------------- -Ran 19 tests +Ran 31 tests OK diff --git a/tests/qemu-iotests/178 b/tests/qemu-iotests/178 index 51a70fe669..7cf0e27154 100755 --- a/tests/qemu-iotests/178 +++ b/tests/qemu-iotests/178 @@ -50,7 +50,7 @@ _make_test_img 1G $QEMU_IMG measure # missing arguments $QEMU_IMG measure --size 2G "$TEST_IMG" # only one allowed $QEMU_IMG measure "$TEST_IMG" a # only one filename allowed -$QEMU_IMG measure --object secret,id=sec0,data=MTIzNDU2,format=base64 # missing filename +$QEMU_IMG measure --object secret,id=sec0,data=MTIzNDU2,format=base64 # size or filename needed $QEMU_IMG measure --image-opts # missing filename $QEMU_IMG measure -f qcow2 # missing filename $QEMU_IMG measure -l snap1 # missing filename diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2 index 9e7d8c44df..f59bf4b2fb 100644 --- a/tests/qemu-iotests/178.out.qcow2 +++ b/tests/qemu-iotests/178.out.qcow2 @@ -5,10 +5,10 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 qemu-img: Either --size N or one filename must be specified. qemu-img: --size N cannot be used together with a filename. qemu-img: At most one filename argument is allowed. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. +qemu-img: Either --size N or one filename must be specified. +qemu-img: --image-opts, -f, and -l require a filename argument. +qemu-img: --image-opts, -f, and -l require a filename argument. +qemu-img: --image-opts, -f, and -l require a filename argument. qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo' diff --git a/tests/qemu-iotests/178.out.raw b/tests/qemu-iotests/178.out.raw index 6478365905..404ca908d8 100644 --- a/tests/qemu-iotests/178.out.raw +++ b/tests/qemu-iotests/178.out.raw @@ -5,10 +5,10 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 qemu-img: Either --size N or one filename must be specified. qemu-img: --size N cannot be used together with a filename. qemu-img: At most one filename argument is allowed. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. -qemu-img: --object, --image-opts, -f, and -l require a filename argument. +qemu-img: Either --size N or one filename must be specified. +qemu-img: --image-opts, -f, and -l require a filename argument. +qemu-img: --image-opts, -f, and -l require a filename argument. +qemu-img: --image-opts, -f, and -l require a filename argument. qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo' diff --git a/tests/qemu-iotests/282 b/tests/qemu-iotests/282 new file mode 100755 index 0000000000..081eb12080 --- /dev/null +++ b/tests/qemu-iotests/282 @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# +# Test qemu-img file cleanup for LUKS when using a non-UTF8 secret +# +# Copyright (C) 2020, IBM Corporation. +# +# 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 <http://www.gnu.org/licenses/>. +# + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! +TEST_IMAGE_FILE='vol.img' + +_cleanup() +{ + _cleanup_test_img + rm non_utf8_secret + rm -f $TEST_IMAGE_FILE +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt luks +_supported_proto generic +_unsupported_proto vxhs + +echo "== Create non-UTF8 secret ==" +echo -n -e '\x3a\x3c\x3b\xff' > non_utf8_secret +SECRET="secret,id=sec0,file=non_utf8_secret" + +echo "== Throws an error because of invalid UTF-8 secret ==" +$QEMU_IMG create -f $IMGFMT --object $SECRET -o "key-secret=sec0" $TEST_IMAGE_FILE 4M + +echo "== Image file should not exist after the error ==" +if test -f "$TEST_IMAGE_FILE"; then + exit 1 +fi + +echo "== Create a stub image file and run qemu-img again ==" +touch $TEST_IMAGE_FILE +$QEMU_IMG create -f $IMGFMT --object $SECRET -o "key-secret=sec0" $TEST_IMAGE_FILE 4M + +echo "== Pre-existing image file should also be deleted after the error ==" +if test -f "$TEST_IMAGE_FILE"; then + exit 1 +fi + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/282.out b/tests/qemu-iotests/282.out new file mode 100644 index 0000000000..5d079dabce --- /dev/null +++ b/tests/qemu-iotests/282.out @@ -0,0 +1,11 @@ +QA output created by 282 +== Create non-UTF8 secret == +== Throws an error because of invalid UTF-8 secret == +qemu-img: vol.img: Data from secret sec0 is not valid UTF-8 +Formatting 'vol.img', fmt=luks size=4194304 key-secret=sec0 +== Image file should not exist after the error == +== Create a stub image file and run qemu-img again == +qemu-img: vol.img: Data from secret sec0 is not valid UTF-8 +Formatting 'vol.img', fmt=luks size=4194304 key-secret=sec0 +== Pre-existing image file should also be deleted after the error == + *** done diff --git a/tests/qemu-iotests/288 b/tests/qemu-iotests/288 new file mode 100755 index 0000000000..6c62065aef --- /dev/null +++ b/tests/qemu-iotests/288 @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# +# qemu-img measure tests for LUKS images +# +# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. +# + +# creator +owner=stefanha@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_IMG.converted" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.pattern + +_supported_fmt luks +_supported_proto file +_supported_os Linux + +SECRET=secret,id=sec0,data=passphrase + +echo "== measure 1G image file ==" +echo + +$QEMU_IMG measure --object "$SECRET" \ + -O "$IMGFMT" \ + -o key-secret=sec0,iter-time=10 \ + --size 1G + +echo +echo "== create 1G image file (size should be no greater than measured) ==" +echo + +_make_test_img 1G +stat -c "image file size in bytes: %s" "$TEST_IMG_FILE" + +echo +echo "== modified 1G image file (size should be no greater than measured) ==" +echo + +$QEMU_IO --object "$SECRET" --image-opts "$TEST_IMG" -c "write -P 0x51 0x10000 0x400" | _filter_qemu_io | _filter_testdir +stat -c "image file size in bytes: %s" "$TEST_IMG_FILE" + +echo +echo "== measure preallocation=falloc 1G image file ==" +echo + +$QEMU_IMG measure --object "$SECRET" \ + -O "$IMGFMT" \ + -o key-secret=sec0,iter-time=10,preallocation=falloc \ + --size 1G + +echo +echo "== measure with input image file ==" +echo + +IMGFMT=raw IMGKEYSECRET= IMGOPTS= _make_test_img 1G | _filter_imgfmt +QEMU_IO_OPTIONS= IMGOPTSSYNTAX= $QEMU_IO -f raw -c "write -P 0x51 0x10000 0x400" "$TEST_IMG_FILE" | _filter_qemu_io | _filter_testdir +$QEMU_IMG measure --object "$SECRET" \ + -O "$IMGFMT" \ + -o key-secret=sec0,iter-time=10 \ + -f raw \ + "$TEST_IMG_FILE" + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/288.out b/tests/qemu-iotests/288.out new file mode 100644 index 0000000000..4bc593dc48 --- /dev/null +++ b/tests/qemu-iotests/288.out @@ -0,0 +1,30 @@ +QA output created by 288 +== measure 1G image file == + +required size: 1075810304 +fully allocated size: 1075810304 + +== create 1G image file (size should be no greater than measured) == + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +image file size in bytes: 1075810304 + +== modified 1G image file (size should be no greater than measured) == + +wrote 1024/1024 bytes at offset 65536 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +image file size in bytes: 1075810304 + +== measure preallocation=falloc 1G image file == + +required size: 1075810304 +fully allocated size: 1075810304 + +== measure with input image file == + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +wrote 1024/1024 bytes at offset 65536 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +required size: 1075810304 +fully allocated size: 1075810304 +*** done diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 8a6366c09d..4c246c0450 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -56,18 +56,30 @@ poke_file() # peek_file_le 'test.img' 512 2 => 65534 peek_file_le() { - # Wrap in echo $() to strip spaces - echo $(od -j"$2" -N"$3" --endian=little -An -vtu"$3" "$1") + local val=0 shift=0 byte + + # coreutils' od --endian is not portable, so manually assemble bytes. + for byte in $(od -j"$2" -N"$3" -An -v -tu1 "$1"); do + val=$(( val | (byte << shift) )) + shift=$((shift + 8)) + done + printf %llu $val } # peek_file_be 'test.img' 512 2 => 65279 peek_file_be() { - # Wrap in echo $() to strip spaces - echo $(od -j"$2" -N"$3" --endian=big -An -vtu"$3" "$1") + local val=0 byte + + # coreutils' od --endian is not portable, so manually assemble bytes. + for byte in $(od -j"$2" -N"$3" -An -v -tu1 "$1"); do + val=$(( (val << 8) | byte )) + done + printf %llu $val } -# peek_file_raw 'test.img' 512 2 => '\xff\xfe' +# peek_file_raw 'test.img' 512 2 => '\xff\xfe'. Do not use if the raw data +# is likely to contain \0 or trailing \n. peek_file_raw() { dd if="$1" bs=1 skip="$2" count="$3" status=none diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 0317667695..ec2b2302e5 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -290,6 +290,8 @@ 279 rw backing quick 280 rw migration quick 281 rw quick +282 rw img quick 283 auto quick 284 rw 286 rw quick +288 quick diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 8815052eb5..23043baa26 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -624,7 +624,10 @@ class VM(qtest.QEMUQtestMachine): if use_log: log('Job failed: %s' % (j['error'])) elif status == 'ready': - self.qmp_log('job-complete', id=job) + if use_log: + self.qmp_log('job-complete', id=job) + else: + self.qmp('job-complete', id=job) elif status == 'pending' and not auto_finalize: if pre_finalize: pre_finalize() diff --git a/tests/qtest/Makefile.include b/tests/qtest/Makefile.include index 383b0ab217..10a28de8a3 100644 --- a/tests/qtest/Makefile.include +++ b/tests/qtest/Makefile.include @@ -18,7 +18,8 @@ check-qtest-pci-$(CONFIG_IVSHMEM_DEVICE) += ivshmem-test DBUS_DAEMON := $(shell which dbus-daemon 2>/dev/null) ifneq ($(GDBUS_CODEGEN),) ifneq ($(DBUS_DAEMON),) -check-qtest-pci-$(CONFIG_GIO) += dbus-vmstate-test +# Temporarily disabled due to Patchew failures: +#check-qtest-pci-$(CONFIG_GIO) += dbus-vmstate-test endif endif @@ -287,7 +288,6 @@ tests/qtest/usb-hcd-ehci-test$(EXESUF): tests/qtest/usb-hcd-ehci-test.o $(libqos tests/qtest/usb-hcd-xhci-test$(EXESUF): tests/qtest/usb-hcd-xhci-test.o $(libqos-usb-obj-y) tests/qtest/cpu-plug-test$(EXESUF): tests/qtest/cpu-plug-test.o tests/qtest/migration-test$(EXESUF): tests/qtest/migration-test.o tests/qtest/migration-helpers.o -tests/qtest/qemu-iotests/qtest/socket_scm_helper$(EXESUF): tests/qtest/qemu-iotests/qtest/socket_scm_helper.o tests/qtest/test-netfilter$(EXESUF): tests/qtest/test-netfilter.o $(qtest-obj-y) tests/qtest/test-filter-mirror$(EXESUF): tests/qtest/test-filter-mirror.o $(qtest-obj-y) tests/qtest/test-filter-redirector$(EXESUF): tests/qtest/test-filter-redirector.o $(qtest-obj-y) diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c index 1a99277d60..af28c92866 100644 --- a/tests/qtest/fuzz/qos_fuzz.c +++ b/tests/qtest/fuzz/qos_fuzz.c @@ -55,10 +55,8 @@ static void qos_set_machines_devices_available(void) QObject *response; QDict *args = qdict_new(); QList *lst; - Error *err = NULL; - qmp_marshal_query_machines(NULL, &response, &err); - assert(!err); + qmp_marshal_query_machines(NULL, &response, &error_abort); lst = qobject_to(QList, response); apply_to_qlist(lst, true); @@ -70,8 +68,7 @@ static void qos_set_machines_devices_available(void) qdict_put_bool(args, "abstract", true); qdict_put_obj(req, "arguments", (QObject *) args); - qmp_marshal_qom_list_types(args, &response, &err); - assert(!err); + qmp_marshal_qom_list_types(args, &response, &error_abort); lst = qobject_to(QList, response); apply_to_qlist(lst, false); qobject_unref(response); diff --git a/tests/qtest/hd-geo-test.c b/tests/qtest/hd-geo-test.c index a249800544..48e8e02d6e 100644 --- a/tests/qtest/hd-geo-test.c +++ b/tests/qtest/hd-geo-test.c @@ -421,7 +421,7 @@ static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors) char *raw_path = strdup(template); char *qcow2_path = strdup(template); char cmd[100 + 2 * PATH_MAX]; - uint8_t buf[512]; + uint8_t buf[512] = {}; int i, ret, fd, offset; uint64_t qcow2_size = sectors * 512; uint8_t status, parttype, head, sector, cyl; @@ -457,8 +457,8 @@ static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors) buf[offset + 0x6] = sector; buf[offset + 0x7] = cyl; - (*(uint32_t *)&buf[offset + 0x8]) = cpu_to_le32(mbr[i].start_sect); - (*(uint32_t *)&buf[offset + 0xc]) = cpu_to_le32(mbr[i].nr_sects); + stl_le_p(&buf[offset + 0x8], mbr[i].start_sect); + stl_le_p(&buf[offset + 0xc], mbr[i].nr_sects); offset += 0x10; } diff --git a/tests/qtest/libqos/ahci.h b/tests/qtest/libqos/ahci.h index f05b3e5fce..44ab1104b5 100644 --- a/tests/qtest/libqos/ahci.h +++ b/tests/qtest/libqos/ahci.h @@ -351,7 +351,7 @@ typedef struct AHCIQState { typedef struct FIS { uint8_t fis_type; uint8_t flags; - char data[0]; + char data[]; } __attribute__((__packed__)) FIS; /** diff --git a/tests/qtest/libqos/libqos-spapr.h b/tests/qtest/libqos/libqos-spapr.h index d9c4c22343..49bd72d20b 100644 --- a/tests/qtest/libqos/libqos-spapr.h +++ b/tests/qtest/libqos/libqos-spapr.h @@ -12,7 +12,6 @@ void qtest_spapr_shutdown(QOSState *qs); "cap-cfpc=broken," \ "cap-sbbc=broken," \ "cap-ibs=broken," \ - "cap-ccf-assist=off," \ - "cap-fwnmi-mce=off" + "cap-ccf-assist=off," #endif diff --git a/tests/qtest/machine-none-test.c b/tests/qtest/machine-none-test.c index 5953d31755..8bb54a6360 100644 --- a/tests/qtest/machine-none-test.c +++ b/tests/qtest/machine-none-test.c @@ -56,6 +56,7 @@ static struct arch2cpu cpus_map[] = { { "hppa", "hppa" }, { "riscv64", "rv64gcsu-v1.10.0" }, { "riscv32", "rv32gcsu-v1.9.1" }, + { "rx", "rx62n" }, }; static const char *get_cpu_model_by_arch(const char *arch) diff --git a/tests/requirements.txt b/tests/requirements.txt index f4f1736a08..f9c84b4ba1 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -2,3 +2,4 @@ # in the tests/venv Python virtual environment. For more info, # refer to: https://pip.pypa.io/en/stable/user_guide/#id1 avocado-framework==76.0 +pycdlib==1.9.0 diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 8ed477d0d5..d99b2a9ece 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -42,4 +42,36 @@ run-semiconsole: semiconsole run-plugin-semiconsole-with-%: $(call skip-test, $<, "MANUAL ONLY") +ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_SVE),) +# System Registers Tests +AARCH64_TESTS += sysregs +sysregs: CFLAGS+=-march=armv8.1-a+sve + +# SVE ioctl test +AARCH64_TESTS += sve-ioctls +sve-ioctls: CFLAGS+=-march=armv8.1-a+sve + +ifneq ($(HAVE_GDB_BIN),) +GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py + +AARCH64_TESTS += gdbstub-sysregs gdbstub-sve-ioctls + +.PHONY: gdbstub-sysregs gdbstub-sve-ioctls +run-gdbstub-sysregs: sysregs + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(HAVE_GDB_BIN) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-sve.py, \ + "basic gdbstub SVE support") + +run-gdbstub-sve-ioctls: sve-ioctls + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(HAVE_GDB_BIN) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-sve-ioctl.py, \ + "basic gdbstub SVE ZLEN support") +endif + +endif + TESTS += $(AARCH64_TESTS) diff --git a/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py new file mode 100644 index 0000000000..984fbeb277 --- /dev/null +++ b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py @@ -0,0 +1,85 @@ +from __future__ import print_function +# +# Test the SVE ZReg reports the right amount of data. It uses the +# sve-ioctl test and examines the register data each time the +# __sve_ld_done breakpoint is hit. +# +# This is launched via tests/guest-debug/run-test.py +# + +import gdb +import sys + +initial_vlen = 0 +failcount = 0 + +def report(cond, msg): + "Report success/fail of test" + if cond: + print ("PASS: %s" % (msg)) + else: + print ("FAIL: %s" % (msg)) + global failcount + failcount += 1 + +class TestBreakpoint(gdb.Breakpoint): + def __init__(self, sym_name="__sve_ld_done"): + super(TestBreakpoint, self).__init__(sym_name) + # self.sym, ok = gdb.lookup_symbol(sym_name) + + def stop(self): + val_i = gdb.parse_and_eval('i') + global initial_vlen + try: + for i in range(0, int(val_i)): + val_z = gdb.parse_and_eval("$z0.b.u[%d]" % i) + report(int(val_z) == i, "z0.b.u[%d] == %d" % (i, i)) + for i in range(i + 1, initial_vlen): + val_z = gdb.parse_and_eval("$z0.b.u[%d]" % i) + report(int(val_z) == 0, "z0.b.u[%d] == 0" % (i)) + except gdb.error: + report(False, "checking zregs (out of range)") + + +def run_test(): + "Run through the tests one by one" + + print ("Setup breakpoint") + bp = TestBreakpoint() + + global initial_vlen + vg = gdb.parse_and_eval("$vg") + initial_vlen = int(vg) * 8 + + gdb.execute("c") + +# +# This runs as the script it sourced (via -x, via run-test.py) +# +try: + inferior = gdb.selected_inferior() + if inferior.was_attached == False: + print("SKIPPING (failed to attach)", file=sys.stderr) + exit(0) + arch = inferior.architecture() + report(arch.name() == "aarch64", "connected to aarch64") +except (gdb.error, AttributeError): + print("SKIPPING (not connected)", file=sys.stderr) + exit(0) + +try: + # These are not very useful in scripts + gdb.execute("set pagination off") + gdb.execute("set confirm off") + + # Run the actual tests + run_test() +except: + print ("GDB Exception: %s" % (sys.exc_info()[0])) + failcount += 1 + import code + code.InteractiveConsole(locals=globals()).interact() + raise + +print("All tests complete: %d failures" % failcount) +exit(failcount) diff --git a/tests/tcg/aarch64/gdbstub/test-sve.py b/tests/tcg/aarch64/gdbstub/test-sve.py new file mode 100644 index 0000000000..dbe7f2aa93 --- /dev/null +++ b/tests/tcg/aarch64/gdbstub/test-sve.py @@ -0,0 +1,84 @@ +from __future__ import print_function +# +# Test the SVE registers are visable and changeable via gdbstub +# +# This is launched via tests/guest-debug/run-test.py +# + +import gdb +import sys + +MAGIC = 0xDEADBEEF + +failcount = 0 + +def report(cond, msg): + "Report success/fail of test" + if cond: + print ("PASS: %s" % (msg)) + else: + print ("FAIL: %s" % (msg)) + global failcount + failcount += 1 + +def run_test(): + "Run through the tests one by one" + + gdb.execute("info registers") + report(True, "info registers") + + gdb.execute("info registers vector") + report(True, "info registers vector") + + # Now all the zregs + frame = gdb.selected_frame() + for i in range(0, 32): + rname = "z%d" % (i) + zreg = frame.read_register(rname) + report(True, "Reading %s" % rname) + for j in range(0, 4): + cmd = "set $%s.q.u[%d] = 0x%x" % (rname, j, MAGIC) + gdb.execute(cmd) + report(True, "%s" % cmd) + for j in range(0, 4): + reg = "$%s.q.u[%d]" % (rname, j) + v = gdb.parse_and_eval(reg) + report(str(v.type) == "uint128_t", "size of %s" % (reg)) + for j in range(0, 8): + cmd = "set $%s.d.u[%d] = 0x%x" % (rname, j, MAGIC) + gdb.execute(cmd) + report(True, "%s" % cmd) + for j in range(0, 8): + reg = "$%s.d.u[%d]" % (rname, j) + v = gdb.parse_and_eval(reg) + report(str(v.type) == "uint64_t", "size of %s" % (reg)) + report(int(v) == MAGIC, "%s is 0x%x" % (reg, MAGIC)) + +# +# This runs as the script it sourced (via -x, via run-test.py) +# +try: + inferior = gdb.selected_inferior() + if inferior.was_attached == False: + print("SKIPPING (failed to attach)", file=sys.stderr) + exit(0) + arch = inferior.architecture() + report(arch.name() == "aarch64", "connected to aarch64") +except (gdb.error, AttributeError): + print("SKIPPING (not connected)", file=sys.stderr) + exit(0) + +try: + # These are not very useful in scripts + gdb.execute("set pagination off") + gdb.execute("set confirm off") + + # Run the actual tests + run_test() +except: + print ("GDB Exception: %s" % (sys.exc_info()[0])) + failcount += 1 + +print("All tests complete: %d failures" % failcount) + +exit(failcount) diff --git a/tests/tcg/aarch64/sve-ioctls.c b/tests/tcg/aarch64/sve-ioctls.c new file mode 100644 index 0000000000..9544dffa0e --- /dev/null +++ b/tests/tcg/aarch64/sve-ioctls.c @@ -0,0 +1,70 @@ +/* + * SVE ioctls tests + * + * Test the SVE width setting ioctls work and provide a base for + * testing the gdbstub. + * + * Copyright (c) 2019 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <sys/prctl.h> +#include <asm/hwcap.h> +#include <stdio.h> +#include <sys/auxv.h> +#include <stdint.h> +#include <stdlib.h> + +#ifndef HWCAP_CPUID +#define HWCAP_CPUID (1 << 11) +#endif + +#define SVE_MAX_QUADS (2048 / 128) +#define BYTES_PER_QUAD (128 / 8) + +#define get_cpu_reg(id) ({ \ + unsigned long __val; \ + asm("mrs %0, "#id : "=r" (__val)); \ + __val; \ + }) + +static int do_sve_ioctl_test(void) +{ + int i, res, init_vq; + + res = prctl(PR_SVE_GET_VL, 0, 0, 0, 0); + if (res < 0) { + printf("FAILED to PR_SVE_GET_VL (%d)", res); + return -1; + } + init_vq = res & PR_SVE_VL_LEN_MASK; + + for (i = init_vq; i > 15; i /= 2) { + printf("Checking PR_SVE_SET_VL=%d\n", i); + res = prctl(PR_SVE_SET_VL, i, 0, 0, 0, 0); + if (res < 0) { + printf("FAILED to PR_SVE_SET_VL (%d)", res); + return -1; + } + asm("index z0.b, #0, #1\n" + ".global __sve_ld_done\n" + "__sve_ld_done:\n" + "mov z0.b, #0\n" + : /* no outputs kept */ + : /* no inputs */ + : "memory", "z0"); + } + printf("PASS\n"); + return 0; +} + +int main(int argc, char **argv) +{ + /* we also need to probe for the ioctl support */ + if (getauxval(AT_HWCAP) & HWCAP_SVE) { + return do_sve_ioctl_test(); + } else { + printf("SKIP: no HWCAP_SVE on this system\n"); + return 0; + } +} diff --git a/tests/tcg/aarch64/sysregs.c b/tests/tcg/aarch64/sysregs.c new file mode 100644 index 0000000000..40cf8d2877 --- /dev/null +++ b/tests/tcg/aarch64/sysregs.c @@ -0,0 +1,172 @@ +/* + * Check emulated system register access for linux-user mode. + * + * See: https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt + * + * Copyright (c) 2019 Linaro + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <asm/hwcap.h> +#include <stdio.h> +#include <sys/auxv.h> +#include <signal.h> +#include <string.h> +#include <stdbool.h> + +#ifndef HWCAP_CPUID +#define HWCAP_CPUID (1 << 11) +#endif + +int failed_bit_count; + +/* Read and print system register `id' value */ +#define get_cpu_reg(id) ({ \ + unsigned long __val = 0xdeadbeef; \ + asm("mrs %0, "#id : "=r" (__val)); \ + printf("%-20s: 0x%016lx\n", #id, __val); \ + __val; \ + }) + +/* As above but also check no bits outside of `mask' are set*/ +#define get_cpu_reg_check_mask(id, mask) ({ \ + unsigned long __cval = get_cpu_reg(id); \ + unsigned long __extra = __cval & ~mask; \ + if (__extra) { \ + printf("%-20s: 0x%016lx\n", " !!extra bits!!", __extra); \ + failed_bit_count++; \ + } \ +}) + +/* As above but check RAZ */ +#define get_cpu_reg_check_zero(id) ({ \ + unsigned long __val = 0xdeadbeef; \ + asm("mrs %0, "#id : "=r" (__val)); \ + if (__val) { \ + printf("%-20s: 0x%016lx (not RAZ!)\n", #id, __val); \ + failed_bit_count++; \ + } \ +}) + +/* Chunk up mask into 63:48, 47:32, 31:16, 15:0 to ease counting */ +#define _m(a, b, c, d) (0x ## a ## b ## c ## d ##ULL) + +bool should_fail; +int should_fail_count; +int should_not_fail_count; +uintptr_t failed_pc[10]; + +void sigill_handler(int signo, siginfo_t *si, void *data) +{ + ucontext_t *uc = (ucontext_t *)data; + + if (should_fail) { + should_fail_count++; + } else { + uintptr_t pc = (uintptr_t) uc->uc_mcontext.pc; + failed_pc[should_not_fail_count++] = pc; + } + uc->uc_mcontext.pc += 4; +} + +int main(void) +{ + struct sigaction sa; + + /* Hook in a SIGILL handler */ + memset(&sa, 0, sizeof(struct sigaction)); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = &sigill_handler; + sigemptyset(&sa.sa_mask); + + if (sigaction(SIGILL, &sa, 0) != 0) { + perror("sigaction"); + return 1; + } + + /* Counter values have been exposed since Linux 4.12 */ + printf("Checking Counter registers\n"); + + get_cpu_reg(ctr_el0); + get_cpu_reg(cntvct_el0); + get_cpu_reg(cntfrq_el0); + + /* HWCAP_CPUID indicates we can read feature registers, since Linux 4.11 */ + if (!(getauxval(AT_HWCAP) & HWCAP_CPUID)) { + printf("CPUID registers unavailable\n"); + return 1; + } else { + printf("Checking CPUID registers\n"); + } + + /* + * Some registers only expose some bits to user-space. Anything + * that is IMPDEF is exported as 0 to user-space. The _mask checks + * assert no extra bits are set. + * + * This check is *not* comprehensive as some fields are set to + * minimum valid fields - for the purposes of this check allowed + * to have non-zero values. + */ + get_cpu_reg_check_mask(id_aa64isar0_el1, _m(00ff,ffff,f0ff,fff0)); + get_cpu_reg_check_mask(id_aa64isar1_el1, _m(0000,00f0,ffff,ffff)); + /* TGran4 & TGran64 as pegged to -1 */ + get_cpu_reg_check_mask(id_aa64mmfr0_el1, _m(0000,0000,ff00,0000)); + get_cpu_reg_check_zero(id_aa64mmfr1_el1); + /* EL1/EL0 reported as AA64 only */ + get_cpu_reg_check_mask(id_aa64pfr0_el1, _m(000f,000f,00ff,0011)); + get_cpu_reg_check_mask(id_aa64pfr1_el1, _m(0000,0000,0000,00f0)); + /* all hidden, DebugVer fixed to 0x6 (ARMv8 debug architecture) */ + get_cpu_reg_check_mask(id_aa64dfr0_el1, _m(0000,0000,0000,0006)); + get_cpu_reg_check_zero(id_aa64dfr1_el1); + get_cpu_reg_check_zero(id_aa64zfr0_el1); + + get_cpu_reg_check_zero(id_aa64afr0_el1); + get_cpu_reg_check_zero(id_aa64afr1_el1); + + get_cpu_reg_check_mask(midr_el1, _m(0000,0000,ffff,ffff)); + /* mpidr sets bit 31, everything else hidden */ + get_cpu_reg_check_mask(mpidr_el1, _m(0000,0000,8000,0000)); + /* REVIDR is all IMPDEF so should be all zeros to user-space */ + get_cpu_reg_check_zero(revidr_el1); + + /* + * There are a block of more registers that are RAZ in the rest of + * the Op0=3, Op1=0, CRn=0, CRm=0,4,5,6,7 space. However for + * brevity we don't check stuff that is currently un-allocated + * here. Feel free to add them ;-) + */ + + printf("Remaining registers should fail\n"); + should_fail = true; + + /* Unexposed register access causes SIGILL */ + get_cpu_reg(id_mmfr0_el1); + get_cpu_reg(id_mmfr1_el1); + get_cpu_reg(id_mmfr2_el1); + get_cpu_reg(id_mmfr3_el1); + + get_cpu_reg(mvfr0_el1); + get_cpu_reg(mvfr1_el1); + + if (should_not_fail_count > 0) { + int i; + for (i = 0; i < should_not_fail_count; i++) { + uintptr_t pc = failed_pc[i]; + uint32_t insn = *(uint32_t *) pc; + printf("insn %#x @ %#lx unexpected FAIL\n", insn, pc); + } + return 1; + } + + if (failed_bit_count > 0) { + printf("Extra information leaked to user-space!\n"); + return 1; + } + + return should_fail_count == 6 ? 0 : 1; +} diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c index e1f867085f..b6726cf76b 100644 --- a/tests/test-hbitmap.c +++ b/tests/test-hbitmap.c @@ -22,7 +22,6 @@ typedef struct TestHBitmapData { HBitmap *hb; - HBitmap *meta; unsigned long *bits; size_t size; size_t old_size; @@ -94,14 +93,6 @@ static void hbitmap_test_init(TestHBitmapData *data, } } -static void hbitmap_test_init_meta(TestHBitmapData *data, - uint64_t size, int granularity, - int meta_chunk) -{ - hbitmap_test_init(data, size, granularity); - data->meta = hbitmap_create_meta(data->hb, meta_chunk); -} - static inline size_t hbitmap_test_array_size(size_t bits) { size_t n = DIV_ROUND_UP(bits, BITS_PER_LONG); @@ -144,9 +135,6 @@ static void hbitmap_test_teardown(TestHBitmapData *data, const void *unused) { if (data->hb) { - if (data->meta) { - hbitmap_free_meta(data->hb); - } hbitmap_free(data->hb); data->hb = NULL; } @@ -648,96 +636,6 @@ static void test_hbitmap_truncate_shrink_large(TestHBitmapData *data, hbitmap_test_truncate(data, size, -diff, 0); } -static void hbitmap_check_meta(TestHBitmapData *data, - int64_t start, int count) -{ - int64_t i; - - for (i = 0; i < data->size; i++) { - if (i >= start && i < start + count) { - g_assert(hbitmap_get(data->meta, i)); - } else { - g_assert(!hbitmap_get(data->meta, i)); - } - } -} - -static void hbitmap_test_meta(TestHBitmapData *data, - int64_t start, int count, - int64_t check_start, int check_count) -{ - hbitmap_reset_all(data->hb); - hbitmap_reset_all(data->meta); - - /* Test "unset" -> "unset" will not update meta. */ - hbitmap_reset(data->hb, start, count); - hbitmap_check_meta(data, 0, 0); - - /* Test "unset" -> "set" will update meta */ - hbitmap_set(data->hb, start, count); - hbitmap_check_meta(data, check_start, check_count); - - /* Test "set" -> "set" will not update meta */ - hbitmap_reset_all(data->meta); - hbitmap_set(data->hb, start, count); - hbitmap_check_meta(data, 0, 0); - - /* Test "set" -> "unset" will update meta */ - hbitmap_reset_all(data->meta); - hbitmap_reset(data->hb, start, count); - hbitmap_check_meta(data, check_start, check_count); -} - -static void hbitmap_test_meta_do(TestHBitmapData *data, int chunk_size) -{ - uint64_t size = chunk_size * 100; - hbitmap_test_init_meta(data, size, 0, chunk_size); - - hbitmap_test_meta(data, 0, 1, 0, chunk_size); - hbitmap_test_meta(data, 0, chunk_size, 0, chunk_size); - hbitmap_test_meta(data, chunk_size - 1, 1, 0, chunk_size); - hbitmap_test_meta(data, chunk_size - 1, 2, 0, chunk_size * 2); - hbitmap_test_meta(data, chunk_size - 1, chunk_size + 1, 0, chunk_size * 2); - hbitmap_test_meta(data, chunk_size - 1, chunk_size + 2, 0, chunk_size * 3); - hbitmap_test_meta(data, 7 * chunk_size - 1, chunk_size + 2, - 6 * chunk_size, chunk_size * 3); - hbitmap_test_meta(data, size - 1, 1, size - chunk_size, chunk_size); - hbitmap_test_meta(data, 0, size, 0, size); -} - -static void test_hbitmap_meta_byte(TestHBitmapData *data, const void *unused) -{ - hbitmap_test_meta_do(data, BITS_PER_BYTE); -} - -static void test_hbitmap_meta_word(TestHBitmapData *data, const void *unused) -{ - hbitmap_test_meta_do(data, BITS_PER_LONG); -} - -static void test_hbitmap_meta_sector(TestHBitmapData *data, const void *unused) -{ - hbitmap_test_meta_do(data, BDRV_SECTOR_SIZE * BITS_PER_BYTE); -} - -/** - * Create an HBitmap and test set/unset. - */ -static void test_hbitmap_meta_one(TestHBitmapData *data, const void *unused) -{ - int i; - int64_t offsets[] = { - 0, 1, L1 - 1, L1, L1 + 1, L2 - 1, L2, L2 + 1, L3 - 1, L3, L3 + 1 - }; - - hbitmap_test_init_meta(data, L3 * 2, 0, 1); - for (i = 0; i < ARRAY_SIZE(offsets); i++) { - hbitmap_test_meta(data, offsets[i], 1, offsets[i], 1); - hbitmap_test_meta(data, offsets[i], L1, offsets[i], L1); - hbitmap_test_meta(data, offsets[i], L2, offsets[i], L2); - } -} - static void test_hbitmap_serialize_align(TestHBitmapData *data, const void *unused) { @@ -750,13 +648,6 @@ static void test_hbitmap_serialize_align(TestHBitmapData *data, g_assert_cmpint(r, ==, 64 << 3); } -static void test_hbitmap_meta_zero(TestHBitmapData *data, const void *unused) -{ - hbitmap_test_init_meta(data, 0, 0, 1); - - hbitmap_check_meta(data, 0, 0); -} - static void hbitmap_test_serialize_range(TestHBitmapData *data, uint8_t *buf, size_t buf_size, uint64_t pos, uint64_t count) @@ -925,106 +816,123 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data, hbitmap_iter_next(&hbi); } -static void test_hbitmap_next_zero_check_range(TestHBitmapData *data, - uint64_t start, - uint64_t count) +static void test_hbitmap_next_x_check_range(TestHBitmapData *data, + int64_t start, + int64_t count) { - int64_t ret1 = hbitmap_next_zero(data->hb, start, count); - int64_t ret2 = start; + int64_t next_zero = hbitmap_next_zero(data->hb, start, count); + int64_t next_dirty = hbitmap_next_dirty(data->hb, start, count); + int64_t next; int64_t end = start >= data->size || data->size - start < count ? data->size : start + count; + bool first_bit = hbitmap_get(data->hb, start); - for ( ; ret2 < end && hbitmap_get(data->hb, ret2); ret2++) { + for (next = start; + next < end && hbitmap_get(data->hb, next) == first_bit; + next++) + { ; } - if (ret2 == end) { - ret2 = -1; + + if (next == end) { + next = -1; } - g_assert_cmpint(ret1, ==, ret2); + g_assert_cmpint(next_dirty, ==, first_bit ? start : next); + g_assert_cmpint(next_zero, ==, first_bit ? next : start); } -static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start) +static void test_hbitmap_next_x_check(TestHBitmapData *data, int64_t start) { - test_hbitmap_next_zero_check_range(data, start, UINT64_MAX); + test_hbitmap_next_x_check_range(data, start, INT64_MAX); } -static void test_hbitmap_next_zero_do(TestHBitmapData *data, int granularity) +static void test_hbitmap_next_x_do(TestHBitmapData *data, int granularity) { hbitmap_test_init(data, L3, granularity); - test_hbitmap_next_zero_check(data, 0); - test_hbitmap_next_zero_check(data, L3 - 1); - test_hbitmap_next_zero_check_range(data, 0, 1); - test_hbitmap_next_zero_check_range(data, L3 - 1, 1); + test_hbitmap_next_x_check(data, 0); + test_hbitmap_next_x_check(data, L3 - 1); + test_hbitmap_next_x_check_range(data, 0, 1); + test_hbitmap_next_x_check_range(data, L3 - 1, 1); hbitmap_set(data->hb, L2, 1); - test_hbitmap_next_zero_check(data, 0); - test_hbitmap_next_zero_check(data, L2 - 1); - test_hbitmap_next_zero_check(data, L2); - test_hbitmap_next_zero_check(data, L2 + 1); - test_hbitmap_next_zero_check_range(data, 0, 1); - test_hbitmap_next_zero_check_range(data, 0, L2); - test_hbitmap_next_zero_check_range(data, L2 - 1, 1); - test_hbitmap_next_zero_check_range(data, L2 - 1, 2); - test_hbitmap_next_zero_check_range(data, L2, 1); - test_hbitmap_next_zero_check_range(data, L2 + 1, 1); + test_hbitmap_next_x_check(data, 0); + test_hbitmap_next_x_check(data, L2 - 1); + test_hbitmap_next_x_check(data, L2); + test_hbitmap_next_x_check(data, L2 + 1); + test_hbitmap_next_x_check_range(data, 0, 1); + test_hbitmap_next_x_check_range(data, 0, L2); + test_hbitmap_next_x_check_range(data, L2 - 1, 1); + test_hbitmap_next_x_check_range(data, L2 - 1, 2); + test_hbitmap_next_x_check_range(data, L2, 1); + test_hbitmap_next_x_check_range(data, L2 + 1, 1); hbitmap_set(data->hb, L2 + 5, L1); - test_hbitmap_next_zero_check(data, 0); - test_hbitmap_next_zero_check(data, L2 + 1); - test_hbitmap_next_zero_check(data, L2 + 2); - test_hbitmap_next_zero_check(data, L2 + 5); - test_hbitmap_next_zero_check(data, L2 + L1 - 1); - test_hbitmap_next_zero_check(data, L2 + L1); - test_hbitmap_next_zero_check_range(data, L2, 6); - test_hbitmap_next_zero_check_range(data, L2 + 1, 3); - test_hbitmap_next_zero_check_range(data, L2 + 4, L1); - test_hbitmap_next_zero_check_range(data, L2 + 5, L1); + test_hbitmap_next_x_check(data, 0); + test_hbitmap_next_x_check(data, L2 - L1); + test_hbitmap_next_x_check(data, L2 + 1); + test_hbitmap_next_x_check(data, L2 + 2); + test_hbitmap_next_x_check(data, L2 + 5); + test_hbitmap_next_x_check(data, L2 + L1 - 1); + test_hbitmap_next_x_check(data, L2 + L1); + test_hbitmap_next_x_check(data, L2 + L1 + 1); + test_hbitmap_next_x_check_range(data, L2 - 2, L1); + test_hbitmap_next_x_check_range(data, L2, 4); + test_hbitmap_next_x_check_range(data, L2, 6); + test_hbitmap_next_x_check_range(data, L2 + 1, 3); + test_hbitmap_next_x_check_range(data, L2 + 4, L1); + test_hbitmap_next_x_check_range(data, L2 + 5, L1); + test_hbitmap_next_x_check_range(data, L2 + 5 + L1 - 1, 1); + test_hbitmap_next_x_check_range(data, L2 + 5 + L1, 1); + test_hbitmap_next_x_check_range(data, L2 + 5 + L1 + 1, 1); hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2); - test_hbitmap_next_zero_check(data, L2 * 2 - L1); - test_hbitmap_next_zero_check(data, L2 * 2 - 2); - test_hbitmap_next_zero_check(data, L2 * 2 - 1); - test_hbitmap_next_zero_check(data, L2 * 2); - test_hbitmap_next_zero_check(data, L3 - 1); - test_hbitmap_next_zero_check_range(data, L2 * 2 - L1, L1 + 1); - test_hbitmap_next_zero_check_range(data, L2 * 2, L2); + test_hbitmap_next_x_check(data, L2 * 2 - L1); + test_hbitmap_next_x_check(data, L2 * 2 - 2); + test_hbitmap_next_x_check(data, L2 * 2 - 1); + test_hbitmap_next_x_check(data, L2 * 2); + test_hbitmap_next_x_check(data, L2 * 2 + 1); + test_hbitmap_next_x_check(data, L2 * 2 + L1); + test_hbitmap_next_x_check(data, L3 - 1); + test_hbitmap_next_x_check_range(data, L2 * 2 - L1, L1 + 1); + test_hbitmap_next_x_check_range(data, L2 * 2, L2); hbitmap_set(data->hb, 0, L3); - test_hbitmap_next_zero_check(data, 0); + test_hbitmap_next_x_check(data, 0); } -static void test_hbitmap_next_zero_0(TestHBitmapData *data, const void *unused) +static void test_hbitmap_next_x_0(TestHBitmapData *data, const void *unused) { - test_hbitmap_next_zero_do(data, 0); + test_hbitmap_next_x_do(data, 0); } -static void test_hbitmap_next_zero_4(TestHBitmapData *data, const void *unused) +static void test_hbitmap_next_x_4(TestHBitmapData *data, const void *unused) { - test_hbitmap_next_zero_do(data, 4); + test_hbitmap_next_x_do(data, 4); } -static void test_hbitmap_next_zero_after_truncate(TestHBitmapData *data, - const void *unused) +static void test_hbitmap_next_x_after_truncate(TestHBitmapData *data, + const void *unused) { hbitmap_test_init(data, L1, 0); hbitmap_test_truncate_impl(data, L1 * 2); hbitmap_set(data->hb, 0, L1); - test_hbitmap_next_zero_check(data, 0); + test_hbitmap_next_x_check(data, 0); } -static void test_hbitmap_next_dirty_area_check(TestHBitmapData *data, - uint64_t offset, - uint64_t count) +static void test_hbitmap_next_dirty_area_check_limited(TestHBitmapData *data, + int64_t offset, + int64_t count, + int64_t max_dirty) { - uint64_t off1, off2; - uint64_t len1 = 0, len2; + int64_t off1, off2; + int64_t len1 = 0, len2; bool ret1, ret2; int64_t end; - off1 = offset; - len1 = count; - ret1 = hbitmap_next_dirty_area(data->hb, &off1, &len1); + ret1 = hbitmap_next_dirty_area(data->hb, + offset, count == INT64_MAX ? INT64_MAX : offset + count, max_dirty, + &off1, &len1); end = offset > data->size || data->size - offset < count ? data->size : offset + count; @@ -1033,45 +941,52 @@ static void test_hbitmap_next_dirty_area_check(TestHBitmapData *data, ; } - for (len2 = 1; off2 + len2 < end && hbitmap_get(data->hb, off2 + len2); - len2++) { + for (len2 = 1; (off2 + len2 < end && len2 < max_dirty && + hbitmap_get(data->hb, off2 + len2)); len2++) + { ; } ret2 = off2 < end; - if (!ret2) { - /* leave unchanged */ - off2 = offset; - len2 = count; + g_assert_cmpint(ret1, ==, ret2); + + if (ret2) { + g_assert_cmpint(off1, ==, off2); + g_assert_cmpint(len1, ==, len2); } +} - g_assert_cmpint(ret1, ==, ret2); - g_assert_cmpint(off1, ==, off2); - g_assert_cmpint(len1, ==, len2); +static void test_hbitmap_next_dirty_area_check(TestHBitmapData *data, + int64_t offset, int64_t count) +{ + test_hbitmap_next_dirty_area_check_limited(data, offset, count, INT64_MAX); } static void test_hbitmap_next_dirty_area_do(TestHBitmapData *data, int granularity) { hbitmap_test_init(data, L3, granularity); - test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); test_hbitmap_next_dirty_area_check(data, 0, 1); test_hbitmap_next_dirty_area_check(data, L3 - 1, 1); + test_hbitmap_next_dirty_area_check_limited(data, 0, INT64_MAX, 1); hbitmap_set(data->hb, L2, 1); test_hbitmap_next_dirty_area_check(data, 0, 1); test_hbitmap_next_dirty_area_check(data, 0, L2); - test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2 - 1, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 - 1, INT64_MAX); test_hbitmap_next_dirty_area_check(data, L2 - 1, 1); test_hbitmap_next_dirty_area_check(data, L2 - 1, 2); test_hbitmap_next_dirty_area_check(data, L2 - 1, 3); - test_hbitmap_next_dirty_area_check(data, L2, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2, INT64_MAX); test_hbitmap_next_dirty_area_check(data, L2, 1); test_hbitmap_next_dirty_area_check(data, L2 + 1, 1); + test_hbitmap_next_dirty_area_check_limited(data, 0, INT64_MAX, 1); + test_hbitmap_next_dirty_area_check_limited(data, L2 - 1, 2, 1); hbitmap_set(data->hb, L2 + 5, L1); - test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); test_hbitmap_next_dirty_area_check(data, L2 - 2, 8); test_hbitmap_next_dirty_area_check(data, L2 + 1, 5); test_hbitmap_next_dirty_area_check(data, L2 + 1, 3); @@ -1081,18 +996,23 @@ static void test_hbitmap_next_dirty_area_do(TestHBitmapData *data, test_hbitmap_next_dirty_area_check(data, L2 + L1, L1); test_hbitmap_next_dirty_area_check(data, L2, 0); test_hbitmap_next_dirty_area_check(data, L2 + 1, 0); + test_hbitmap_next_dirty_area_check_limited(data, L2 + 3, INT64_MAX, 3); + test_hbitmap_next_dirty_area_check_limited(data, L2 + 3, 7, 10); hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2); - test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2, UINT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2 + 1, UINT64_MAX); - test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1 - 1, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 + 1, INT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1 - 1, INT64_MAX); test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1, 5); test_hbitmap_next_dirty_area_check(data, L2 * 2 - L1, L1 + 1); test_hbitmap_next_dirty_area_check(data, L2 * 2, L2); + test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, INT64_MAX, 5); + test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, 10, 5); + test_hbitmap_next_dirty_area_check_limited(data, L2 * 2 + 1, 2, 5); hbitmap_set(data->hb, 0, L3); - test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); } static void test_hbitmap_next_dirty_area_0(TestHBitmapData *data, @@ -1119,7 +1039,7 @@ static void test_hbitmap_next_dirty_area_after_truncate(TestHBitmapData *data, hbitmap_test_init(data, L1, 0); hbitmap_test_truncate_impl(data, L1 * 2); hbitmap_set(data->hb, L1 + 1, 1); - test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, INT64_MAX); } int main(int argc, char **argv) @@ -1165,12 +1085,6 @@ int main(int argc, char **argv) hbitmap_test_add("/hbitmap/truncate/shrink/large", test_hbitmap_truncate_shrink_large); - hbitmap_test_add("/hbitmap/meta/zero", test_hbitmap_meta_zero); - hbitmap_test_add("/hbitmap/meta/one", test_hbitmap_meta_one); - hbitmap_test_add("/hbitmap/meta/byte", test_hbitmap_meta_byte); - hbitmap_test_add("/hbitmap/meta/word", test_hbitmap_meta_word); - hbitmap_test_add("/hbitmap/meta/sector", test_hbitmap_meta_sector); - hbitmap_test_add("/hbitmap/serialize/align", test_hbitmap_serialize_align); hbitmap_test_add("/hbitmap/serialize/basic", @@ -1183,12 +1097,12 @@ int main(int argc, char **argv) hbitmap_test_add("/hbitmap/iter/iter_and_reset", test_hbitmap_iter_and_reset); - hbitmap_test_add("/hbitmap/next_zero/next_zero_0", - test_hbitmap_next_zero_0); - hbitmap_test_add("/hbitmap/next_zero/next_zero_4", - test_hbitmap_next_zero_4); - hbitmap_test_add("/hbitmap/next_zero/next_zero_after_truncate", - test_hbitmap_next_zero_after_truncate); + hbitmap_test_add("/hbitmap/next_zero/next_x_0", + test_hbitmap_next_x_0); + hbitmap_test_add("/hbitmap/next_zero/next_x_4", + test_hbitmap_next_x_4); + hbitmap_test_add("/hbitmap/next_zero/next_x_after_truncate", + test_hbitmap_next_x_after_truncate); hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_0", test_hbitmap_next_dirty_area_0); diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 79507d9e54..d12ff47e26 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -1,5 +1,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qapi/error.h" @@ -44,7 +45,7 @@ void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp) { } -void qmp_test_features(FeatureStruct0 *fs0, FeatureStruct1 *fs1, +void qmp_test_features0(FeatureStruct0 *fs0, FeatureStruct1 *fs1, FeatureStruct2 *fs2, FeatureStruct3 *fs3, FeatureStruct4 *fs4, CondFeatureStruct1 *cfs1, CondFeatureStruct2 *cfs2, CondFeatureStruct3 *cfs3, @@ -52,10 +53,6 @@ void qmp_test_features(FeatureStruct0 *fs0, FeatureStruct1 *fs1, { } -void qmp_test_command_features0(Error **errp) -{ -} - void qmp_test_command_features1(Error **errp) { } @@ -145,66 +142,87 @@ __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a, } -/* test commands with no input and no return value */ -static void test_dispatch_cmd(void) +static QObject *do_qmp_dispatch(bool allow_oob, const char *template, ...) { - QDict *req = qdict_new(); - QDict *resp; + va_list ap; + QDict *req, *resp; + QObject *ret; - qdict_put_str(req, "execute", "user_def_cmd"); + va_start(ap, template); + req = qdict_from_vjsonf_nofail(template, ap); + va_end(ap); - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); - assert(resp != NULL); - assert(!qdict_haskey(resp, "error")); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), allow_oob); + g_assert(resp); + ret = qdict_get(resp, "return"); + g_assert(ret); + g_assert(qdict_size(resp) == 1); + qobject_ref(ret); qobject_unref(resp); qobject_unref(req); + return ret; } -static void test_dispatch_cmd_oob(void) +static void do_qmp_dispatch_error(bool allow_oob, ErrorClass cls, + const char *template, ...) { - QDict *req = qdict_new(); - QDict *resp; - - qdict_put_str(req, "exec-oob", "test-flags-command"); - - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), true); - assert(resp != NULL); - assert(!qdict_haskey(resp, "error")); + va_list ap; + QDict *req, *resp; + QDict *error; + + va_start(ap, template); + req = qdict_from_vjsonf_nofail(template, ap); + va_end(ap); + + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), allow_oob); + g_assert(resp); + error = qdict_get_qdict(resp, "error"); + g_assert(error); + g_assert_cmpstr(qdict_get_try_str(error, "class"), + ==, QapiErrorClass_str(cls)); + g_assert(qdict_get_try_str(error, "desc")); + g_assert(qdict_size(error) == 2); + g_assert(qdict_size(resp) == 1); qobject_unref(resp); qobject_unref(req); } -/* test commands that return an error due to invalid parameters */ -static void test_dispatch_cmd_failure(void) +/* test commands with no input and no return value */ +static void test_dispatch_cmd(void) { - QDict *req = qdict_new(); - QDict *args = qdict_new(); - QDict *resp; - - qdict_put_str(req, "execute", "user_def_cmd2"); - - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); - assert(resp != NULL); - assert(qdict_haskey(resp, "error")); - - qobject_unref(resp); - qobject_unref(req); + QDict *ret; - /* check that with extra arguments it throws an error */ - req = qdict_new(); - qdict_put_int(args, "a", 66); - qdict_put(req, "arguments", args); + ret = qobject_to(QDict, + do_qmp_dispatch(false, + "{ 'execute': 'user_def_cmd' }")); + assert(ret && qdict_size(ret) == 0); + qobject_unref(ret); +} - qdict_put_str(req, "execute", "user_def_cmd"); +static void test_dispatch_cmd_oob(void) +{ + QDict *ret; - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); - assert(resp != NULL); - assert(qdict_haskey(resp, "error")); + ret = qobject_to(QDict, + do_qmp_dispatch(true, + "{ 'exec-oob': 'test-flags-command' }")); + assert(ret && qdict_size(ret) == 0); + qobject_unref(ret); +} - qobject_unref(resp); - qobject_unref(req); +/* test commands that return an error due to invalid parameters */ +static void test_dispatch_cmd_failure(void) +{ + /* missing arguments */ + do_qmp_dispatch_error(false, ERROR_CLASS_GENERIC_ERROR, + "{ 'execute': 'user_def_cmd2' }"); + + /* extra arguments */ + do_qmp_dispatch_error(false, ERROR_CLASS_GENERIC_ERROR, + "{ 'execute': 'user_def_cmd'," + " 'arguments': { 'a': 66 } }"); } static void test_dispatch_cmd_success_response(void) @@ -218,43 +236,18 @@ static void test_dispatch_cmd_success_response(void) qobject_unref(req); } -static QObject *test_qmp_dispatch(QDict *req) -{ - QDict *resp; - QObject *ret; - - resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); - assert(resp && !qdict_haskey(resp, "error")); - ret = qdict_get(resp, "return"); - assert(ret); - qobject_ref(ret); - qobject_unref(resp); - return ret; -} - /* test commands that involve both input parameters and return values */ static void test_dispatch_cmd_io(void) { - QDict *req = qdict_new(); - QDict *args = qdict_new(); - QDict *args3 = qdict_new(); - QDict *ud1a = qdict_new(); - QDict *ud1b = qdict_new(); QDict *ret, *ret_dict, *ret_dict_dict, *ret_dict_dict_userdef; QDict *ret_dict_dict2, *ret_dict_dict2_userdef; QNum *ret3; int64_t val; - qdict_put_int(ud1a, "integer", 42); - qdict_put_str(ud1a, "string", "hello"); - qdict_put_int(ud1b, "integer", 422); - qdict_put_str(ud1b, "string", "hello2"); - qdict_put(args, "ud1a", ud1a); - qdict_put(args, "ud1b", ud1b); - qdict_put(req, "arguments", args); - qdict_put_str(req, "execute", "user_def_cmd2"); - - ret = qobject_to(QDict, test_qmp_dispatch(req)); + ret = qobject_to(QDict, do_qmp_dispatch(false, + "{ 'execute': 'user_def_cmd2', 'arguments': {" + " 'ud1a': { 'integer': 42, 'string': 'hello' }," + " 'ud1b': { 'integer': 422, 'string': 'hello2' } } }")); assert(!strcmp(qdict_get_str(ret, "string0"), "blah1")); ret_dict = qdict_get_qdict(ret, "dict1"); @@ -271,16 +264,11 @@ static void test_dispatch_cmd_io(void) assert(!strcmp(qdict_get_str(ret_dict_dict2, "string"), "blah4")); qobject_unref(ret); - qdict_put_int(args3, "a", 66); - qdict_put(req, "arguments", args3); - qdict_put_str(req, "execute", "guest-get-time"); - - ret3 = qobject_to(QNum, test_qmp_dispatch(req)); + ret3 = qobject_to(QNum, do_qmp_dispatch(false, + "{ 'execute': 'guest-get-time', 'arguments': { 'a': 66 } }")); g_assert(qnum_get_try_int(ret3, &val)); g_assert_cmpint(val, ==, 66); qobject_unref(ret3); - - qobject_unref(req); } /* test generated dealloc functions for generated types */ diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c index eee7e08ab6..7dd0053190 100644 --- a/tests/test-qmp-event.c +++ b/tests/test-qmp-event.c @@ -17,6 +17,7 @@ #include "qapi/error.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp-event.h" @@ -25,75 +26,12 @@ typedef struct TestEventData { QDict *expect; + bool emitted; } TestEventData; -typedef struct QDictCmpData { - QDict *expect; - bool result; -} QDictCmpData; - TestEventData *test_event_data; static GMutex test_event_lock; -/* Only compares bool, int, string */ -static -void qdict_cmp_do_simple(const char *key, QObject *obj1, void *opaque) - -{ - QObject *obj2; - QDictCmpData d_new, *d = opaque; - int64_t val1, val2; - - if (!d->result) { - return; - } - - obj2 = qdict_get(d->expect, key); - if (!obj2) { - d->result = false; - return; - } - - if (qobject_type(obj1) != qobject_type(obj2)) { - d->result = false; - return; - } - - switch (qobject_type(obj1)) { - case QTYPE_QBOOL: - d->result = (qbool_get_bool(qobject_to(QBool, obj1)) == - qbool_get_bool(qobject_to(QBool, obj2))); - return; - case QTYPE_QNUM: - g_assert(qnum_get_try_int(qobject_to(QNum, obj1), &val1)); - g_assert(qnum_get_try_int(qobject_to(QNum, obj2), &val2)); - d->result = val1 == val2; - return; - case QTYPE_QSTRING: - d->result = g_strcmp0(qstring_get_str(qobject_to(QString, obj1)), - qstring_get_str(qobject_to(QString, obj2))) == 0; - return; - case QTYPE_QDICT: - d_new.expect = qobject_to(QDict, obj2); - d_new.result = true; - qdict_iter(qobject_to(QDict, obj1), qdict_cmp_do_simple, &d_new); - d->result = d_new.result; - return; - default: - abort(); - } -} - -static bool qdict_cmp_simple(QDict *a, QDict *b) -{ - QDictCmpData d; - - d.expect = b; - d.result = true; - qdict_iter(a, qdict_cmp_do_simple, &d); - return d.result; -} - void test_qapi_event_emit(test_QAPIEvent event, QDict *d) { QDict *t; @@ -114,8 +52,8 @@ void test_qapi_event_emit(test_QAPIEvent event, QDict *d) qdict_del(d, "timestamp"); - g_assert(qdict_cmp_simple(d, test_event_data->expect)); - + g_assert(qobject_is_equal(QOBJECT(d), QOBJECT(test_event_data->expect))); + test_event_data->emitted = true; } static void event_prepare(TestEventData *data, @@ -124,17 +62,13 @@ static void event_prepare(TestEventData *data, /* Global variable test_event_data was used to pass the expectation, so test cases can't be executed at same time. */ g_mutex_lock(&test_event_lock); - - data->expect = qdict_new(); test_event_data = data; } static void event_teardown(TestEventData *data, const void *unused) { - qobject_unref(data->expect); test_event_data = NULL; - g_mutex_unlock(&test_event_lock); } @@ -152,90 +86,58 @@ static void event_test_add(const char *testpath, static void test_event_a(TestEventData *data, const void *unused) { - QDict *d; - d = data->expect; - qdict_put_str(d, "event", "EVENT_A"); + data->expect = qdict_from_jsonf_nofail("{ 'event': 'EVENT_A' }"); qapi_event_send_event_a(); + g_assert(data->emitted); + qobject_unref(data->expect); } static void test_event_b(TestEventData *data, const void *unused) { - QDict *d; - d = data->expect; - qdict_put_str(d, "event", "EVENT_B"); + data->expect = qdict_from_jsonf_nofail("{ 'event': 'EVENT_B' }"); qapi_event_send_event_b(); + g_assert(data->emitted); + qobject_unref(data->expect); } static void test_event_c(TestEventData *data, const void *unused) { - QDict *d, *d_data, *d_b; - - UserDefOne b; - b.integer = 2; - b.string = g_strdup("test1"); - b.has_enum1 = false; - - d_b = qdict_new(); - qdict_put_int(d_b, "integer", 2); - qdict_put_str(d_b, "string", "test1"); - - d_data = qdict_new(); - qdict_put_int(d_data, "a", 1); - qdict_put(d_data, "b", d_b); - qdict_put_str(d_data, "c", "test2"); - - d = data->expect; - qdict_put_str(d, "event", "EVENT_C"); - qdict_put(d, "data", d_data); + UserDefOne b = { .integer = 2, .string = (char *)"test1" }; + data->expect = qdict_from_jsonf_nofail( + "{ 'event': 'EVENT_C', 'data': {" + " 'a': 1, 'b': { 'integer': 2, 'string': 'test1' }, 'c': 'test2' } }"); qapi_event_send_event_c(true, 1, true, &b, "test2"); - - g_free(b.string); + g_assert(data->emitted); + qobject_unref(data->expect); } /* Complex type */ static void test_event_d(TestEventData *data, const void *unused) { - UserDefOne struct1; - EventStructOne a; - QDict *d, *d_data, *d_a, *d_struct1; - - struct1.integer = 2; - struct1.string = g_strdup("test1"); - struct1.has_enum1 = true; - struct1.enum1 = ENUM_ONE_VALUE1; - - a.struct1 = &struct1; - a.string = g_strdup("test2"); - a.has_enum2 = true; - a.enum2 = ENUM_ONE_VALUE2; - - d_struct1 = qdict_new(); - qdict_put_int(d_struct1, "integer", 2); - qdict_put_str(d_struct1, "string", "test1"); - qdict_put_str(d_struct1, "enum1", "value1"); - - d_a = qdict_new(); - qdict_put(d_a, "struct1", d_struct1); - qdict_put_str(d_a, "string", "test2"); - qdict_put_str(d_a, "enum2", "value2"); - - d_data = qdict_new(); - qdict_put(d_data, "a", d_a); - qdict_put_str(d_data, "b", "test3"); - qdict_put_str(d_data, "enum3", "value3"); - - d = data->expect; - qdict_put_str(d, "event", "EVENT_D"); - qdict_put(d, "data", d_data); - + UserDefOne struct1 = { + .integer = 2, .string = (char *)"test1", + .has_enum1 = true, .enum1 = ENUM_ONE_VALUE1, + }; + EventStructOne a = { + .struct1 = &struct1, + .string = (char *)"test2", + .has_enum2 = true, + .enum2 = ENUM_ONE_VALUE2, + }; + + data->expect = qdict_from_jsonf_nofail( + "{ 'event': 'EVENT_D', 'data': {" + " 'a': {" + " 'struct1': { 'integer': 2, 'string': 'test1', 'enum1': 'value1' }," + " 'string': 'test2', 'enum2': 'value2' }," + " 'b': 'test3', 'enum3': 'value3' } }"); qapi_event_send_event_d(&a, "test3", false, NULL, true, ENUM_ONE_VALUE3); - - g_free(struct1.string); - g_free(a.string); + g_assert(data->emitted); + qobject_unref(data->expect); } int main(int argc, char **argv) diff --git a/tests/test-x86-cpuid.c b/tests/test-x86-cpuid.c index 1942287f33..049030a50e 100644 --- a/tests/test-x86-cpuid.c +++ b/tests/test-x86-cpuid.c @@ -28,79 +28,101 @@ static void test_topo_bits(void) { + X86CPUTopoInfo topo_info = {0}; + /* simple tests for 1 thread per core, 1 core per die, 1 die per package */ - g_assert_cmpuint(apicid_smt_width(1, 1, 1), ==, 0); - g_assert_cmpuint(apicid_core_width(1, 1, 1), ==, 0); - g_assert_cmpuint(apicid_die_width(1, 1, 1), ==, 0); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 1}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 0); + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 0); + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 1, 0), ==, 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 1, 1), ==, 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 1, 2), ==, 2); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 1, 1, 3), ==, 3); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 1}; + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 3), ==, 3); /* Test field width calculation for multiple values */ - g_assert_cmpuint(apicid_smt_width(1, 1, 2), ==, 1); - g_assert_cmpuint(apicid_smt_width(1, 1, 3), ==, 2); - g_assert_cmpuint(apicid_smt_width(1, 1, 4), ==, 2); - - g_assert_cmpuint(apicid_smt_width(1, 1, 14), ==, 4); - g_assert_cmpuint(apicid_smt_width(1, 1, 15), ==, 4); - g_assert_cmpuint(apicid_smt_width(1, 1, 16), ==, 4); - g_assert_cmpuint(apicid_smt_width(1, 1, 17), ==, 5); - - - g_assert_cmpuint(apicid_core_width(1, 30, 2), ==, 5); - g_assert_cmpuint(apicid_core_width(1, 31, 2), ==, 5); - g_assert_cmpuint(apicid_core_width(1, 32, 2), ==, 5); - g_assert_cmpuint(apicid_core_width(1, 33, 2), ==, 6); - - g_assert_cmpuint(apicid_die_width(1, 30, 2), ==, 0); - g_assert_cmpuint(apicid_die_width(2, 30, 2), ==, 1); - g_assert_cmpuint(apicid_die_width(3, 30, 2), ==, 2); - g_assert_cmpuint(apicid_die_width(4, 30, 2), ==, 2); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 2}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 1); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 3}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 4}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); + + topo_info = (X86CPUTopoInfo) {0, 1, 1, 14}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 15}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 16}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); + topo_info = (X86CPUTopoInfo) {0, 1, 1, 17}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 5); + + + topo_info = (X86CPUTopoInfo) {0, 1, 30, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); + topo_info = (X86CPUTopoInfo) {0, 1, 31, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); + topo_info = (X86CPUTopoInfo) {0, 1, 32, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); + topo_info = (X86CPUTopoInfo) {0, 1, 33, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 6); + + topo_info = (X86CPUTopoInfo) {0, 1, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); + topo_info = (X86CPUTopoInfo) {0, 2, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 1); + topo_info = (X86CPUTopoInfo) {0, 3, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); + topo_info = (X86CPUTopoInfo) {0, 4, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); /* 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(1, 6, 3), ==, 2); - g_assert_cmpuint(apicid_core_offset(1, 6, 3), ==, 2); - g_assert_cmpuint(apicid_die_offset(1, 6, 3), ==, 5); - g_assert_cmpuint(apicid_pkg_offset(1, 6, 3), ==, 5); - - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 0), ==, 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 1), ==, 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 2), ==, 2); - - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 1 * 3 + 0), ==, + topo_info = (X86CPUTopoInfo) {0, 1, 6, 3}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); + g_assert_cmpuint(apicid_core_offset(&topo_info), ==, 2); + g_assert_cmpuint(apicid_die_offset(&topo_info), ==, 5); + g_assert_cmpuint(apicid_pkg_offset(&topo_info), ==, 5); + + topo_info = (X86CPUTopoInfo) {0, 1, 6, 3}; + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); + + topo_info = (X86CPUTopoInfo) {0, 1, 6, 3}; + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 0), ==, (1 << 2) | 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 1 * 3 + 1), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 1), ==, (1 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 1 * 3 + 2), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 2), ==, (1 << 2) | 2); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 2 * 3 + 0), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 0), ==, (2 << 2) | 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 2 * 3 + 1), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 1), ==, (2 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 2 * 3 + 2), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 2), ==, (2 << 2) | 2); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 5 * 3 + 0), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 0), ==, (5 << 2) | 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 5 * 3 + 1), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 1), ==, (5 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 5 * 3 + 2), ==, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 2), ==, (5 << 2) | 2); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 6 * 3 + 0 * 3 + 0), ==, (1 << 5)); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 6 * 3 + 1 * 3 + 1), ==, (1 << 5) | (1 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 3 * 6 * 3 + 5 * 3 + 2), ==, (3 << 5) | (5 << 2) | 2); } diff --git a/ui/console.c b/ui/console.c index 179901c35e..184e173687 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1299,8 +1299,8 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, object_property_allow_set_link, OBJ_PROP_LINK_STRONG, &error_abort); - object_property_add_uint32_ptr(obj, "head", - &s->head, &error_abort); + object_property_add_uint32_ptr(obj, "head", &s->head, + OBJ_PROP_FLAG_READ, &error_abort); if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) && (console_type == GRAPHIC_CONSOLE))) { diff --git a/ui/curses.c b/ui/curses.c index 3a1b71451c..a59b23a9cf 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -54,13 +54,13 @@ enum maybe_keycode { }; static DisplayChangeListener *dcl; -static console_ch_t screen[160 * 100]; +static console_ch_t *screen; static WINDOW *screenpad = NULL; static int width, height, gwidth, gheight, invalidate; static int px, py, sminx, sminy, smaxx, smaxy; static const char *font_charset = "CP437"; -static cchar_t vga_to_curses[256]; +static cchar_t *vga_to_curses; static void curses_update(DisplayChangeListener *dcl, int x, int y, int w, int h) @@ -405,6 +405,8 @@ static void curses_refresh(DisplayChangeListener *dcl) static void curses_atexit(void) { endwin(); + g_free(vga_to_curses); + g_free(screen); } /* @@ -529,7 +531,7 @@ static void font_setup(void) * Control characters are normally non-printable, but VGA does have * well-known glyphs for them. */ - static uint16_t control_characters[0x20] = { + static const uint16_t control_characters[0x20] = { 0x0020, 0x263a, 0x263b, @@ -783,6 +785,8 @@ static void curses_display_init(DisplayState *ds, DisplayOptions *opts) if (opts->u.curses.charset) { font_charset = opts->u.curses.charset; } + screen = g_new0(console_ch_t, 160 * 100); + vga_to_curses = g_new0(cchar_t, 256); curses_setup(); curses_keyboard_setup(); atexit(curses_atexit); diff --git a/util/Makefile.objs b/util/Makefile.objs index 6b38b67cf1..6718a38b61 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -5,6 +5,9 @@ util-obj-y += aiocb.o async.o aio-wait.o thread-pool.o qemu-timer.o util-obj-y += main-loop.o util-obj-$(call lnot,$(CONFIG_ATOMIC64)) += atomic64.o util-obj-$(CONFIG_POSIX) += aio-posix.o +util-obj-$(CONFIG_POSIX) += fdmon-poll.o +util-obj-$(CONFIG_EPOLL_CREATE1) += fdmon-epoll.o +util-obj-$(CONFIG_LINUX_IO_URING) += fdmon-io_uring.o util-obj-$(CONFIG_POSIX) += compatfd.o util-obj-$(CONFIG_POSIX) += event_notifier-posix.o util-obj-$(CONFIG_POSIX) += mmap-alloc.o diff --git a/util/aio-posix.c b/util/aio-posix.c index 9e1befc0c0..cd6cf0a4a9 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -20,191 +20,25 @@ #include "qemu/sockets.h" #include "qemu/cutils.h" #include "trace.h" -#ifdef CONFIG_EPOLL_CREATE1 -#include <sys/epoll.h> -#endif +#include "aio-posix.h" -struct AioHandler -{ - GPollFD pfd; - IOHandler *io_read; - IOHandler *io_write; - AioPollFn *io_poll; - IOHandler *io_poll_begin; - IOHandler *io_poll_end; - void *opaque; - bool is_external; - QLIST_ENTRY(AioHandler) node; - QLIST_ENTRY(AioHandler) node_ready; /* only used during aio_poll() */ - QLIST_ENTRY(AioHandler) node_deleted; -}; - -/* Add a handler to a ready list */ -static void add_ready_handler(AioHandlerList *ready_list, - AioHandler *node, - int revents) -{ - QLIST_SAFE_REMOVE(node, node_ready); /* remove from nested parent's list */ - node->pfd.revents = revents; - QLIST_INSERT_HEAD(ready_list, node, node_ready); -} - -#ifdef CONFIG_EPOLL_CREATE1 - -/* The fd number threshold to switch to epoll */ -#define EPOLL_ENABLE_THRESHOLD 64 - -static void aio_epoll_disable(AioContext *ctx) -{ - ctx->epoll_enabled = false; - if (!ctx->epoll_available) { - return; - } - ctx->epoll_available = false; - close(ctx->epollfd); -} - -static inline int epoll_events_from_pfd(int pfd_events) -{ - return (pfd_events & G_IO_IN ? EPOLLIN : 0) | - (pfd_events & G_IO_OUT ? EPOLLOUT : 0) | - (pfd_events & G_IO_HUP ? EPOLLHUP : 0) | - (pfd_events & G_IO_ERR ? EPOLLERR : 0); -} - -static bool aio_epoll_try_enable(AioContext *ctx) -{ - AioHandler *node; - struct epoll_event event; - - QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { - int r; - if (QLIST_IS_INSERTED(node, node_deleted) || !node->pfd.events) { - continue; - } - event.events = epoll_events_from_pfd(node->pfd.events); - event.data.ptr = node; - r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event); - if (r) { - return false; - } - } - ctx->epoll_enabled = true; - return true; -} - -static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new) -{ - struct epoll_event event; - int r; - int ctl; - - if (!ctx->epoll_enabled) { - return; - } - if (!node->pfd.events) { - ctl = EPOLL_CTL_DEL; - } else { - event.data.ptr = node; - event.events = epoll_events_from_pfd(node->pfd.events); - ctl = is_new ? EPOLL_CTL_ADD : EPOLL_CTL_MOD; - } - - r = epoll_ctl(ctx->epollfd, ctl, node->pfd.fd, &event); - if (r) { - aio_epoll_disable(ctx); - } -} - -static int aio_epoll(AioContext *ctx, AioHandlerList *ready_list, - int64_t timeout) -{ - GPollFD pfd = { - .fd = ctx->epollfd, - .events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR, - }; - AioHandler *node; - int i, ret = 0; - struct epoll_event events[128]; - - if (timeout > 0) { - ret = qemu_poll_ns(&pfd, 1, timeout); - if (ret > 0) { - timeout = 0; - } - } - if (timeout <= 0 || ret > 0) { - ret = epoll_wait(ctx->epollfd, events, - ARRAY_SIZE(events), - timeout); - if (ret <= 0) { - goto out; - } - for (i = 0; i < ret; i++) { - int ev = events[i].events; - int revents = (ev & EPOLLIN ? G_IO_IN : 0) | - (ev & EPOLLOUT ? G_IO_OUT : 0) | - (ev & EPOLLHUP ? G_IO_HUP : 0) | - (ev & EPOLLERR ? G_IO_ERR : 0); - - node = events[i].data.ptr; - add_ready_handler(ready_list, node, revents); - } - } -out: - return ret; -} +/* Stop userspace polling on a handler if it isn't active for some time */ +#define POLL_IDLE_INTERVAL_NS (7 * NANOSECONDS_PER_SECOND) -static bool aio_epoll_enabled(AioContext *ctx) +bool aio_poll_disabled(AioContext *ctx) { - /* Fall back to ppoll when external clients are disabled. */ - return !aio_external_disabled(ctx) && ctx->epoll_enabled; + return atomic_read(&ctx->poll_disable_cnt); } -static bool aio_epoll_check_poll(AioContext *ctx, GPollFD *pfds, - unsigned npfd, int64_t timeout) +void aio_add_ready_handler(AioHandlerList *ready_list, + AioHandler *node, + int revents) { - if (!ctx->epoll_available) { - return false; - } - if (aio_epoll_enabled(ctx)) { - return true; - } - if (npfd >= EPOLL_ENABLE_THRESHOLD) { - if (aio_epoll_try_enable(ctx)) { - return true; - } else { - aio_epoll_disable(ctx); - } - } - return false; -} - -#else - -static void aio_epoll_update(AioContext *ctx, AioHandler *node, bool is_new) -{ -} - -static int aio_epoll(AioContext *ctx, AioHandlerList *ready_list, - int64_t timeout) -{ - assert(false); -} - -static bool aio_epoll_enabled(AioContext *ctx) -{ - return false; -} - -static bool aio_epoll_check_poll(AioContext *ctx, GPollFD *pfds, - unsigned npfd, int64_t timeout) -{ - return false; + QLIST_SAFE_REMOVE(node, node_ready); /* remove from nested parent's list */ + node->pfd.revents = revents; + QLIST_INSERT_HEAD(ready_list, node, node_ready); } -#endif - static AioHandler *find_aio_handler(AioContext *ctx, int fd) { AioHandler *node; @@ -231,16 +65,23 @@ static bool aio_remove_fd_handler(AioContext *ctx, AioHandler *node) g_source_remove_poll(&ctx->source, &node->pfd); } + node->pfd.revents = 0; + + /* If the fd monitor has already marked it deleted, leave it alone */ + if (QLIST_IS_INSERTED(node, node_deleted)) { + return false; + } + /* If a read is in progress, just mark the node as deleted */ if (qemu_lockcnt_count(&ctx->list_lock)) { QLIST_INSERT_HEAD_RCU(&ctx->deleted_aio_handlers, node, node_deleted); - node->pfd.revents = 0; return false; } /* Otherwise, delete it for real. We can't just mark it as * deleted because deleted nodes are only cleaned up while * no one is walking the handlers list. */ + QLIST_SAFE_REMOVE(node, node_poll); QLIST_REMOVE(node, node); return true; } @@ -300,9 +141,6 @@ void aio_set_fd_handler(AioContext *ctx, QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, new_node, node); } - if (node) { - deleted = aio_remove_fd_handler(ctx, node); - } /* No need to order poll_disable_cnt writes against other updates; * the counter is only used to avoid wasting time and latency on @@ -313,11 +151,9 @@ void aio_set_fd_handler(AioContext *ctx, atomic_set(&ctx->poll_disable_cnt, atomic_read(&ctx->poll_disable_cnt) + poll_disable_change); - if (new_node) { - aio_epoll_update(ctx, new_node, is_new); - } else if (node) { - /* Unregister deleted fd_handler */ - aio_epoll_update(ctx, node, false); + ctx->fdmon_ops->update(ctx, node, new_node); + if (node) { + deleted = aio_remove_fd_handler(ctx, node); } qemu_lockcnt_unlock(&ctx->list_lock); aio_notify(ctx); @@ -361,18 +197,19 @@ void aio_set_event_notifier_poll(AioContext *ctx, (IOHandler *)io_poll_end); } -static void poll_set_started(AioContext *ctx, bool started) +static bool poll_set_started(AioContext *ctx, bool started) { AioHandler *node; + bool progress = false; if (started == ctx->poll_started) { - return; + return false; } ctx->poll_started = started; qemu_lockcnt_inc(&ctx->list_lock); - QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { + QLIST_FOREACH(node, &ctx->poll_aio_handlers, node_poll) { IOHandler *fn; if (QLIST_IS_INSERTED(node, node_deleted)) { @@ -388,8 +225,15 @@ static void poll_set_started(AioContext *ctx, bool started) if (fn) { fn(node->opaque); } + + /* Poll one last time in case ->io_poll_end() raced with the event */ + if (!started) { + progress = node->io_poll(node->opaque) || progress; + } } qemu_lockcnt_dec(&ctx->list_lock); + + return progress; } @@ -446,6 +290,7 @@ static void aio_free_deleted_handlers(AioContext *ctx) while ((node = QLIST_FIRST_RCU(&ctx->deleted_aio_handlers))) { QLIST_REMOVE(node, node); QLIST_REMOVE(node, node_deleted); + QLIST_SAFE_REMOVE(node, node_poll); g_free(node); } @@ -460,6 +305,22 @@ static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node) revents = node->pfd.revents & node->pfd.events; node->pfd.revents = 0; + /* + * Start polling AioHandlers when they become ready because activity is + * likely to continue. Note that starvation is theoretically possible when + * fdmon_supports_polling(), but only until the fd fires for the first + * time. + */ + if (!QLIST_IS_INSERTED(node, node_deleted) && + !QLIST_IS_INSERTED(node, node_poll) && + node->io_poll) { + trace_poll_add(ctx, node, node->pfd.fd, revents); + if (ctx->poll_started && node->io_poll_begin) { + node->io_poll_begin(node->opaque); + } + QLIST_INSERT_HEAD(&ctx->poll_aio_handlers, node, node_poll); + } + if (!QLIST_IS_INSERTED(node, node_deleted) && (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) && aio_node_check(ctx, node->is_external) && @@ -493,7 +354,7 @@ static bool aio_dispatch_ready_handlers(AioContext *ctx, AioHandler *node; while ((node = QLIST_FIRST(ready_list))) { - QLIST_SAFE_REMOVE(node, node_ready); + QLIST_REMOVE(node, node_ready); progress = aio_dispatch_handler(ctx, node) || progress; } @@ -524,71 +385,19 @@ void aio_dispatch(AioContext *ctx) timerlistgroup_run_timers(&ctx->tlg); } -/* These thread-local variables are used only in a small part of aio_poll - * around the call to the poll() system call. In particular they are not - * used while aio_poll is performing callbacks, which makes it much easier - * to think about reentrancy! - * - * Stack-allocated arrays would be perfect but they have size limitations; - * heap allocation is expensive enough that we want to reuse arrays across - * calls to aio_poll(). And because poll() has to be called without holding - * any lock, the arrays cannot be stored in AioContext. Thread-local data - * has none of the disadvantages of these three options. - */ -static __thread GPollFD *pollfds; -static __thread AioHandler **nodes; -static __thread unsigned npfd, nalloc; -static __thread Notifier pollfds_cleanup_notifier; - -static void pollfds_cleanup(Notifier *n, void *unused) -{ - g_assert(npfd == 0); - g_free(pollfds); - g_free(nodes); - nalloc = 0; -} - -static void add_pollfd(AioHandler *node) -{ - if (npfd == nalloc) { - if (nalloc == 0) { - pollfds_cleanup_notifier.notify = pollfds_cleanup; - qemu_thread_atexit_add(&pollfds_cleanup_notifier); - nalloc = 8; - } else { - g_assert(nalloc <= INT_MAX); - nalloc *= 2; - } - pollfds = g_renew(GPollFD, pollfds, nalloc); - nodes = g_renew(AioHandler *, nodes, nalloc); - } - nodes[npfd] = node; - pollfds[npfd] = (GPollFD) { - .fd = node->pfd.fd, - .events = node->pfd.events, - }; - npfd++; -} - -static bool run_poll_handlers_once(AioContext *ctx, int64_t *timeout) +static bool run_poll_handlers_once(AioContext *ctx, + int64_t now, + int64_t *timeout) { bool progress = false; AioHandler *node; + AioHandler *tmp; - /* - * Optimization: ->io_poll() handlers often contain RCU read critical - * sections and we therefore see many rcu_read_lock() -> rcu_read_unlock() - * -> rcu_read_lock() -> ... sequences with expensive memory - * synchronization primitives. Make the entire polling loop an RCU - * critical section because nested rcu_read_lock()/rcu_read_unlock() calls - * are cheap. - */ - RCU_READ_LOCK_GUARD(); - - QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { - if (!QLIST_IS_INSERTED(node, node_deleted) && node->io_poll && - aio_node_check(ctx, node->is_external) && + QLIST_FOREACH_SAFE(node, &ctx->poll_aio_handlers, node_poll, tmp) { + if (aio_node_check(ctx, node->is_external) && node->io_poll(node->opaque)) { + node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS; + /* * Polling was successful, exit try_poll_mode immediately * to adjust the next polling time. @@ -605,6 +414,50 @@ static bool run_poll_handlers_once(AioContext *ctx, int64_t *timeout) return progress; } +static bool fdmon_supports_polling(AioContext *ctx) +{ + return ctx->fdmon_ops->need_wait != aio_poll_disabled; +} + +static bool remove_idle_poll_handlers(AioContext *ctx, int64_t now) +{ + AioHandler *node; + AioHandler *tmp; + bool progress = false; + + /* + * File descriptor monitoring implementations without userspace polling + * support suffer from starvation when a subset of handlers is polled + * because fds will not be processed in a timely fashion. Don't remove + * idle poll handlers. + */ + if (!fdmon_supports_polling(ctx)) { + return false; + } + + QLIST_FOREACH_SAFE(node, &ctx->poll_aio_handlers, node_poll, tmp) { + if (node->poll_idle_timeout == 0LL) { + node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS; + } else if (now >= node->poll_idle_timeout) { + trace_poll_remove(ctx, node, node->pfd.fd); + node->poll_idle_timeout = 0LL; + QLIST_SAFE_REMOVE(node, node_poll); + if (ctx->poll_started && node->io_poll_end) { + node->io_poll_end(node->opaque); + + /* + * Final poll in case ->io_poll_end() races with an event. + * Nevermind about re-adding the handler in the rare case where + * this causes progress. + */ + progress = node->io_poll(node->opaque) || progress; + } + } + } + + return progress; +} + /* run_poll_handlers: * @ctx: the AioContext * @max_ns: maximum time to poll for, in nanoseconds @@ -628,13 +481,28 @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout) trace_run_poll_handlers_begin(ctx, max_ns, *timeout); + /* + * Optimization: ->io_poll() handlers often contain RCU read critical + * sections and we therefore see many rcu_read_lock() -> rcu_read_unlock() + * -> rcu_read_lock() -> ... sequences with expensive memory + * synchronization primitives. Make the entire polling loop an RCU + * critical section because nested rcu_read_lock()/rcu_read_unlock() calls + * are cheap. + */ + RCU_READ_LOCK_GUARD(); + start_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); do { - progress = run_poll_handlers_once(ctx, timeout); + progress = run_poll_handlers_once(ctx, start_time, timeout); elapsed_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start_time; max_ns = qemu_soonest_timeout(*timeout, max_ns); assert(!(max_ns && progress)); - } while (elapsed_time < max_ns && !atomic_read(&ctx->poll_disable_cnt)); + } while (elapsed_time < max_ns && !ctx->fdmon_ops->need_wait(ctx)); + + if (remove_idle_poll_handlers(ctx, start_time + elapsed_time)) { + *timeout = 0; + progress = true; + } /* If time has passed with no successful polling, adjust *timeout to * keep the same ending time. @@ -660,9 +528,14 @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout) */ static bool try_poll_mode(AioContext *ctx, int64_t *timeout) { - int64_t max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns); + int64_t max_ns; - if (max_ns && !atomic_read(&ctx->poll_disable_cnt)) { + if (QLIST_EMPTY_RCU(&ctx->poll_aio_handlers)) { + return false; + } + + max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns); + if (max_ns && !ctx->fdmon_ops->need_wait(ctx)) { poll_set_started(ctx, true); if (run_poll_handlers(ctx, max_ns, timeout)) { @@ -670,19 +543,17 @@ static bool try_poll_mode(AioContext *ctx, int64_t *timeout) } } - poll_set_started(ctx, false); + if (poll_set_started(ctx, false)) { + *timeout = 0; + return true; + } - /* Even if we don't run busy polling, try polling once in case it can make - * progress and the caller will be able to avoid ppoll(2)/epoll_wait(2). - */ - return run_poll_handlers_once(ctx, timeout); + return false; } bool aio_poll(AioContext *ctx, bool blocking) { AioHandlerList ready_list = QLIST_HEAD_INITIALIZER(ready_list); - AioHandler *node; - int i; int ret = 0; bool progress; int64_t timeout; @@ -714,27 +585,8 @@ bool aio_poll(AioContext *ctx, bool blocking) /* If polling is allowed, non-blocking aio_poll does not need the * system call---a single round of run_poll_handlers_once suffices. */ - if (timeout || atomic_read(&ctx->poll_disable_cnt)) { - assert(npfd == 0); - - /* fill pollfds */ - - if (!aio_epoll_enabled(ctx)) { - QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { - if (!QLIST_IS_INSERTED(node, node_deleted) && node->pfd.events - && aio_node_check(ctx, node->is_external)) { - add_pollfd(node); - } - } - } - - /* wait until next event */ - if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) { - npfd = 0; /* pollfds[] is not being used */ - ret = aio_epoll(ctx, &ready_list, timeout); - } else { - ret = qemu_poll_ns(pollfds, npfd, timeout); - } + if (timeout || ctx->fdmon_ops->need_wait(ctx)) { + ret = ctx->fdmon_ops->wait(ctx, &ready_list, timeout); } if (blocking) { @@ -783,19 +635,6 @@ bool aio_poll(AioContext *ctx, bool blocking) } } - /* if we have any readable fds, dispatch event */ - if (ret > 0) { - for (i = 0; i < npfd; i++) { - int revents = pollfds[i].revents; - - if (revents) { - add_ready_handler(&ready_list, nodes[i], revents); - } - } - } - - npfd = 0; - progress |= aio_bh_poll(ctx); if (ret > 0) { @@ -813,23 +652,21 @@ bool aio_poll(AioContext *ctx, bool blocking) void aio_context_setup(AioContext *ctx) { -#ifdef CONFIG_EPOLL_CREATE1 - assert(!ctx->epollfd); - ctx->epollfd = epoll_create1(EPOLL_CLOEXEC); - if (ctx->epollfd == -1) { - fprintf(stderr, "Failed to create epoll instance: %s", strerror(errno)); - ctx->epoll_available = false; - } else { - ctx->epoll_available = true; + ctx->fdmon_ops = &fdmon_poll_ops; + ctx->epollfd = -1; + + /* Use the fastest fd monitoring implementation if available */ + if (fdmon_io_uring_setup(ctx)) { + return; } -#endif + + fdmon_epoll_setup(ctx); } void aio_context_destroy(AioContext *ctx) { -#ifdef CONFIG_EPOLL_CREATE1 - aio_epoll_disable(ctx); -#endif + fdmon_io_uring_destroy(ctx); + fdmon_epoll_disable(ctx); } void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, diff --git a/util/aio-posix.h b/util/aio-posix.h new file mode 100644 index 0000000000..c80c04506a --- /dev/null +++ b/util/aio-posix.h @@ -0,0 +1,81 @@ +/* + * AioContext POSIX event loop implementation internal APIs + * + * Copyright IBM, Corp. 2008 + * Copyright Red Hat, Inc. 2020 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com> + * + * 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. + */ + +#ifndef AIO_POSIX_H +#define AIO_POSIX_H + +#include "block/aio.h" + +struct AioHandler { + GPollFD pfd; + IOHandler *io_read; + IOHandler *io_write; + AioPollFn *io_poll; + IOHandler *io_poll_begin; + IOHandler *io_poll_end; + void *opaque; + QLIST_ENTRY(AioHandler) node; + QLIST_ENTRY(AioHandler) node_ready; /* only used during aio_poll() */ + QLIST_ENTRY(AioHandler) node_deleted; + QLIST_ENTRY(AioHandler) node_poll; +#ifdef CONFIG_LINUX_IO_URING + QSLIST_ENTRY(AioHandler) node_submitted; + unsigned flags; /* see fdmon-io_uring.c */ +#endif + int64_t poll_idle_timeout; /* when to stop userspace polling */ + bool is_external; +}; + +/* Add a handler to a ready list */ +void aio_add_ready_handler(AioHandlerList *ready_list, AioHandler *node, + int revents); + +extern const FDMonOps fdmon_poll_ops; + +#ifdef CONFIG_EPOLL_CREATE1 +bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd); +void fdmon_epoll_setup(AioContext *ctx); +void fdmon_epoll_disable(AioContext *ctx); +#else +static inline bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd) +{ + return false; +} + +static inline void fdmon_epoll_setup(AioContext *ctx) +{ +} + +static inline void fdmon_epoll_disable(AioContext *ctx) +{ +} +#endif /* !CONFIG_EPOLL_CREATE1 */ + +#ifdef CONFIG_LINUX_IO_URING +bool fdmon_io_uring_setup(AioContext *ctx); +void fdmon_io_uring_destroy(AioContext *ctx); +#else +static inline bool fdmon_io_uring_setup(AioContext *ctx) +{ + return false; +} + +static inline void fdmon_io_uring_destroy(AioContext *ctx) +{ +} +#endif /* !CONFIG_LINUX_IO_URING */ + +#endif /* AIO_POSIX_H */ diff --git a/util/bufferiszero.c b/util/bufferiszero.c index bfb2605466..663903553a 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -63,11 +63,11 @@ buffer_zero_int(const void *buf, size_t len) } } -#if defined(CONFIG_AVX2_OPT) || defined(__SSE2__) +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) || defined(__SSE2__) /* Do not use push_options pragmas unnecessarily, because clang * does not support them. */ -#ifdef CONFIG_AVX2_OPT +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) #pragma GCC push_options #pragma GCC target("sse2") #endif @@ -104,7 +104,7 @@ buffer_zero_sse2(const void *buf, size_t len) return _mm_movemask_epi8(_mm_cmpeq_epi8(t, zero)) == 0xFFFF; } -#ifdef CONFIG_AVX2_OPT +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) #pragma GCC pop_options #endif @@ -187,18 +187,54 @@ buffer_zero_avx2(const void *buf, size_t len) #pragma GCC pop_options #endif /* CONFIG_AVX2_OPT */ +#ifdef CONFIG_AVX512F_OPT +#pragma GCC push_options +#pragma GCC target("avx512f") +#include <immintrin.h> + +static bool +buffer_zero_avx512(const void *buf, size_t len) +{ + /* Begin with an unaligned head of 64 bytes. */ + __m512i t = _mm512_loadu_si512(buf); + __m512i *p = (__m512i *)(((uintptr_t)buf + 5 * 64) & -64); + __m512i *e = (__m512i *)(((uintptr_t)buf + len) & -64); + + /* Loop over 64-byte aligned blocks of 256. */ + while (p <= e) { + __builtin_prefetch(p); + if (unlikely(_mm512_test_epi64_mask(t, t))) { + return false; + } + t = p[-4] | p[-3] | p[-2] | p[-1]; + p += 4; + } + + t |= _mm512_loadu_si512(buf + len - 4 * 64); + t |= _mm512_loadu_si512(buf + len - 3 * 64); + t |= _mm512_loadu_si512(buf + len - 2 * 64); + t |= _mm512_loadu_si512(buf + len - 1 * 64); + + return !_mm512_test_epi64_mask(t, t); + +} +#pragma GCC pop_options +#endif + + /* Note that for test_buffer_is_zero_next_accel, the most preferred * ISA must have the least significant bit. */ -#define CACHE_AVX2 1 -#define CACHE_SSE4 2 -#define CACHE_SSE2 4 +#define CACHE_AVX512F 1 +#define CACHE_AVX2 2 +#define CACHE_SSE4 4 +#define CACHE_SSE2 8 /* Make sure that these variables are appropriately initialized when * SSE2 is enabled on the compiler command-line, but the compiler is * too old to support CONFIG_AVX2_OPT. */ -#ifdef CONFIG_AVX2_OPT +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) # define INIT_CACHE 0 # define INIT_ACCEL buffer_zero_int #else @@ -211,6 +247,7 @@ buffer_zero_avx2(const void *buf, size_t len) static unsigned cpuid_cache = INIT_CACHE; static bool (*buffer_accel)(const void *, size_t) = INIT_ACCEL; +static int length_to_accel = 64; static void init_accel(unsigned cache) { @@ -226,10 +263,16 @@ static void init_accel(unsigned cache) fn = buffer_zero_avx2; } #endif +#ifdef CONFIG_AVX512F_OPT + if (cache & CACHE_AVX512F) { + fn = buffer_zero_avx512; + length_to_accel = 256; + } +#endif buffer_accel = fn; } -#ifdef CONFIG_AVX2_OPT +#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) #include "qemu/cpuid.h" static void __attribute__((constructor)) init_cpuid_cache(void) @@ -252,9 +295,17 @@ static void __attribute__((constructor)) init_cpuid_cache(void) int bv; __asm("xgetbv" : "=a"(bv), "=d"(d) : "c"(0)); __cpuid_count(7, 0, a, b, c, d); - if ((bv & 6) == 6 && (b & bit_AVX2)) { + if ((bv & 0x6) == 0x6 && (b & bit_AVX2)) { cache |= CACHE_AVX2; } + /* 0xe6: + * XCR0[7:5] = 111b (OPMASK state, upper 256-bit of ZMM0-ZMM15 + * and ZMM16-ZMM31 state are enabled by OS) + * XCR0[2:1] = 11b (XMM state and YMM state are enabled by OS) + */ + if ((bv & 0xe6) == 0xe6 && (b & bit_AVX512F)) { + cache |= CACHE_AVX512F; + } } } cpuid_cache = cache; @@ -277,7 +328,7 @@ bool test_buffer_is_zero_next_accel(void) static bool select_accel_fn(const void *buf, size_t len) { - if (likely(len >= 64)) { + if (likely(len >= length_to_accel)) { return buffer_accel(buf, len); } return buffer_zero_int(buf, len); diff --git a/util/fdmon-epoll.c b/util/fdmon-epoll.c new file mode 100644 index 0000000000..fcd989d47d --- /dev/null +++ b/util/fdmon-epoll.c @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * epoll(7) file descriptor monitoring + */ + +#include "qemu/osdep.h" +#include <sys/epoll.h> +#include "qemu/rcu_queue.h" +#include "aio-posix.h" + +/* The fd number threshold to switch to epoll */ +#define EPOLL_ENABLE_THRESHOLD 64 + +void fdmon_epoll_disable(AioContext *ctx) +{ + if (ctx->epollfd >= 0) { + close(ctx->epollfd); + ctx->epollfd = -1; + } + + /* Switch back */ + ctx->fdmon_ops = &fdmon_poll_ops; +} + +static inline int epoll_events_from_pfd(int pfd_events) +{ + return (pfd_events & G_IO_IN ? EPOLLIN : 0) | + (pfd_events & G_IO_OUT ? EPOLLOUT : 0) | + (pfd_events & G_IO_HUP ? EPOLLHUP : 0) | + (pfd_events & G_IO_ERR ? EPOLLERR : 0); +} + +static void fdmon_epoll_update(AioContext *ctx, + AioHandler *old_node, + AioHandler *new_node) +{ + struct epoll_event event = { + .data.ptr = new_node, + .events = new_node ? epoll_events_from_pfd(new_node->pfd.events) : 0, + }; + int r; + + if (!new_node) { + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_DEL, old_node->pfd.fd, &event); + } else if (!old_node) { + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, new_node->pfd.fd, &event); + } else { + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_MOD, new_node->pfd.fd, &event); + } + + if (r) { + fdmon_epoll_disable(ctx); + } +} + +static int fdmon_epoll_wait(AioContext *ctx, AioHandlerList *ready_list, + int64_t timeout) +{ + GPollFD pfd = { + .fd = ctx->epollfd, + .events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR, + }; + AioHandler *node; + int i, ret = 0; + struct epoll_event events[128]; + + /* Fall back while external clients are disabled */ + if (atomic_read(&ctx->external_disable_cnt)) { + return fdmon_poll_ops.wait(ctx, ready_list, timeout); + } + + if (timeout > 0) { + ret = qemu_poll_ns(&pfd, 1, timeout); + if (ret > 0) { + timeout = 0; + } + } + if (timeout <= 0 || ret > 0) { + ret = epoll_wait(ctx->epollfd, events, + ARRAY_SIZE(events), + timeout); + if (ret <= 0) { + goto out; + } + for (i = 0; i < ret; i++) { + int ev = events[i].events; + int revents = (ev & EPOLLIN ? G_IO_IN : 0) | + (ev & EPOLLOUT ? G_IO_OUT : 0) | + (ev & EPOLLHUP ? G_IO_HUP : 0) | + (ev & EPOLLERR ? G_IO_ERR : 0); + + node = events[i].data.ptr; + aio_add_ready_handler(ready_list, node, revents); + } + } +out: + return ret; +} + +static const FDMonOps fdmon_epoll_ops = { + .update = fdmon_epoll_update, + .wait = fdmon_epoll_wait, + .need_wait = aio_poll_disabled, +}; + +static bool fdmon_epoll_try_enable(AioContext *ctx) +{ + AioHandler *node; + struct epoll_event event; + + QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { + int r; + if (QLIST_IS_INSERTED(node, node_deleted) || !node->pfd.events) { + continue; + } + event.events = epoll_events_from_pfd(node->pfd.events); + event.data.ptr = node; + r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event); + if (r) { + return false; + } + } + + ctx->fdmon_ops = &fdmon_epoll_ops; + return true; +} + +bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd) +{ + if (ctx->epollfd < 0) { + return false; + } + + /* Do not upgrade while external clients are disabled */ + if (atomic_read(&ctx->external_disable_cnt)) { + return false; + } + + if (npfd >= EPOLL_ENABLE_THRESHOLD) { + if (fdmon_epoll_try_enable(ctx)) { + return true; + } else { + fdmon_epoll_disable(ctx); + } + } + return false; +} + +void fdmon_epoll_setup(AioContext *ctx) +{ + ctx->epollfd = epoll_create1(EPOLL_CLOEXEC); + if (ctx->epollfd == -1) { + fprintf(stderr, "Failed to create epoll instance: %s", strerror(errno)); + } +} diff --git a/util/fdmon-io_uring.c b/util/fdmon-io_uring.c new file mode 100644 index 0000000000..893b79b622 --- /dev/null +++ b/util/fdmon-io_uring.c @@ -0,0 +1,332 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Linux io_uring file descriptor monitoring + * + * The Linux io_uring API supports file descriptor monitoring with a few + * advantages over existing APIs like poll(2) and epoll(7): + * + * 1. Userspace polling of events is possible because the completion queue (cq + * ring) is shared between the kernel and userspace. This allows + * applications that rely on userspace polling to also monitor file + * descriptors in the same userspace polling loop. + * + * 2. Submission and completion is batched and done together in a single system + * call. This minimizes the number of system calls. + * + * 3. File descriptor monitoring is O(1) like epoll(7) so it scales better than + * poll(2). + * + * 4. Nanosecond timeouts are supported so it requires fewer syscalls than + * epoll(7). + * + * This code only monitors file descriptors and does not do asynchronous disk + * I/O. Implementing disk I/O efficiently has other requirements and should + * use a separate io_uring so it does not make sense to unify the code. + * + * File descriptor monitoring is implemented using the following operations: + * + * 1. IORING_OP_POLL_ADD - adds a file descriptor to be monitored. + * 2. IORING_OP_POLL_REMOVE - removes a file descriptor being monitored. When + * the poll mask changes for a file descriptor it is first removed and then + * re-added with the new poll mask, so this operation is also used as part + * of modifying an existing monitored file descriptor. + * 3. IORING_OP_TIMEOUT - added every time a blocking syscall is made to wait + * for events. This operation self-cancels if another event completes + * before the timeout. + * + * io_uring calls the submission queue the "sq ring" and the completion queue + * the "cq ring". Ring entries are called "sqe" and "cqe", respectively. + * + * The code is structured so that sq/cq rings are only modified within + * fdmon_io_uring_wait(). Changes to AioHandlers are made by enqueuing them on + * ctx->submit_list so that fdmon_io_uring_wait() can submit IORING_OP_POLL_ADD + * and/or IORING_OP_POLL_REMOVE sqes for them. + */ + +#include "qemu/osdep.h" +#include <poll.h> +#include "qemu/rcu_queue.h" +#include "aio-posix.h" + +enum { + FDMON_IO_URING_ENTRIES = 128, /* sq/cq ring size */ + + /* AioHandler::flags */ + FDMON_IO_URING_PENDING = (1 << 0), + FDMON_IO_URING_ADD = (1 << 1), + FDMON_IO_URING_REMOVE = (1 << 2), +}; + +static inline int poll_events_from_pfd(int pfd_events) +{ + return (pfd_events & G_IO_IN ? POLLIN : 0) | + (pfd_events & G_IO_OUT ? POLLOUT : 0) | + (pfd_events & G_IO_HUP ? POLLHUP : 0) | + (pfd_events & G_IO_ERR ? POLLERR : 0); +} + +static inline int pfd_events_from_poll(int poll_events) +{ + return (poll_events & POLLIN ? G_IO_IN : 0) | + (poll_events & POLLOUT ? G_IO_OUT : 0) | + (poll_events & POLLHUP ? G_IO_HUP : 0) | + (poll_events & POLLERR ? G_IO_ERR : 0); +} + +/* + * Returns an sqe for submitting a request. Only be called within + * fdmon_io_uring_wait(). + */ +static struct io_uring_sqe *get_sqe(AioContext *ctx) +{ + struct io_uring *ring = &ctx->fdmon_io_uring; + struct io_uring_sqe *sqe = io_uring_get_sqe(ring); + int ret; + + if (likely(sqe)) { + return sqe; + } + + /* No free sqes left, submit pending sqes first */ + ret = io_uring_submit(ring); + assert(ret > 1); + sqe = io_uring_get_sqe(ring); + assert(sqe); + return sqe; +} + +/* Atomically enqueue an AioHandler for sq ring submission */ +static void enqueue(AioHandlerSList *head, AioHandler *node, unsigned flags) +{ + unsigned old_flags; + + old_flags = atomic_fetch_or(&node->flags, FDMON_IO_URING_PENDING | flags); + if (!(old_flags & FDMON_IO_URING_PENDING)) { + QSLIST_INSERT_HEAD_ATOMIC(head, node, node_submitted); + } +} + +/* Dequeue an AioHandler for sq ring submission. Called by fill_sq_ring(). */ +static AioHandler *dequeue(AioHandlerSList *head, unsigned *flags) +{ + AioHandler *node = QSLIST_FIRST(head); + + if (!node) { + return NULL; + } + + /* Doesn't need to be atomic since fill_sq_ring() moves the list */ + QSLIST_REMOVE_HEAD(head, node_submitted); + + /* + * Don't clear FDMON_IO_URING_REMOVE. It's sticky so it can serve two + * purposes: telling fill_sq_ring() to submit IORING_OP_POLL_REMOVE and + * telling process_cqe() to delete the AioHandler when its + * IORING_OP_POLL_ADD completes. + */ + *flags = atomic_fetch_and(&node->flags, ~(FDMON_IO_URING_PENDING | + FDMON_IO_URING_ADD)); + return node; +} + +static void fdmon_io_uring_update(AioContext *ctx, + AioHandler *old_node, + AioHandler *new_node) +{ + if (new_node) { + enqueue(&ctx->submit_list, new_node, FDMON_IO_URING_ADD); + } + + if (old_node) { + /* + * Deletion is tricky because IORING_OP_POLL_ADD and + * IORING_OP_POLL_REMOVE are async. We need to wait for the original + * IORING_OP_POLL_ADD to complete before this handler can be freed + * safely. + * + * It's possible that the file descriptor becomes ready and the + * IORING_OP_POLL_ADD cqe is enqueued before IORING_OP_POLL_REMOVE is + * submitted, too. + * + * Mark this handler deleted right now but don't place it on + * ctx->deleted_aio_handlers yet. Instead, manually fudge the list + * entry to make QLIST_IS_INSERTED() think this handler has been + * inserted and other code recognizes this AioHandler as deleted. + * + * Once the original IORING_OP_POLL_ADD completes we enqueue the + * handler on the real ctx->deleted_aio_handlers list to be freed. + */ + assert(!QLIST_IS_INSERTED(old_node, node_deleted)); + old_node->node_deleted.le_prev = &old_node->node_deleted.le_next; + + enqueue(&ctx->submit_list, old_node, FDMON_IO_URING_REMOVE); + } +} + +static void add_poll_add_sqe(AioContext *ctx, AioHandler *node) +{ + struct io_uring_sqe *sqe = get_sqe(ctx); + int events = poll_events_from_pfd(node->pfd.events); + + io_uring_prep_poll_add(sqe, node->pfd.fd, events); + io_uring_sqe_set_data(sqe, node); +} + +static void add_poll_remove_sqe(AioContext *ctx, AioHandler *node) +{ + struct io_uring_sqe *sqe = get_sqe(ctx); + + io_uring_prep_poll_remove(sqe, node); +} + +/* Add a timeout that self-cancels when another cqe becomes ready */ +static void add_timeout_sqe(AioContext *ctx, int64_t ns) +{ + struct io_uring_sqe *sqe; + struct __kernel_timespec ts = { + .tv_sec = ns / NANOSECONDS_PER_SECOND, + .tv_nsec = ns % NANOSECONDS_PER_SECOND, + }; + + sqe = get_sqe(ctx); + io_uring_prep_timeout(sqe, &ts, 1, 0); +} + +/* Add sqes from ctx->submit_list for submission */ +static void fill_sq_ring(AioContext *ctx) +{ + AioHandlerSList submit_list; + AioHandler *node; + unsigned flags; + + QSLIST_MOVE_ATOMIC(&submit_list, &ctx->submit_list); + + while ((node = dequeue(&submit_list, &flags))) { + /* Order matters, just in case both flags were set */ + if (flags & FDMON_IO_URING_ADD) { + add_poll_add_sqe(ctx, node); + } + if (flags & FDMON_IO_URING_REMOVE) { + add_poll_remove_sqe(ctx, node); + } + } +} + +/* Returns true if a handler became ready */ +static bool process_cqe(AioContext *ctx, + AioHandlerList *ready_list, + struct io_uring_cqe *cqe) +{ + AioHandler *node = io_uring_cqe_get_data(cqe); + unsigned flags; + + /* poll_timeout and poll_remove have a zero user_data field */ + if (!node) { + return false; + } + + /* + * Deletion can only happen when IORING_OP_POLL_ADD completes. If we race + * with enqueue() here then we can safely clear the FDMON_IO_URING_REMOVE + * bit before IORING_OP_POLL_REMOVE is submitted. + */ + flags = atomic_fetch_and(&node->flags, ~FDMON_IO_URING_REMOVE); + if (flags & FDMON_IO_URING_REMOVE) { + QLIST_INSERT_HEAD_RCU(&ctx->deleted_aio_handlers, node, node_deleted); + return false; + } + + aio_add_ready_handler(ready_list, node, pfd_events_from_poll(cqe->res)); + + /* IORING_OP_POLL_ADD is one-shot so we must re-arm it */ + add_poll_add_sqe(ctx, node); + return true; +} + +static int process_cq_ring(AioContext *ctx, AioHandlerList *ready_list) +{ + struct io_uring *ring = &ctx->fdmon_io_uring; + struct io_uring_cqe *cqe; + unsigned num_cqes = 0; + unsigned num_ready = 0; + unsigned head; + + io_uring_for_each_cqe(ring, head, cqe) { + if (process_cqe(ctx, ready_list, cqe)) { + num_ready++; + } + + num_cqes++; + } + + io_uring_cq_advance(ring, num_cqes); + return num_ready; +} + +static int fdmon_io_uring_wait(AioContext *ctx, AioHandlerList *ready_list, + int64_t timeout) +{ + unsigned wait_nr = 1; /* block until at least one cqe is ready */ + int ret; + + /* Fall back while external clients are disabled */ + if (atomic_read(&ctx->external_disable_cnt)) { + return fdmon_poll_ops.wait(ctx, ready_list, timeout); + } + + if (timeout == 0) { + wait_nr = 0; /* non-blocking */ + } else if (timeout > 0) { + add_timeout_sqe(ctx, timeout); + } + + fill_sq_ring(ctx); + + ret = io_uring_submit_and_wait(&ctx->fdmon_io_uring, wait_nr); + assert(ret >= 0); + + return process_cq_ring(ctx, ready_list); +} + +static bool fdmon_io_uring_need_wait(AioContext *ctx) +{ + return io_uring_cq_ready(&ctx->fdmon_io_uring); +} + +static const FDMonOps fdmon_io_uring_ops = { + .update = fdmon_io_uring_update, + .wait = fdmon_io_uring_wait, + .need_wait = fdmon_io_uring_need_wait, +}; + +bool fdmon_io_uring_setup(AioContext *ctx) +{ + int ret; + + ret = io_uring_queue_init(FDMON_IO_URING_ENTRIES, &ctx->fdmon_io_uring, 0); + if (ret != 0) { + return false; + } + + QSLIST_INIT(&ctx->submit_list); + ctx->fdmon_ops = &fdmon_io_uring_ops; + return true; +} + +void fdmon_io_uring_destroy(AioContext *ctx) +{ + if (ctx->fdmon_ops == &fdmon_io_uring_ops) { + AioHandler *node; + + io_uring_queue_exit(&ctx->fdmon_io_uring); + + /* No need to submit these anymore, just free them. */ + while ((node = QSLIST_FIRST_RCU(&ctx->submit_list))) { + QSLIST_REMOVE_HEAD_RCU(&ctx->submit_list, node_submitted); + QLIST_REMOVE(node, node); + g_free(node); + } + + ctx->fdmon_ops = &fdmon_poll_ops; + } +} diff --git a/util/fdmon-poll.c b/util/fdmon-poll.c new file mode 100644 index 0000000000..488067b679 --- /dev/null +++ b/util/fdmon-poll.c @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * poll(2) file descriptor monitoring + * + * Uses ppoll(2) when available, g_poll() otherwise. + */ + +#include "qemu/osdep.h" +#include "aio-posix.h" +#include "qemu/rcu_queue.h" + +/* + * These thread-local variables are used only in fdmon_poll_wait() around the + * call to the poll() system call. In particular they are not used while + * aio_poll is performing callbacks, which makes it much easier to think about + * reentrancy! + * + * Stack-allocated arrays would be perfect but they have size limitations; + * heap allocation is expensive enough that we want to reuse arrays across + * calls to aio_poll(). And because poll() has to be called without holding + * any lock, the arrays cannot be stored in AioContext. Thread-local data + * has none of the disadvantages of these three options. + */ +static __thread GPollFD *pollfds; +static __thread AioHandler **nodes; +static __thread unsigned npfd, nalloc; +static __thread Notifier pollfds_cleanup_notifier; + +static void pollfds_cleanup(Notifier *n, void *unused) +{ + g_assert(npfd == 0); + g_free(pollfds); + g_free(nodes); + nalloc = 0; +} + +static void add_pollfd(AioHandler *node) +{ + if (npfd == nalloc) { + if (nalloc == 0) { + pollfds_cleanup_notifier.notify = pollfds_cleanup; + qemu_thread_atexit_add(&pollfds_cleanup_notifier); + nalloc = 8; + } else { + g_assert(nalloc <= INT_MAX); + nalloc *= 2; + } + pollfds = g_renew(GPollFD, pollfds, nalloc); + nodes = g_renew(AioHandler *, nodes, nalloc); + } + nodes[npfd] = node; + pollfds[npfd] = (GPollFD) { + .fd = node->pfd.fd, + .events = node->pfd.events, + }; + npfd++; +} + +static int fdmon_poll_wait(AioContext *ctx, AioHandlerList *ready_list, + int64_t timeout) +{ + AioHandler *node; + int ret; + + assert(npfd == 0); + + QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { + if (!QLIST_IS_INSERTED(node, node_deleted) && node->pfd.events + && aio_node_check(ctx, node->is_external)) { + add_pollfd(node); + } + } + + /* epoll(7) is faster above a certain number of fds */ + if (fdmon_epoll_try_upgrade(ctx, npfd)) { + return ctx->fdmon_ops->wait(ctx, ready_list, timeout); + } + + ret = qemu_poll_ns(pollfds, npfd, timeout); + if (ret > 0) { + int i; + + for (i = 0; i < npfd; i++) { + int revents = pollfds[i].revents; + + if (revents) { + aio_add_ready_handler(ready_list, nodes[i], revents); + } + } + } + + npfd = 0; + return ret; +} + +static void fdmon_poll_update(AioContext *ctx, + AioHandler *old_node, + AioHandler *new_node) +{ + /* Do nothing, AioHandler already contains the state we'll need */ +} + +const FDMonOps fdmon_poll_ops = { + .update = fdmon_poll_update, + .wait = fdmon_poll_wait, + .need_wait = aio_poll_disabled, +}; diff --git a/util/hbitmap.c b/util/hbitmap.c index 242c6e519c..305b894a63 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -104,7 +104,7 @@ struct HBitmap { /* 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) +static unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) { size_t pos = hbi->pos; const HBitmap *hb = hbi->hb; @@ -193,7 +193,31 @@ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first) } } -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count) +int64_t hbitmap_next_dirty(const HBitmap *hb, int64_t start, int64_t count) +{ + HBitmapIter hbi; + int64_t first_dirty_off; + uint64_t end; + + assert(start >= 0 && count >= 0); + + if (start >= hb->orig_size || count == 0) { + return -1; + } + + end = count > hb->orig_size - start ? hb->orig_size : start + count; + + hbitmap_iter_init(&hbi, hb, start); + first_dirty_off = hbitmap_iter_next(&hbi); + + if (first_dirty_off < 0 || first_dirty_off >= end) { + return -1; + } + + return MAX(start, first_dirty_off); +} + +int64_t hbitmap_next_zero(const HBitmap *hb, int64_t start, int64_t count) { size_t pos = (start >> hb->granularity) >> BITS_PER_LEVEL; unsigned long *last_lev = hb->levels[HBITMAP_LEVELS - 1]; @@ -202,6 +226,8 @@ int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count) uint64_t end_bit, sz; int64_t res; + assert(start >= 0 && count >= 0); + if (start >= hb->orig_size || count == 0) { return -1; } @@ -244,41 +270,33 @@ int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count) return res; } -bool hbitmap_next_dirty_area(const HBitmap *hb, uint64_t *start, - uint64_t *count) +bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end, + int64_t max_dirty_count, + int64_t *dirty_start, int64_t *dirty_count) { - HBitmapIter hbi; - int64_t firt_dirty_off, area_end; - uint32_t granularity = 1UL << hb->granularity; - uint64_t end; + int64_t next_zero; - if (*start >= hb->orig_size || *count == 0) { + assert(start >= 0 && end >= 0 && max_dirty_count > 0); + + end = MIN(end, hb->orig_size); + if (start >= end) { return false; } - end = *count > hb->orig_size - *start ? hb->orig_size : *start + *count; - - hbitmap_iter_init(&hbi, hb, *start); - firt_dirty_off = hbitmap_iter_next(&hbi); - - if (firt_dirty_off < 0 || firt_dirty_off >= end) { + start = hbitmap_next_dirty(hb, start, end - start); + if (start < 0) { return false; } - if (firt_dirty_off + granularity >= end) { - area_end = end; - } else { - area_end = hbitmap_next_zero(hb, firt_dirty_off + granularity, - end - firt_dirty_off - granularity); - if (area_end < 0) { - area_end = end; - } - } + end = start + MIN(end - start, max_dirty_count); - if (firt_dirty_off > *start) { - *start = firt_dirty_off; + next_zero = hbitmap_next_zero(hb, start, end - start); + if (next_zero >= 0) { + end = next_zero; } - *count = area_end - *start; + + *dirty_start = start; + *dirty_count = end - start; return true; } @@ -298,6 +316,35 @@ uint64_t hbitmap_count(const HBitmap *hb) return hb->count << hb->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 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; +} + /* 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. */ @@ -716,6 +763,7 @@ HBitmap *hbitmap_alloc(uint64_t size, int granularity) HBitmap *hb = g_new0(struct HBitmap, 1); unsigned i; + assert(size <= INT64_MAX); hb->orig_size = size; assert(granularity >= 0 && granularity < 64); @@ -746,6 +794,7 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size) uint64_t num_elements = size; uint64_t old; + assert(size <= INT64_MAX); hb->orig_size = size; /* Size comes in as logical elements, adjust for granularity. */ @@ -803,16 +852,15 @@ bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b) */ static void hbitmap_sparse_merge(HBitmap *dst, const HBitmap *src) { - uint64_t offset = 0; - uint64_t count = src->orig_size; + int64_t offset; + int64_t count; - while (hbitmap_next_dirty_area(src, &offset, &count)) { + for (offset = 0; + hbitmap_next_dirty_area(src, offset, src->orig_size, INT64_MAX, + &offset, &count); + offset += count) + { hbitmap_set(dst, offset, count); - offset += count; - if (offset >= src->orig_size) { - break; - } - count = src->orig_size - offset; } } @@ -874,22 +922,6 @@ bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) return true; } -HBitmap *hbitmap_create_meta(HBitmap *hb, int chunk_size) -{ - assert(!(chunk_size & (chunk_size - 1))); - assert(!hb->meta); - hb->meta = hbitmap_alloc(hb->size << hb->granularity, - hb->granularity + ctz32(chunk_size)); - return hb->meta; -} - -void hbitmap_free_meta(HBitmap *hb) -{ - assert(hb->meta); - hbitmap_free(hb->meta); - hb->meta = NULL; -} - char *hbitmap_sha256(const HBitmap *bitmap, Error **errp) { size_t size = bitmap->sizes[HBITMAP_LEVELS - 1] * sizeof(unsigned long); diff --git a/util/module.c b/util/module.c index 236a7bb52a..5f7896870a 100644 --- a/util/module.c +++ b/util/module.c @@ -19,6 +19,9 @@ #endif #include "qemu/queue.h" #include "qemu/module.h" +#ifdef CONFIG_MODULE_UPGRADES +#include "qemu-version.h" +#endif typedef struct ModuleEntry { @@ -170,6 +173,9 @@ bool module_load_one(const char *prefix, const char *lib_name) #ifdef CONFIG_MODULES char *fname = NULL; char *exec_dir; +#ifdef CONFIG_MODULE_UPGRADES + char *version_dir; +#endif const char *search_dir; char *dirs[4]; char *module_name; @@ -201,6 +207,14 @@ bool module_load_one(const char *prefix, const char *lib_name) dirs[n_dirs++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR); dirs[n_dirs++] = g_strdup_printf("%s/..", exec_dir ? : ""); dirs[n_dirs++] = g_strdup_printf("%s", exec_dir ? : ""); + +#ifdef CONFIG_MODULE_UPGRADES + version_dir = g_strcanon(g_strdup(QEMU_PKGVERSION), + G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "+-.~", + '_'); + dirs[n_dirs++] = g_strdup_printf("/var/run/qemu/%s", version_dir); +#endif + assert(n_dirs <= ARRAY_SIZE(dirs)); g_free(exec_dir); diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 897e8f3ba6..4dd6d7d4b4 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -466,10 +466,17 @@ static inline int get_memset_num_threads(int smp_cpus) static bool touch_all_pages(char *area, size_t hpagesize, size_t numpages, int smp_cpus) { + static gsize initialized = 0; size_t numpages_per_thread, leftover; char *addr = area; int i = 0; + if (g_once_init_enter(&initialized)) { + qemu_mutex_init(&page_mutex); + qemu_cond_init(&page_cond); + g_once_init_leave(&initialized, 1); + } + memset_thread_failed = false; threads_created_flag = false; memset_num_threads = get_memset_num_threads(smp_cpus); diff --git a/util/qemu-timer.c b/util/qemu-timer.c index ef52d28d37..d548d3c1ad 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/timer.h" +#include "qemu/lockable.h" #include "sysemu/replay.h" #include "sysemu/cpus.h" @@ -186,13 +187,12 @@ bool timerlist_expired(QEMUTimerList *timer_list) return false; } - qemu_mutex_lock(&timer_list->active_timers_lock); - if (!timer_list->active_timers) { - qemu_mutex_unlock(&timer_list->active_timers_lock); - return false; + WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { + if (!timer_list->active_timers) { + return false; + } + expire_time = timer_list->active_timers->expire_time; } - expire_time = timer_list->active_timers->expire_time; - qemu_mutex_unlock(&timer_list->active_timers_lock); return expire_time <= qemu_clock_get_ns(timer_list->clock->type); } @@ -225,13 +225,12 @@ int64_t timerlist_deadline_ns(QEMUTimerList *timer_list) * value but ->notify_cb() is called when the deadline changes. Therefore * the caller should notice the change and there is no race condition. */ - qemu_mutex_lock(&timer_list->active_timers_lock); - if (!timer_list->active_timers) { - qemu_mutex_unlock(&timer_list->active_timers_lock); - return -1; + WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { + if (!timer_list->active_timers) { + return -1; + } + expire_time = timer_list->active_timers->expire_time; } - expire_time = timer_list->active_timers->expire_time; - qemu_mutex_unlock(&timer_list->active_timers_lock); delta = expire_time - qemu_clock_get_ns(timer_list->clock->type); diff --git a/util/trace-events b/util/trace-events index 83b6639018..0ce42822eb 100644 --- a/util/trace-events +++ b/util/trace-events @@ -5,6 +5,8 @@ run_poll_handlers_begin(void *ctx, int64_t max_ns, int64_t timeout) "ctx %p max_ run_poll_handlers_end(void *ctx, bool progress, int64_t timeout) "ctx %p progress %d new timeout %"PRId64 poll_shrink(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64 poll_grow(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64 +poll_add(void *ctx, void *node, int fd, unsigned revents) "ctx %p node %p fd %d revents 0x%x" +poll_remove(void *ctx, void *node, int fd) "ctx %p node %p fd %d" # async.c aio_co_schedule(void *ctx, void *co) "ctx %p co %p" |