Merge remote-tracking branch 'remotes/vivier2/tags/linux-user-for-5.0-pull-request' into staging
update syscall numbers to linux 5.5 (with scripts)
add clock_gettime64/clock_settime64
add AT_EXECFN
v4: restore syscall.tbl series but remove vsyscall series
v3: remove syscall.tbl series
v2: guard copy_to_user_timezone() with TARGET_NR_gettimeofday
remove "Support futex_time64" patch
guard sys_futex with TARGET_NR_exit
# gpg: Signature made Fri 20 Mar 2020 15:23:29 GMT
# gpg: using RSA key CD2F75DDC8E3A4DC2E4F5173F30C38BD3F2FBE3C
# gpg: issuer "laurent@vivier.eu"
# gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>" [full]
# gpg: aka "Laurent Vivier <laurent@vivier.eu>" [full]
# gpg: aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>" [full]
# Primary key fingerprint: CD2F 75DD C8E3 A4DC 2E4F 5173 F30C 38BD 3F2F BE3C
* remotes/vivier2/tags/linux-user-for-5.0-pull-request: (32 commits)
linux-user, openrisc: sync syscall numbers with kernel v5.5
linux-user, nios2: sync syscall numbers with kernel v5.5
linux-user, aarch64: sync syscall numbers with kernel v5.5
scripts: add a script to generate syscall_nr.h
linux-user,mips: update syscall-args-o32.c.inc
linux-user,mips: move content of mips_syscall_args
linux-user: update syscall.tbl from linux 0bf999f9c5e7
linux-user, scripts: add a script to update syscall.tbl
linux-user, mips64: add syscall table generation support
linux-user, mips: add syscall table generation support
linux-user, x86_64: add syscall table generation support
linux-user, i386: add syscall table generation support
linux-user, x86_64, i386: cleanup TARGET_NR_arch_prctl
linux-user, sparc, sparc64: add syscall table generation support
linux-user, s390x: add syscall table generation support
linux-user, s390x: remove syscall definitions for !TARGET_S390X
linux-user, ppc: add syscall table generation support
linux-user, arm: add syscall table generation support
linux-user, microblaze: add syscall table generation support
linux-user, sh4: add syscall table generation support
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/.gitlab-ci-opensbi.yml b/.gitlab-ci-opensbi.yml
new file mode 100644
index 0000000..dd051c0
--- /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 0000000..4ba8a4d
--- /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 72f8b8a..b889fb9 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
diff --git a/.mailmap b/.mailmap
index 76154c7..6412067 100644
--- a/.mailmap
+++ b/.mailmap
@@ -39,8 +39,9 @@
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 b92798a..5672d12 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -79,6 +79,7 @@
- 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 @@
- 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 45d4899..2b46f3c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -155,6 +155,7 @@
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: 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/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: 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 @@
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/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: 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/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 @@
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/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 @@
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/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: 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/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 @@
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 @@
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/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/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 @@
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 @@
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: 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: 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: 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 @@
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
diff --git a/Makefile b/Makefile
index 37aed4a..fc2808f 100644
--- a/Makefile
+++ b/Makefile
@@ -795,7 +795,7 @@
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 @@
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 @@
# 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 @@
$(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 @@
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 e288663..a7c9676 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -48,7 +48,7 @@
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/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 cb3a6fa..8ed1eba 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -12,7 +12,7 @@
$(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 5b1902d..ca44970 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 @@
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 @@
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 @@
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 @@
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_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_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_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_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 @@
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 @@
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 @@
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 @@
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_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_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_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_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 @@
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 @@
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 @@
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 @@
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_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_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_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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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_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_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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 705d0b9..d9eb0ec 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -77,6 +77,8 @@
#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 c14b0d8..739a500 100644
--- a/audio/mixeng.c
+++ b/audio/mixeng.c
@@ -268,17 +268,17 @@
};
#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 @@
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 @@
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 @@
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 @@
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 77cc89b..bc8509e 100644
--- a/audio/mixeng_template.h
+++ b/audio/mixeng_template.h
@@ -41,32 +41,31 @@
#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 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;
}
diff --git a/block.c b/block.c
index 957630b..a2542c9 100644
--- a/block.c
+++ b/block.c
@@ -668,6 +668,32 @@
}
}
+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 @@
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_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 @@
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 cb36ae2..3635b6b 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -45,6 +45,7 @@
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 1bfb360..3b50c06 100644
--- a/block/backup-top.c
+++ b/block/backup-top.c
@@ -38,6 +38,7 @@
BlockCopyState *bcs;
BdrvChild *target;
bool active;
+ int64_t cluster_size;
} BDRVBackupTopState;
static coroutine_fn int backup_top_co_preadv(
@@ -57,8 +58,8 @@
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 @@
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 1383e21..7430ca5 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -57,15 +57,6 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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 79798a1..05227e1 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 @@
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 @@
.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 @@
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 @@
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 @@
/*
* 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 @@
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 @@
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 @@
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));
+ assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
+ assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
- block_copy_wait_inflight_reqs(s, start, bytes);
- block_copy_inflight_req_begin(s, &req, start, end);
+ while (bytes) {
+ BlockCopyInFlightReq req;
+ int64_t next_zero, cur_bytes, status_bytes;
- while (start < end) {
- int64_t next_zero, chunk_end;
-
- 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;
- next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, start,
- chunk_end - start);
+ cur_bytes = MIN(bytes, s->copy_size);
+
+ 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;
+ }
+ 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;
}
- 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);
- }
+ trace_block_copy_process(s, offset);
- trace_block_copy_process(s, start);
-
- bdrv_reset_dirty_bitmap(s->copy_bitmap, start, chunk_end - start);
-
- 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 2482383..4425ebe 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 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 @@
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 @@
.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 f862993..6e32590 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -214,11 +214,35 @@
{
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 7039e82..063793e 100644
--- a/block/dirty-bitmap.c
+++ b/block/dirty-bitmap.c
@@ -860,16 +860,24 @@
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 0f77447..9bc3838 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -2445,6 +2445,28 @@
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 @@
.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 7e4cb74..aba67f6 100644
--- a/block/io.c
+++ b/block/io.c
@@ -1399,7 +1399,7 @@
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 91204a2..3c0527c 100644
--- a/block/linux-aio.c
+++ b/block/linux-aio.c
@@ -121,7 +121,7 @@
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 0000000..0a74f9a
--- /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 0000000..4c8c375
--- /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 8cccc2c..cb06954 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -1288,7 +1288,6 @@
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 @@
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 @@
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 77bb578..a68126f 100644
--- a/block/qcow2-threads.c
+++ b/block/qcow2-threads.c
@@ -128,12 +128,12 @@
* @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 @@
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 3640e8c..d44b456 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2610,6 +2610,7 @@
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 @@
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 @@
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 1a7329b..29dff88 100644
--- a/block/trace-events
+++ b/block/trace-events
@@ -48,6 +48,7 @@
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 20e909d..8466051 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -187,7 +187,7 @@
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 3e44fa7..fa8630c 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 @@
}
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 5d63b1e..fc85031 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -299,8 +299,8 @@
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 @@
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 @@
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 @@
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 09e8aed..f8bb1e5 100644
--- a/bsd-user/qemu.h
+++ b/bsd-user/qemu.h
@@ -95,7 +95,7 @@
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);
diff --git a/configure b/configure
index a60292d..4f12cd0 100755
--- a/configure
+++ b/configure
@@ -303,6 +303,7 @@
debug_info="yes"
stack_protector=""
use_containers="yes"
+gdb_bin=$(command -v "gdb")
if test -e "$source_path/.git"
then
@@ -405,6 +406,7 @@
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="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 @@
--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 @@
;;
--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 @@
;;
--disable-fuzzing) fuzzing=no
;;
+ --gdb=*) gdb_bin="$optarg"
+ ;;
*)
echo "ERROR: unknown option $opt"
echo "Try '$0 --help' for more information"
@@ -1773,6 +1786,7 @@
--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 @@
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 @@
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 @@
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 @@
# 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 "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 "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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 6fc8000..f30394f 100644
--- a/contrib/libvhost-user/libvhost-user.h
+++ b/contrib/libvhost-user/libvhost-user.h
@@ -286,7 +286,7 @@
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 6170c91..0929609 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
similarity index 100%
rename from contrib/vhost-user-gpu/main.c
rename to contrib/vhost-user-gpu/vhost-user-gpu.c
diff --git a/cpus.c b/cpus.c
index b4f8b84..ef441bd 100644
--- a/cpus.c
+++ b/cpus.c
@@ -1026,9 +1026,9 @@
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 @@
{
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 3257528..6f42b32 100644
--- a/crypto/block.c
+++ b/crypto/block.c
@@ -115,6 +115,42 @@
}
+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 645e620..36a0e89 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -41,3 +41,4 @@
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 0000000..7c4eb2c
--- /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 f01d537..0000000
--- 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 9fa6ed5..11ed8f2 100644
--- a/docs/can.txt
+++ b/docs/can.txt
@@ -13,7 +13,7 @@
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 a4db3a4..67bdf82 100644
--- a/docs/devel/atomics.txt
+++ b/docs/devel/atomics.txt
@@ -87,7 +87,7 @@
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 b7bca44..e5df72b 100644
--- a/docs/devel/kconfig.rst
+++ b/docs/devel/kconfig.rst
@@ -8,7 +8,7 @@
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 03aa9e7..0d99eb2 100644
--- a/docs/devel/loads-stores.rst
+++ b/docs/devel/loads-stores.rst
@@ -302,7 +302,7 @@
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 782bebc..3c85ac0 100644
--- a/docs/devel/multi-thread-tcg.txt
+++ b/docs/devel/multi-thread-tcg.txt
@@ -227,7 +227,7 @@
(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 @@
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 @@
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 @@
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 59d6973..1967adf 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -172,7 +172,8 @@
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 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 @@
'*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 @@
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 @@
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 @@
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 @@
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 @@
'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 @@
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 @@
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 @@
'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 @@
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 @@
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 @@
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 @@
{ "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 4956a30..4ebde44 100644
--- a/docs/devel/tcg.rst
+++ b/docs/devel/tcg.rst
@@ -83,7 +83,7 @@
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 cc19aad..e9a1603 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="system/index.html">System Emulation User's 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="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="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 376dab2..763e3d0 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -10,9 +10,9 @@
: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 4016523..3b1b660 100644
--- a/docs/interop/vhost-user.rst
+++ b/docs/interop/vhost-user.rst
@@ -568,7 +568,7 @@
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 @@
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 23cfcb4..7e09773 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 f4619a6..70c27ed 100644
--- a/docs/replay.txt
+++ b/docs/replay.txt
@@ -19,7 +19,7 @@
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 08c00bd..8f1ebc6 100644
--- a/docs/specs/fw_cfg.txt
+++ b/docs/specs/fw_cfg.txt
@@ -82,7 +82,7 @@
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 da9eb39..5e61238 100644
--- a/docs/specs/tpm.rst
+++ b/docs/specs/tpm.rst
@@ -25,7 +25,7 @@
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 @@
-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 @@
-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 7dd223f..fb0649a 100644
--- a/docs/sphinx/hxtool.py
+++ b/docs/sphinx/hxtool.py
@@ -37,13 +37,11 @@
__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 @@
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
similarity index 98%
rename from docs/arm-cpu-features.rst
rename to docs/system/arm/cpu-features.rst
index fc1623a..2d5c06c 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 0000000..e6f050f
--- /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 0000000..9de380e
--- /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 0000000..cd9edf5
--- /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 0000000..c41adad
--- /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 0000000..47ff9b3
--- /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 0000000..65f5be3
--- /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 0000000..8af4ad7
--- /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 0000000..8bce30d
--- /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 0000000..51221c3
--- /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 0000000..89ec93e
--- /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 cbad930..9a23278 100644
--- a/docs/system/cpu-models-x86.rst.inc
+++ b/docs/system/cpu-models-x86.rst.inc
@@ -49,10 +49,15 @@
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 @@
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 6c1d903..c633fe2 100644
--- a/docs/system/deprecated.rst
+++ b/docs/system/deprecated.rst
@@ -180,27 +180,67 @@
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 @@
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 @@
``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 @@
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 d2a3b44..324e2af 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:
+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``.
-- ARM926E, ARM1026E, ARM946E, ARM1136 or Cortex-A8 CPU
+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.
-- Two PL011 UARTs
+The situation for 64-bit Arm is fairly similar, except that we don't
+implement so many different machines.
-- SMC 91c111 Ethernet adapter
+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.
-- PL110 LCD controller
+Choosing a board model
+======================
-- PL050 KMI with PS/2 keyboard and mouse.
+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.
-- PL181 MultiMedia Card Interface with SD card.
+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.)
-The ARM Versatile baseboard is emulated with the following devices:
+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.)
-- ARM926E, ARM1136 or Cortex-A8 CPU
+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.
-- PL190 Vectored Interrupt Controller
+Board-specific documentation
+============================
-- Four PL011 UARTs
+Unfortunately many of the Arm boards QEMU supports are currently
+undocumented; you can get a complete list by running
+``qemu-system-aarch64 --machine help``.
-- SMC 91c111 Ethernet adapter
+.. toctree::
+ :maxdepth: 1
-- PL110 LCD controller
+ arm/integratorcp
+ arm/versatile
+ arm/realview
+ arm/xscale
+ arm/palm
+ arm/nseries
+ arm/stellaris
+ arm/musicpal
+ arm/sx1
+ arm/orangepi
-- PL050 KMI with PS/2 keyboard and mouse.
+Arm CPU features
+================
-- 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.
+.. toctree::
+ arm/cpu-features
diff --git a/docs/user/main.rst b/docs/user/main.rst
index ca69f77..bd99b0f 100644
--- a/docs/user/main.rst
+++ b/docs/user/main.rst
@@ -35,7 +35,7 @@
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 @@
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.
diff --git a/exec.c b/exec.c
index 0cc500d..de9d949 100644
--- a/exec.c
+++ b/exec.c
@@ -1315,7 +1315,7 @@
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 @@
}
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 @@
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 0000000..b5aa9ac
--- /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>
diff --git a/gdbstub.c b/gdbstub.c
index 22a2d63..013fb1a 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -319,8 +319,8 @@
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 @@
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 @@
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 @@
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 @@
*/
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 @@
/* 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 @@
/* -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 @@
* 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 @@
cpu_single_step(cpu, sstep_flags);
}
}
- s->running_state = 1;
+ gdbserver_state.running_state = 1;
#else
int flag = 0;
@@ -503,13 +524,13 @@
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 @@
#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 @@
}
/* 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 @@
}
/* 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 @@
}
/* 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 @@
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 @@
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 @@
}
/* 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 @@
}
/* 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 @@
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 @@
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 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 @@
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 @@
*/
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 @@
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 @@
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 @@
#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 @@
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_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 @@
* 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 @@
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 @@
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 @@
}
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 @@
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 @@
return -1;
}
- gdb_ctx.s = s;
cmd->handler(&gdb_ctx, user_ctx);
return 0;
}
@@ -1481,53 +1499,54 @@
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 @@
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 @@
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 @@
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 @@
*/
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 @@
int res;
if (gdb_ctx->num_params != 3) {
- put_packet(gdb_ctx->s, "E22");
+ put_packet("E22");
return;
}
@@ -1641,14 +1660,14 @@
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 @@
int res;
if (gdb_ctx->num_params != 3) {
- put_packet(gdb_ctx->s, "E22");
+ put_packet("E22");
return;
}
@@ -1664,14 +1683,14 @@
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 @@
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 @@
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 @@
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 @@
} 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 @@
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 @@
{
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 @@
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 @@
}
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 @@
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 @@
{
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 @@
{
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 @@
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 @@
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 @@
}
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 @@
} else {
phy_memory_mode = 1;
}
- put_packet(gdb_ctx->s, "OK");
+ put_packet("OK");
}
#endif
@@ -2316,16 +2336,16 @@
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 @@
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 @@
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 @@
switch (line_buf[0]) {
case '!':
- put_packet(s, "OK");
+ put_packet("OK");
break;
case '?':
{
@@ -2588,12 +2605,12 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
*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 @@
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 @@
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 @@
} 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 @@
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 @@
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 @@
/* 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 @@
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/* 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 @@
int i;
for (i = 0; i < size; i++) {
- gdb_read_byte(gdbserver_state, buf[i]);
+ gdb_read_byte(buf[i]);
}
}
@@ -3183,7 +3189,7 @@
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_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 @@
{
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 @@
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 @@
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 499d6d5..ca51984 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 eb3d160..7f0f397 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 @@
#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 @@
#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 8136e13..6f598a0 100644
--- a/hw/9pfs/9p-proxy.c
+++ b/hw/9pfs/9p-proxy.c
@@ -1139,10 +1139,10 @@
}
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 4e74284..336cace 100644
--- a/hw/acpi/ich9.c
+++ b/hw/acpi/ich9.c
@@ -357,81 +357,6 @@
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 @@
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 @@
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 5219dd0..eb6a37b 100644
--- a/hw/acpi/nvdimm.c
+++ b/hw/acpi/nvdimm.c
@@ -485,7 +485,7 @@
/* 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 @@
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 @@
/* 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 8413348..4dcef37 100644
--- a/hw/acpi/pcihp.c
+++ b/hw/acpi/pcihp.c
@@ -80,7 +80,8 @@
*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 @@
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 b84dbba..964d6f5 100644
--- a/hw/acpi/piix4.c
+++ b/hw/acpi/piix4.c
@@ -444,17 +444,17 @@
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 e5350a2..f7751b1 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 @@
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 @@
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 bc54fd6..188419d 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -297,6 +297,18 @@
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 @@
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 336f6dd..534a6a1 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -35,6 +35,7 @@
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 2ae9c15..62a67a3 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 @@
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 @@
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 0000000..9e4ce36
--- /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 871b1be..0b8ba44 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 @@
{
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 @@
}
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 @@
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 59a27bd..1f7253e 100644
--- a/hw/arm/exynos4210.c
+++ b/hw/arm/exynos4210.c
@@ -305,23 +305,21 @@
/*** 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 da3471b..6f1a82c 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 @@
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 @@
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 @@
&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 55e90d1..8472d2e 100644
--- a/hw/arm/fsl-imx31.c
+++ b/hw/arm/fsl-imx31.c
@@ -206,7 +206,7 @@
}
/* 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 @@
&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 @@
&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 ecc6285..13f1bf2 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 @@
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 @@
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 @@
}
/* 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 @@
&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 @@
&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 c405b68..56dfd7c 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 @@
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 @@
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 @@
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 @@
/*
* 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 @@
/*
* 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 @@
/*
* 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 26713d9..b3ca82b 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 @@
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 1042017..6bc6436 100644
--- a/hw/arm/mainstone.c
+++ b/hw/arm/mainstone.c
@@ -124,9 +124,8 @@
/* 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 8f84692..588d643 100644
--- a/hw/arm/msf2-soc.c
+++ b/hw/arm/msf2-soc.c
@@ -96,7 +96,7 @@
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 @@
* 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 4817a76..57eff63 100644
--- a/hw/arm/nrf51_soc.c
+++ b/hw/arm/nrf51_soc.c
@@ -165,7 +165,7 @@
}
/* 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 de5ff44..57829b3 100644
--- a/hw/arm/omap_sx1.c
+++ b/hw/arm/omap_sx1.c
@@ -131,9 +131,8 @@
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 @@
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 0000000..181f5ba
--- /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 99554bd..97ca105 100644
--- a/hw/arm/palm.c
+++ b/hw/arm/palm.c
@@ -213,9 +213,8 @@
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 56a3620..336c9ba 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -2290,9 +2290,6 @@
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 cbfa693..c28d9b5 100644
--- a/hw/arm/spitz.c
+++ b/hw/arm/spitz.c
@@ -929,8 +929,7 @@
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 221a786..d136ba1 100644
--- a/hw/arm/stellaris.c
+++ b/hw/arm/stellaris.c
@@ -1300,9 +1300,8 @@
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 627fd44..118c342 100644
--- a/hw/arm/stm32f205_soc.c
+++ b/hw/arm/stm32f205_soc.c
@@ -93,13 +93,10 @@
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 9bcad97..4f10ce6 100644
--- a/hw/arm/stm32f405_soc.c
+++ b/hw/arm/stm32f405_soc.c
@@ -95,17 +95,15 @@
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 4d95a1f..5dee2d7 100644
--- a/hw/arm/tosa.c
+++ b/hw/arm/tosa.c
@@ -226,8 +226,7 @@
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 32d865a..94f93dd 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -299,7 +299,7 @@
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 @@
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 @@
}
}
- 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 @@
* 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 @@
}
}
+/*
+ * 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 @@
/* 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 @@
/* 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 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 @@
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 @@
"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 cab0160..49f1c8d 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -318,9 +318,9 @@
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 @@
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 173a752..356d4df 100644
--- a/hw/audio/fmopl.c
+++ b/hw/audio/fmopl.c
@@ -186,7 +186,7 @@
/* 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 @@
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 @@
#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 1bcc3e5..e8d18b7 100644
--- a/hw/audio/intel-hda.c
+++ b/hw/audio/intel-hda.c
@@ -181,7 +181,9 @@
IntelHDAStream st[8];
/* state */
+ MemoryRegion container;
MemoryRegion mmio;
+ MemoryRegion alias;
uint32_t rirb_count;
int64_t wall_base_ns;
@@ -670,12 +672,6 @@
.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 @@
.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 @@
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 61f2fb8..8227088 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 @@
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 @@
}
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 @@
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 @@
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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 c03e80c..f78939f 100644
--- a/hw/block/trace-events
+++ b/hw/block/trace-events
@@ -134,3 +134,19 @@
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 3885464..07bb32e 100644
--- a/hw/block/xen-block.c
+++ b/hw/block/xen-block.c
@@ -998,29 +998,27 @@
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 c420dc0..2b5f37b 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 1fa124d..5c76649 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 9298881..2ab8b69 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -997,7 +997,7 @@
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 @@
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 fe65ca6..786a1be 100644
--- a/hw/core/cpu.c
+++ b/hw/core/cpu.c
@@ -177,7 +177,7 @@
}
-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_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 @@
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 @@
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 d1b78f6..eeef6da 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -1119,19 +1119,26 @@
{
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 9e8c060..de0c425 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -53,7 +53,7 @@
{ "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 @@
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 @@
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 22c5f76..d494e5c 100644
--- a/hw/core/platform-bus.c
+++ b/hw/core/platform-bus.c
@@ -187,7 +187,8 @@
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 62085f9..70eb619 100644
--- a/hw/display/bochs-display.c
+++ b/hw/display/bochs-display.c
@@ -284,8 +284,8 @@
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 4fb67c6..a1ede10 100644
--- a/hw/display/cg3.c
+++ b/hw/display/cg3.c
@@ -287,9 +287,8 @@
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 55185c9..adcba96 100644
--- a/hw/display/g364fb.c
+++ b/hw/display/g364fb.c
@@ -477,7 +477,8 @@
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 8bff16d..b68faff 100644
--- a/hw/display/macfb.c
+++ b/hw/display/macfb.c
@@ -362,8 +362,8 @@
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 ca458f9..76de16e 100644
--- a/hw/display/tcx.c
+++ b/hw/display/tcx.c
@@ -755,9 +755,8 @@
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 b346324..6b9db86 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -249,8 +249,8 @@
/* 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 @@
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 bad8deb..ef15c06 100644
--- a/hw/dma/i8257.c
+++ b/hw/dma/i8257.c
@@ -553,7 +553,7 @@
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 21e2c36..7434d27 100644
--- a/hw/dma/rc4030.c
+++ b/hw/dma/rc4030.c
@@ -679,9 +679,9 @@
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 c3e4158..3a43005 100644
--- a/hw/dma/soc_dma.c
+++ b/hw/dma/soc_dma.c
@@ -80,7 +80,7 @@
} *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 4d08501..0b18271 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 9175f4b..00dd9f5 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 204b684..df7ad25 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -3094,6 +3094,12 @@
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 362eb2a..98ee763 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -781,6 +781,7 @@
ms->smp.cpus = cpus;
ms->smp.cores = cores;
ms->smp.threads = threads;
+ ms->smp.sockets = sockets;
x86ms->smp_dies = dies;
}
@@ -1505,7 +1506,7 @@
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 @@
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 @@
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 @@
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 @@
/* 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 e2d9824..e675621 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 @@
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 @@
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 7f38e6b..87b73fe 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 @@
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 @@
{
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 @@
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 @@
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 @@
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 7342451..bab0459 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 335c060..699f258 100644
--- a/hw/ide/cmd646.c
+++ b/hw/ide/cmd646.c
@@ -207,9 +207,9 @@
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 @@
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 @@
}
}
-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 @@
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 1a6a287..97347f0 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -476,17 +476,20 @@
}
};
-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 bc575b4..3b2de4c 100644
--- a/hw/ide/piix.c
+++ b/hw/ide/piix.c
@@ -197,15 +197,6 @@
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 @@
}
}
-/* 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 @@
.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 096de8d..8de4945 100644
--- a/hw/ide/via.c
+++ b/hw/ide/via.c
@@ -113,10 +113,7 @@
}
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 @@
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 @@
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 @@
}
}
-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 a62587e..1ad35e5 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -2593,6 +2593,12 @@
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 20c8155..6608d72 100644
--- a/hw/intc/spapr_xive.c
+++ b/hw/intc/spapr_xive.c
@@ -677,8 +677,8 @@
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 cb79616..fbc3165 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -625,36 +625,21 @@
.endianness = DEVICE_LITTLE_ENDIAN
};
-static void ich9_lpc_get_sci_int(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
-{
- 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(OBJECT(lpc), ACPI_PM_PROP_ACPI_ENABLE_CMD,
- &acpi_enable_cmd, 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);
- ich9_lpc_add_properties(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_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, OBJ_PROP_FLAG_READ, NULL);
+ object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_DISABLE_CMD,
+ &acpi_disable_cmd, OBJ_PROP_FLAG_READ, NULL);
+
+ 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 7edec5e..9a10fb9 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 @@
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 5f8ded2..c954270 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 c5699f6..81749e7 100644
--- a/hw/m68k/q800.c
+++ b/hw/m68k/q800.c
@@ -399,13 +399,12 @@
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 4727b1d..5040afd 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 @@
{
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 @@
/* 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 d380f73..e4c4de1 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -1403,7 +1403,7 @@
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 ad8b75e..3487013 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 da993f4..68aae2e 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -28,6 +28,11 @@
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 0000000..bbd33a7
--- /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 0000000..18d1074
--- /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 0000000..2b52602
--- /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 0000000..1d07efa
--- /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 0000000..196380c
--- /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 d5e2bdb..ff10f5b 100644
--- a/hw/misc/edu.c
+++ b/hw/misc/edu.c
@@ -396,21 +396,14 @@
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 1a0fad7..a8dc9b3 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -832,7 +832,6 @@
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 @@
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 b7d0012..8134330 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 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 61b6df5..54aeaec 100644
--- a/hw/misc/omap_l4.c
+++ b/hw/misc/omap_l4.c
@@ -24,7 +24,7 @@
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 7f0f5df..a5862b2 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 54411d3..e43c96d 100644
--- a/hw/net/Kconfig
+++ b/hw/net/Kconfig
@@ -79,6 +79,9 @@
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 991c46c..af4d194 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -23,6 +23,7 @@
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 0000000..3fc5e34
--- /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 81fc13e..1563c11 100644
--- a/hw/net/dp8393x.c
+++ b/hw/net/dp8393x.c
@@ -986,13 +986,12 @@
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 6a124a1..5c145a8 100644
--- a/hw/net/imx_fec.c
+++ b/hw/net/imx_fec.c
@@ -855,13 +855,15 @@
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 a1da98a..e18f883 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 07f0954..ca6f591 100644
--- a/hw/nvram/eeprom93xx.c
+++ b/hw/nvram/eeprom93xx.c
@@ -86,7 +86,7 @@
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 1aff72b..1a02e9a 100644
--- a/hw/pci-host/prep.c
+++ b/hw/pci-host/prep.c
@@ -325,9 +325,8 @@
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 993f467..2bbc90b 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -166,14 +166,6 @@
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 @@
{
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 @@
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 e1ed667..b5bc842 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -302,8 +302,11 @@
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 b8189bf..428cf63 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 @@
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 440c406..101bdc5 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 @@
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 f150dec..b5ffa48 100644
--- a/hw/ppc/pnv_lpc.c
+++ b/hw/ppc/pnv_lpc.c
@@ -829,7 +829,7 @@
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 de93c40..e6bffb9 100644
--- a/hw/ppc/ppc405_boards.c
+++ b/hw/ppc/ppc405_boards.c
@@ -199,7 +199,7 @@
#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 @@
/* 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 @@
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 @@
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 111cc80..44be9d2 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 cc10798..9a2bd50 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 @@
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 @@
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,9 +340,294 @@
return off;
}
-static int spapr_populate_memory(SpaprMachineState *spapr, void *fdt)
+static uint32_t spapr_pc_dimm_node(MemoryDeviceInfoList *list, ram_addr_t addr)
+{
+ MemoryDeviceInfoList *info;
+
+ for (info = list; info; info = info->next) {
+ MemoryDeviceInfo *value = info->value;
+
+ if (value && value->type == MEMORY_DEVICE_INFO_KIND_DIMM) {
+ PCDIMMDeviceInfo *pcdimm_info = value->u.dimm.data;
+
+ if (addr >= pcdimm_info->addr &&
+ addr < (pcdimm_info->addr + pcdimm_info->size)) {
+ return pcdimm_info->node;
+ }
+ }
+ }
+
+ return -1;
+}
+
+struct sPAPRDrconfCellV2 {
+ uint32_t seq_lmbs;
+ uint64_t base_addr;
+ uint32_t drc_index;
+ uint32_t aa_index;
+ uint32_t flags;
+} QEMU_PACKED;
+
+typedef struct DrconfCellQueue {
+ struct sPAPRDrconfCellV2 cell;
+ QSIMPLEQ_ENTRY(DrconfCellQueue) entry;
+} DrconfCellQueue;
+
+static DrconfCellQueue *
+spapr_get_drconf_cell(uint32_t seq_lmbs, uint64_t base_addr,
+ uint32_t drc_index, uint32_t aa_index,
+ uint32_t flags)
+{
+ DrconfCellQueue *elem;
+
+ elem = g_malloc0(sizeof(*elem));
+ elem->cell.seq_lmbs = cpu_to_be32(seq_lmbs);
+ elem->cell.base_addr = cpu_to_be64(base_addr);
+ elem->cell.drc_index = cpu_to_be32(drc_index);
+ elem->cell.aa_index = cpu_to_be32(aa_index);
+ elem->cell.flags = cpu_to_be32(flags);
+
+ return elem;
+}
+
+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;
+ int ret;
+ uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE;
+ uint64_t addr, cur_addr, size;
+ uint32_t nr_boot_lmbs = (machine->device_memory->base / lmb_size);
+ uint64_t mem_end = machine->device_memory->base +
+ memory_region_size(&machine->device_memory->mr);
+ uint32_t node, buf_len, nr_entries = 0;
+ SpaprDrc *drc;
+ DrconfCellQueue *elem, *next;
+ MemoryDeviceInfoList *info;
+ QSIMPLEQ_HEAD(, DrconfCellQueue) drconf_queue
+ = QSIMPLEQ_HEAD_INITIALIZER(drconf_queue);
+
+ /* Entry to cover RAM and the gap area */
+ elem = spapr_get_drconf_cell(nr_boot_lmbs, 0, 0, -1,
+ SPAPR_LMB_FLAGS_RESERVED |
+ SPAPR_LMB_FLAGS_DRC_INVALID);
+ QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry);
+ nr_entries++;
+
+ cur_addr = machine->device_memory->base;
+ for (info = dimms; info; info = info->next) {
+ PCDIMMDeviceInfo *di = info->value->u.dimm.data;
+
+ addr = di->addr;
+ size = di->size;
+ node = di->node;
+
+ /*
+ * The NVDIMM area is hotpluggable after the NVDIMM is unplugged. The
+ * area is marked hotpluggable in the next iteration for the bigger
+ * chunk including the NVDIMM occupied area.
+ */
+ if (info->value->type == MEMORY_DEVICE_INFO_KIND_NVDIMM)
+ continue;
+
+ /* Entry for hot-pluggable area */
+ if (cur_addr < addr) {
+ drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, cur_addr / lmb_size);
+ g_assert(drc);
+ elem = spapr_get_drconf_cell((addr - cur_addr) / lmb_size,
+ cur_addr, spapr_drc_index(drc), -1, 0);
+ QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry);
+ nr_entries++;
+ }
+
+ /* Entry for DIMM */
+ drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, addr / lmb_size);
+ g_assert(drc);
+ elem = spapr_get_drconf_cell(size / lmb_size, addr,
+ spapr_drc_index(drc), node,
+ SPAPR_LMB_FLAGS_ASSIGNED);
+ QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry);
+ nr_entries++;
+ cur_addr = addr + size;
+ }
+
+ /* Entry for remaining hotpluggable area */
+ if (cur_addr < mem_end) {
+ drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, cur_addr / lmb_size);
+ g_assert(drc);
+ elem = spapr_get_drconf_cell((mem_end - cur_addr) / lmb_size,
+ cur_addr, spapr_drc_index(drc), -1, 0);
+ QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry);
+ nr_entries++;
+ }
+
+ buf_len = nr_entries * sizeof(struct sPAPRDrconfCellV2) + sizeof(uint32_t);
+ int_buf = cur_index = g_malloc0(buf_len);
+ *(uint32_t *)int_buf = cpu_to_be32(nr_entries);
+ cur_index += sizeof(nr_entries);
+
+ QSIMPLEQ_FOREACH_SAFE(elem, &drconf_queue, entry, next) {
+ memcpy(cur_index, &elem->cell, sizeof(elem->cell));
+ cur_index += sizeof(elem->cell);
+ QSIMPLEQ_REMOVE(&drconf_queue, elem, DrconfCellQueue, entry);
+ g_free(elem);
+ }
+
+ ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory-v2", int_buf, buf_len);
+ g_free(int_buf);
+ if (ret < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static int spapr_dt_dynamic_memory(SpaprMachineState *spapr, void *fdt,
+ int offset, MemoryDeviceInfoList *dimms)
+{
+ MachineState *machine = MACHINE(spapr);
+ int i, ret;
+ uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE;
+ uint32_t device_lmb_start = machine->device_memory->base / lmb_size;
+ uint32_t nr_lmbs = (machine->device_memory->base +
+ memory_region_size(&machine->device_memory->mr)) /
+ lmb_size;
+ uint32_t *int_buf, *cur_index, buf_len;
+
+ /*
+ * Allocate enough buffer size to fit in ibm,dynamic-memory
+ */
+ buf_len = (nr_lmbs * SPAPR_DR_LMB_LIST_ENTRY_SIZE + 1) * sizeof(uint32_t);
+ cur_index = int_buf = g_malloc0(buf_len);
+ int_buf[0] = cpu_to_be32(nr_lmbs);
+ cur_index++;
+ for (i = 0; i < nr_lmbs; i++) {
+ uint64_t addr = i * lmb_size;
+ uint32_t *dynamic_memory = cur_index;
+
+ if (i >= device_lmb_start) {
+ SpaprDrc *drc;
+
+ drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, i);
+ g_assert(drc);
+
+ dynamic_memory[0] = cpu_to_be32(addr >> 32);
+ dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff);
+ dynamic_memory[2] = cpu_to_be32(spapr_drc_index(drc));
+ dynamic_memory[3] = cpu_to_be32(0); /* reserved */
+ dynamic_memory[4] = cpu_to_be32(spapr_pc_dimm_node(dimms, addr));
+ if (memory_region_present(get_system_memory(), addr)) {
+ dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_ASSIGNED);
+ } else {
+ dynamic_memory[5] = cpu_to_be32(0);
+ }
+ } else {
+ /*
+ * LMB information for RMA, boot time RAM and gap b/n RAM and
+ * device memory region -- all these are marked as reserved
+ * and as having no valid DRC.
+ */
+ dynamic_memory[0] = cpu_to_be32(addr >> 32);
+ dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff);
+ dynamic_memory[2] = cpu_to_be32(0);
+ dynamic_memory[3] = cpu_to_be32(0); /* reserved */
+ dynamic_memory[4] = cpu_to_be32(-1);
+ dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_RESERVED |
+ SPAPR_LMB_FLAGS_DRC_INVALID);
+ }
+
+ cur_index += SPAPR_DR_LMB_LIST_ENTRY_SIZE;
+ }
+ ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory", int_buf, buf_len);
+ g_free(int_buf);
+ if (ret < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Adds ibm,dynamic-reconfiguration-memory node.
+ * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation
+ * of this device tree node.
+ */
+static int spapr_dt_dynamic_reconfiguration_memory(SpaprMachineState *spapr,
+ void *fdt)
+{
+ MachineState *machine = MACHINE(spapr);
+ int nb_numa_nodes = machine->numa_state->num_nodes;
+ int ret, i, offset;
+ uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE;
+ uint32_t prop_lmb_size[] = {0, cpu_to_be32(lmb_size)};
+ uint32_t *int_buf, *cur_index, buf_len;
+ int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1;
+ MemoryDeviceInfoList *dimms = NULL;
+
+ /*
+ * Don't create the node if there is no device memory
+ */
+ if (machine->ram_size == machine->maxram_size) {
+ return 0;
+ }
+
+ offset = fdt_add_subnode(fdt, 0, "ibm,dynamic-reconfiguration-memory");
+
+ ret = fdt_setprop(fdt, offset, "ibm,lmb-size", prop_lmb_size,
+ sizeof(prop_lmb_size));
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = fdt_setprop_cell(fdt, offset, "ibm,memory-flags-mask", 0xff);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = fdt_setprop_cell(fdt, offset, "ibm,memory-preservation-time", 0x0);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* 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_dt_dynamic_memory_v2(spapr, fdt, offset, dimms);
+ } else {
+ ret = spapr_dt_dynamic_memory(spapr, fdt, offset, dimms);
+ }
+ qapi_free_MemoryDeviceInfoList(dimms);
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* ibm,associativity-lookup-arrays */
+ buf_len = (nr_nodes * 4 + 2) * sizeof(uint32_t);
+ cur_index = int_buf = g_malloc0(buf_len);
+ int_buf[0] = cpu_to_be32(nr_nodes);
+ int_buf[1] = cpu_to_be32(4); /* Number of entries per associativity list */
+ cur_index += 2;
+ for (i = 0; i < nr_nodes; i++) {
+ uint32_t associativity[] = {
+ cpu_to_be32(0x0),
+ cpu_to_be32(0x0),
+ cpu_to_be32(0x0),
+ cpu_to_be32(i)
+ };
+ memcpy(cur_index, associativity, sizeof(associativity));
+ cur_index += 4;
+ }
+ ret = fdt_setprop(fdt, offset, "ibm,associativity-lookup-arrays", int_buf,
+ (cur_index - int_buf) * sizeof(uint32_t));
+ g_free(int_buf);
+
+ return ret;
+}
+
+static int spapr_dt_memory(SpaprMachineState *spapr, void *fdt)
+{
+ MachineState *machine = MACHINE(spapr);
+ SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
hwaddr mem_start, node_size;
int i, nb_nodes = machine->numa_state->num_nodes;
NodeInfo *nodes = machine->numa_state->nodes;
@@ -363,7 +647,7 @@
if (!mem_start) {
/* spapr_machine_init() checks for rma_size <= node0_size
* already */
- spapr_populate_memory_node(fdt, i, 0, spapr->rma_size);
+ spapr_dt_memory_node(fdt, i, 0, spapr->rma_size);
mem_start += spapr->rma_size;
node_size -= spapr->rma_size;
}
@@ -375,17 +659,28 @@
sizetmp = 1ULL << ctzl(mem_start);
}
- spapr_populate_memory_node(fdt, i, mem_start, sizetmp);
+ 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(spapr->ov5_cas, OV5_DRCONF_MEMORY)) {
+ int ret;
+
+ g_assert(smc->dr_lmb_enabled);
+ ret = spapr_dt_dynamic_reconfiguration_memory(spapr, fdt);
+ if (ret) {
+ return ret;
+ }
+ }
+
return 0;
}
-static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
- SpaprMachineState *spapr)
+static void spapr_dt_cpu(CPUState *cs, void *fdt, int offset,
+ SpaprMachineState *spapr)
{
MachineState *ms = MACHINE(spapr);
PowerPCCPU *cpu = POWERPC_CPU(cs);
@@ -485,7 +780,7 @@
page_sizes_prop, page_sizes_prop_size)));
}
- spapr_populate_pa_features(spapr, cpu, fdt, offset);
+ spapr_dt_pa_features(spapr, cpu, fdt, offset);
_FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id",
cs->cpu_index / vcpus_per_socket)));
@@ -519,7 +814,7 @@
pcc->lrg_decr_bits)));
}
-static void spapr_populate_cpus_dt_node(void *fdt, SpaprMachineState *spapr)
+static void spapr_dt_cpus(void *fdt, SpaprMachineState *spapr)
{
CPUState **rev;
CPUState *cs;
@@ -563,13 +858,13 @@
offset = fdt_add_subnode(fdt, cpus_offset, nodename);
g_free(nodename);
_FDT(offset);
- spapr_populate_cpu_dt(cs, fdt, offset, spapr);
+ spapr_dt_cpu(cs, fdt, offset, spapr);
}
g_free(rev);
}
-static int spapr_rng_populate_dt(void *fdt)
+static int spapr_dt_rng(void *fdt)
{
int node;
int ret;
@@ -592,317 +887,6 @@
return ret ? -1 : 0;
}
-static uint32_t spapr_pc_dimm_node(MemoryDeviceInfoList *list, ram_addr_t addr)
-{
- MemoryDeviceInfoList *info;
-
- for (info = list; info; info = info->next) {
- MemoryDeviceInfo *value = info->value;
-
- if (value && value->type == MEMORY_DEVICE_INFO_KIND_DIMM) {
- PCDIMMDeviceInfo *pcdimm_info = value->u.dimm.data;
-
- if (addr >= pcdimm_info->addr &&
- addr < (pcdimm_info->addr + pcdimm_info->size)) {
- return pcdimm_info->node;
- }
- }
- }
-
- return -1;
-}
-
-struct sPAPRDrconfCellV2 {
- uint32_t seq_lmbs;
- uint64_t base_addr;
- uint32_t drc_index;
- uint32_t aa_index;
- uint32_t flags;
-} QEMU_PACKED;
-
-typedef struct DrconfCellQueue {
- struct sPAPRDrconfCellV2 cell;
- QSIMPLEQ_ENTRY(DrconfCellQueue) entry;
-} DrconfCellQueue;
-
-static DrconfCellQueue *
-spapr_get_drconf_cell(uint32_t seq_lmbs, uint64_t base_addr,
- uint32_t drc_index, uint32_t aa_index,
- uint32_t flags)
-{
- DrconfCellQueue *elem;
-
- elem = g_malloc0(sizeof(*elem));
- elem->cell.seq_lmbs = cpu_to_be32(seq_lmbs);
- elem->cell.base_addr = cpu_to_be64(base_addr);
- elem->cell.drc_index = cpu_to_be32(drc_index);
- elem->cell.aa_index = cpu_to_be32(aa_index);
- elem->cell.flags = cpu_to_be32(flags);
-
- return elem;
-}
-
-/* ibm,dynamic-memory-v2 */
-static int spapr_populate_drmem_v2(SpaprMachineState *spapr, void *fdt,
- int offset, MemoryDeviceInfoList *dimms)
-{
- MachineState *machine = MACHINE(spapr);
- uint8_t *int_buf, *cur_index;
- int ret;
- uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE;
- uint64_t addr, cur_addr, size;
- uint32_t nr_boot_lmbs = (machine->device_memory->base / lmb_size);
- uint64_t mem_end = machine->device_memory->base +
- memory_region_size(&machine->device_memory->mr);
- uint32_t node, buf_len, nr_entries = 0;
- SpaprDrc *drc;
- DrconfCellQueue *elem, *next;
- MemoryDeviceInfoList *info;
- QSIMPLEQ_HEAD(, DrconfCellQueue) drconf_queue
- = QSIMPLEQ_HEAD_INITIALIZER(drconf_queue);
-
- /* Entry to cover RAM and the gap area */
- elem = spapr_get_drconf_cell(nr_boot_lmbs, 0, 0, -1,
- SPAPR_LMB_FLAGS_RESERVED |
- SPAPR_LMB_FLAGS_DRC_INVALID);
- QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry);
- nr_entries++;
-
- cur_addr = machine->device_memory->base;
- for (info = dimms; info; info = info->next) {
- PCDIMMDeviceInfo *di = info->value->u.dimm.data;
-
- addr = di->addr;
- size = di->size;
- node = di->node;
-
- /*
- * The NVDIMM area is hotpluggable after the NVDIMM is unplugged. The
- * area is marked hotpluggable in the next iteration for the bigger
- * chunk including the NVDIMM occupied area.
- */
- if (info->value->type == MEMORY_DEVICE_INFO_KIND_NVDIMM)
- continue;
-
- /* Entry for hot-pluggable area */
- if (cur_addr < addr) {
- drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, cur_addr / lmb_size);
- g_assert(drc);
- elem = spapr_get_drconf_cell((addr - cur_addr) / lmb_size,
- cur_addr, spapr_drc_index(drc), -1, 0);
- QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry);
- nr_entries++;
- }
-
- /* Entry for DIMM */
- drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, addr / lmb_size);
- g_assert(drc);
- elem = spapr_get_drconf_cell(size / lmb_size, addr,
- spapr_drc_index(drc), node,
- SPAPR_LMB_FLAGS_ASSIGNED);
- QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry);
- nr_entries++;
- cur_addr = addr + size;
- }
-
- /* Entry for remaining hotpluggable area */
- if (cur_addr < mem_end) {
- drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, cur_addr / lmb_size);
- g_assert(drc);
- elem = spapr_get_drconf_cell((mem_end - cur_addr) / lmb_size,
- cur_addr, spapr_drc_index(drc), -1, 0);
- QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry);
- nr_entries++;
- }
-
- buf_len = nr_entries * sizeof(struct sPAPRDrconfCellV2) + sizeof(uint32_t);
- int_buf = cur_index = g_malloc0(buf_len);
- *(uint32_t *)int_buf = cpu_to_be32(nr_entries);
- cur_index += sizeof(nr_entries);
-
- QSIMPLEQ_FOREACH_SAFE(elem, &drconf_queue, entry, next) {
- memcpy(cur_index, &elem->cell, sizeof(elem->cell));
- cur_index += sizeof(elem->cell);
- QSIMPLEQ_REMOVE(&drconf_queue, elem, DrconfCellQueue, entry);
- g_free(elem);
- }
-
- ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory-v2", int_buf, buf_len);
- g_free(int_buf);
- if (ret < 0) {
- return -1;
- }
- return 0;
-}
-
-/* ibm,dynamic-memory */
-static int spapr_populate_drmem_v1(SpaprMachineState *spapr, void *fdt,
- int offset, MemoryDeviceInfoList *dimms)
-{
- MachineState *machine = MACHINE(spapr);
- int i, ret;
- uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE;
- uint32_t device_lmb_start = machine->device_memory->base / lmb_size;
- uint32_t nr_lmbs = (machine->device_memory->base +
- memory_region_size(&machine->device_memory->mr)) /
- lmb_size;
- uint32_t *int_buf, *cur_index, buf_len;
-
- /*
- * Allocate enough buffer size to fit in ibm,dynamic-memory
- */
- buf_len = (nr_lmbs * SPAPR_DR_LMB_LIST_ENTRY_SIZE + 1) * sizeof(uint32_t);
- cur_index = int_buf = g_malloc0(buf_len);
- int_buf[0] = cpu_to_be32(nr_lmbs);
- cur_index++;
- for (i = 0; i < nr_lmbs; i++) {
- uint64_t addr = i * lmb_size;
- uint32_t *dynamic_memory = cur_index;
-
- if (i >= device_lmb_start) {
- SpaprDrc *drc;
-
- drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, i);
- g_assert(drc);
-
- dynamic_memory[0] = cpu_to_be32(addr >> 32);
- dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff);
- dynamic_memory[2] = cpu_to_be32(spapr_drc_index(drc));
- dynamic_memory[3] = cpu_to_be32(0); /* reserved */
- dynamic_memory[4] = cpu_to_be32(spapr_pc_dimm_node(dimms, addr));
- if (memory_region_present(get_system_memory(), addr)) {
- dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_ASSIGNED);
- } else {
- dynamic_memory[5] = cpu_to_be32(0);
- }
- } else {
- /*
- * LMB information for RMA, boot time RAM and gap b/n RAM and
- * device memory region -- all these are marked as reserved
- * and as having no valid DRC.
- */
- dynamic_memory[0] = cpu_to_be32(addr >> 32);
- dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff);
- dynamic_memory[2] = cpu_to_be32(0);
- dynamic_memory[3] = cpu_to_be32(0); /* reserved */
- dynamic_memory[4] = cpu_to_be32(-1);
- dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_RESERVED |
- SPAPR_LMB_FLAGS_DRC_INVALID);
- }
-
- cur_index += SPAPR_DR_LMB_LIST_ENTRY_SIZE;
- }
- ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory", int_buf, buf_len);
- g_free(int_buf);
- if (ret < 0) {
- return -1;
- }
- return 0;
-}
-
-/*
- * Adds ibm,dynamic-reconfiguration-memory node.
- * 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)
-{
- MachineState *machine = MACHINE(spapr);
- int nb_numa_nodes = machine->numa_state->num_nodes;
- int ret, i, offset;
- uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE;
- uint32_t prop_lmb_size[] = {0, cpu_to_be32(lmb_size)};
- uint32_t *int_buf, *cur_index, buf_len;
- int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1;
- MemoryDeviceInfoList *dimms = NULL;
-
- /*
- * Don't create the node if there is no device memory
- */
- if (machine->ram_size == machine->maxram_size) {
- return 0;
- }
-
- offset = fdt_add_subnode(fdt, 0, "ibm,dynamic-reconfiguration-memory");
-
- ret = fdt_setprop(fdt, offset, "ibm,lmb-size", prop_lmb_size,
- sizeof(prop_lmb_size));
- if (ret < 0) {
- return ret;
- }
-
- ret = fdt_setprop_cell(fdt, offset, "ibm,memory-flags-mask", 0xff);
- if (ret < 0) {
- return ret;
- }
-
- ret = fdt_setprop_cell(fdt, offset, "ibm,memory-preservation-time", 0x0);
- if (ret < 0) {
- return ret;
- }
-
- /* 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);
- } else {
- ret = spapr_populate_drmem_v1(spapr, fdt, offset, dimms);
- }
- qapi_free_MemoryDeviceInfoList(dimms);
-
- if (ret < 0) {
- return ret;
- }
-
- /* ibm,associativity-lookup-arrays */
- buf_len = (nr_nodes * 4 + 2) * sizeof(uint32_t);
- cur_index = int_buf = g_malloc0(buf_len);
- int_buf[0] = cpu_to_be32(nr_nodes);
- int_buf[1] = cpu_to_be32(4); /* Number of entries per associativity list */
- cur_index += 2;
- for (i = 0; i < nr_nodes; i++) {
- uint32_t associativity[] = {
- cpu_to_be32(0x0),
- cpu_to_be32(0x0),
- cpu_to_be32(0x0),
- cpu_to_be32(i)
- };
- memcpy(cur_index, associativity, sizeof(associativity));
- cur_index += 4;
- }
- ret = fdt_setprop(fdt, offset, "ibm,associativity-lookup-arrays", int_buf,
- (cur_index - int_buf) * sizeof(uint32_t));
- g_free(int_buf);
-
- return ret;
-}
-
-static int spapr_dt_cas_updates(SpaprMachineState *spapr, void *fdt,
- SpaprOptionVector *ov5_updates)
-{
- SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
- int ret = 0, offset;
-
- /* Generate ibm,dynamic-reconfiguration-memory node if required */
- if (spapr_ovec_test(ov5_updates, OV5_DRCONF_MEMORY)) {
- g_assert(smc->dr_lmb_enabled);
- ret = spapr_populate_drconf_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 spapr_ovec_populate_dt(fdt, offset, spapr->ov5_cas,
- "ibm,architecture-vec-5");
-}
-
static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt)
{
MachineState *ms = MACHINE(spapr);
@@ -967,6 +951,29 @@
_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 @@
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 (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",
- &kprop, sizeof(kprop)));
- if (spapr->kernel_le) {
- _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0));
+ if (machine->kernel_cmdline && machine->kernel_cmdline[0]) {
+ _FDT(fdt_setprop_string(fdt, chosen, "bootargs",
+ machine->kernel_cmdline));
}
- }
- 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 (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));
+ }
- for (i = 0; i < cb; i++) {
- if (bootlist[i] == '\n') {
- bootlist[i] = ' ';
+ 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",
+ &kprop, sizeof(kprop)));
+ if (spapr->kernel_le) {
+ _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0));
}
}
- _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-list", bootlist));
- }
+ 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_device && strlen(boot_device)) {
- _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-device", boot_device));
- }
+ if (cb && bootlist) {
+ int i;
- if (!spapr->has_graphics && stdout_path) {
+ for (i = 0; i < cb; i++) {
+ if (bootlist[i] == '\n') {
+ bootlist[i] = ' ';
+ }
+ }
+ _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 (!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));
+ }
+
/*
- * "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));
+ }
+
+ spapr_dt_ov5_platform_support(spapr, fdt, chosen);
+
+ g_free(stdout_path);
+ g_free(bootlist);
}
- /* 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);
-
- 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 @@
/* /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 @@
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 @@
}
}
- /* 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 @@
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 @@
}
}
- /* 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 @@
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 @@
}
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 @@
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 @@
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 @@
{
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 @@
* 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 @@
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 @@
&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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 8b27d3a..679ae79 100644
--- a/hw/ppc/spapr_caps.c
+++ b/hw/ppc/spapr_caps.c
@@ -509,17 +509,14 @@
}
}
-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 @@
.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(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 d09125d..ac1c109 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -50,22 +50,14 @@
* 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 @@
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 e373d34..47e6bb1 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -583,7 +583,8 @@
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 8b32b7e..323fcef 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -786,28 +786,12 @@
{
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 @@
/* 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 @@
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 @@
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 @@
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 @@
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_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 934eb12..40c86e9 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -1458,7 +1458,7 @@
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 @@
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 @@
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 @@
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 @@
* (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 74eeb8b..25be808 100644
--- a/hw/ppc/spapr_nvdimm.c
+++ b/hw/ppc/spapr_nvdimm.c
@@ -35,6 +35,7 @@
{
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 @@
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 0ff6d1a..dd003f1 100644
--- a/hw/ppc/spapr_ovec.c
+++ b/hw/ppc/spapr_ovec.c
@@ -200,8 +200,8 @@
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 656fdd2..9fb8c86 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -190,7 +190,7 @@
*/
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 @@
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 @@
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 @@
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 bd6db85..8050287 100644
--- a/hw/rdma/vmw/pvrdma_qp_ops.c
+++ b/hw/rdma/vmw/pvrdma_qp_ops.c
@@ -34,13 +34,13 @@
/* 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 a254cad..646553a 100644
--- a/hw/riscv/sifive_e.c
+++ b/hw/riscv/sifive_e.c
@@ -145,8 +145,8 @@
&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 @@
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 156a003..56351c4 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 @@
&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 aa208d0..e4c1b86 100644
--- a/hw/rtc/Makefile.objs
+++ b/hw/rtc/Makefile.objs
@@ -12,3 +12,4 @@
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 0000000..5606a51
--- /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 c9894e1..1bc7147 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 9c1ecd4..b81942e 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -538,6 +538,30 @@
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 @@
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 d481310..3e44abe 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -173,16 +173,16 @@
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 50cf95b..64f928f 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -193,7 +193,7 @@
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 7d584e7..923488b 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 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 @@
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 @@
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 @@
{
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 @@
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 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 @@
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 @@
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 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 @@
.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_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 @@
*/
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_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 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_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 @@
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 @@
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 @@
}
/* 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_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 @@
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 @@
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_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 @@
* 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 @@
}
/* 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 b082005..9a4a60c 100644
--- a/hw/scsi/trace-events
+++ b/hw/scsi/trace-events
@@ -227,6 +227,7 @@
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 d8e365d..e5f9768 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 @@
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 e371281..0d1df17 100644
--- a/hw/sd/Makefile.objs
+++ b/hw/sd/Makefile.objs
@@ -4,6 +4,7 @@
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 0000000..f404e1f
--- /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 91db069..829797b 100644
--- a/hw/sd/ssi-sd.c
+++ b/hw/sd/ssi-sd.c
@@ -255,13 +255,25 @@
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 efcff66..5f09d32 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 68b14ee..f410c08 100644
--- a/hw/sh4/shix.c
+++ b/hw/sh4/shix.c
@@ -53,8 +53,7 @@
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 5fa58aa..8f024da 100644
--- a/hw/sparc/leon3.c
+++ b/hw/sparc/leon3.c
@@ -255,8 +255,7 @@
/* 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 d33e84f..6abfcb3 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 @@
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 @@
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 23c8d2f..9d5c696 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 @@
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 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 @@
__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 @@
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 @@
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 @@
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 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;
- s->snoop_index = aspeed_smc_is_ce_stop_active(fl) ? SNOOP_OFF : SNOOP_START;
+ /* User mode selects the CS, other modes unselect */
+ unselect = (value & CTRL_CMD_MODE_MASK) != CTRL_USERMODE;
- qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl));
+ /* 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->regs[s->r_ctrl0 + fl->id] = value;
+
+ 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 @@
(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 @@
__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 @@
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 @@
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 0000000..0a70629
--- /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 5e70ed5..464348b 100644
--- a/hw/usb/Kconfig
+++ b/hw/usb/Kconfig
@@ -91,3 +91,8 @@
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 2b10868..66835e5 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -61,3 +61,5 @@
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 9a78ad9..6210427 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -626,7 +626,7 @@
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 9846599..d2c0368 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -29,7 +29,7 @@
#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 @@
typedef struct {
USBDevice dev;
+ USBEndpoint *intr;
uint8_t recv_buf[RECV_BUF];
uint16_t recv_ptr;
uint16_t recv_used;
@@ -153,7 +154,7 @@
{
.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 @@
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_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 @@
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 @@
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 @@
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 02693a2..ef72738 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -227,7 +227,7 @@
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 @@
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 4883c1d..5c4b57b 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 5b7991c..3730736 100644
--- a/hw/usb/hcd-ehci-sysbus.c
+++ b/hw/usb/hcd-ehci-sysbus.c
@@ -131,6 +131,22 @@
.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 @@
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 56ab2f4..29d49c2 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -1301,7 +1301,6 @@
/* 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 @@
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 0298238..edb5931 100644
--- a/hw/usb/hcd-ehci.h
+++ b/hw/usb/hcd-ehci.h
@@ -342,6 +342,7 @@
#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 0000000..e705a03
--- /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 38a9c56..23ea7a2 100644
--- a/hw/usb/quirks.c
+++ b/hw/usb/quirks.c
@@ -22,10 +22,10 @@
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 89480be..50ef2f9 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 @@
{ 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 @@
{ 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 b2d415e..b6c8ef5 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -54,7 +54,7 @@
{
uint16_t flags;
uint16_t idx;
- uint16_t ring[0];
+ uint16_t ring[];
} VRingAvail;
typedef struct VRingUsedElem
@@ -67,7 +67,7 @@
{
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 9167bba..179775d 100644
--- a/hw/xen/xen_pt.h
+++ b/hw/xen/xen_pt.h
@@ -203,7 +203,7 @@
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 9dd61ce..cb19891 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 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 @@
QSIMPLEQ_ENTRY(BHListSlice) next;
};
+typedef QSLIST_HEAD(, AioHandler) AioHandlerSList;
+
struct AioContext {
GSource source;
@@ -150,6 +205,10 @@
* 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 @@
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 0a16172..aac85e1 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 0000000..3412e10
--- /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 cd6b5b9..e569a4d 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -363,6 +363,7 @@
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 f422c0b..ae9c4da 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -314,6 +314,10 @@
*/
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 @@
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 @@
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 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 e2b20ec..8a10029 100644
--- a/include/block/dirty-bitmap.h
+++ b/include/block/dirty-bitmap.h
@@ -105,10 +105,13 @@
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 d49d2c2..c77ccaf 100644
--- a/include/crypto/block.h
+++ b/include/crypto/block.h
@@ -146,6 +146,26 @@
/**
+ * 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 @@
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 f87f468..c5f9fa0 100644
--- a/include/disas/dis-asm.h
+++ b/include/disas/dis-asm.h
@@ -226,6 +226,10 @@
#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_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 0836396..30b909e 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -68,40 +68,76 @@
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 955eb86..7b9ac36 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 57a3f58..c13327f 100644
--- a/include/hw/acpi/acpi-defs.h
+++ b/include/hw/acpi/acpi-defs.h
@@ -152,7 +152,7 @@
*/
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 @@
*/
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 {
uint8_t device;
uint8_t function;
- } path[0];
+ } path[];
} QEMU_PACKED;
typedef struct AcpiDmarDeviceScope AcpiDmarDeviceScope;
@@ -530,7 +530,7 @@
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 @@
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 @@
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 @@
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 @@
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 8af7245..77c82a9 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 @@
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 0000000..82e4e59
--- /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 1c86bb5..5e196bb 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 @@
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 @@
#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 @@
#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 60eadcc..973bcb7 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 @@
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 eda389a..1a0bab8 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 @@
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 @@
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 @@
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 @@
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 1f37844..ca4a4b1 100644
--- a/include/hw/arm/smmu-common.h
+++ b/include/hw/arm/smmu-common.h
@@ -85,7 +85,7 @@
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 02f500c..893796d 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -95,6 +95,14 @@
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 @@
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 @@
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 9bc42df..236d239 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -71,7 +71,7 @@
*/
typedef struct {
int len;
- CPUArchId cpus[0];
+ CPUArchId cpus[];
} CPUArchIdList;
/**
@@ -236,12 +236,14 @@
* @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 73e9a86..5bf94d2 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -79,7 +79,6 @@
* @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 @@
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 @@
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 @@
*/
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 a1c4afc..3870052 100644
--- a/include/hw/i386/intel_iommu.h
+++ b/include/hw/i386/intel_iommu.h
@@ -114,7 +114,8 @@
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 4ff5b2d..b9593b9 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 @@
/* 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 41fe37b..22babcb 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 @@
#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 28d8a06..c5ce5da 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 1bc1fc7..55da35d 100644
--- a/include/hw/ide/internal.h
+++ b/include/hw/ide/internal.h
@@ -27,6 +27,8 @@
#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 a9f2c33..dd504e5 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 @@
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 0000000..2c3693a
--- /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 0000000..eec5964
--- /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 0000000..bacdf23
--- /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 0000000..af4119e
--- /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 0000000..4c1fa47
--- /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 070a694..87335a9 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 0000000..eda034e
--- /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 0911096..42d64a0 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -79,10 +79,10 @@
#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 @@
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 @@
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 @@
* 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.
*/
- int mc_status;
- QemuCond mc_delivery_cond;
+ 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 fwnmi_machine_check_interlock;
+ QemuCond fwnmi_machine_check_interlock_cond;
/*< public >*/
char *kvm_type;
@@ -736,6 +744,7 @@
#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_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 @@
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 1c4cc65..7aed8f5 100644
--- a/include/hw/ppc/spapr_cpu_core.h
+++ b/include/hw/ppc/spapr_cpu_core.h
@@ -40,7 +40,9 @@
} 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 2bed517..d4dee9e 100644
--- a/include/hw/ppc/spapr_ovec.h
+++ b/include/hw/ppc/spapr_ovec.h
@@ -72,8 +72,8 @@
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 2659a58..0407edb 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 0000000..7893f74
--- /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 bdc32a3..700a610 100644
--- a/include/hw/s390x/event-facility.h
+++ b/include/hw/s390x/event-facility.h
@@ -122,7 +122,7 @@
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 c54413b..cd7b243 100644
--- a/include/hw/s390x/sclp.h
+++ b/include/hw/s390x/sclp.h
@@ -132,7 +132,7 @@
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 @@
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 @@
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 @@
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 0000000..d94606a
--- /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 152628c..02bd741 100644
--- a/include/hw/southbridge/piix.h
+++ b/include/hw/southbridge/piix.h
@@ -68,7 +68,6 @@
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 0000000..07f0235
--- /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 6f67f10..e653004 100644
--- a/include/hw/virtio/virtio-iommu.h
+++ b/include/hw/virtio/virtio-iommu.h
@@ -41,7 +41,7 @@
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 3d32985..e33ca5a 100644
--- a/include/monitor/hmp.h
+++ b/include/monitor/hmp.h
@@ -30,8 +30,6 @@
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_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_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_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_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 e175ba9..094e966 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -98,6 +98,7 @@
unsigned rxfilter_notify_enabled:1;
int vring_enable;
int vnet_hdr_len;
+ bool is_netdev;
QTAILQ_HEAD(, NetFilterState) filters;
};
@@ -203,7 +204,6 @@
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 9aa426a..5a9cf82 100644
--- a/include/qapi/qmp/dispatch.h
+++ b/include/qapi/qmp/dispatch.h
@@ -39,7 +39,8 @@
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 @@
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 6930170..09fc245 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 1bf944c..5e71b6d 100644
--- a/include/qemu/hbitmap.h
+++ b/include/qemu/hbitmap.h
@@ -297,12 +297,18 @@
*/
void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first);
-/* hbitmap_iter_skip_words:
- * @hbi: HBitmapIter to operate on.
+/*
+ * hbitmap_next_dirty:
*
- * Internal function used by hbitmap_iter_next and hbitmap_iter_next_word.
+ * Find next dirty bit within selected range. If not found, return -1.
+ *
+ * @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 @@
* @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
+ * @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, @start + @count), returns true and sets
- * @offset and @bytes appropriately. Otherwise returns false and leaves @offset
- * and @bytes unchanged.
+ * 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, 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);
+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 @@
*/
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 bd59cd8..32aabb1 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 @@
/** 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 84ea794..1aeb2cb 100644
--- a/include/qemu/lockable.h
+++ b/include/qemu/lockable.h
@@ -50,6 +50,7 @@
#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 @@
#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 @@
* 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,12 +75,25 @@
/* 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, 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(x) \
+#define QEMU_MAKE_LOCKABLE_NONNULL(x) \
QEMU_GENERIC(x, \
(QemuLockable *, (x)), \
QEMU_MAKE_LOCKABLE_(x))
@@ -93,4 +108,69 @@
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 0000000..9a23ff0
--- /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 294db54..456a5b0 100644
--- a/include/qemu/queue.h
+++ b/include/qemu/queue.h
@@ -142,6 +142,8 @@
(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 @@
} 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 @@
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 @@
} 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 @@
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 @@
(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 2954649..784c97c 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -1664,69 +1664,101 @@
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 01392dc..71a7a28 100644
--- a/include/sysemu/arch_init.h
+++ b/include/sysemu/arch_init.h
@@ -24,6 +24,7 @@
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 d34c492..a86d99b 100644
--- a/include/sysemu/blockdev.h
+++ b/include/sysemu/blockdev.h
@@ -57,8 +57,4 @@
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 a9afb7e..35eab06 100644
--- a/include/sysemu/cryptodev.h
+++ b/include/sysemu/cryptodev.h
@@ -143,7 +143,7 @@
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 479d90b..ef81302 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -63,9 +63,6 @@
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 4794e8e..a84b49e 100644
--- a/include/sysemu/whpx.h
+++ b/include/sysemu/whpx.h
@@ -35,4 +35,11 @@
#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 54e5446..c48bd76 100644
--- a/include/tcg/tcg.h
+++ b/include/tcg/tcg.h
@@ -267,7 +267,7 @@
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
diff --git a/job-qmp.c b/job-qmp.c
index fbfed25..fecc939 100644
--- a/job-qmp.c
+++ b/job-qmp.c
@@ -143,8 +143,8 @@
.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,
diff --git a/job.c b/job.c
index 04409b4..134a07b 100644
--- a/job.c
+++ b/job.c
@@ -369,17 +369,17 @@
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)
diff --git a/memory.c b/memory.c
index 09be40e..601b749 100644
--- a/memory.c
+++ b/memory.c
@@ -1170,15 +1170,6 @@
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 @@
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 @@
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 @@
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 93c5a45..44942c4 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 @@
*/
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 0b2045c..c1d88ac 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 0ef6879..c12cfdb 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -896,11 +896,38 @@
}
}
+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 @@
/* 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 @@
/* 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 *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 @@
* 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 @@
}
return -errno;
}
- memcpy(block->colo_cache, block->host, block->used_length);
}
}
@@ -3000,19 +3008,36 @@
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 @@
}
}
}
- qemu_mutex_destroy(&ram_state->bitmap_mutex);
- g_free(ram_state);
- ram_state = NULL;
+ ram_state_cleanup(&ram_state);
}
/**
@@ -3348,7 +3371,7 @@
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 @@
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 @@
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 a553d40..5ceaff7 100644
--- a/migration/ram.h
+++ b/migration/ram.h
@@ -66,5 +66,6 @@
/* 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 6fd7aca..5872403 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 1748ab3..6c45fa4 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 @@
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 @@
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 3e6baba..8f60ccc 100644
--- a/monitor/monitor-internal.h
+++ b/monitor/monitor-internal.h
@@ -133,7 +133,7 @@
* 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 5cd9bb8..8f04cfa 100644
--- a/monitor/qmp-cmds-control.c
+++ b/monitor/qmp-cmds-control.c
@@ -101,7 +101,7 @@
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 11a3109..02b1ed0 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -1909,27 +1909,98 @@
return ret;
}
-/*
- * 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.
- */
-static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
- uint64_t *bytes, NBDExtent *extents,
- unsigned int *nb_extents)
-{
- uint64_t remaining_bytes = *bytes;
- NBDExtent *extent = extents, *extents_end = extents + *nb_extents;
- bool first_extent = true;
+typedef struct NBDExtentArray {
+ NBDExtent *extents;
+ unsigned int nb_alloc;
+ unsigned int count;
+ uint64_t total_length;
+ bool can_add;
+ bool converted_to_be;
+} NBDExtentArray;
- assert(*nb_extents);
- while (remaining_bytes) {
+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);
+ }
+}
+
+/*
+ * 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 nbd_extent_array_add(NBDExtentArray *ea,
+ uint32_t length, uint32_t flags)
+{
+ 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;
+ }
+ }
+
+ 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 @@
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;
+ 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);
- }
-
- *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 @@
{
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);
+ 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;
}
- 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;
- }
-
- 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 @@
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;
+ g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents);
- nb_extents = bitmap_to_extents(bitmap, offset, &final_length, extents,
- nb_extents, dont_fragment);
+ bitmap_to_extents(bitmap, offset, length, ea);
- 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);
}
/* nbd_co_receive_request
diff --git a/net/hub.c b/net/hub.c
index 5795a67..88cfb87 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -194,29 +194,6 @@
}
/**
- * 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)
diff --git a/net/hub.h b/net/hub.h
index 66d3322..ce45f7b 100644
--- a/net/hub.h
+++ b/net/hub.h
@@ -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);
diff --git a/net/net.c b/net/net.c
index 9e93c3f..38778e8 100644
--- a/net/net.c
+++ b/net/net.c
@@ -1060,6 +1060,15 @@
}
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 @@
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 @@
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 61276ca..0164727 100644
--- a/net/queue.c
+++ b/net/queue.c
@@ -46,7 +46,7 @@
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 c4334ee..77042e6 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -610,25 +610,13 @@
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 @@
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 @@
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 d6d33d2..f54c274 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
new file mode 100644
index 0000000..bab13f5
--- /dev/null
+++ b/pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin
Binary files differ
diff --git a/pc-bios/opensbi-riscv32-virt-fw_jump.bin b/pc-bios/opensbi-riscv32-virt-fw_jump.bin
index 6c5b7b8..c9654e7 100644
--- a/pc-bios/opensbi-riscv32-virt-fw_jump.bin
+++ b/pc-bios/opensbi-riscv32-virt-fw_jump.bin
Binary files differ
diff --git a/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin b/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
index 971f2be..77f4dc8 100644
--- a/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
+++ b/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
Binary files differ
diff --git a/pc-bios/opensbi-riscv64-virt-fw_jump.bin b/pc-bios/opensbi-riscv64-virt-fw_jump.bin
index 45a5aed..31e74d1 100644
--- a/pc-bios/opensbi-riscv64-virt-fw_jump.bin
+++ b/pc-bios/opensbi-riscv64-virt-fw_jump.bin
Binary files differ
diff --git a/pc-bios/optionrom/pvh_main.c b/pc-bios/optionrom/pvh_main.c
index a015e1b..28e79d7 100644
--- a/pc-bios/optionrom/pvh_main.c
+++ b/pc-bios/optionrom/pvh_main.c
@@ -29,7 +29,7 @@
#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
index 91cdee4..b9da9d8 100644
--- a/pc-bios/s390-ccw.img
+++ b/pc-bios/s390-ccw.img
Binary files differ
diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index 94f53a5..12a0166 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -136,7 +136,7 @@
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 da13c43..4eba251 100644
--- a/pc-bios/s390-ccw/jump2ipl.c
+++ b/pc-bios/s390-ccw/jump2ipl.c
@@ -35,6 +35,7 @@
{
/* 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 a21b386..4e65b41 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 @@
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 @@
*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 f2dcc01..309ffa3 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 504fc7c..5f36361 100644
--- a/pc-bios/s390-ccw/s390-arch.h
+++ b/pc-bios/s390-ccw/s390-arch.h
@@ -36,7 +36,13 @@
/* 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 @@
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 11bce7d..21f27e7 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -57,6 +57,7 @@
/* 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 8450161..64b53ca 100644
--- a/pc-bios/s390-ccw/sclp.h
+++ b/pc-bios/s390-ccw/sclp.h
@@ -95,7 +95,7 @@
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
index 78d8b26..40499a1 100644
--- a/pc-bios/slof.bin
+++ b/pc-bios/slof.bin
Binary files differ
diff --git a/plugins/core.c b/plugins/core.c
index ed86301..51bfc94 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 @@
{
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 @@
} 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 15fc7e5..685d334 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 @@
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 183d8f3..f53abfa 100644
--- a/python/qemu/machine.py
+++ b/python/qemu/machine.py
@@ -270,7 +270,8 @@
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 f40586e..d6c9b2f 100644
--- a/python/qemu/qmp.py
+++ b/python/qemu/qmp.py
@@ -46,7 +46,7 @@
#: 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 @@
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 d8c507c..c31251f 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 9758fc4..943df19 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 97bf52b..2ddbfa8 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 6907b2b..daceb20 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 85b12fe..6b816bb 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 8756e79..b1aabd4 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 6c11e3c..ff7b503 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 d44d99c..eca2981 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 c18fe68..99b90ac 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 1cb9a7d..cebb1b5 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 bc264b3..c30c7ff 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,75 +68,6 @@
return dict;
}
-static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
- bool allow_oob, Error **errp)
-{
- Error *local_err = NULL;
- bool oob;
- const char *command;
- QDict *args, *dict;
- QmpCommand *cmd;
- QObject *ret = NULL;
-
- dict = qmp_dispatch_check_obj(request, allow_oob, errp);
- if (!dict) {
- return NULL;
- }
-
- command = qdict_get_try_str(dict, "execute");
- oob = false;
- if (!command) {
- assert(allow_oob);
- command = qdict_get_str(dict, "exec-oob");
- oob = true;
- }
- cmd = qmp_find_command(cmds, command);
- if (cmd == NULL) {
- error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
- "The command %s has not been found", command);
- return NULL;
- }
- if (!cmd->enabled) {
- error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
- "The command %s has been disabled for this instance",
- command);
- return NULL;
- }
- if (oob && !(cmd->options & QCO_ALLOW_OOB)) {
- error_setg(errp, "The command %s does not support OOB",
- command);
- return NULL;
- }
-
- if (runstate_check(RUN_STATE_PRECONFIG) &&
- !(cmd->options & QCO_ALLOW_PRECONFIG)) {
- error_setg(errp, "The command '%s' isn't permitted in '%s' state",
- cmd->name, RunState_str(RUN_STATE_PRECONFIG));
- return NULL;
- }
-
- if (!qdict_haskey(dict, "arguments")) {
- args = qdict_new();
- } else {
- args = qdict_get_qdict(dict, "arguments");
- qobject_ref(args);
- }
-
- cmd->fn(args, &ret, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- } else if (cmd->options & QCO_NO_SUCCESS_RESP) {
- g_assert(!ret);
- } else if (!ret) {
- /* TODO turn into assertion */
- ret = QOBJECT(qdict_new());
- }
-
- qobject_unref(args);
-
- return ret;
-}
-
QDict *qmp_error_response(Error *err)
{
QDict *rsp;
@@ -164,26 +88,100 @@
&& !qdict_haskey(dict, "execute");
}
-QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
+QDict *qmp_dispatch(const 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;
+ bool oob;
+ const char *command;
+ QDict *args;
+ const QmpCommand *cmd;
+ QDict *dict;
+ QObject *id;
+ QObject *ret = NULL;
+ QDict *rsp = NULL;
- ret = do_qmp_dispatch(cmds, request, allow_oob, &err);
- if (err) {
- 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;
+ dict = qobject_to(QDict, request);
+ if (!dict) {
+ id = NULL;
+ error_setg(&err, "QMP input must be a JSON object");
+ goto out;
}
- if (rsp && id) {
+ id = qdict_get(dict, "id");
+
+ if (!qmp_dispatch_check_obj(dict, allow_oob, &err)) {
+ goto out;
+ }
+
+ command = qdict_get_try_str(dict, "execute");
+ oob = false;
+ if (!command) {
+ assert(allow_oob);
+ command = qdict_get_str(dict, "exec-oob");
+ oob = true;
+ }
+ cmd = qmp_find_command(cmds, command);
+ if (cmd == NULL) {
+ error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND,
+ "The command %s has not been found", command);
+ goto out;
+ }
+ if (!cmd->enabled) {
+ error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND,
+ "The command %s has been disabled for this instance",
+ command);
+ goto out;
+ }
+ if (oob && !(cmd->options & QCO_ALLOW_OOB)) {
+ error_setg(&err, "The command %s does not support OOB",
+ command);
+ goto out;
+ }
+
+ if (runstate_check(RUN_STATE_PRECONFIG) &&
+ !(cmd->options & QCO_ALLOW_PRECONFIG)) {
+ error_setg(&err, "The command '%s' isn't permitted in '%s' state",
+ cmd->name, RunState_str(RUN_STATE_PRECONFIG));
+ goto out;
+ }
+
+ if (!qdict_haskey(dict, "arguments")) {
+ args = qdict_new();
+ } else {
+ args = qdict_get_qdict(dict, "arguments");
+ qobject_ref(args);
+ }
+ cmd->fn(args, &ret, &err);
+ qobject_unref(args);
+ if (err) {
+ goto out;
+ }
+
+ if (cmd->options & QCO_NO_SUCCESS_RESP) {
+ g_assert(!ret);
+ return NULL;
+ } else if (!ret) {
+ /*
+ * 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());
+ }
+
+ rsp = qdict_new();
+ qdict_put_obj(rsp, "return", ret);
+
+out:
+ if (err) {
+ assert(!rsp);
+ rsp = qmp_error_response(err);
+ }
+
+ 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 ca00f74..d0f9a1d 100644
--- a/qapi/qmp-registry.c
+++ b/qapi/qmp-registry.c
@@ -27,7 +27,7 @@
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 @@
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 804630a..afddf33 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -817,6 +817,8 @@
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 @@
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 @@
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 f9fefd4..962a5eb 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 @@
" 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 @@
``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 43c323c..f8852be 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -54,7 +54,7 @@
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 e5c39c1..8ee2736 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -359,7 +359,7 @@
}
/* 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 @@
}
/* [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 @@
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 555c8b9..1812f79 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -2498,6 +2498,22 @@
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 @@
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 @@
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 @@
visit_type_uint64(v, name, &value, errp);
}
-void object_property_add_uint8_ptr(Object *obj, const char *name,
- const uint8_t *v, Error **errp)
+static void property_set_uint64_ptr(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
- object_property_add(obj, name, "uint8", property_get_uint8_ptr,
- NULL, NULL, (void *)v, 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,
+ 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;
+ }
+
+ 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 49db926..435193b 100644
--- a/qom/qom-qmp-cmds.c
+++ b/qom/qom-qmp-cmds.c
@@ -247,26 +247,22 @@
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 28e1e55..f9acf39 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -66,6 +66,7 @@
@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 @@
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
index 9546892..ab6984f 160000
--- a/roms/SLOF
+++ b/roms/SLOF
@@ -1 +1 @@
-Subproject commit 9546892a80d5a4c73deea6719de46372f007f4a6
+Subproject commit ab6984f5a6d054e1f634dda855b32e5357111974
diff --git a/roms/opensbi b/roms/opensbi
index be92da2..ac5e821 160000
--- a/roms/opensbi
+++ b/roms/opensbi
@@ -1 +1 @@
-Subproject commit be92da280d87c38a2e0adc5d3f43bab7b5468f09
+Subproject commit ac5e821d50be631f26274765a59bc1b444ffd862
diff --git a/scripts/coccinelle/cpu-reset.cocci b/scripts/coccinelle/cpu-reset.cocci
new file mode 100644
index 0000000..396a724
--- /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 0000000..c768d81
--- /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 d290150..0000000
--- 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 0003e7b..7b1452f 100644
--- a/scripts/hxtool
+++ b/scripts/hxtool
@@ -7,7 +7,7 @@
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 @@
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 0e13e82..bc30876 100644
--- a/scripts/qapi/commands.py
+++ b/scripts/qapi/commands.py
@@ -283,9 +283,9 @@
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 1787a53..92f584e 100644
--- a/scripts/qapi/doc.py
+++ b/scripts/qapi/doc.py
@@ -243,34 +243,34 @@
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 a98b9f5..b544af5 100644
--- a/scripts/qapi/events.py
+++ b/scripts/qapi/events.py
@@ -189,7 +189,7 @@
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 fecf466..2942520 100644
--- a/scripts/qapi/expr.py
+++ b/scripts/qapi/expr.py
@@ -167,8 +167,9 @@
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 @@
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 @@
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 @@
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 @@
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 b5537ed..23652be 100644
--- a/scripts/qapi/introspect.py
+++ b/scripts/qapi/introspect.py
@@ -16,7 +16,19 @@
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 @@
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 @@
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 @@
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 @@
' * 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 @@
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 @@
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)
+ _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, 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):
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 d759308..78309a00f 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -53,13 +53,13 @@
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 @@
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 @@
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 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 @@
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 @@
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 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 @@
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 @@
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 @@
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)
+ 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)
-class QAPISchemaMember:
- """ Represents object members, enum members and features """
- role = 'member'
+class QAPISchemaAlternateType(QAPISchemaType):
+ meta = 'alternate'
- 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
-
- 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'
-
-
-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 __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):
- assert self.defined_in
- self.type = schema.resolve_type(self._type_name, self.info,
- self.describe)
+ 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
+
+ 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
+
+ def json_type(self):
+ return 'value'
+
+ 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 @@
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 @@
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 @@
v.type.check_clash(info, dict(seen))
-class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
+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
+
+ 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'
+
+
+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):
+ assert self.defined_in
+ self.type = schema.resolve_type(self._type_name, self.info,
+ self.describe)
+ seen = {}
+ for f in self.features:
+ f.check_clash(self.info, seen)
+
+ def connect_doc(self, doc):
+ super().connect_doc(doc)
+ if doc:
+ for f in self.features:
+ doc.connect_feature(f)
+
+
+class QAPISchemaVariant(QAPISchemaObjectTypeMember):
role = 'branch'
def __init__(self, name, info, typ, ifcond=None):
super().__init__(name, info, typ, False, ifcond)
-class QAPISchemaAlternateType(QAPISchemaType):
- meta = 'alternate'
-
- 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
-
- 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
-
- def connect_doc(self, doc=None):
- doc = doc or self.doc
- if doc:
- for v in self.variants.variants:
- doc.connect_member(v)
-
- def c_type(self):
- return c_name(self.name) + pointer_suffix
-
- def json_type(self):
- return 'value'
-
- def visit(self, visitor):
- super().visit(visitor)
- visitor.visit_alternate_type(self.name, self.info, self.ifcond,
- self.variants)
-
-
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 @@
% 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 @@
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 @@
% 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 @@
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 @@
('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 @@
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 @@
# 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 @@
# 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 3c83b6e..3ad33af 100644
--- a/scripts/qapi/types.py
+++ b/scripts/qapi/types.py
@@ -278,7 +278,7 @@
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 @@
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 @@
# 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 421e5bd..23d9194 100644
--- a/scripts/qapi/visit.py
+++ b/scripts/qapi/visit.py
@@ -316,7 +316,7 @@
''',
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 @@
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 @@
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 0000000..c642a5b
--- /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 0000000..9808d69
--- /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 0000000..59e7314
--- /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 0659cee..181ed4a 100644
--- a/scsi/qemu-pr-helper.c
+++ b/scsi/qemu-pr-helper.c
@@ -421,10 +421,13 @@
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 @@
* 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 @@
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 ff2685d..1d33a28 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -3789,6 +3789,22 @@
*/
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 @@
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 @@
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 @@
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 6f0a0ad..08832fa 100644
--- a/target/alpha/cpu-qom.h
+++ b/target/alpha/cpu-qom.h
@@ -44,7 +44,7 @@
/*< 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 3f782c0..be29bdd 100644
--- a/target/alpha/cpu.h
+++ b/target/alpha/cpu.h
@@ -280,7 +280,7 @@
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 7f9cc09..0cd76dd 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 3a9d31e..d95568b 100644
--- a/target/arm/cpu-qom.h
+++ b/target/arm/cpu-qom.h
@@ -51,7 +51,7 @@
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 3623ece..a79f233 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -155,14 +155,14 @@
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 @@
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 @@
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 @@
* 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 @@
&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 4ffd991..8b9f296 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -128,14 +128,20 @@
/**
* 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 @@
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 @@
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 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 1239abd..d9ef7d2 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 @@
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 @@
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 @@
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 @@
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 665ebb3..35d0b80 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 f91e5d5..d2ec2c5 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -48,30 +48,27 @@
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 @@
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 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 @@
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 @@
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 @@
}
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 @@
* 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 @@
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 @@
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 @@
/* 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 @@
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 @@
/*
* 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 72eb9e6..f37b867 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -90,6 +90,7 @@
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 85860e6..390077c 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -874,15 +874,17 @@
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 f703c4f..f271181 100644
--- a/target/arm/kvm32.c
+++ b/target/arm/kvm32.c
@@ -409,17 +409,22 @@
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 93ba144..be5b31c 100644
--- a/target/arm/kvm64.c
+++ b/target/arm/kvm64.c
@@ -1094,17 +1094,22 @@
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 ae9e075..48bf5e1 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 c2dc790..ea6598c 100644
--- a/target/arm/monitor.c
+++ b/target/arm/monitor.c
@@ -206,9 +206,7 @@
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 @@
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 fefe8af..8fffb52 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -228,7 +228,18 @@
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 6259064..9f9f4e1 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -7296,7 +7296,7 @@
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_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 @@
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_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 @@
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 308c1f9..f1de604 100644
--- a/target/cris/cpu-qom.h
+++ b/target/cris/cpu-qom.h
@@ -45,7 +45,7 @@
/*< 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 17c6712..cff6b9e 100644
--- a/target/cris/cpu.c
+++ b/target/cris/cpu.c
@@ -40,15 +40,15 @@
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 @@
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 ca240bc..8f08d76 100644
--- a/target/cris/cpu.h
+++ b/target/cris/cpu.h
@@ -195,8 +195,8 @@
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 a3d76d2..b01b2aa 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 @@
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 6367dc4..b1f6045 100644
--- a/target/hppa/cpu-qom.h
+++ b/target/hppa/cpu-qom.h
@@ -44,7 +44,7 @@
/*< 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 6713d04..801a4fb 100644
--- a/target/hppa/cpu.h
+++ b/target/hppa/cpu.h
@@ -321,7 +321,7 @@
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 341888a..a6428a2 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 0efab2f..3e96f8d 100644
--- a/target/i386/cpu-qom.h
+++ b/target/i386/cpu-qom.h
@@ -71,7 +71,7 @@
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 92fafa2..34b511f 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -1133,7 +1133,7 @@
"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 X86CPUVersionDefinition {
X86CPUVersion version;
const char *alias;
+ const char *note;
PropValue *props;
} X86CPUVersionDefinition;
@@ -1720,6 +1721,7 @@
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_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 @@
.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 @@
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 @@
{ /* 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 @@
.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
*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 @@
}
}
-/* 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 @@
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 @@
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 @@
&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 576f309..60d797d 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -792,6 +792,8 @@
#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 @@
TPRAccess tpr_access_type;
unsigned nr_dies;
+ unsigned nr_nodes;
} CPUX86State;
struct kvm_msrs;
@@ -1766,7 +1769,7 @@
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 572ead6..f3d23b6 100644
--- a/target/i386/gdbstub.c
+++ b/target/i386/gdbstub.c
@@ -79,7 +79,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 @@
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 @@
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 a5426a6..3bad89f 100644
--- a/target/i386/hax-posix.c
+++ b/target/i386/hax-posix.c
@@ -108,41 +108,12 @@
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 5729ad9..0ba488c 100644
--- a/target/i386/hax-windows.c
+++ b/target/i386/hax-windows.c
@@ -185,41 +185,12 @@
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 024bb24..846018a 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -267,109 +267,21 @@
}
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 87d049c..e4695c3 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 @@
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 683d49d..c78baac 100644
--- a/target/i386/whpx-all.c
+++ b/target/i386/whpx-all.c
@@ -114,7 +114,6 @@
WHvX64RegisterXmmControlStatus,
/* X64 MSRs */
- WHvX64RegisterTsc,
WHvX64RegisterEfer,
#ifdef TARGET_X86_64
WHvX64RegisterKernelGsBase,
@@ -215,7 +214,44 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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;
+ cpuid_fn = vcpu->exit_ctx.CpuidAccess.Rax;
- rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
- rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
- break;
+ /*
+ * 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 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 @@
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 @@
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 @@
#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 @@
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 @@
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 dc9ac9a..bdedb37 100644
--- a/target/lm32/cpu-qom.h
+++ b/target/lm32/cpu-qom.h
@@ -44,7 +44,7 @@
/*< 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 687bf35..c50ad5f 100644
--- a/target/lm32/cpu.c
+++ b/target/lm32/cpu.c
@@ -99,14 +99,14 @@
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 @@
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 064c6b1..01d408e 100644
--- a/target/lm32/cpu.h
+++ b/target/lm32/cpu.h
@@ -202,7 +202,7 @@
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 82ede43..b6fe12e 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 b56da8a..88b11b6 100644
--- a/target/m68k/cpu-qom.h
+++ b/target/m68k/cpu-qom.h
@@ -44,7 +44,7 @@
/*< 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 f0653cd..9445fcd 100644
--- a/target/m68k/cpu.c
+++ b/target/m68k/cpu.c
@@ -41,16 +41,16 @@
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 @@
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 3de8e06..521ac67 100644
--- a/target/m68k/cpu.h
+++ b/target/m68k/cpu.h
@@ -168,7 +168,7 @@
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 fdc96f5..eb2d030 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 baf7729..014657c 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -68,23 +68,19 @@
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 @@
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 49b07cc..053ba44 100644
--- a/target/microblaze/cpu-qom.h
+++ b/target/microblaze/cpu-qom.h
@@ -44,7 +44,7 @@
/*< 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 8c90110..a2c2f27 100644
--- a/target/microblaze/cpu.c
+++ b/target/microblaze/cpu.c
@@ -102,14 +102,14 @@
}
#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 @@
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 32522f6..1a700a8 100644
--- a/target/microblaze/cpu.h
+++ b/target/microblaze/cpu.h
@@ -313,7 +313,7 @@
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 30677b6..f41ebf1 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 a430c0f..9d0df6c 100644
--- a/target/mips/cpu-qom.h
+++ b/target/mips/cpu-qom.h
@@ -48,7 +48,7 @@
/*< 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 6cd6b96..e86cd06 100644
--- a/target/mips/cpu.c
+++ b/target/mips/cpu.c
@@ -96,14 +96,14 @@
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 @@
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 bbb2544..98f56e6 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 df55f84..1bf274b 100644
--- a/target/mips/internal.h
+++ b/target/mips/internal.h
@@ -82,7 +82,7 @@
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 cf47bc7..6e0443c 100644
--- a/target/moxie/cpu.c
+++ b/target/moxie/cpu.c
@@ -35,13 +35,14 @@
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 @@
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 01dca54..455553b 100644
--- a/target/moxie/cpu.h
+++ b/target/moxie/cpu.h
@@ -69,7 +69,7 @@
/*< 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 1c0c855..8f7011f 100644
--- a/target/nios2/cpu.c
+++ b/target/nios2/cpu.c
@@ -39,9 +39,9 @@
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 @@
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 @@
#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 @@
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 78f633f..4dddf9c 100644
--- a/target/nios2/cpu.h
+++ b/target/nios2/cpu.h
@@ -50,7 +50,7 @@
/*< 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 5cd04da..5528c09 100644
--- a/target/openrisc/cpu.c
+++ b/target/openrisc/cpu.c
@@ -41,13 +41,13 @@
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 @@
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 0ad02ea..f37a52e 100644
--- a/target/openrisc/cpu.h
+++ b/target/openrisc/cpu.h
@@ -48,7 +48,7 @@
/*< public >*/
DeviceRealize parent_realize;
- void (*parent_reset)(CPUState *cpu);
+ DeviceReset parent_reset;
} OpenRISCCPUClass;
#define TARGET_INSN_START_EXTRA_WORDS 1
@@ -320,7 +320,7 @@
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 0fcdb79..095bf76 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 e499575..000c7d4 100644
--- a/target/ppc/cpu-qom.h
+++ b/target/ppc/cpu-qom.h
@@ -166,7 +166,7 @@
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 @@
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 b283042..88d9449 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 @@
#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 @@
#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 @@
/* 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 @@
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 @@
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 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 027f54c..08bc885 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -128,6 +128,37 @@
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 @@
}
}
#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 @@
}
}
-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 823759c..eb362dd 100644
--- a/target/ppc/gdbstub.c
+++ b/target/ppc/gdbstub.c
@@ -114,10 +114,11 @@
* 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 @@
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 @@
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 @@
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 @@
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 7f44b1a..597f72b 100644
--- a/target/ppc/kvm.c
+++ b/target/ppc/kvm.c
@@ -2113,7 +2113,7 @@
#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 @@
}
}
- 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 9e4f235..332fa0a 100644
--- a/target/ppc/kvm_ppc.h
+++ b/target/ppc/kvm_ppc.h
@@ -47,7 +47,7 @@
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 @@
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 da8966c..34f6009 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 @@
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 @@
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 @@
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 @@
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 @@
*/
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 @@
}
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 @@
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 @@
/* 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];
- }
-
- /* Otherwise, check VPM for RMA vs VRMA */
- if (env->spr[SPR_LPCR] & LPCR_VPM0) {
- slb = &env->vrma_slb;
- if (!slb->sps) {
+ } else if (ppc_hash64_use_vrma(env)) {
+ /* Emulated VRMA mode */
+ slb = &vrma_slbe;
+ if (build_vrma_slbe(cpu, slb) != 0) {
return -1;
}
- } else if (raddr < env->rmls) {
- /* RMA. Check bounds in RMLS */
- return raddr | env->spr[SPR_RMOR];
} else {
- return -1;
+ target_ulong limit = rmls_limit(cpu);
+
+ /* Emulated old-style RMO mode, bounds check against RMLS */
+ if (raddr >= limit) {
+ return -1;
+ }
+ return raddr | env->spr[SPR_RMOR];
}
} else {
slb = slb_lookup(cpu, addr);
@@ -997,168 +1068,12 @@
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 36fa273..127c82a 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -1938,15 +1938,17 @@
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 53995f6..e853164 100644
--- a/target/ppc/translate_init.inc.c
+++ b/target/ppc/translate_init.inc.c
@@ -7895,25 +7895,21 @@
{
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 @@
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 @@
(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 @@
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 @@
/* 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 @@
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 @@
(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 @@
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 @@
/* 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 @@
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 @@
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 @@
(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 @@
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 @@
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 @@
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 @@
(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 @@
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 @@
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 @@
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 @@
(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 @@
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 @@
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 @@
}
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 @@
}
#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 @@
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 @@
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 @@
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 @@
*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 @@
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 @@
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 c0b7023..4e57823 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -330,13 +330,14 @@
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 @@
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 3dcdf92..7d21add 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -234,7 +234,7 @@
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_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 5ea5d13..d3ba9ef 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -46,7 +46,7 @@
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 @@
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 2f32750..eba12a8 100644
--- a/target/riscv/gdbstub.c
+++ b/target/riscv/gdbstub.c
@@ -270,7 +270,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 8736f68..c6412f6 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -85,7 +85,7 @@
}
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 0000000..a0018d5
--- /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 0000000..b156ad1
--- /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 0000000..3e81856
--- /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 0000000..219e053
--- /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 0000000..d1fb1ef
--- /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 0000000..6dee7a0
--- /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 0000000..9391e81
--- /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 0000000..a6a337a
--- /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 0000000..f0b7ebb
--- /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 0000000..232a61f
--- /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 0000000..f89d294
--- /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 0000000..b3d7305
--- /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 dbe5346..1630818 100644
--- a/target/s390x/cpu-qom.h
+++ b/target/s390x/cpu-qom.h
@@ -61,7 +61,7 @@
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 3dd396e..427a46e 100644
--- a/target/s390x/cpu.c
+++ b/target/s390x/cpu.c
@@ -96,8 +96,9 @@
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 @@
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 @@
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 b5aec06..54e5670 100644
--- a/target/s390x/diag.c
+++ b/target/s390x/diag.c
@@ -117,7 +117,7 @@
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 e24a49f..d6fce5f 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 @@
/* 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 @@
/* 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 @@
/* 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 @@
#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 @@
/* 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 @@
/* 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 d378161..8c95c73 100644
--- a/target/s390x/internal.h
+++ b/target/s390x/internal.h
@@ -292,7 +292,7 @@
/* 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 c437a1d..0e840cc 100644
--- a/target/s390x/ioinst.c
+++ b/target/s390x/ioinst.c
@@ -347,7 +347,7 @@
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 0c56d05..72a63f3 100644
--- a/target/sh4/cpu-qom.h
+++ b/target/sh4/cpu-qom.h
@@ -51,7 +51,7 @@
/*< 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 70c8d81..3c68021 100644
--- a/target/sh4/cpu.c
+++ b/target/sh4/cpu.c
@@ -47,14 +47,14 @@
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 @@
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 452a596..dbe58c7 100644
--- a/target/sh4/cpu.h
+++ b/target/sh4/cpu.h
@@ -208,7 +208,7 @@
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 44c1679..49fc4a0 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 7442e27..8b4d33c 100644
--- a/target/sparc/cpu-qom.h
+++ b/target/sparc/cpu-qom.h
@@ -49,7 +49,7 @@
/*< 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 eeaecbd..3f05aba 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 @@
&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 ae97c7d..b936939 100644
--- a/target/sparc/cpu.h
+++ b/target/sparc/cpu.h
@@ -571,7 +571,7 @@
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 8be742b..78dc8dc 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 cd422a0..1fee87c 100644
--- a/target/tilegx/cpu.c
+++ b/target/tilegx/cpu.c
@@ -68,13 +68,14 @@
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 @@
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 9cbec24..193b6bb 100644
--- a/target/tilegx/cpu.h
+++ b/target/tilegx/cpu.h
@@ -118,7 +118,7 @@
/*< 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 7c1e130..cd819e6 100644
--- a/target/tricore/cpu-qom.h
+++ b/target/tricore/cpu-qom.h
@@ -36,7 +36,7 @@
/*< 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 85bc9f0..743b404 100644
--- a/target/tricore/cpu.c
+++ b/target/tricore/cpu.c
@@ -53,13 +53,14 @@
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 @@
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 9ac5424..3ea93ce 100644
--- a/target/xtensa/cpu-qom.h
+++ b/target/xtensa/cpu-qom.h
@@ -56,7 +56,7 @@
/*< 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 4856aee..82c2ee0 100644
--- a/target/xtensa/cpu.c
+++ b/target/xtensa/cpu.c
@@ -67,14 +67,14 @@
}
#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 @@
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 493f4fc..c0d69fa 100644
--- a/target/xtensa/cpu.h
+++ b/target/xtensa/cpu.h
@@ -569,7 +569,7 @@
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 5472788..0ee3fea 100644
--- a/target/xtensa/gdbstub.c
+++ b/target/xtensa/gdbstub.c
@@ -63,7 +63,7 @@
}
}
-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 cdedcb2..223dba9 100644
--- a/tcg/i386/tcg-target.inc.c
+++ b/tcg/i386/tcg-target.inc.c
@@ -3391,12 +3391,15 @@
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 7306866..d03c037 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -10,6 +10,7 @@
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 edcbd47..51de676 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -20,6 +20,8 @@
@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-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 @@
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 @@
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 @@
# 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 d4358eb..59e7b4f 100644
--- a/tests/acceptance/avocado_qemu/__init__.py
+++ b/tests/acceptance/avocado_qemu/__init__.py
@@ -16,8 +16,21 @@
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 @@
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 @@
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 0000000..075a386
--- /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 34d37eb..f825cd9 100644
--- a/tests/acceptance/boot_linux_console.py
+++ b/tests/acceptance/boot_linux_console.py
@@ -16,10 +16,17 @@
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 @@
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 0000000..bfa0d33
--- /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 3b860af..d4849f5 100644
--- a/tests/docker/dockerfiles/debian-amd64.docker
+++ b/tests/docker/dockerfiles/debian-amd64.docker
@@ -17,6 +17,7 @@
libbz2-dev \
liblzo2-dev \
librdmacm-dev \
+ libsasl2-dev \
libsnappy-dev \
libvte-dev
@@ -27,9 +28,9 @@
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 9d7053e..d16d643 100644
--- a/tests/docker/dockerfiles/debian-win32-cross.docker
+++ b/tests/docker/dockerfiles/debian-win32-cross.docker
@@ -9,7 +9,7 @@
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 5de79ae..2fcdc40 100644
--- a/tests/docker/dockerfiles/debian10.docker
+++ b/tests/docker/dockerfiles/debian10.docker
@@ -17,14 +17,17 @@
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 8cbd742..92edbbf 100644
--- a/tests/docker/dockerfiles/debian9.docker
+++ b/tests/docker/dockerfiles/debian9.docker
@@ -17,13 +17,16 @@
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 0000000..8c49ee2
--- /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 31ebe56..970a08a 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 d992e71..ddd89d1 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 4c9406a..6757dd2 100644
--- a/tests/qapi-schema/doc-good.out
+++ b/tests/qapi-schema/doc-good.out
@@ -15,11 +15,13 @@
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 @@
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 @@
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 @@
feature cmd-feat2
event EVT-BOXED Object
boxed=True
+ feature feat3
doc freeform
body=
= Section
@@ -112,6 +118,8 @@
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 @@
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 @@
@b is undocumented
arg=b
+ feature=alt-feat
+a feature
doc freeform
body=
== Another subsection
@@ -197,3 +213,5 @@
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 d4b15da..7f28fb7 100644
--- a/tests/qapi-schema/doc-good.texi
+++ b/tests/qapi-schema/doc-good.texi
@@ -88,6 +88,12 @@
@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 @@
@table @asis
@item @code{variant1-feat}
a feature
+@item @code{member-feat}
+a member feature
@end table
@end deftp
@@ -151,6 +159,12 @@
@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 @@
@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 @@
Not documented
@end table
+@b{Features:}
+@table @asis
+@item @code{alt-feat}
+a feature
+@end table
+
@end deftp
@@ -283,5 +309,11 @@
@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 0000000..af4ffe2
--- /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 0000000..4b5bf5b
--- /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 0000000..e69de29
--- /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 9abf175..6b1f05a 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 9bd3c4a..891b410 100644
--- a/tests/qapi-schema/qapi-schema-test.out
+++ b/tests/qapi-schema/qapi-schema-test.out
@@ -359,6 +359,7 @@
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 @@
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 @@
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 @@
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 bee18ee..f396b47 100755
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -30,7 +30,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 d94ad22..fd11aae 100644
--- a/tests/qemu-iotests/085.out
+++ b/tests/qemu-iotests/085.out
@@ -82,7 +82,7 @@
=== 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 @@
=== 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 f237868..571bce9 100755
--- a/tests/qemu-iotests/155
+++ b/tests/qemu-iotests/155
@@ -45,10 +45,18 @@
# 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 @@
'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 @@
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 @@
# 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 @@
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 @@
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 @@
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 4176bb9..ed714d5 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 51a70fe..7cf0e27 100755
--- a/tests/qemu-iotests/178
+++ b/tests/qemu-iotests/178
@@ -50,7 +50,7 @@
$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 9e7d8c4..f59bf4b 100644
--- a/tests/qemu-iotests/178.out.qcow2
+++ b/tests/qemu-iotests/178.out.qcow2
@@ -5,10 +5,10 @@
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 6478365..404ca90 100644
--- a/tests/qemu-iotests/178.out.raw
+++ b/tests/qemu-iotests/178.out.raw
@@ -5,10 +5,10 @@
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 0000000..081eb12
--- /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 0000000..5d079da
--- /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 0000000..6c62065
--- /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 0000000..4bc593d
--- /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 8a6366c..4c246c0 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -56,18 +56,30 @@
# 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 0317667..ec2b230 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 8815052..23043ba 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -624,7 +624,10 @@
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 383b0ab..10a28de 100644
--- a/tests/qtest/Makefile.include
+++ b/tests/qtest/Makefile.include
@@ -18,7 +18,8 @@
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-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 1a99277..af28c92 100644
--- a/tests/qtest/fuzz/qos_fuzz.c
+++ b/tests/qtest/fuzz/qos_fuzz.c
@@ -55,10 +55,8 @@
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 @@
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 a2498005..48e8e02 100644
--- a/tests/qtest/hd-geo-test.c
+++ b/tests/qtest/hd-geo-test.c
@@ -421,7 +421,7 @@
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 @@
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 f05b3e5..44ab110 100644
--- a/tests/qtest/libqos/ahci.h
+++ b/tests/qtest/libqos/ahci.h
@@ -351,7 +351,7 @@
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 d9c4c22..49bd72d 100644
--- a/tests/qtest/libqos/libqos-spapr.h
+++ b/tests/qtest/libqos/libqos-spapr.h
@@ -12,7 +12,6 @@
"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 5953d31..8bb54a6 100644
--- a/tests/qtest/machine-none-test.c
+++ b/tests/qtest/machine-none-test.c
@@ -56,6 +56,7 @@
{ "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 f4f1736..f9c84b4 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 8ed477d..d99b2a9 100644
--- a/tests/tcg/aarch64/Makefile.target
+++ b/tests/tcg/aarch64/Makefile.target
@@ -42,4 +42,36 @@
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 0000000..984fbeb
--- /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 0000000..dbe7f2a
--- /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 0000000..9544dff
--- /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 0000000..40cf8d2
--- /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 e1f8670..b6726cf 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_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 @@
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 @@
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 @@
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 @@
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 @@
;
}
- 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);
- g_assert_cmpint(off1, ==, off2);
- g_assert_cmpint(len1, ==, len2);
+
+ if (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 @@
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 @@
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 @@
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 @@
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 79507d9..d12ff47 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_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_command_features0(Error **errp)
-{
-}
-
void qmp_test_command_features1(Error **errp)
{
}
@@ -145,66 +142,87 @@
}
-/* 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 do_qmp_dispatch_error(bool allow_oob, ErrorClass cls,
+ const char *template, ...)
+{
+ 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 with no input and no return value */
+static void test_dispatch_cmd(void)
+{
+ QDict *ret;
+
+ ret = qobject_to(QDict,
+ do_qmp_dispatch(false,
+ "{ 'execute': 'user_def_cmd' }"));
+ assert(ret && qdict_size(ret) == 0);
+ qobject_unref(ret);
+}
+
static void test_dispatch_cmd_oob(void)
{
- QDict *req = qdict_new();
- QDict *resp;
+ QDict *ret;
- 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"));
-
- qobject_unref(resp);
- qobject_unref(req);
+ ret = qobject_to(QDict,
+ do_qmp_dispatch(true,
+ "{ 'exec-oob': 'test-flags-command' }"));
+ assert(ret && qdict_size(ret) == 0);
+ qobject_unref(ret);
}
/* test commands that return an error due to invalid parameters */
static void test_dispatch_cmd_failure(void)
{
- QDict *req = qdict_new();
- QDict *args = qdict_new();
- QDict *resp;
+ /* missing arguments */
+ do_qmp_dispatch_error(false, ERROR_CLASS_GENERIC_ERROR,
+ "{ 'execute': 'user_def_cmd2' }");
- 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);
-
- /* check that with extra arguments it throws an error */
- req = qdict_new();
- qdict_put_int(args, "a", 66);
- qdict_put(req, "arguments", args);
-
- qdict_put_str(req, "execute", "user_def_cmd");
-
- resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false);
- assert(resp != NULL);
- assert(qdict_haskey(resp, "error"));
-
- qobject_unref(resp);
- qobject_unref(req);
+ /* 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 @@
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 @@
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 eee7e08..7dd0053 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 @@
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 @@
/* 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 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 = { .integer = 2, .string = (char *)"test1" };
- 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);
-
+ 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;
+ 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,
+ };
- 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);
-
+ 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 1942287..049030a 100644
--- a/tests/test-x86-cpuid.c
+++ b/tests/test-x86-cpuid.c
@@ -28,79 +28,101 @@
static void test_topo_bits(void)
{
- /* 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);
+ X86CPUTopoInfo 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);
+ /* simple tests for 1 thread per core, 1 core per die, 1 die per package */
+ 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);
+
+ 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);
+ 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);
- 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);
+ 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);
- 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);
+ 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);
- 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, 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);
+ 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);
- 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);
+ 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);
- g_assert_cmpuint(x86_apicid_from_cpu_idx(1, 6, 3, 1 * 3 + 0), ==,
+ 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 179901c..184e173 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1299,8 +1299,8 @@
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 3a1b714..a59b23a 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -54,13 +54,13 @@
};
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_atexit(void)
{
endwin();
+ g_free(vga_to_curses);
+ g_free(screen);
}
/*
@@ -529,7 +531,7 @@
* 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 @@
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 6b38b67..6718a38 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -5,6 +5,9 @@
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 9e1befc..cd6cf0a 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
+/* Stop userspace polling on a handler if it isn't active for some time */
+#define POLL_IDLE_INTERVAL_NS (7 * NANOSECONDS_PER_SECOND)
+
+bool aio_poll_disabled(AioContext *ctx)
{
- 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;
-};
+ return atomic_read(&ctx->poll_disable_cnt);
+}
-/* Add a handler to a ready list */
-static void add_ready_handler(AioHandlerList *ready_list,
- AioHandler *node,
- int revents)
+void aio_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;
-}
-
-static bool aio_epoll_enabled(AioContext *ctx)
-{
- /* Fall back to ppoll when external clients are disabled. */
- return !aio_external_disabled(ctx) && ctx->epoll_enabled;
-}
-
-static bool aio_epoll_check_poll(AioContext *ctx, GPollFD *pfds,
- unsigned npfd, int64_t timeout)
-{
- 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;
-}
-
-#endif
-
static AioHandler *find_aio_handler(AioContext *ctx, int fd)
{
AioHandler *node;
@@ -231,16 +65,23 @@
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 @@
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 @@
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 @@
(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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
}
}
- 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 @@
/* 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 @@
}
}
- /* 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 @@
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 0000000..c80c045
--- /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 bfb2605..6639035 100644
--- a/util/bufferiszero.c
+++ b/util/bufferiszero.c
@@ -63,11 +63,11 @@
}
}
-#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 @@
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 @@
#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 @@
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 @@
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 @@
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 @@
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 0000000..fcd989d
--- /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 0000000..893b79b
--- /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 0000000..488067b
--- /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 242c6e5..305b894 100644
--- a/util/hbitmap.c
+++ b/util/hbitmap.c
@@ -104,7 +104,7 @@
/* 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 @@
}
}
-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 @@
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 @@
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);
+
+ next_zero = hbitmap_next_zero(hb, start, end - start);
+ if (next_zero >= 0) {
+ end = next_zero;
}
- if (firt_dirty_off > *start) {
- *start = firt_dirty_off;
- }
- *count = area_end - *start;
+ *dirty_start = start;
+ *dirty_count = end - start;
return true;
}
@@ -298,6 +316,35 @@
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 *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 @@
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 @@
*/
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 @@
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 236a7bb..5f78968 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 @@
#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 @@
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 897e8f3..4dd6d7d 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -466,10 +466,17 @@
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 ef52d28..d548d3c 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 @@
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 @@
* 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 83b6639..0ce4282 100644
--- a/util/trace-events
+++ b/util/trace-events
@@ -5,6 +5,8 @@
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"