From 3cb3cd310d74c4eacc83d0b907302d85f40f3424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 10:54:16 +0100 Subject: [PATCH 01/76] tests/.gitignore: don't ignore docker tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .gitignore was being a little over enthusiastic hiding files. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- tests/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/.gitignore b/tests/.gitignore index 08e2df1ce1f..72c18aaab04 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -9,6 +9,7 @@ qht-bench rcutorture test-* !test-*.c +!docker/test-* test-qapi-commands.[ch] test-qapi-events.[ch] test-qapi-types.[ch] From 2d9bf2454ea84f3f7d6a0147ae8244754f79b08c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 12:20:19 +0100 Subject: [PATCH 02/76] docker: base debian-tricore on qemu:debian9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need both git and a working compiler to build the tools. Although the qemu:debian9 image also has a bunch of extra dependencies it would be fairly unusual for a user not to already have this layer available for one of our many other docker images so lets not complicate things. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- tests/docker/dockerfiles/debian-tricore-cross.docker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker index 898b8dd511d..180ca646c83 100644 --- a/tests/docker/dockerfiles/debian-tricore-cross.docker +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -7,7 +7,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later # -FROM debian:9 +FROM qemu:debian9 MAINTAINER Philippe Mathieu-Daudé From 4c5e2174aa3cabd8ba7d85031200f20719bec6e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 13 Jul 2018 16:02:31 +0100 Subject: [PATCH 03/76] docker: par down QEMU_CONFIGURE_OPTS in debian-tricore-cross MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This image isn't going to build anything significant as it is just intended for building test cases. In case it does end up getting inadvertently included in a build lets aim for the minimal possible product. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- tests/docker/dockerfiles/debian-tricore-cross.docker | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker index 180ca646c83..4a0f7706a39 100644 --- a/tests/docker/dockerfiles/debian-tricore-cross.docker +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -19,5 +19,5 @@ RUN git clone --single-branch \ make && make install && \ rm -rf /usr/src/binutils -# Specify the cross prefix for this image (see tests/docker/common.rc) -ENV QEMU_CONFIGURE_OPTS --cross-prefix=tricore- +# This image isn't designed for building QEMU but building tests +ENV QEMU_CONFIGURE_OPTS --disable-system --disable-user From 1c2416bc27ea262c7dfaf004148af8324aabd423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 14:08:25 +0100 Subject: [PATCH 04/76] docker: fail more gracefully on docker.py check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As this is called directly from the Makefile while determining dependencies and it is possible the user was configured in one window but not have credentials in the other. Let's catch the Exceptions and deal with it quietly. Signed-off-by: Alex Bennée Reported-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé --- tests/docker/docker.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 69e7130db7c..2f81c6b13b9 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -479,7 +479,12 @@ def args(self, parser): def run(self, args, argv): tag = args.tag - dkr = Docker() + try: + dkr = Docker() + except: + print("Docker not set up") + return 1 + info = dkr.inspect_tag(tag) if info is None: print("Image does not exist") From f72927aac228c4c38d868de5be8cf2d012a3fe3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 10:58:34 +0100 Subject: [PATCH 05/76] docker: split configure_qemu from build_qemu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows some tests that just want to configure QEMU's source tree to do so. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/common.rc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/docker/common.rc b/tests/docker/common.rc index 046f8a59210..ba1f942328a 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -21,7 +21,7 @@ requires() done } -build_qemu() +configure_qemu() { config_opts="--enable-werror \ ${TARGET_LIST:+--target-list=${TARGET_LIST}} \ @@ -32,6 +32,11 @@ build_qemu() echo $config_opts $QEMU_SRC/configure $config_opts || \ { cat config.log && test_fail "Failed to run 'configure'"; } +} + +build_qemu() +{ + configure_qemu $@ make $MAKEFLAGS } From 4210a7ff508857f1a5e47b535d3005cf012a1028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 13:27:54 +0100 Subject: [PATCH 06/76] docker: move make check into check_qemu helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not all docker images can run the check step. Let's move everything into a common helper so we don't need to replicate checks in the future. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/common.rc | 11 +++++++++++ tests/docker/test-clang | 2 +- tests/docker/test-debug | 2 +- tests/docker/test-full | 2 +- tests/docker/test-quick | 2 +- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/docker/common.rc b/tests/docker/common.rc index ba1f942328a..4ff5974016a 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -40,6 +40,17 @@ build_qemu() make $MAKEFLAGS } +check_qemu() +{ + # default to make check unless the caller specifies + if test -z "$@"; then + INVOCATION="check" + else + INVOCATION="$@" + fi + make $MAKEFLAGS $INVOCATION +} + test_fail() { echo "$@" diff --git a/tests/docker/test-clang b/tests/docker/test-clang index e90a793178c..324e341cea9 100755 --- a/tests/docker/test-clang +++ b/tests/docker/test-clang @@ -23,5 +23,5 @@ OPTS="--cxx=clang++ --cc=clang --host-cc=clang" #OPTS="$OPTS --extra-cflags=-fsanitize=undefined \ #--extra-cflags=-fno-sanitize=float-divide-by-zero" build_qemu $OPTS -make $MAKEFLAGS check +check_qemu install_qemu diff --git a/tests/docker/test-debug b/tests/docker/test-debug index d3f9f70d01f..137f4f2ddc1 100755 --- a/tests/docker/test-debug +++ b/tests/docker/test-debug @@ -22,5 +22,5 @@ OPTS="--cxx=clang++ --cc=clang --host-cc=clang" OPTS="--enable-debug --enable-sanitizers $OPTS" build_qemu $OPTS -make $MAKEFLAGS V=1 check +check_qemu check V=1 install_qemu diff --git a/tests/docker/test-full b/tests/docker/test-full index b4e42d25d7a..aadc0f00a2a 100755 --- a/tests/docker/test-full +++ b/tests/docker/test-full @@ -15,4 +15,4 @@ cd "$BUILD_DIR" -build_qemu && make check $MAKEFLAGS && install_qemu +build_qemu && check_qemu && install_qemu diff --git a/tests/docker/test-quick b/tests/docker/test-quick index 3b7bce6105d..eee59c55fba 100755 --- a/tests/docker/test-quick +++ b/tests/docker/test-quick @@ -18,5 +18,5 @@ cd "$BUILD_DIR" DEF_TARGET_LIST="x86_64-softmmu,aarch64-softmmu" TARGET_LIST=${TARGET_LIST:-$DEF_TARGET_LIST} \ build_qemu -make check $MAKEFLAGS +check_qemu install_qemu From 2b64400444df9e0920018808fe59f9fa86561e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 14:27:47 +0100 Subject: [PATCH 07/76] docker: gracefully skip check_qemu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not all our images are able to run the tests. Rather than use features we can just check for the existence and run-ability of gtester. If the image has been setup for binfmt_misc it will be able to run anyway. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/common.rc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/docker/common.rc b/tests/docker/common.rc index 4ff5974016a..4011561587a 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -48,7 +48,13 @@ check_qemu() else INVOCATION="$@" fi - make $MAKEFLAGS $INVOCATION + + if command -v gtester > /dev/null 2>&1 && \ + gtester --version > /dev/null 2>&1; then + make $MAKEFLAGS $INVOCATION + else + echo "No working gtester, skipping make $INVOCATION" + fi } test_fail() From ce06c4c6c9c11deaae379eaba8d36ff9fb4dd062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 11:51:57 +0100 Subject: [PATCH 08/76] docker: Makefile.include don't include partial images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename DOCKER_INTERMEDIATE_IMAGES to DOCKER_PARTIAL_IMAGES and add the incomplete cross compiler images that can build tests but can't build QEMU itself. We also add debian, debian-bootstrap and the tricode images to the list. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index b2a7e761cc5..09fb7db7fab 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -6,7 +6,7 @@ DOCKER_SUFFIX := .docker DOCKER_FILES_DIR := $(SRC_PATH)/tests/docker/dockerfiles DOCKER_DEPRECATED_IMAGES := debian # we don't run tests on intermediate images (used as base by another image) -DOCKER_INTERMEDIATE_IMAGES := debian8 debian9 debian8-mxe debian-ports debian-sid +DOCKER_PARTIAL_IMAGES := debian debian8 debian9 debian8-mxe debian-ports debian-sid debian-bootstrap DOCKER_IMAGES := $(filter-out $(DOCKER_DEPRECATED_IMAGES),$(sort $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker))))) DOCKER_TARGETS := $(patsubst %,docker-image-%,$(DOCKER_IMAGES)) # Use a global constant ccache directory to speed up repetitive builds @@ -121,6 +121,11 @@ docker-image-travis: NOUSER=1 # Specialist build images, sometimes very limited tools docker-image-tricore-cross: docker-image-debian9 +# These images may be good enough for building tests but not for test builds +DOCKER_PARTIAL_IMAGES += debian-alpha-cross debian-hppa-cross debian-m68k-cross debian-sh4-cross +DOCKER_PARTIAL_IMAGES += debian-sparc64-cross debian-mips64-cross debian-riscv64-cross +DOCKER_PARTIAL_IMAGES += debian-tricore-cross debian-powerpc-cross fedora-i386-cross + # Rules for building linux-user powered images # # These are slower than using native cross compiler setups but can @@ -137,7 +142,7 @@ docker-image-debian-powerpc-user-cross: docker-binfmt-image-debian-powerpc-user DOCKER_USER_IMAGES += debian-powerpc-user # Expand all the pre-requistes for each docker image and test combination -$(foreach i,$(filter-out $(DOCKER_INTERMEDIATE_IMAGES),$(DOCKER_IMAGES) $(DOCKER_DEPRECATED_IMAGES)), \ +$(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES),$(DOCKER_IMAGES) $(DOCKER_DEPRECATED_IMAGES)), \ $(foreach t,$(DOCKER_TESTS) $(DOCKER_TOOLS), \ $(eval .PHONY: docker-$t@$i) \ $(eval docker-$t@$i: docker-image-$i docker-run-$t@$i) \ From bbf71719781bd51f9105c94f217a10cf80e1ef5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 10:55:34 +0100 Subject: [PATCH 09/76] docker: add test-unit runner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test doesn't even build QEMU, it just builds and runs all the unit tests. Intended to make checking unit tests on all docker images easier. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/test-unit | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100755 tests/docker/test-unit diff --git a/tests/docker/test-unit b/tests/docker/test-unit new file mode 100755 index 00000000000..8905d011507 --- /dev/null +++ b/tests/docker/test-unit @@ -0,0 +1,21 @@ +#!/bin/bash -e +# +# Build and run the unit tests +# +# Copyright (c) 2018 Linaro Ltd. +# +# Authors: +# Alex Bennée +# +# This work is licensed under the terms of the GNU GPL, version 2 +# or (at your option) any later version. See the COPYING file in +# the top-level directory. + +. common.rc + +cd "$BUILD_DIR" + +# although we are not building QEMU itself we still need a configured +# build for the unit tests to be built and run +configure_qemu +check_qemu check-unit From a792c988d62db0995ffb9b6134e0dc51d6661f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 9 Jul 2018 13:24:52 +0100 Subject: [PATCH 10/76] docker: add expansion for docker-test-FOO to Makefile.include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows us to run a particular test on all docker images. For example: make docker-test-unit Will run the unit tests on every supported image. At the same time rename docker-test to docker-all-tests to be clearer. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 09fb7db7fab..8fbb076396b 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -148,7 +148,8 @@ $(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES),$(DOCKER_IMAGES) $(DOCKER_DEPR $(eval docker-$t@$i: docker-image-$i docker-run-$t@$i) \ ) \ $(foreach t,$(DOCKER_TESTS), \ - $(eval docker-test: docker-$t@$i) \ + $(eval docker-all-tests: docker-$t@$i) \ + $(eval docker-$t: docker-$t@$i) \ ) \ ) @@ -158,7 +159,8 @@ docker: @echo 'Available targets:' @echo @echo ' docker: Print this help.' - @echo ' docker-test: Run all image/test combinations.' + @echo ' docker-all-tests: Run all image/test combinations.' + @echo ' docker-TEST: Run TEST on all image combinations.' @echo ' docker-clean: Kill and remove residual docker testing containers.' @echo ' docker-TEST@IMAGE: Run "TEST" in container "IMAGE".' @echo ' Note: "TEST" is one of the listed test name,' From c6557419dd2a66ddecdccc11b5d5fb41c94b8798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 12 Jul 2018 10:35:54 +0100 Subject: [PATCH 11/76] docker: drop QEMU_TARGET check, fallback in EXECUTABLE not set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The addition of QEMU_TARGET was intended to ensure we fall back to checking for the existence of an image if the build system was not currently configured to build it. However this breaks the direct use of the rule for building custom binfmt_misc images. We already check for EXECUTABLE so let us just use that as a proxy for deciding if we are just going to check the image exits. Signed-off-by: Alex Bennée Tested-by: Philippe Mathieu-Daudé --- tests/docker/Makefile.include | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 8fbb076396b..05afeb64a75 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -58,13 +58,11 @@ docker-image-%: $(DOCKER_FILES_DIR)/%.docker docker-binfmt-image-debian-%: $(DOCKER_FILES_DIR)/debian-bootstrap.docker $(if $(EXECUTABLE),,\ $(error EXECUTABLE not set, debootstrap of debian-$* would fail)) - $(if $(wildcard $(EXECUTABLE)),,\ - $(error Please build $(EXECUTABLE) first)) $(if $(DEB_ARCH),,\ $(error DEB_ARCH not set, debootstrap of debian-$* would fail)) $(if $(DEB_TYPE),,\ $(error DEB_TYPE not set, debootstrap of debian-$* would fail)) - $(if $(filter $(QEMU_TARGET),$(TARGET_DIRS)), \ + $(if $(wildcard $(EXECUTABLE)), \ $(call quiet-command, \ DEB_ARCH=$(DEB_ARCH) \ DEB_TYPE=$(DEB_TYPE) \ @@ -136,7 +134,6 @@ DOCKER_PARTIAL_IMAGES += debian-tricore-cross debian-powerpc-cross fedora-i386-c # broken so we need a qemu-linux-user for this target docker-binfmt-image-debian-powerpc-user: DEB_ARCH = powerpc docker-binfmt-image-debian-powerpc-user: DEB_TYPE = jessie -docker-binfmt-image-debian-powerpc-user: QEMU_TARGET = ppc-linux-user docker-binfmt-image-debian-powerpc-user: EXECUTABLE = ${BUILD_DIR}/ppc-linux-user/qemu-ppc docker-image-debian-powerpc-user-cross: docker-binfmt-image-debian-powerpc-user DOCKER_USER_IMAGES += debian-powerpc-user From 87066531f277bf3c983e44f52afe3f8c5a57614f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 12 Jul 2018 14:36:18 +0100 Subject: [PATCH 12/76] docker: report hint when docker.py check fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a check fails we currently just report why we failed. This is not totally helpful to people who want to boot-strap a new image. Report a hint as to why it failed. Signed-off-by: Alex Bennée Suggested-by: Fam Zheng --- tests/docker/Makefile.include | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 05afeb64a75..1aaa7957436 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -73,7 +73,8 @@ docker-binfmt-image-debian-%: $(DOCKER_FILES_DIR)/debian-bootstrap.docker $(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)), \ "BUILD","binfmt debian-$* (debootstrapped)"), \ $(call quiet-command, \ - $(DOCKER_SCRIPT) check --quiet qemu:debian-$* $<, \ + $(DOCKER_SCRIPT) check --quiet qemu:debian-$* $< || \ + { echo "You will need to build $(EXECUTABLE)"; exit 1;},\ "CHECK", "debian-$* exists")) endif From a4af82a699eaaf60c0367c6729fd1a5df9754566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 3 Jul 2018 12:35:30 -0300 Subject: [PATCH 13/76] docker: Update debootstrap script after Debian migration from Alioth to Salsa MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This silents the following warning: Cloning into './debootstrap.git'... warning: redirecting to https://salsa.debian.org/installer-team/debootstrap.git/ See https://lists.debian.org/debian-devel-announce/2018/01/msg00004.html Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée --- tests/docker/dockerfiles/debian-bootstrap.pre | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/dockerfiles/debian-bootstrap.pre b/tests/docker/dockerfiles/debian-bootstrap.pre index 56e1aa7a214..ea324d6e4a7 100755 --- a/tests/docker/dockerfiles/debian-bootstrap.pre +++ b/tests/docker/dockerfiles/debian-bootstrap.pre @@ -62,7 +62,7 @@ if [ -z $DEBOOTSTRAP_DIR ]; then NEED_DEBOOTSTRAP=true fi if $NEED_DEBOOTSTRAP; then - DEBOOTSTRAP_SOURCE=https://anonscm.debian.org/git/d-i/debootstrap.git + DEBOOTSTRAP_SOURCE=https://salsa.debian.org/installer-team/debootstrap.git git clone ${DEBOOTSTRAP_SOURCE} ./debootstrap.git export DEBOOTSTRAP_DIR=./debootstrap.git DEBOOTSTRAP=./debootstrap.git/debootstrap From d879235c45115840adeae9d55eff0a755053ad8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 13 Jul 2018 11:56:49 +0100 Subject: [PATCH 14/76] docker: add commentary to debian-bootstrap.docker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is just a note that later versions of debootstrap don't technically need this hack. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé --- tests/docker/dockerfiles/debian-bootstrap.docker | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/docker/dockerfiles/debian-bootstrap.docker b/tests/docker/dockerfiles/debian-bootstrap.docker index 14212b9cf46..e13c26a7eda 100644 --- a/tests/docker/dockerfiles/debian-bootstrap.docker +++ b/tests/docker/dockerfiles/debian-bootstrap.docker @@ -9,6 +9,7 @@ FROM scratch ADD . / # Patch all mounts as docker already has stuff set up +# (this is not needed for later debootstraps but is harmless atm) RUN sed -i 's/in_target mount/echo not for docker in_target mount/g' /debootstrap/functions # Run stage 2 From 36048c01e4a7e0bdf729527d5cd16dc022138685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 13 Jul 2018 12:23:14 +0100 Subject: [PATCH 15/76] docker: ignore distro versioning of debootstrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We do a minimum version check for the debootstrap but if the distro has added their own minor version tick it would fail and fall-back to the SCM version. This is sub-optimal as the latest/greatest version may be broken at any one particular time. We fix that with a little sed magic on the version string before passing to our ugly shell versioning check. Signed-off-by: Alex Bennée Tested-by: Philippe Mathieu-Daudé --- tests/docker/dockerfiles/debian-bootstrap.pre | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/docker/dockerfiles/debian-bootstrap.pre b/tests/docker/dockerfiles/debian-bootstrap.pre index ea324d6e4a7..3b0ef953747 100755 --- a/tests/docker/dockerfiles/debian-bootstrap.pre +++ b/tests/docker/dockerfiles/debian-bootstrap.pre @@ -56,10 +56,13 @@ if [ -z $DEBOOTSTRAP_DIR ]; then if [ -z $DEBOOTSTRAP ]; then echo "No debootstrap installed, attempting to install from SCM" NEED_DEBOOTSTRAP=true - elif ! (echo "${MIN_DEBOOTSTRAP_VERSION}" ; "${DEBOOTSTRAP}" --version \ - | cut -d ' ' -f 2) | sort -t . -n -k 1,1 -k 2,2 -k 3,3 -c &>/dev/null; then - echo "debootstrap too old, attempting to install from SCM" - NEED_DEBOOTSTRAP=true + else + INSTALLED_VERSION=$(${DEBOOTSTRAP} --version | sed 's/debootstrap \([0-9\.]*\)[^0-9\.]*.*/\1/') + if ! (echo "${MIN_DEBOOTSTRAP_VERSION}" ; echo "${INSTALLED_VERSION}") \ + | sort -t . -n -k 1,1 -k 2,2 -k 3,3 -C ; then + echo "debootstrap too old, attempting to install from SCM" + NEED_DEBOOTSTRAP=true + fi fi if $NEED_DEBOOTSTRAP; then DEBOOTSTRAP_SOURCE=https://salsa.debian.org/installer-team/debootstrap.git From b8c7544c14365b493b2b337261f7d6fec8cdcbc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 17 Jul 2018 17:11:26 +0100 Subject: [PATCH 16/76] docker: perform basic binfmt_misc validation in docker.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting up binfmt_misc is outside of the scope of the docker.py script but we can at least validate it with any given executable so we have a more useful error message than the sed line of deboostrap failing cryptically. Signed-off-by: Alex Bennée Reported-by: Richard Henderson --- tests/docker/docker.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 2f81c6b13b9..d3006d4dae1 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -112,6 +112,31 @@ def _copy_binary_with_libs(src, dest_dir): so_path = os.path.dirname(l) _copy_with_mkdir(l , dest_dir, so_path) + +def _check_binfmt_misc(executable): + """Check binfmt_misc has entry for executable in the right place. + + The details of setting up binfmt_misc are outside the scope of + this script but we should at least fail early with a useful + message if it won't work.""" + + binary = os.path.basename(executable) + binfmt_entry = "/proc/sys/fs/binfmt_misc/%s" % (binary) + + if not os.path.exists(binfmt_entry): + print ("No binfmt_misc entry for %s" % (binary)) + return False + + with open(binfmt_entry) as x: entry = x.read() + + qpath = "/usr/bin/%s" % (binary) + if not re.search("interpreter %s\n" % (qpath), entry): + print ("binfmt_misc for %s does not point to %s" % (binary, qpath)) + return False + + return True + + def _read_qemu_dockerfile(img_name): # special case for Debian linux-user images if img_name.startswith("debian") and img_name.endswith("user"): @@ -315,6 +340,11 @@ def run(self, args, argv): # Create a docker context directory for the build docker_dir = tempfile.mkdtemp(prefix="docker_build") + # Validate binfmt_misc will work + if args.include_executable: + if not _check_binfmt_misc(args.include_executable): + return 1 + # Is there a .pre file to run in the build context? docker_pre = os.path.splitext(args.dockerfile)[0]+".pre" if os.path.exists(docker_pre): From 3c1aed814c3ae5a4593704d3d1595714ed67d6ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 17 Jul 2018 17:17:53 +0100 Subject: [PATCH 17/76] tests/tcg: remove runcom test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The combination of being rather esoteric and needing to support mmap @ 0 means this only ever worked under translation. It has now regressed even further and is no longer useful. Kill it. Signed-off-by: Alex Bennée Reviewed-by: Peter Maydell --- tests/tcg/i386/Makefile.target | 5 - tests/tcg/i386/README | 3 - tests/tcg/i386/pi_10.com | Bin 54 -> 0 bytes tests/tcg/i386/runcom.c | 192 --------------------------------- 4 files changed, 200 deletions(-) delete mode 100644 tests/tcg/i386/pi_10.com delete mode 100644 tests/tcg/i386/runcom.c diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index c1997a1624d..b4033ba3d1b 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -29,11 +29,6 @@ test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S test-i386.h test-i386 $(CC) $(CFLAGS) $(LDFLAGS) -o $@ \ $( -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -extern int vm86 (unsigned long int subfunction, - struct vm86plus_struct *info); - -#define VIF_MASK 0x00080000 - -//#define SIGTEST - -#define COM_BASE_ADDR 0x10100 - -static void usage(void) -{ - printf("runcom version 0.1 (c) 2003 Fabrice Bellard\n" - "usage: runcom file.com\n" - "VM86 Run simple .com DOS executables (linux vm86 test mode)\n"); - exit(1); -} - -static inline void set_bit(uint8_t *a, unsigned int bit) -{ - a[bit / 8] |= (1 << (bit % 8)); -} - -static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg) -{ - return (uint8_t *)((seg << 4) + (reg & 0xffff)); -} - -static inline void pushw(struct vm86_regs *r, int val) -{ - r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff); - *(uint16_t *)seg_to_linear(r->ss, r->esp) = val; -} - -void dump_regs(struct vm86_regs *r) -{ - fprintf(stderr, - "EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n" - "ESI=%08lx EDI=%08lx EBP=%08lx ESP=%08lx\n" - "EIP=%08lx EFL=%08lx\n" - "CS=%04x DS=%04x ES=%04x SS=%04x FS=%04x GS=%04x\n", - r->eax, r->ebx, r->ecx, r->edx, r->esi, r->edi, r->ebp, r->esp, - r->eip, r->eflags, - r->cs, r->ds, r->es, r->ss, r->fs, r->gs); -} - -#ifdef SIGTEST -void alarm_handler(int sig) -{ - fprintf(stderr, "alarm signal=%d\n", sig); - alarm(1); -} -#endif - -int main(int argc, char **argv) -{ - uint8_t *vm86_mem; - const char *filename; - int fd, ret, seg; - struct vm86plus_struct ctx; - struct vm86_regs *r; - - if (argc != 2) - usage(); - filename = argv[1]; - - vm86_mem = mmap((void *)0x00000000, 0x110000, - PROT_WRITE | PROT_READ | PROT_EXEC, - MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); - if (vm86_mem == MAP_FAILED) { - perror("mmap"); - exit(1); - } -#ifdef SIGTEST - { - struct sigaction act; - - act.sa_handler = alarm_handler; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - sigaction(SIGALRM, &act, NULL); - alarm(1); - } -#endif - - /* load the MSDOS .com executable */ - fd = open(filename, O_RDONLY); - if (fd < 0) { - perror(filename); - exit(1); - } - ret = read(fd, vm86_mem + COM_BASE_ADDR, 65536 - 256); - if (ret < 0) { - perror("read"); - exit(1); - } - close(fd); - - memset(&ctx, 0, sizeof(ctx)); - /* init basic registers */ - r = &ctx.regs; - r->eip = 0x100; - r->esp = 0xfffe; - seg = (COM_BASE_ADDR - 0x100) >> 4; - r->cs = seg; - r->ss = seg; - r->ds = seg; - r->es = seg; - r->fs = seg; - r->gs = seg; - r->eflags = VIF_MASK; - - /* put return code */ - set_bit((uint8_t *)&ctx.int_revectored, 0x21); - *seg_to_linear(r->cs, 0) = 0xb4; /* mov ah, $0 */ - *seg_to_linear(r->cs, 1) = 0x00; - *seg_to_linear(r->cs, 2) = 0xcd; /* int $0x21 */ - *seg_to_linear(r->cs, 3) = 0x21; - pushw(&ctx.regs, 0x0000); - - /* the value of these registers seem to be assumed by pi_10.com */ - r->esi = 0x100; - r->ecx = 0xff; - r->ebp = 0x0900; - r->edi = 0xfffe; - - for(;;) { - ret = vm86(VM86_ENTER, &ctx); - switch(VM86_TYPE(ret)) { - case VM86_INTx: - { - int int_num, ah; - - int_num = VM86_ARG(ret); - if (int_num != 0x21) - goto unknown_int; - ah = (r->eax >> 8) & 0xff; - switch(ah) { - case 0x00: /* exit */ - exit(0); - case 0x02: /* write char */ - { - uint8_t c = r->edx; - write(1, &c, 1); - } - break; - case 0x09: /* write string */ - { - uint8_t c; - for(;;) { - c = *seg_to_linear(r->ds, r->edx); - if (c == '$') - break; - write(1, &c, 1); - } - r->eax = (r->eax & ~0xff) | '$'; - } - break; - default: - unknown_int: - fprintf(stderr, "unsupported int 0x%02x\n", int_num); - dump_regs(&ctx.regs); - // exit(1); - } - } - break; - case VM86_SIGNAL: - /* a signal came, we just ignore that */ - break; - case VM86_STI: - break; - default: - fprintf(stderr, "unhandled vm86 return code (0x%x)\n", ret); - dump_regs(&ctx.regs); - exit(1); - } - } -} From 733f491e8200c9e29f9cbae696802ea71e898711 Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Wed, 18 Jul 2018 14:12:56 -0700 Subject: [PATCH 18/76] block/file-posix: add bdrv_attach_aio_context callback for host dev and cdrom In ed6e2161 ("linux-aio: properly bubble up errors from initialzation"), I only added a bdrv_attach_aio_context callback for the bdrv_file driver. There are several other drivers that use the shared aio_plug callback, though, and they will trip the assertion added to aio_get_linux_aio because they did not call aio_setup_linux_aio first. Add the appropriate callback definition to the affected driver definitions. Fixes: ed6e2161 ("linux-aio: properly bubble up errors from initialization") Reported-by: Farhan Ali Signed-off-by: Nishanth Aravamudan Reviewed-by: John Snow Message-id: 20180718211256.29774-1-naravamudan@digitalocean.com Cc: Eric Blake Cc: Kevin Wolf Cc: John Snow Cc: Max Reitz Cc: Stefan Hajnoczi Cc: Fam Zheng Cc: Paolo Bonzini Cc: qemu-block@nongnu.org Cc: qemu-devel@nongnu.org Signed-off-by: Stefan Hajnoczi --- block/file-posix.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/block/file-posix.c b/block/file-posix.c index 60af4b3d51d..ad299beb38b 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -3158,6 +3158,7 @@ static BlockDriver bdrv_host_device = { .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, + .bdrv_attach_aio_context = raw_aio_attach_aio_context, .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, @@ -3280,6 +3281,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, + .bdrv_attach_aio_context = raw_aio_attach_aio_context, .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, @@ -3410,6 +3412,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, + .bdrv_attach_aio_context = raw_aio_attach_aio_context, .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, From 46c9f7a02324fcbf30afbc24f9cfeb8f12af8ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 17 Jul 2018 17:54:17 +0100 Subject: [PATCH 19/76] tests: call qcrypto_init instead of gnutls_global_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calling qcrypto_init ensures that all relevant initialization is done. In particular this honours the debugging settings and thread settings. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- include/crypto/init.h | 2 ++ tests/crypto-tls-x509-helpers.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/crypto/init.h b/include/crypto/init.h index 04c1edf7706..f79c02266b3 100644 --- a/include/crypto/init.h +++ b/include/crypto/init.h @@ -21,6 +21,8 @@ #ifndef QCRYPTO_INIT_H #define QCRYPTO_INIT_H +#include "qapi/error.h" + int qcrypto_init(Error **errp); #endif /* QCRYPTO_INIT_H */ diff --git a/tests/crypto-tls-x509-helpers.c b/tests/crypto-tls-x509-helpers.c index 173d4e28fb4..9b669c2a4bc 100644 --- a/tests/crypto-tls-x509-helpers.c +++ b/tests/crypto-tls-x509-helpers.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "crypto-tls-x509-helpers.h" +#include "crypto/init.h" #include "qemu/sockets.h" #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT @@ -95,7 +96,7 @@ static gnutls_x509_privkey_t test_tls_load_key(void) void test_tls_init(const char *keyfile) { - gnutls_global_init(); + qcrypto_init(&error_abort); if (asn1_array2tree(pkix_asn1_tab, &pkix_asn1, NULL) != ASN1_SUCCESS) { abort(); From 07ff3f383f98e7d2d72047440461e9517e586bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 18 Jul 2018 09:34:47 +0100 Subject: [PATCH 20/76] tests: don't silence error reporting for all tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test-vmstate test is a bit chatty because it triggers various expected failure scenarios and the code in question uses error_report instead of accepting 'Error **errp' parameters. To silence this test the stubs for error_vprintf() were changed to send errors via g_test_message() instead of stderr: commit 28017e010ddf6849cfa830e898da3e44e6610952 Author: Paolo Bonzini Date: Mon Oct 24 18:31:03 2016 +0200 tests: send error_report to test log Implement error_vprintf to send the output of error_report to the test log. This silences test-vmstate. Signed-off-by: Paolo Bonzini Message-Id: <1477326663-67817-3-git-send-email-pbonzini@redhat.com> Unfortunately this change has global impact across the entire test suite and means that when tests fail for unexpected reasons, the message is not displayed on stderr. eg when using &error_abort in a call the test merely prints Unexpected error in qcrypto_tls_session_check_certificate() at crypto/tlssession.c:280: and the actual error message is hidden, making it impossible to diagnose the failure. This is especially problematic in CI or build systems where it isn't possible to easily pass the --debug-log flag to tests and re-run with the test log visible. This change makes the previous big hammer much more nuanced, providing a flag in the stub error_vprintf() that can used on a per-test basis to silence the errors. Only the test-vmstate silences errors initially. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- stubs/error-printf.c | 3 ++- tests/test-vmstate.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/stubs/error-printf.c b/stubs/error-printf.c index ac6b92aa697..99c64066686 100644 --- a/stubs/error-printf.c +++ b/stubs/error-printf.c @@ -4,7 +4,8 @@ void error_vprintf(const char *fmt, va_list ap) { - if (g_test_initialized() && !g_test_subprocess()) { + if (g_test_initialized() && !g_test_subprocess() && + getenv("QTEST_SILENT_ERRORS")) { char *msg = g_strdup_vprintf(fmt, ap); g_test_message("%s", msg); g_free(msg); diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c index 087844b6c81..37a7a937846 100644 --- a/tests/test-vmstate.c +++ b/tests/test-vmstate.c @@ -859,6 +859,8 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); + setenv("QTEST_SILENT_ERRORS", "1", 1); + g_test_init(&argc, &argv, NULL); g_test_add_func("/vmstate/simple/primitive", test_simple_primitive); g_test_add_func("/vmstate/versioned/load/v1", test_load_v1); From a73a8784ca4d98427ac0eb4e356336aa5392fd9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 18 Jul 2018 10:06:43 +0100 Subject: [PATCH 21/76] tests: use error_abort in places expecting errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the TLS related tests are passing an in a "Error" object to methods that are expected to fail, but then ignoring any error that is set and instead asserting on a return value. This means that when an error is unexpectedly raised, no information about it is printed out, making failures hard to diagnose. Changing these tests to pass in &error_abort will make unexpected failures print messages to stderr. Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- tests/test-crypto-tlscredsx509.c | 11 +---- tests/test-crypto-tlssession.c | 76 ++++++++++++-------------------- tests/test-io-channel-tls.c | 24 ++++------ 3 files changed, 39 insertions(+), 72 deletions(-) diff --git a/tests/test-crypto-tlscredsx509.c b/tests/test-crypto-tlscredsx509.c index af2f80e89c2..30f9ac4bbf2 100644 --- a/tests/test-crypto-tlscredsx509.c +++ b/tests/test-crypto-tlscredsx509.c @@ -54,7 +54,7 @@ static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, "sanity-check", "yes", NULL); - if (*errp) { + if (!creds) { return NULL; } return QCRYPTO_TLS_CREDS(creds); @@ -74,7 +74,6 @@ static void test_tls_creds(const void *opaque) struct QCryptoTLSCredsTestData *data = (struct QCryptoTLSCredsTestData *)opaque; QCryptoTLSCreds *creds; - Error *err = NULL; #define CERT_DIR "tests/test-crypto-tlscredsx509-certs/" mkdir(CERT_DIR, 0700); @@ -113,17 +112,11 @@ static void test_tls_creds(const void *opaque) QCRYPTO_TLS_CREDS_ENDPOINT_SERVER : QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT), CERT_DIR, - &err); + data->expectFail ? NULL : &error_abort); if (data->expectFail) { - error_free(err); g_assert(creds == NULL); } else { - if (err) { - g_printerr("Failed to generate creds: %s\n", - error_get_pretty(err)); - error_free(err); - } g_assert(creds != NULL); } diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c index 7bd811796e8..fd9acf90677 100644 --- a/tests/test-crypto-tlssession.c +++ b/tests/test-crypto-tlssession.c @@ -52,28 +52,21 @@ static ssize_t testRead(char *buf, size_t len, void *opaque) static QCryptoTLSCreds *test_tls_creds_psk_create( QCryptoTLSCredsEndpoint endpoint, - const char *dir, - Error **errp) + const char *dir) { - Error *err = NULL; Object *parent = object_get_objects_root(); Object *creds = object_new_with_props( TYPE_QCRYPTO_TLS_CREDS_PSK, parent, (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "testtlscredsserver" : "testtlscredsclient"), - &err, + &error_abort, "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "server" : "client"), "dir", dir, "priority", "NORMAL", NULL ); - - if (err) { - error_propagate(errp, err); - return NULL; - } return QCRYPTO_TLS_CREDS(creds); } @@ -87,7 +80,6 @@ static void test_crypto_tls_session_psk(void) int channel[2]; bool clientShake = false; bool serverShake = false; - Error *err = NULL; int ret; /* We'll use this for our fake client-server connection */ @@ -104,25 +96,23 @@ static void test_crypto_tls_session_psk(void) clientCreds = test_tls_creds_psk_create( QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, - WORKDIR, - &err); + WORKDIR); g_assert(clientCreds != NULL); serverCreds = test_tls_creds_psk_create( QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, - WORKDIR, - &err); + WORKDIR); g_assert(serverCreds != NULL); /* Now the real part of the test, setup the sessions */ clientSess = qcrypto_tls_session_new( clientCreds, NULL, NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &err); + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &error_abort); + g_assert(clientSess != NULL); + serverSess = qcrypto_tls_session_new( serverCreds, NULL, NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &err); - - g_assert(clientSess != NULL); + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &error_abort); g_assert(serverSess != NULL); /* For handshake to work, we need to set the I/O callbacks @@ -145,7 +135,7 @@ static void test_crypto_tls_session_psk(void) int rv; if (!serverShake) { rv = qcrypto_tls_session_handshake(serverSess, - &err); + &error_abort); g_assert(rv >= 0); if (qcrypto_tls_session_get_handshake_status(serverSess) == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { @@ -154,7 +144,7 @@ static void test_crypto_tls_session_psk(void) } if (!clientShake) { rv = qcrypto_tls_session_handshake(clientSess, - &err); + &error_abort); g_assert(rv >= 0); if (qcrypto_tls_session_get_handshake_status(clientSess) == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { @@ -165,8 +155,10 @@ static void test_crypto_tls_session_psk(void) /* Finally make sure the server & client validation is successful. */ - g_assert(qcrypto_tls_session_check_credentials(serverSess, &err) == 0); - g_assert(qcrypto_tls_session_check_credentials(clientSess, &err) == 0); + g_assert(qcrypto_tls_session_check_credentials(serverSess, + &error_abort) == 0); + g_assert(qcrypto_tls_session_check_credentials(clientSess, + &error_abort) == 0); object_unparent(OBJECT(serverCreds)); object_unparent(OBJECT(clientCreds)); @@ -192,17 +184,15 @@ struct QCryptoTLSSessionTestData { static QCryptoTLSCreds *test_tls_creds_x509_create( QCryptoTLSCredsEndpoint endpoint, - const char *certdir, - Error **errp) + const char *certdir) { - Error *err = NULL; Object *parent = object_get_objects_root(); Object *creds = object_new_with_props( TYPE_QCRYPTO_TLS_CREDS_X509, parent, (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "testtlscredsserver" : "testtlscredsclient"), - &err, + &error_abort, "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "server" : "client"), "dir", certdir, @@ -217,11 +207,6 @@ static QCryptoTLSCreds *test_tls_creds_x509_create( "sanity-check", "no", NULL ); - - if (err) { - error_propagate(errp, err); - return NULL; - } return QCRYPTO_TLS_CREDS(creds); } @@ -249,7 +234,6 @@ static void test_crypto_tls_session_x509(const void *opaque) int channel[2]; bool clientShake = false; bool serverShake = false; - Error *err = NULL; int ret; /* We'll use this for our fake client-server connection */ @@ -293,14 +277,12 @@ static void test_crypto_tls_session_x509(const void *opaque) clientCreds = test_tls_creds_x509_create( QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, - CLIENT_CERT_DIR, - &err); + CLIENT_CERT_DIR); g_assert(clientCreds != NULL); serverCreds = test_tls_creds_x509_create( QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, - SERVER_CERT_DIR, - &err); + SERVER_CERT_DIR); g_assert(serverCreds != NULL); acl = qemu_acl_init("tlssessionacl"); @@ -314,13 +296,13 @@ static void test_crypto_tls_session_x509(const void *opaque) /* Now the real part of the test, setup the sessions */ clientSess = qcrypto_tls_session_new( clientCreds, data->hostname, NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &err); + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &error_abort); + g_assert(clientSess != NULL); + serverSess = qcrypto_tls_session_new( serverCreds, NULL, data->wildcards ? "tlssessionacl" : NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &err); - - g_assert(clientSess != NULL); + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &error_abort); g_assert(serverSess != NULL); /* For handshake to work, we need to set the I/O callbacks @@ -343,7 +325,7 @@ static void test_crypto_tls_session_x509(const void *opaque) int rv; if (!serverShake) { rv = qcrypto_tls_session_handshake(serverSess, - &err); + &error_abort); g_assert(rv >= 0); if (qcrypto_tls_session_get_handshake_status(serverSess) == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { @@ -352,7 +334,7 @@ static void test_crypto_tls_session_x509(const void *opaque) } if (!clientShake) { rv = qcrypto_tls_session_handshake(clientSess, - &err); + &error_abort); g_assert(rv >= 0); if (qcrypto_tls_session_get_handshake_status(clientSess) == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { @@ -365,10 +347,9 @@ static void test_crypto_tls_session_x509(const void *opaque) /* Finally make sure the server validation does what * we were expecting */ - if (qcrypto_tls_session_check_credentials(serverSess, &err) < 0) { + if (qcrypto_tls_session_check_credentials( + serverSess, data->expectServerFail ? NULL : &error_abort) < 0) { g_assert(data->expectServerFail); - error_free(err); - err = NULL; } else { g_assert(!data->expectServerFail); } @@ -376,10 +357,9 @@ static void test_crypto_tls_session_x509(const void *opaque) /* * And the same for the client validation check */ - if (qcrypto_tls_session_check_credentials(clientSess, &err) < 0) { + if (qcrypto_tls_session_check_credentials( + clientSess, data->expectClientFail ? NULL : &error_abort) < 0) { g_assert(data->expectClientFail); - error_free(err); - err = NULL; } else { g_assert(!data->expectClientFail); } diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c index bb88ee870f0..4900c6d433d 100644 --- a/tests/test-io-channel-tls.c +++ b/tests/test-io-channel-tls.c @@ -30,6 +30,7 @@ #include "crypto/init.h" #include "crypto/tlscredsx509.h" #include "qemu/acl.h" +#include "qapi/error.h" #include "qom/object_interfaces.h" #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT @@ -64,8 +65,7 @@ static void test_tls_handshake_done(QIOTask *task, static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, - const char *certdir, - Error **errp) + const char *certdir) { Object *parent = object_get_objects_root(); Object *creds = object_new_with_props( @@ -73,7 +73,7 @@ static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, parent, (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "testtlscredsserver" : "testtlscredsclient"), - errp, + &error_abort, "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "server" : "client"), "dir", certdir, @@ -89,9 +89,6 @@ static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, NULL ); - if (*errp) { - return NULL; - } return QCRYPTO_TLS_CREDS(creds); } @@ -121,7 +118,6 @@ static void test_io_channel_tls(const void *opaque) int channel[2]; struct QIOChannelTLSHandshakeData clientHandshake = { false, false }; struct QIOChannelTLSHandshakeData serverHandshake = { false, false }; - Error *err = NULL; QIOChannelTest *test; GMainContext *mainloop; @@ -157,14 +153,12 @@ static void test_io_channel_tls(const void *opaque) clientCreds = test_tls_creds_create( QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, - CLIENT_CERT_DIR, - &err); + CLIENT_CERT_DIR); g_assert(clientCreds != NULL); serverCreds = test_tls_creds_create( QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, - SERVER_CERT_DIR, - &err); + SERVER_CERT_DIR); g_assert(serverCreds != NULL); acl = qemu_acl_init("channeltlsacl"); @@ -176,10 +170,10 @@ static void test_io_channel_tls(const void *opaque) } clientChanSock = qio_channel_socket_new_fd( - channel[0], &err); + channel[0], &error_abort); g_assert(clientChanSock != NULL); serverChanSock = qio_channel_socket_new_fd( - channel[1], &err); + channel[1], &error_abort); g_assert(serverChanSock != NULL); /* @@ -193,12 +187,12 @@ static void test_io_channel_tls(const void *opaque) /* Now the real part of the test, setup the sessions */ clientChanTLS = qio_channel_tls_new_client( QIO_CHANNEL(clientChanSock), clientCreds, - data->hostname, &err); + data->hostname, &error_abort); g_assert(clientChanTLS != NULL); serverChanTLS = qio_channel_tls_new_server( QIO_CHANNEL(serverChanSock), serverCreds, - "channeltlsacl", &err); + "channeltlsacl", &error_abort); g_assert(serverChanTLS != NULL); qio_channel_tls_handshake(clientChanTLS, From 2c0c3d5ba4680ff1ece84491225135c7759658d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 18 Jul 2018 10:24:59 +0100 Subject: [PATCH 22/76] tests: fix TLS handshake failure with TLS 1.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When gnutls negotiates TLS 1.3 instead of 1.2, the order of messages sent by the handshake changes. This exposed a logic bug in the test suite which caused us to wait for the server to see handshake completion, but not wait for the client to see completion. The result was the client didn't receive the certificate for verification and the test failed. This is exposed in Fedora 29 rawhide which has just enabled TLS 1.3 in its GNUTLS builds. Reviewed-by: Eric Blake Signed-off-by: Daniel P. Berrangé --- tests/test-crypto-tlssession.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c index fd9acf90677..6fa9950afbb 100644 --- a/tests/test-crypto-tlssession.c +++ b/tests/test-crypto-tlssession.c @@ -151,7 +151,7 @@ static void test_crypto_tls_session_psk(void) clientShake = true; } } - } while (!clientShake && !serverShake); + } while (!clientShake || !serverShake); /* Finally make sure the server & client validation is successful. */ @@ -341,7 +341,7 @@ static void test_crypto_tls_session_x509(const void *opaque) clientShake = true; } } - } while (!clientShake && !serverShake); + } while (!clientShake || !serverShake); /* Finally make sure the server validation does what From de357f02930a7c46c658fe89160829ea6fd2a7c4 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 20 Jul 2018 11:47:13 +0800 Subject: [PATCH 23/76] migration: fix potential overflow in multifd send I would guess it won't happen normally, but this should ease Coverity. >>> CID 1394385: Integer handling issues (OVERFLOW_BEFORE_WIDEN) >>> Potentially overflowing expression "pages->used * 8192U" with type "unsigned int" (32 bits, unsigned) is evaluated using 32-bit arithmetic, and then used in a context that expects an expression of type "uint64_t" (64 bits, unsigned). 854 transferred = pages->used * TARGET_PAGE_SIZE + p->packet_len; Fixes: CID 1394385 CC: Juan Quintela Signed-off-by: Peter Xu Message-Id: <20180720034713.11711-1-peterx@redhat.com> Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index 52dd678092e..fdd108475c6 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -851,7 +851,7 @@ static void multifd_send_pages(void) p->pages->block = NULL; multifd_send_state->pages = p->pages; p->pages = pages; - transferred = pages->used * TARGET_PAGE_SIZE + p->packet_len; + transferred = ((uint64_t) pages->used) * TARGET_PAGE_SIZE + p->packet_len; ram_counters.multifd_bytes += transferred; ram_counters.transferred += transferred;; qemu_mutex_unlock(&p->mutex); From e7d8e9c2721368762135ded6594bedca39dbbd3c Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 19 Jul 2018 10:22:57 +0100 Subject: [PATCH 24/76] migrate: Fix cancelling state warning We've been getting the warning: migration_iteration_finish: Unknown ending state 2 on a cancel. I think that's originally due to 39b9e17905c; although I've only seen the warning, I think that in some cases that we could find the VM stays paused after a cancel where it should restart. Signed-off-by: Dr. David Alan Gilbert Message-Id: <20180719092257.12703-1-dgilbert@redhat.com> Reviewed-by: Peter Xu Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/migration.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/migration.c b/migration/migration.c index 8d56d569305..db6bde74538 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2877,6 +2877,7 @@ static void migration_iteration_finish(MigrationState *s) /* Fallthrough */ case MIGRATION_STATUS_FAILED: case MIGRATION_STATUS_CANCELLED: + case MIGRATION_STATUS_CANCELLING: if (s->vm_was_running) { vm_start(); } else { From c04c74f993d92b33a4dc1966a99c5dbd5f67f01a Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Tue, 24 Jul 2018 11:22:15 +0100 Subject: [PATCH 25/76] audio/hda: Fix migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix outgoing migration which was crashing in vmstate_hda_audio_stream_buf_needed, I think the problem is that we have room for upto 4 streams in the array but only use 2, when we come to try and save the state of the unused streams we hit st->state == NULL. Fixes: 280c1e1cdb24d80ecdfc Signed-off-by: Dr. David Alan Gilbert Message-Id: <20180724102215.31866-1-dgilbert@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- hw/audio/hda-codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 2b58c3505bd..617a1c1016b 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -786,7 +786,7 @@ static void hda_audio_reset(DeviceState *dev) static bool vmstate_hda_audio_stream_buf_needed(void *opaque) { HDAAudioStream *st = opaque; - return st->state->use_timer; + return st->state && st->state->use_timer; } static const VMStateDescription vmstate_hda_audio_stream_buf = { From f808d45cb80dd0ed69e196b4618fd66f15361736 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 23 Jul 2018 20:33:02 +0800 Subject: [PATCH 26/76] migration: update recv bitmap only on dest vm We shouldn't update the received bitmap if we're the source VM. This fixes a breakage when release-ram is enabled on postcopy. Signed-off-by: Peter Xu Message-Id: <20180723123305.24792-2-peterx@redhat.com> Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/ram.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index fdd108475c6..24dea2730c5 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2827,8 +2827,15 @@ int ram_discard_range(const char *rbname, uint64_t start, size_t length) goto err; } - bitmap_clear(rb->receivedmap, start >> qemu_target_page_bits(), - length >> qemu_target_page_bits()); + /* + * On source VM, we don't need to update the received bitmap since + * we don't even have one. + */ + if (rb->receivedmap) { + bitmap_clear(rb->receivedmap, start >> qemu_target_page_bits(), + length >> qemu_target_page_bits()); + } + ret = ram_block_discard_range(rb, start, length); err: From d0542c3905e39825585d07f2860da9357b5317e5 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 23 Jul 2018 20:33:03 +0800 Subject: [PATCH 27/76] migration: disallow recovery for release-ram Postcopy recovery won't work well with release-ram capability since release-ram will drop the page buffer as long as the page is put into the send buffer. So if there is a network failure happened, any page buffers that have not yet reached the destination VM but have already been sent from the source VM will be lost forever. Let's refuse the client from resuming such a postcopy migration. Luckily release-ram was designed to only be used when src and destination VMs are on the same host, so it should be fine. Signed-off-by: Peter Xu Message-Id: <20180723123305.24792-3-peterx@redhat.com> Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/migration.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/migration/migration.c b/migration/migration.c index db6bde74538..bfc4d09aae5 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1629,6 +1629,25 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, "paused migration"); return false; } + + /* + * Postcopy recovery won't work well with release-ram + * capability since release-ram will drop the page buffer as + * long as the page is put into the send buffer. So if there + * is a network failure happened, any page buffers that have + * not yet reached the destination VM but have already been + * sent from the source VM will be lost forever. Let's refuse + * the client from resuming such a postcopy migration. + * Luckily release-ram was designed to only be used when src + * and destination VMs are on the same host, so it should be + * fine. + */ + if (migrate_release_ram()) { + error_setg(errp, "Postcopy recovery cannot work " + "when release-ram capability is set"); + return false; + } + /* This is a resume, skip init status */ return true; } From 8c57b469947c926627369c841230dd6a1ee0a19d Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Mon, 23 Jul 2018 20:33:04 +0800 Subject: [PATCH 28/76] tests: only update last_byte when at the edge The only possible change of last_byte is when it reaches the edge. Setting it every time might let last_byte contain an invalid data when memory corruption is detected, then the check of the next byte will be incorrect. For example, a single page corruption at address 0x14ad000 will also lead to a "fake" corruption at 0x14ae000: Memory content inconsistency at 14ad000 first_byte = 44 last_byte = 44 current = ef hit_edge = 0 Memory content inconsistency at 14ae000 first_byte = 44 last_byte = ef current = 44 hit_edge = 0 After the patch, it'll only report the corrputed page: Memory content inconsistency at 14ad000 first_byte = 44 last_byte = 44 current = ef hit_edge = 0 Signed-off-by: Peter Xu Message-Id: <20180723123305.24792-4-peterx@redhat.com> Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- tests/migration-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/migration-test.c b/tests/migration-test.c index 086f727b34e..e079e0bdb68 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -300,6 +300,7 @@ static void check_guests_ram(QTestState *who) * to us yet. */ hit_edge = true; + last_byte = b; } else { fprintf(stderr, "Memory content inconsistency at %x" " first_byte = %x last_byte = %x current = %x" @@ -308,7 +309,6 @@ static void check_guests_ram(QTestState *who) bad = true; } } - last_byte = b; } g_assert_false(bad); } From afd26295dfa2599430f935e2c047f6d4dab94c85 Mon Sep 17 00:00:00 2001 From: Lidong Chen Date: Tue, 24 Jul 2018 20:16:25 +0800 Subject: [PATCH 29/76] migration: fix duplicate initialization for expected_downtime and cleanup_bh migrate_fd_connect duplicate initialize expected_downtime and cleanup_bh. Signed-off-by: Lidong Chen Message-Id: <1532434585-14732-2-git-send-email-lidongchen@tencent.com> Reviewed-by: Juan Quintela Signed-off-by: Dr. David Alan Gilbert --- migration/migration.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index bfc4d09aae5..b7d9854bdad 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -3052,8 +3052,6 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) } else { /* This is a fresh new migration */ rate_limit = s->parameters.max_bandwidth / XFER_LIMIT_RATIO; - s->expected_downtime = s->parameters.downtime_limit; - s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s); /* Notify before starting migration thread */ notifier_list_notify(&migration_state_notifiers, s); From a6338a5b21847066ecf6588724055b5e3e857c40 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 24 Jul 2018 22:06:31 +0100 Subject: [PATCH 30/76] Update version for v3.0.0-rc2 release Signed-off-by: Peter Maydell --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 65be8fd280e..c4eae179871 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.12.91 +2.12.92 From e6ea0ea41863ea34547434baa6e9d5158b56e925 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Tue, 6 Mar 2018 10:07:30 +1300 Subject: [PATCH 31/76] RISC-V: Update address bits to support sv39 and sv48 Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 34abc383e3d..e0608e6d5f0 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -24,12 +24,12 @@ #define TARGET_PAGE_BITS 12 /* 4 KiB Pages */ #if defined(TARGET_RISCV64) #define TARGET_LONG_BITS 64 -#define TARGET_PHYS_ADDR_SPACE_BITS 50 -#define TARGET_VIRT_ADDR_SPACE_BITS 39 +#define TARGET_PHYS_ADDR_SPACE_BITS 56 /* 44-bit PPN */ +#define TARGET_VIRT_ADDR_SPACE_BITS 48 /* sv48 */ #elif defined(TARGET_RISCV32) #define TARGET_LONG_BITS 32 -#define TARGET_PHYS_ADDR_SPACE_BITS 34 -#define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_PHYS_ADDR_SPACE_BITS 34 /* 22-bit PPN */ +#define TARGET_VIRT_ADDR_SPACE_BITS 32 /* sv32 */ #endif #define TCG_GUEST_DEFAULT_MO 0 From 15deef516c27d039770756b5d892a4c45c6735d2 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 5 Mar 2018 09:27:28 +1300 Subject: [PATCH 32/76] RISC-V: Improve page table walker spec compliance - Inline PTE_TABLE check for better readability - Change access checks from ternary operator to if - Improve readibility of User page U mode and SUM test - Disallow non U mode from fetching from User pages - Add reserved PTE flag check: W or W|X - Add misaligned PPN check - Set READ protection for PTE X flag and mstatus.mxr - Use memory_region_is_ram in pte update Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark Reviewed-by: Alistair Francis --- target/riscv/cpu_bits.h | 2 -- target/riscv/helper.c | 64 +++++++++++++++++++++++++++++------------ 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 64aa097181f..12b4757088f 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -407,5 +407,3 @@ #define PTE_SOFT 0x300 /* Reserved for Software */ #define PTE_PPN_SHIFT 10 - -#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V) diff --git a/target/riscv/helper.c b/target/riscv/helper.c index 29e1a603dc3..1f0527e07f2 100644 --- a/target/riscv/helper.c +++ b/target/riscv/helper.c @@ -185,16 +185,39 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, #endif target_ulong ppn = pte >> PTE_PPN_SHIFT; - if (PTE_TABLE(pte)) { /* next level of page table */ + if (!(pte & PTE_V)) { + /* Invalid PTE */ + return TRANSLATE_FAIL; + } else if (!(pte & (PTE_R | PTE_W | PTE_X))) { + /* Inner PTE, continue walking */ base = ppn << PGSHIFT; - } else if ((pte & PTE_U) ? (mode == PRV_S) && !sum : !(mode == PRV_S)) { - break; - } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) { - break; - } else if (access_type == MMU_INST_FETCH ? !(pte & PTE_X) : - access_type == MMU_DATA_LOAD ? !(pte & PTE_R) && - !(mxr && (pte & PTE_X)) : !((pte & PTE_R) && (pte & PTE_W))) { - break; + } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) { + /* Reserved leaf PTE flags: PTE_W */ + return TRANSLATE_FAIL; + } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) { + /* Reserved leaf PTE flags: PTE_W + PTE_X */ + return TRANSLATE_FAIL; + } else if ((pte & PTE_U) && ((mode != PRV_U) && + (!sum || access_type == MMU_INST_FETCH))) { + /* User PTE flags when not U mode and mstatus.SUM is not set, + or the access type is an instruction fetch */ + return TRANSLATE_FAIL; + } else if (!(pte & PTE_U) && (mode != PRV_S)) { + /* Supervisor PTE flags when not S mode */ + return TRANSLATE_FAIL; + } else if (ppn & ((1ULL << ptshift) - 1)) { + /* Misaligned PPN */ + return TRANSLATE_FAIL; + } else if (access_type == MMU_DATA_LOAD && !((pte & PTE_R) || + ((pte & PTE_X) && mxr))) { + /* Read access check failed */ + return TRANSLATE_FAIL; + } else if (access_type == MMU_DATA_STORE && !(pte & PTE_W)) { + /* Write access check failed */ + return TRANSLATE_FAIL; + } else if (access_type == MMU_INST_FETCH && !(pte & PTE_X)) { + /* Fetch access check failed */ + return TRANSLATE_FAIL; } else { /* if necessary, set accessed and dirty bits. */ target_ulong updated_pte = pte | PTE_A | @@ -202,16 +225,19 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, /* Page table updates need to be atomic with MTTCG enabled */ if (updated_pte != pte) { - /* if accessed or dirty bits need updating, and the PTE is - * in RAM, then we do so atomically with a compare and swap. - * if the PTE is in IO space, then it can't be updated. - * if the PTE changed, then we must re-walk the page table - as the PTE is no longer valid */ + /* + * - if accessed or dirty bits need updating, and the PTE is + * in RAM, then we do so atomically with a compare and swap. + * - if the PTE is in IO space or ROM, then it can't be updated + * and we return TRANSLATE_FAIL. + * - if the PTE changed by the time we went to update it, then + * it is no longer valid and we must re-walk the page table. + */ MemoryRegion *mr; hwaddr l = sizeof(target_ulong), addr1; mr = address_space_translate(cs->as, pte_addr, &addr1, &l, false, MEMTXATTRS_UNSPECIFIED); - if (memory_access_is_direct(mr, true)) { + if (memory_region_is_ram(mr)) { target_ulong *pte_pa = qemu_map_ram_ptr(mr->ram_block, addr1); #if TCG_OVERSIZED_GUEST @@ -239,15 +265,15 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, target_ulong vpn = addr >> PGSHIFT; *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT; - if ((pte & PTE_R)) { + /* set permissions on the TLB entry */ + if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) { *prot |= PAGE_READ; } if ((pte & PTE_X)) { *prot |= PAGE_EXEC; } - /* only add write permission on stores or if the page - is already dirty, so that we don't miss further - page table walks to update the dirty bit */ + /* add write permission on stores or if the page is already dirty, + so that we TLB miss on later writes to update the dirty bit */ if ((pte & PTE_W) && (access_type == MMU_DATA_STORE || (pte & PTE_D))) { *prot |= PAGE_WRITE; From bd4be3f5d790c71be618a554f0914ae8fa06426f Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Tue, 10 Apr 2018 20:02:46 +1200 Subject: [PATCH 33/76] RISC-V: Use atomic_cmpxchg to update PLIC bitmaps The PLIC previously used a mutex to protect against concurrent access to the claimed and pending bitfields. Instead of using a mutex, we update the bitfields using atomic_cmpxchg. Rename sifive_plic_num_irqs_pending to sifive_plic_irqs_pending and add an early out if any interrupts are pending as the count of pending interrupts is not used. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark Reviewed-by: Richard Henderson --- hw/riscv/sifive_plic.c | 49 +++++++++++++++------------------- include/hw/riscv/sifive_plic.h | 1 - 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c index a91aeb97aba..f635e6ff676 100644 --- a/hw/riscv/sifive_plic.c +++ b/hw/riscv/sifive_plic.c @@ -81,36 +81,32 @@ static void sifive_plic_print_state(SiFivePLICState *plic) } } -static -void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool pending) +static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value) { - qemu_mutex_lock(&plic->lock); - uint32_t word = irq >> 5; - if (pending) { - plic->pending[word] |= (1 << (irq & 31)); - } else { - plic->pending[word] &= ~(1 << (irq & 31)); - } - qemu_mutex_unlock(&plic->lock); + uint32_t old, new, cmp = atomic_read(a); + + do { + old = cmp; + new = (old & ~mask) | (value & mask); + cmp = atomic_cmpxchg(a, old, new); + } while (old != cmp); + + return old; } -static -void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool claimed) +static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level) { - qemu_mutex_lock(&plic->lock); - uint32_t word = irq >> 5; - if (claimed) { - plic->claimed[word] |= (1 << (irq & 31)); - } else { - plic->claimed[word] &= ~(1 << (irq & 31)); - } - qemu_mutex_unlock(&plic->lock); + atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level); } -static -int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid) +static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level) { - int i, j, count = 0; + atomic_set_masked(&plic->claimed[irq >> 5], 1 << (irq & 31), -!!level); +} + +static int sifive_plic_irqs_pending(SiFivePLICState *plic, uint32_t addrid) +{ + int i, j; for (i = 0; i < plic->bitfield_words; i++) { uint32_t pending_enabled_not_claimed = (plic->pending[i] & ~plic->claimed[i]) & @@ -123,11 +119,11 @@ int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid) uint32_t prio = plic->source_priority[irq]; int enabled = pending_enabled_not_claimed & (1 << j); if (enabled && prio > plic->target_priority[addrid]) { - count++; + return 1; } } } - return count; + return 0; } static void sifive_plic_update(SiFivePLICState *plic) @@ -143,7 +139,7 @@ static void sifive_plic_update(SiFivePLICState *plic) if (!env) { continue; } - int level = sifive_plic_num_irqs_pending(plic, addrid) > 0; + int level = sifive_plic_irqs_pending(plic, addrid); switch (mode) { case PLICMode_M: riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level); @@ -439,7 +435,6 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic, TYPE_SIFIVE_PLIC, plic->aperture_size); parse_hart_config(plic); - qemu_mutex_init(&plic->lock); plic->bitfield_words = (plic->num_sources + 31) >> 5; plic->source_priority = g_new0(uint32_t, plic->num_sources); plic->target_priority = g_new(uint32_t, plic->num_addrs); diff --git a/include/hw/riscv/sifive_plic.h b/include/hw/riscv/sifive_plic.h index 2f2af7e6862..688cd97f826 100644 --- a/include/hw/riscv/sifive_plic.h +++ b/include/hw/riscv/sifive_plic.h @@ -55,7 +55,6 @@ typedef struct SiFivePLICState { uint32_t *pending; uint32_t *claimed; uint32_t *enable; - QemuMutex lock; /* config */ char *hart_config; From bf8cf2cdce5c1978791ad0192e4a69a26b368f28 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Thu, 19 Apr 2018 13:19:06 +1200 Subject: [PATCH 34/76] RISC-V: Simplify riscv_cpu_local_irqs_pending This commit is intended to improve readability. There is no change to the logic. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark Reviewed-by: Alistair Francis --- target/riscv/helper.c | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/target/riscv/helper.c b/target/riscv/helper.c index 1f0527e07f2..63b3386b765 100644 --- a/target/riscv/helper.c +++ b/target/riscv/helper.c @@ -35,28 +35,18 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) } #ifndef CONFIG_USER_ONLY -/* - * Return RISC-V IRQ number if an interrupt should be taken, else -1. - * Used in cpu-exec.c - * - * Adapted from Spike's processor_t::take_interrupt() - */ -static int riscv_cpu_hw_interrupts_pending(CPURISCVState *env) +static int riscv_cpu_local_irq_pending(CPURISCVState *env) { - target_ulong pending_interrupts = atomic_read(&env->mip) & env->mie; - - target_ulong mie = get_field(env->mstatus, MSTATUS_MIE); - target_ulong m_enabled = env->priv < PRV_M || (env->priv == PRV_M && mie); - target_ulong enabled_interrupts = pending_interrupts & - ~env->mideleg & -m_enabled; - - target_ulong sie = get_field(env->mstatus, MSTATUS_SIE); - target_ulong s_enabled = env->priv < PRV_S || (env->priv == PRV_S && sie); - enabled_interrupts |= pending_interrupts & env->mideleg & - -s_enabled; - - if (enabled_interrupts) { - return ctz64(enabled_interrupts); /* since non-zero */ + target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE); + target_ulong mstatus_sie = get_field(env->mstatus, MSTATUS_SIE); + target_ulong pending = atomic_read(&env->mip) & env->mie; + target_ulong mie = env->priv < PRV_M || (env->priv == PRV_M && mstatus_mie); + target_ulong sie = env->priv < PRV_S || (env->priv == PRV_S && mstatus_sie); + target_ulong irqs = (pending & ~env->mideleg & -mie) | + (pending & env->mideleg & -sie); + + if (irqs) { + return ctz64(irqs); /* since non-zero */ } else { return EXCP_NONE; /* indicates no pending interrupt */ } @@ -69,7 +59,7 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) if (interrupt_request & CPU_INTERRUPT_HARD) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - int interruptno = riscv_cpu_hw_interrupts_pending(env); + int interruptno = riscv_cpu_local_irq_pending(env); if (interruptno >= 0) { cs->exception_index = RISCV_EXCP_INT_FLAG | interruptno; riscv_cpu_do_interrupt(cs); From a3d50d09b7438f89b236643e26a8fda276680c65 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 9 Apr 2018 09:25:25 +1200 Subject: [PATCH 35/76] RISC-V: Allow setting and clearing multiple irqs Change the API of riscv_set_local_interrupt to take a write mask and value to allow setting and clearing of multiple local interrupts atomically in a single call. Rename the new function to riscv_cpu_update_mip. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- hw/riscv/sifive_clint.c | 8 ++++---- hw/riscv/sifive_plic.c | 4 ++-- target/riscv/cpu.h | 22 +++++++++++++--------- target/riscv/op_helper.c | 24 +++++++++++++++--------- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c index 7cc606e0654..0d2fd52487e 100644 --- a/hw/riscv/sifive_clint.c +++ b/hw/riscv/sifive_clint.c @@ -47,12 +47,12 @@ static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value) if (cpu->env.timecmp <= rtc_r) { /* if we're setting an MTIMECMP value in the "past", immediately raise the timer interrupt */ - riscv_set_local_interrupt(cpu, MIP_MTIP, 1); + riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1)); return; } /* otherwise, set up the future timer interrupt */ - riscv_set_local_interrupt(cpu, MIP_MTIP, 0); + riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(0)); diff = cpu->env.timecmp - rtc_r; /* back to ns (note args switched in muldiv64) */ next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + @@ -67,7 +67,7 @@ static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value) static void sifive_clint_timer_cb(void *opaque) { RISCVCPU *cpu = opaque; - riscv_set_local_interrupt(cpu, MIP_MTIP, 1); + riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1)); } /* CPU wants to read rtc or timecmp register */ @@ -132,7 +132,7 @@ static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value, if (!env) { error_report("clint: invalid timecmp hartid: %zu", hartid); } else if ((addr & 0x3) == 0) { - riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MSIP, value != 0); + riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MSIP, BOOL_TO_MASK(value)); } else { error_report("clint: invalid sip write: %08x", (uint32_t)addr); } diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c index f635e6ff676..9cf9a1f9864 100644 --- a/hw/riscv/sifive_plic.c +++ b/hw/riscv/sifive_plic.c @@ -142,10 +142,10 @@ static void sifive_plic_update(SiFivePLICState *plic) int level = sifive_plic_irqs_pending(plic, addrid); switch (mode) { case PLICMode_M: - riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level); + riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MEIP, BOOL_TO_MASK(level)); break; case PLICMode_S: - riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_SEIP, level); + riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_SEIP, BOOL_TO_MASK(level)); break; default: break; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index e0608e6d5f0..c5d485769cd 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -126,13 +126,18 @@ struct CPURISCVState { target_ulong mhartid; target_ulong mstatus; + /* * CAUTION! Unlike the rest of this struct, mip is accessed asynchonously - * by I/O threads and other vCPUs, so hold the iothread mutex before - * operating on it. CPU_INTERRUPT_HARD should be in effect iff this is - * non-zero. Use riscv_cpu_set_local_interrupt. + * by I/O threads. It should be read with atomic_read. It should be updated + * using riscv_cpu_update_mip with the iothread mutex held. The iothread + * mutex must be held because mip must be consistent with the CPU inturrept + * state. riscv_cpu_update_mip calls cpu_interrupt or cpu_reset_interrupt + * wuth the invariant that CPU_INTERRUPT_HARD is set iff mip is non-zero. + * mip is 32-bits to allow atomic_read on 32-bit hosts. */ - uint32_t mip; /* allow atomic_read for >= 32-bit hosts */ + uint32_t mip; + target_ulong mie; target_ulong mideleg; @@ -247,7 +252,6 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, uintptr_t retaddr); int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw, int mmu_idx); - char *riscv_isa_string(RISCVCPU *cpu); void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf); @@ -256,6 +260,10 @@ void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf); #define cpu_list riscv_cpu_list #define cpu_mmu_index riscv_cpu_mmu_index +#ifndef CONFIG_USER_ONLY +uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value); +#define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ +#endif void riscv_set_mode(CPURISCVState *env, target_ulong newpriv); void riscv_translate_init(void); @@ -286,10 +294,6 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, target_ulong csrno); target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno); -#ifndef CONFIG_USER_ONLY -void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value); -#endif - #include "exec/cpu-all.h" #endif /* RISCV_CPU_H */ diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index aec7558e1b9..d0883d329b4 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -171,10 +171,8 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, */ qemu_mutex_lock_iothread(); RISCVCPU *cpu = riscv_env_get_cpu(env); - riscv_set_local_interrupt(cpu, MIP_SSIP, - (val_to_write & MIP_SSIP) != 0); - riscv_set_local_interrupt(cpu, MIP_STIP, - (val_to_write & MIP_STIP) != 0); + riscv_cpu_update_mip(cpu, MIP_SSIP | MIP_STIP, + (val_to_write & (MIP_SSIP | MIP_STIP))); /* * csrs, csrc on mip.SEIP is not decomposable into separate read and * write steps, so a different implementation is needed @@ -657,16 +655,24 @@ target_ulong helper_csrrc(CPURISCVState *env, target_ulong src, #ifndef CONFIG_USER_ONLY /* iothread_mutex must be held */ -void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value) +uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) { - target_ulong old_mip = cpu->env.mip; - cpu->env.mip = (old_mip & ~mask) | (value ? mask : 0); + CPURISCVState *env = &cpu->env; + uint32_t old, new, cmp = atomic_read(&env->mip); - if (cpu->env.mip && !old_mip) { + do { + old = cmp; + new = (old & ~mask) | (value & mask); + cmp = atomic_cmpxchg(&env->mip, old, new); + } while (old != cmp); + + if (new && !old) { cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); - } else if (!cpu->env.mip && old_mip) { + } else if (!new && old) { cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); } + + return old; } void riscv_set_mode(CPURISCVState *env, target_ulong newpriv) From 8e55f1194cefc987e37334b36b34e5e5eb40083b Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Tue, 10 Apr 2018 12:29:01 +1200 Subject: [PATCH 36/76] RISC-V: Move non-ops from op_helper to cpu_helper This patch makes op_helper.c contain only instruction operation helpers used by translate.c and moves any unrelated cpu helpers into cpu_helper.c. No logic is changed by this patch. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark Reviewed-by: Alistair Francis --- target/riscv/Makefile.objs | 2 +- target/riscv/{helper.c => cpu_helper.c} | 35 ++++++++++++++++++++++++- target/riscv/op_helper.c | 34 ------------------------ 3 files changed, 35 insertions(+), 36 deletions(-) rename target/riscv/{helper.c => cpu_helper.c} (95%) diff --git a/target/riscv/Makefile.objs b/target/riscv/Makefile.objs index abd0a7cde33..fcc5d34c1f2 100644 --- a/target/riscv/Makefile.objs +++ b/target/riscv/Makefile.objs @@ -1 +1 @@ -obj-y += translate.o op_helper.o helper.o cpu.o fpu_helper.o gdbstub.o pmp.o +obj-y += translate.o op_helper.o cpu_helper.o cpu.o fpu_helper.o gdbstub.o pmp.o diff --git a/target/riscv/helper.c b/target/riscv/cpu_helper.c similarity index 95% rename from target/riscv/helper.c rename to target/riscv/cpu_helper.c index 63b3386b765..86f9f4730c8 100644 --- a/target/riscv/helper.c +++ b/target/riscv/cpu_helper.c @@ -1,5 +1,5 @@ /* - * RISC-V emulation helpers for qemu. + * RISC-V CPU helpers for qemu. * * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu * Copyright (c) 2017-2018 SiFive, Inc. @@ -72,6 +72,39 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) #if !defined(CONFIG_USER_ONLY) +/* iothread_mutex must be held */ +uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) +{ + CPURISCVState *env = &cpu->env; + uint32_t old, new, cmp = atomic_read(&env->mip); + + do { + old = cmp; + new = (old & ~mask) | (value & mask); + cmp = atomic_cmpxchg(&env->mip, old, new); + } while (old != cmp); + + if (new && !old) { + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); + } else if (!new && old) { + cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); + } + + return old; +} + +void riscv_set_mode(CPURISCVState *env, target_ulong newpriv) +{ + if (newpriv > PRV_M) { + g_assert_not_reached(); + } + if (newpriv == PRV_H) { + newpriv = PRV_U; + } + /* tlb_flush is unnecessary as mode is contained in mmu_idx */ + env->priv = newpriv; +} + /* get_physical_address - get the physical address for this virtual address * * Do a page table walk to obtain the physical address corresponding to a diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index d0883d329b4..495390ab1c3 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -654,39 +654,6 @@ target_ulong helper_csrrc(CPURISCVState *env, target_ulong src, #ifndef CONFIG_USER_ONLY -/* iothread_mutex must be held */ -uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) -{ - CPURISCVState *env = &cpu->env; - uint32_t old, new, cmp = atomic_read(&env->mip); - - do { - old = cmp; - new = (old & ~mask) | (value & mask); - cmp = atomic_cmpxchg(&env->mip, old, new); - } while (old != cmp); - - if (new && !old) { - cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); - } else if (!new && old) { - cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); - } - - return old; -} - -void riscv_set_mode(CPURISCVState *env, target_ulong newpriv) -{ - if (newpriv > PRV_M) { - g_assert_not_reached(); - } - if (newpriv == PRV_H) { - newpriv = PRV_U; - } - /* tlb_flush is unnecessary as mode is contained in mmu_idx */ - env->priv = newpriv; -} - target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) { if (!(env->priv >= PRV_S)) { @@ -737,7 +704,6 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) return retpc; } - void helper_wfi(CPURISCVState *env) { CPUState *cs = CPU(riscv_env_get_cpu(env)); From d01a666380ba7ba48fcec2991390c4eccc6e5e37 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Tue, 6 Mar 2018 10:51:53 +1300 Subject: [PATCH 37/76] RISC-V: Update CSR and interrupt definitions * Add user-mode CSR defininitions. * Reorder CSR definitions to match the specification. * Change H mode interrupt comment to 'reserved'. * Remove unused X_COP interrupt. * Add user-mode interrupts. * Remove erroneous until comments on machine mode interrupts. * Move together paging mode and page table bit definitions. * Move together interrupt and exception cause definitions. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark Reviewed-by: Alistair Francis --- target/riscv/cpu.c | 6 +- target/riscv/cpu_bits.h | 683 +++++++++++++++++++++------------------ target/riscv/op_helper.c | 2 +- 3 files changed, 370 insertions(+), 321 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d630e8fd6c6..a025a0a3baa 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -74,8 +74,10 @@ const char * const riscv_intr_names[] = { "s_external", "h_external", "m_external", - "coprocessor", - "host" + "reserved", + "reserved", + "reserved", + "reserved" }; typedef struct RISCVCPUInfo { diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 12b4757088f..5439f4719ee 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -6,242 +6,283 @@ (((target_ulong)(val) * ((mask) & ~((mask) << 1))) & \ (target_ulong)(mask))) -#define PGSHIFT 12 - -#define FSR_RD_SHIFT 5 -#define FSR_RD (0x7 << FSR_RD_SHIFT) - -#define FPEXC_NX 0x01 -#define FPEXC_UF 0x02 -#define FPEXC_OF 0x04 -#define FPEXC_DZ 0x08 -#define FPEXC_NV 0x10 - -#define FSR_AEXC_SHIFT 0 -#define FSR_NVA (FPEXC_NV << FSR_AEXC_SHIFT) -#define FSR_OFA (FPEXC_OF << FSR_AEXC_SHIFT) -#define FSR_UFA (FPEXC_UF << FSR_AEXC_SHIFT) -#define FSR_DZA (FPEXC_DZ << FSR_AEXC_SHIFT) -#define FSR_NXA (FPEXC_NX << FSR_AEXC_SHIFT) -#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA) - -/* CSR numbers */ -#define CSR_FFLAGS 0x1 -#define CSR_FRM 0x2 -#define CSR_FCSR 0x3 -#define CSR_CYCLE 0xc00 -#define CSR_TIME 0xc01 -#define CSR_INSTRET 0xc02 -#define CSR_HPMCOUNTER3 0xc03 -#define CSR_HPMCOUNTER4 0xc04 -#define CSR_HPMCOUNTER5 0xc05 -#define CSR_HPMCOUNTER6 0xc06 -#define CSR_HPMCOUNTER7 0xc07 -#define CSR_HPMCOUNTER8 0xc08 -#define CSR_HPMCOUNTER9 0xc09 -#define CSR_HPMCOUNTER10 0xc0a -#define CSR_HPMCOUNTER11 0xc0b -#define CSR_HPMCOUNTER12 0xc0c -#define CSR_HPMCOUNTER13 0xc0d -#define CSR_HPMCOUNTER14 0xc0e -#define CSR_HPMCOUNTER15 0xc0f -#define CSR_HPMCOUNTER16 0xc10 -#define CSR_HPMCOUNTER17 0xc11 -#define CSR_HPMCOUNTER18 0xc12 -#define CSR_HPMCOUNTER19 0xc13 -#define CSR_HPMCOUNTER20 0xc14 -#define CSR_HPMCOUNTER21 0xc15 -#define CSR_HPMCOUNTER22 0xc16 -#define CSR_HPMCOUNTER23 0xc17 -#define CSR_HPMCOUNTER24 0xc18 -#define CSR_HPMCOUNTER25 0xc19 -#define CSR_HPMCOUNTER26 0xc1a -#define CSR_HPMCOUNTER27 0xc1b -#define CSR_HPMCOUNTER28 0xc1c -#define CSR_HPMCOUNTER29 0xc1d -#define CSR_HPMCOUNTER30 0xc1e -#define CSR_HPMCOUNTER31 0xc1f -#define CSR_SSTATUS 0x100 -#define CSR_SIE 0x104 -#define CSR_STVEC 0x105 -#define CSR_SCOUNTEREN 0x106 -#define CSR_SSCRATCH 0x140 -#define CSR_SEPC 0x141 -#define CSR_SCAUSE 0x142 -#define CSR_SBADADDR 0x143 -#define CSR_SIP 0x144 -#define CSR_SPTBR 0x180 -#define CSR_SATP 0x180 -#define CSR_MSTATUS 0x300 -#define CSR_MISA 0x301 -#define CSR_MEDELEG 0x302 -#define CSR_MIDELEG 0x303 -#define CSR_MIE 0x304 -#define CSR_MTVEC 0x305 -#define CSR_MCOUNTEREN 0x306 -#define CSR_MSCRATCH 0x340 -#define CSR_MEPC 0x341 -#define CSR_MCAUSE 0x342 -#define CSR_MBADADDR 0x343 -#define CSR_MIP 0x344 -#define CSR_PMPCFG0 0x3a0 -#define CSR_PMPCFG1 0x3a1 -#define CSR_PMPCFG2 0x3a2 -#define CSR_PMPCFG3 0x3a3 -#define CSR_PMPADDR0 0x3b0 -#define CSR_PMPADDR1 0x3b1 -#define CSR_PMPADDR2 0x3b2 -#define CSR_PMPADDR3 0x3b3 -#define CSR_PMPADDR4 0x3b4 -#define CSR_PMPADDR5 0x3b5 -#define CSR_PMPADDR6 0x3b6 -#define CSR_PMPADDR7 0x3b7 -#define CSR_PMPADDR8 0x3b8 -#define CSR_PMPADDR9 0x3b9 -#define CSR_PMPADDR10 0x3ba -#define CSR_PMPADDR11 0x3bb -#define CSR_PMPADDR12 0x3bc -#define CSR_PMPADDR13 0x3bd -#define CSR_PMPADDR14 0x3be -#define CSR_PMPADDR15 0x3bf -#define CSR_TSELECT 0x7a0 -#define CSR_TDATA1 0x7a1 -#define CSR_TDATA2 0x7a2 -#define CSR_TDATA3 0x7a3 -#define CSR_DCSR 0x7b0 -#define CSR_DPC 0x7b1 -#define CSR_DSCRATCH 0x7b2 -#define CSR_MCYCLE 0xb00 -#define CSR_MINSTRET 0xb02 -#define CSR_MHPMCOUNTER3 0xb03 -#define CSR_MHPMCOUNTER4 0xb04 -#define CSR_MHPMCOUNTER5 0xb05 -#define CSR_MHPMCOUNTER6 0xb06 -#define CSR_MHPMCOUNTER7 0xb07 -#define CSR_MHPMCOUNTER8 0xb08 -#define CSR_MHPMCOUNTER9 0xb09 -#define CSR_MHPMCOUNTER10 0xb0a -#define CSR_MHPMCOUNTER11 0xb0b -#define CSR_MHPMCOUNTER12 0xb0c -#define CSR_MHPMCOUNTER13 0xb0d -#define CSR_MHPMCOUNTER14 0xb0e -#define CSR_MHPMCOUNTER15 0xb0f -#define CSR_MHPMCOUNTER16 0xb10 -#define CSR_MHPMCOUNTER17 0xb11 -#define CSR_MHPMCOUNTER18 0xb12 -#define CSR_MHPMCOUNTER19 0xb13 -#define CSR_MHPMCOUNTER20 0xb14 -#define CSR_MHPMCOUNTER21 0xb15 -#define CSR_MHPMCOUNTER22 0xb16 -#define CSR_MHPMCOUNTER23 0xb17 -#define CSR_MHPMCOUNTER24 0xb18 -#define CSR_MHPMCOUNTER25 0xb19 -#define CSR_MHPMCOUNTER26 0xb1a -#define CSR_MHPMCOUNTER27 0xb1b -#define CSR_MHPMCOUNTER28 0xb1c -#define CSR_MHPMCOUNTER29 0xb1d -#define CSR_MHPMCOUNTER30 0xb1e -#define CSR_MHPMCOUNTER31 0xb1f -#define CSR_MUCOUNTEREN 0x320 -#define CSR_MSCOUNTEREN 0x321 -#define CSR_MHPMEVENT3 0x323 -#define CSR_MHPMEVENT4 0x324 -#define CSR_MHPMEVENT5 0x325 -#define CSR_MHPMEVENT6 0x326 -#define CSR_MHPMEVENT7 0x327 -#define CSR_MHPMEVENT8 0x328 -#define CSR_MHPMEVENT9 0x329 -#define CSR_MHPMEVENT10 0x32a -#define CSR_MHPMEVENT11 0x32b -#define CSR_MHPMEVENT12 0x32c -#define CSR_MHPMEVENT13 0x32d -#define CSR_MHPMEVENT14 0x32e -#define CSR_MHPMEVENT15 0x32f -#define CSR_MHPMEVENT16 0x330 -#define CSR_MHPMEVENT17 0x331 -#define CSR_MHPMEVENT18 0x332 -#define CSR_MHPMEVENT19 0x333 -#define CSR_MHPMEVENT20 0x334 -#define CSR_MHPMEVENT21 0x335 -#define CSR_MHPMEVENT22 0x336 -#define CSR_MHPMEVENT23 0x337 -#define CSR_MHPMEVENT24 0x338 -#define CSR_MHPMEVENT25 0x339 -#define CSR_MHPMEVENT26 0x33a -#define CSR_MHPMEVENT27 0x33b -#define CSR_MHPMEVENT28 0x33c -#define CSR_MHPMEVENT29 0x33d -#define CSR_MHPMEVENT30 0x33e -#define CSR_MHPMEVENT31 0x33f -#define CSR_MVENDORID 0xf11 -#define CSR_MARCHID 0xf12 -#define CSR_MIMPID 0xf13 -#define CSR_MHARTID 0xf14 -#define CSR_CYCLEH 0xc80 -#define CSR_TIMEH 0xc81 -#define CSR_INSTRETH 0xc82 -#define CSR_HPMCOUNTER3H 0xc83 -#define CSR_HPMCOUNTER4H 0xc84 -#define CSR_HPMCOUNTER5H 0xc85 -#define CSR_HPMCOUNTER6H 0xc86 -#define CSR_HPMCOUNTER7H 0xc87 -#define CSR_HPMCOUNTER8H 0xc88 -#define CSR_HPMCOUNTER9H 0xc89 -#define CSR_HPMCOUNTER10H 0xc8a -#define CSR_HPMCOUNTER11H 0xc8b -#define CSR_HPMCOUNTER12H 0xc8c -#define CSR_HPMCOUNTER13H 0xc8d -#define CSR_HPMCOUNTER14H 0xc8e -#define CSR_HPMCOUNTER15H 0xc8f -#define CSR_HPMCOUNTER16H 0xc90 -#define CSR_HPMCOUNTER17H 0xc91 -#define CSR_HPMCOUNTER18H 0xc92 -#define CSR_HPMCOUNTER19H 0xc93 -#define CSR_HPMCOUNTER20H 0xc94 -#define CSR_HPMCOUNTER21H 0xc95 -#define CSR_HPMCOUNTER22H 0xc96 -#define CSR_HPMCOUNTER23H 0xc97 -#define CSR_HPMCOUNTER24H 0xc98 -#define CSR_HPMCOUNTER25H 0xc99 -#define CSR_HPMCOUNTER26H 0xc9a -#define CSR_HPMCOUNTER27H 0xc9b -#define CSR_HPMCOUNTER28H 0xc9c -#define CSR_HPMCOUNTER29H 0xc9d -#define CSR_HPMCOUNTER30H 0xc9e -#define CSR_HPMCOUNTER31H 0xc9f -#define CSR_MCYCLEH 0xb80 -#define CSR_MINSTRETH 0xb82 -#define CSR_MHPMCOUNTER3H 0xb83 -#define CSR_MHPMCOUNTER4H 0xb84 -#define CSR_MHPMCOUNTER5H 0xb85 -#define CSR_MHPMCOUNTER6H 0xb86 -#define CSR_MHPMCOUNTER7H 0xb87 -#define CSR_MHPMCOUNTER8H 0xb88 -#define CSR_MHPMCOUNTER9H 0xb89 -#define CSR_MHPMCOUNTER10H 0xb8a -#define CSR_MHPMCOUNTER11H 0xb8b -#define CSR_MHPMCOUNTER12H 0xb8c -#define CSR_MHPMCOUNTER13H 0xb8d -#define CSR_MHPMCOUNTER14H 0xb8e -#define CSR_MHPMCOUNTER15H 0xb8f -#define CSR_MHPMCOUNTER16H 0xb90 -#define CSR_MHPMCOUNTER17H 0xb91 -#define CSR_MHPMCOUNTER18H 0xb92 -#define CSR_MHPMCOUNTER19H 0xb93 -#define CSR_MHPMCOUNTER20H 0xb94 -#define CSR_MHPMCOUNTER21H 0xb95 -#define CSR_MHPMCOUNTER22H 0xb96 -#define CSR_MHPMCOUNTER23H 0xb97 -#define CSR_MHPMCOUNTER24H 0xb98 -#define CSR_MHPMCOUNTER25H 0xb99 -#define CSR_MHPMCOUNTER26H 0xb9a -#define CSR_MHPMCOUNTER27H 0xb9b -#define CSR_MHPMCOUNTER28H 0xb9c -#define CSR_MHPMCOUNTER29H 0xb9d -#define CSR_MHPMCOUNTER30H 0xb9e -#define CSR_MHPMCOUNTER31H 0xb9f - -/* mstatus bits */ +/* Floating point round mode */ +#define FSR_RD_SHIFT 5 +#define FSR_RD (0x7 << FSR_RD_SHIFT) + +/* Floating point accrued exception flags */ +#define FPEXC_NX 0x01 +#define FPEXC_UF 0x02 +#define FPEXC_OF 0x04 +#define FPEXC_DZ 0x08 +#define FPEXC_NV 0x10 + +/* Floating point status register bits */ +#define FSR_AEXC_SHIFT 0 +#define FSR_NVA (FPEXC_NV << FSR_AEXC_SHIFT) +#define FSR_OFA (FPEXC_OF << FSR_AEXC_SHIFT) +#define FSR_UFA (FPEXC_UF << FSR_AEXC_SHIFT) +#define FSR_DZA (FPEXC_DZ << FSR_AEXC_SHIFT) +#define FSR_NXA (FPEXC_NX << FSR_AEXC_SHIFT) +#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA) + +/* Control and Status Registers */ + +/* User Trap Setup */ +#define CSR_USTATUS 0x000 +#define CSR_UIE 0x004 +#define CSR_UTVEC 0x005 + +/* User Trap Handling */ +#define CSR_USCRATCH 0x040 +#define CSR_UEPC 0x041 +#define CSR_UCAUSE 0x042 +#define CSR_UTVAL 0x043 +#define CSR_UIP 0x044 + +/* User Floating-Point CSRs */ +#define CSR_FFLAGS 0x001 +#define CSR_FRM 0x002 +#define CSR_FCSR 0x003 + +/* User Timers and Counters */ +#define CSR_CYCLE 0xc00 +#define CSR_TIME 0xc01 +#define CSR_INSTRET 0xc02 +#define CSR_HPMCOUNTER3 0xc03 +#define CSR_HPMCOUNTER4 0xc04 +#define CSR_HPMCOUNTER5 0xc05 +#define CSR_HPMCOUNTER6 0xc06 +#define CSR_HPMCOUNTER7 0xc07 +#define CSR_HPMCOUNTER8 0xc08 +#define CSR_HPMCOUNTER9 0xc09 +#define CSR_HPMCOUNTER10 0xc0a +#define CSR_HPMCOUNTER11 0xc0b +#define CSR_HPMCOUNTER12 0xc0c +#define CSR_HPMCOUNTER13 0xc0d +#define CSR_HPMCOUNTER14 0xc0e +#define CSR_HPMCOUNTER15 0xc0f +#define CSR_HPMCOUNTER16 0xc10 +#define CSR_HPMCOUNTER17 0xc11 +#define CSR_HPMCOUNTER18 0xc12 +#define CSR_HPMCOUNTER19 0xc13 +#define CSR_HPMCOUNTER20 0xc14 +#define CSR_HPMCOUNTER21 0xc15 +#define CSR_HPMCOUNTER22 0xc16 +#define CSR_HPMCOUNTER23 0xc17 +#define CSR_HPMCOUNTER24 0xc18 +#define CSR_HPMCOUNTER25 0xc19 +#define CSR_HPMCOUNTER26 0xc1a +#define CSR_HPMCOUNTER27 0xc1b +#define CSR_HPMCOUNTER28 0xc1c +#define CSR_HPMCOUNTER29 0xc1d +#define CSR_HPMCOUNTER30 0xc1e +#define CSR_HPMCOUNTER31 0xc1f +#define CSR_CYCLEH 0xc80 +#define CSR_TIMEH 0xc81 +#define CSR_INSTRETH 0xc82 +#define CSR_HPMCOUNTER3H 0xc83 +#define CSR_HPMCOUNTER4H 0xc84 +#define CSR_HPMCOUNTER5H 0xc85 +#define CSR_HPMCOUNTER6H 0xc86 +#define CSR_HPMCOUNTER7H 0xc87 +#define CSR_HPMCOUNTER8H 0xc88 +#define CSR_HPMCOUNTER9H 0xc89 +#define CSR_HPMCOUNTER10H 0xc8a +#define CSR_HPMCOUNTER11H 0xc8b +#define CSR_HPMCOUNTER12H 0xc8c +#define CSR_HPMCOUNTER13H 0xc8d +#define CSR_HPMCOUNTER14H 0xc8e +#define CSR_HPMCOUNTER15H 0xc8f +#define CSR_HPMCOUNTER16H 0xc90 +#define CSR_HPMCOUNTER17H 0xc91 +#define CSR_HPMCOUNTER18H 0xc92 +#define CSR_HPMCOUNTER19H 0xc93 +#define CSR_HPMCOUNTER20H 0xc94 +#define CSR_HPMCOUNTER21H 0xc95 +#define CSR_HPMCOUNTER22H 0xc96 +#define CSR_HPMCOUNTER23H 0xc97 +#define CSR_HPMCOUNTER24H 0xc98 +#define CSR_HPMCOUNTER25H 0xc99 +#define CSR_HPMCOUNTER26H 0xc9a +#define CSR_HPMCOUNTER27H 0xc9b +#define CSR_HPMCOUNTER28H 0xc9c +#define CSR_HPMCOUNTER29H 0xc9d +#define CSR_HPMCOUNTER30H 0xc9e +#define CSR_HPMCOUNTER31H 0xc9f + +/* Machine Timers and Counters */ +#define CSR_MCYCLE 0xb00 +#define CSR_MINSTRET 0xb02 +#define CSR_MCYCLEH 0xb80 +#define CSR_MINSTRETH 0xb82 + +/* Machine Information Registers */ +#define CSR_MVENDORID 0xf11 +#define CSR_MARCHID 0xf12 +#define CSR_MIMPID 0xf13 +#define CSR_MHARTID 0xf14 + +/* Machine Trap Setup */ +#define CSR_MSTATUS 0x300 +#define CSR_MISA 0x301 +#define CSR_MEDELEG 0x302 +#define CSR_MIDELEG 0x303 +#define CSR_MIE 0x304 +#define CSR_MTVEC 0x305 +#define CSR_MCOUNTEREN 0x306 + +/* Legacy Counter Setup (priv v1.9.1) */ +#define CSR_MUCOUNTEREN 0x320 +#define CSR_MSCOUNTEREN 0x321 + +/* Machine Trap Handling */ +#define CSR_MSCRATCH 0x340 +#define CSR_MEPC 0x341 +#define CSR_MCAUSE 0x342 +#define CSR_MBADADDR 0x343 +#define CSR_MIP 0x344 + +/* Supervisor Trap Setup */ +#define CSR_SSTATUS 0x100 +#define CSR_SIE 0x104 +#define CSR_STVEC 0x105 +#define CSR_SCOUNTEREN 0x106 + +/* Supervisor Trap Handling */ +#define CSR_SSCRATCH 0x140 +#define CSR_SEPC 0x141 +#define CSR_SCAUSE 0x142 +#define CSR_SBADADDR 0x143 +#define CSR_SIP 0x144 + +/* Supervisor Protection and Translation */ +#define CSR_SPTBR 0x180 +#define CSR_SATP 0x180 + +/* Physical Memory Protection */ +#define CSR_PMPCFG0 0x3a0 +#define CSR_PMPCFG1 0x3a1 +#define CSR_PMPCFG2 0x3a2 +#define CSR_PMPCFG3 0x3a3 +#define CSR_PMPADDR0 0x3b0 +#define CSR_PMPADDR1 0x3b1 +#define CSR_PMPADDR2 0x3b2 +#define CSR_PMPADDR3 0x3b3 +#define CSR_PMPADDR4 0x3b4 +#define CSR_PMPADDR5 0x3b5 +#define CSR_PMPADDR6 0x3b6 +#define CSR_PMPADDR7 0x3b7 +#define CSR_PMPADDR8 0x3b8 +#define CSR_PMPADDR9 0x3b9 +#define CSR_PMPADDR10 0x3ba +#define CSR_PMPADDR11 0x3bb +#define CSR_PMPADDR12 0x3bc +#define CSR_PMPADDR13 0x3bd +#define CSR_PMPADDR14 0x3be +#define CSR_PMPADDR15 0x3bf + +/* Debug/Trace Registers (shared with Debug Mode) */ +#define CSR_TSELECT 0x7a0 +#define CSR_TDATA1 0x7a1 +#define CSR_TDATA2 0x7a2 +#define CSR_TDATA3 0x7a3 + +/* Debug Mode Registers */ +#define CSR_DCSR 0x7b0 +#define CSR_DPC 0x7b1 +#define CSR_DSCRATCH 0x7b2 + +/* Performance Counters */ +#define CSR_MHPMCOUNTER3 0xb03 +#define CSR_MHPMCOUNTER4 0xb04 +#define CSR_MHPMCOUNTER5 0xb05 +#define CSR_MHPMCOUNTER6 0xb06 +#define CSR_MHPMCOUNTER7 0xb07 +#define CSR_MHPMCOUNTER8 0xb08 +#define CSR_MHPMCOUNTER9 0xb09 +#define CSR_MHPMCOUNTER10 0xb0a +#define CSR_MHPMCOUNTER11 0xb0b +#define CSR_MHPMCOUNTER12 0xb0c +#define CSR_MHPMCOUNTER13 0xb0d +#define CSR_MHPMCOUNTER14 0xb0e +#define CSR_MHPMCOUNTER15 0xb0f +#define CSR_MHPMCOUNTER16 0xb10 +#define CSR_MHPMCOUNTER17 0xb11 +#define CSR_MHPMCOUNTER18 0xb12 +#define CSR_MHPMCOUNTER19 0xb13 +#define CSR_MHPMCOUNTER20 0xb14 +#define CSR_MHPMCOUNTER21 0xb15 +#define CSR_MHPMCOUNTER22 0xb16 +#define CSR_MHPMCOUNTER23 0xb17 +#define CSR_MHPMCOUNTER24 0xb18 +#define CSR_MHPMCOUNTER25 0xb19 +#define CSR_MHPMCOUNTER26 0xb1a +#define CSR_MHPMCOUNTER27 0xb1b +#define CSR_MHPMCOUNTER28 0xb1c +#define CSR_MHPMCOUNTER29 0xb1d +#define CSR_MHPMCOUNTER30 0xb1e +#define CSR_MHPMCOUNTER31 0xb1f +#define CSR_MHPMEVENT3 0x323 +#define CSR_MHPMEVENT4 0x324 +#define CSR_MHPMEVENT5 0x325 +#define CSR_MHPMEVENT6 0x326 +#define CSR_MHPMEVENT7 0x327 +#define CSR_MHPMEVENT8 0x328 +#define CSR_MHPMEVENT9 0x329 +#define CSR_MHPMEVENT10 0x32a +#define CSR_MHPMEVENT11 0x32b +#define CSR_MHPMEVENT12 0x32c +#define CSR_MHPMEVENT13 0x32d +#define CSR_MHPMEVENT14 0x32e +#define CSR_MHPMEVENT15 0x32f +#define CSR_MHPMEVENT16 0x330 +#define CSR_MHPMEVENT17 0x331 +#define CSR_MHPMEVENT18 0x332 +#define CSR_MHPMEVENT19 0x333 +#define CSR_MHPMEVENT20 0x334 +#define CSR_MHPMEVENT21 0x335 +#define CSR_MHPMEVENT22 0x336 +#define CSR_MHPMEVENT23 0x337 +#define CSR_MHPMEVENT24 0x338 +#define CSR_MHPMEVENT25 0x339 +#define CSR_MHPMEVENT26 0x33a +#define CSR_MHPMEVENT27 0x33b +#define CSR_MHPMEVENT28 0x33c +#define CSR_MHPMEVENT29 0x33d +#define CSR_MHPMEVENT30 0x33e +#define CSR_MHPMEVENT31 0x33f +#define CSR_MHPMCOUNTER3H 0xb83 +#define CSR_MHPMCOUNTER4H 0xb84 +#define CSR_MHPMCOUNTER5H 0xb85 +#define CSR_MHPMCOUNTER6H 0xb86 +#define CSR_MHPMCOUNTER7H 0xb87 +#define CSR_MHPMCOUNTER8H 0xb88 +#define CSR_MHPMCOUNTER9H 0xb89 +#define CSR_MHPMCOUNTER10H 0xb8a +#define CSR_MHPMCOUNTER11H 0xb8b +#define CSR_MHPMCOUNTER12H 0xb8c +#define CSR_MHPMCOUNTER13H 0xb8d +#define CSR_MHPMCOUNTER14H 0xb8e +#define CSR_MHPMCOUNTER15H 0xb8f +#define CSR_MHPMCOUNTER16H 0xb90 +#define CSR_MHPMCOUNTER17H 0xb91 +#define CSR_MHPMCOUNTER18H 0xb92 +#define CSR_MHPMCOUNTER19H 0xb93 +#define CSR_MHPMCOUNTER20H 0xb94 +#define CSR_MHPMCOUNTER21H 0xb95 +#define CSR_MHPMCOUNTER22H 0xb96 +#define CSR_MHPMCOUNTER23H 0xb97 +#define CSR_MHPMCOUNTER24H 0xb98 +#define CSR_MHPMCOUNTER25H 0xb99 +#define CSR_MHPMCOUNTER26H 0xb9a +#define CSR_MHPMCOUNTER27H 0xb9b +#define CSR_MHPMCOUNTER28H 0xb9c +#define CSR_MHPMCOUNTER29H 0xb9d +#define CSR_MHPMCOUNTER30H 0xb9e +#define CSR_MHPMCOUNTER31H 0xb9f + +/* mstatus CSR bits */ #define MSTATUS_UIE 0x00000001 #define MSTATUS_SIE 0x00000002 #define MSTATUS_HIE 0x00000004 @@ -276,7 +317,7 @@ #define MSTATUS_SD MSTATUS64_SD #endif -/* sstatus bits */ +/* sstatus CSR bits */ #define SSTATUS_UIE 0x00000001 #define SSTATUS_SIE 0x00000002 #define SSTATUS_UPIE 0x00000010 @@ -297,83 +338,71 @@ #define SSTATUS_SD SSTATUS64_SD #endif -/* irqs */ -#define MIP_SSIP (1 << IRQ_S_SOFT) -#define MIP_HSIP (1 << IRQ_H_SOFT) -#define MIP_MSIP (1 << IRQ_M_SOFT) -#define MIP_STIP (1 << IRQ_S_TIMER) -#define MIP_HTIP (1 << IRQ_H_TIMER) -#define MIP_MTIP (1 << IRQ_M_TIMER) -#define MIP_SEIP (1 << IRQ_S_EXT) -#define MIP_HEIP (1 << IRQ_H_EXT) -#define MIP_MEIP (1 << IRQ_M_EXT) - -#define SIP_SSIP MIP_SSIP -#define SIP_STIP MIP_STIP -#define SIP_SEIP MIP_SEIP - +/* Privilege modes */ #define PRV_U 0 #define PRV_S 1 #define PRV_H 2 #define PRV_M 3 -/* privileged ISA 1.9.1 VM modes (mstatus.vm) */ -#define VM_1_09_MBARE 0 -#define VM_1_09_MBB 1 -#define VM_1_09_MBBID 2 -#define VM_1_09_SV32 8 -#define VM_1_09_SV39 9 -#define VM_1_09_SV48 10 - -/* privileged ISA 1.10.0 VM modes (satp.mode) */ -#define VM_1_10_MBARE 0 -#define VM_1_10_SV32 1 -#define VM_1_10_SV39 8 -#define VM_1_10_SV48 9 -#define VM_1_10_SV57 10 -#define VM_1_10_SV64 11 - -/* privileged ISA interrupt causes */ -#define IRQ_U_SOFT 0 /* since: priv-1.10 */ -#define IRQ_S_SOFT 1 -#define IRQ_H_SOFT 2 /* until: priv-1.9.1 */ -#define IRQ_M_SOFT 3 /* until: priv-1.9.1 */ -#define IRQ_U_TIMER 4 /* since: priv-1.10 */ -#define IRQ_S_TIMER 5 -#define IRQ_H_TIMER 6 /* until: priv-1.9.1 */ -#define IRQ_M_TIMER 7 /* until: priv-1.9.1 */ -#define IRQ_U_EXT 8 /* since: priv-1.10 */ -#define IRQ_S_EXT 9 -#define IRQ_H_EXT 10 /* until: priv-1.9.1 */ -#define IRQ_M_EXT 11 /* until: priv-1.9.1 */ -#define IRQ_X_COP 12 /* non-standard */ - -/* Default addresses */ -#define DEFAULT_RSTVEC 0x00001000 - -/* RV32 satp field masks */ -#define SATP32_MODE 0x80000000 -#define SATP32_ASID 0x7fc00000 -#define SATP32_PPN 0x003fffff - -/* RV64 satp field masks */ -#define SATP64_MODE 0xF000000000000000ULL -#define SATP64_ASID 0x0FFFF00000000000ULL -#define SATP64_PPN 0x00000FFFFFFFFFFFULL +/* RV32 satp CSR field masks */ +#define SATP32_MODE 0x80000000 +#define SATP32_ASID 0x7fc00000 +#define SATP32_PPN 0x003fffff + +/* RV64 satp CSR field masks */ +#define SATP64_MODE 0xF000000000000000ULL +#define SATP64_ASID 0x0FFFF00000000000ULL +#define SATP64_PPN 0x00000FFFFFFFFFFFULL #if defined(TARGET_RISCV32) -#define SATP_MODE SATP32_MODE -#define SATP_ASID SATP32_ASID -#define SATP_PPN SATP32_PPN +#define SATP_MODE SATP32_MODE +#define SATP_ASID SATP32_ASID +#define SATP_PPN SATP32_PPN #endif #if defined(TARGET_RISCV64) -#define SATP_MODE SATP64_MODE -#define SATP_ASID SATP64_ASID -#define SATP_PPN SATP64_PPN +#define SATP_MODE SATP64_MODE +#define SATP_ASID SATP64_ASID +#define SATP_PPN SATP64_PPN #endif -/* RISCV Exception Codes */ -#define EXCP_NONE -1 /* not a real RISCV exception code */ +/* VM modes (mstatus.vm) privileged ISA 1.9.1 */ +#define VM_1_09_MBARE 0 +#define VM_1_09_MBB 1 +#define VM_1_09_MBBID 2 +#define VM_1_09_SV32 8 +#define VM_1_09_SV39 9 +#define VM_1_09_SV48 10 + +/* VM modes (satp.mode) privileged ISA 1.10 */ +#define VM_1_10_MBARE 0 +#define VM_1_10_SV32 1 +#define VM_1_10_SV39 8 +#define VM_1_10_SV48 9 +#define VM_1_10_SV57 10 +#define VM_1_10_SV64 11 + +/* Page table entry (PTE) fields */ +#define PTE_V 0x001 /* Valid */ +#define PTE_R 0x002 /* Read */ +#define PTE_W 0x004 /* Write */ +#define PTE_X 0x008 /* Execute */ +#define PTE_U 0x010 /* User */ +#define PTE_G 0x020 /* Global */ +#define PTE_A 0x040 /* Accessed */ +#define PTE_D 0x080 /* Dirty */ +#define PTE_SOFT 0x300 /* Reserved for Software */ + +/* Page table PPN shift amount */ +#define PTE_PPN_SHIFT 10 + +/* Leaf page shift amount */ +#define PGSHIFT 12 + +/* Default Reset Vector adress */ +#define DEFAULT_RSTVEC 0x1000 + +/* Exception causes */ +#define EXCP_NONE -1 /* sentinel value */ #define RISCV_EXCP_INST_ADDR_MIS 0x0 #define RISCV_EXCP_INST_ACCESS_FAULT 0x1 #define RISCV_EXCP_ILLEGAL_INST 0x2 @@ -382,9 +411,7 @@ #define RISCV_EXCP_LOAD_ACCESS_FAULT 0x5 #define RISCV_EXCP_STORE_AMO_ADDR_MIS 0x6 #define RISCV_EXCP_STORE_AMO_ACCESS_FAULT 0x7 -#define RISCV_EXCP_U_ECALL 0x8 /* for convenience, report all - ECALLs as this, handler - fixes */ +#define RISCV_EXCP_U_ECALL 0x8 #define RISCV_EXCP_S_ECALL 0x9 #define RISCV_EXCP_H_ECALL 0xa #define RISCV_EXCP_M_ECALL 0xb @@ -395,15 +422,35 @@ #define RISCV_EXCP_INT_FLAG 0x80000000 #define RISCV_EXCP_INT_MASK 0x7fffffff -/* page table entry (PTE) fields */ -#define PTE_V 0x001 /* Valid */ -#define PTE_R 0x002 /* Read */ -#define PTE_W 0x004 /* Write */ -#define PTE_X 0x008 /* Execute */ -#define PTE_U 0x010 /* User */ -#define PTE_G 0x020 /* Global */ -#define PTE_A 0x040 /* Accessed */ -#define PTE_D 0x080 /* Dirty */ -#define PTE_SOFT 0x300 /* Reserved for Software */ - -#define PTE_PPN_SHIFT 10 +/* Interrupt causes */ +#define IRQ_U_SOFT 0 +#define IRQ_S_SOFT 1 +#define IRQ_H_SOFT 2 /* reserved */ +#define IRQ_M_SOFT 3 +#define IRQ_U_TIMER 4 +#define IRQ_S_TIMER 5 +#define IRQ_H_TIMER 6 /* reserved */ +#define IRQ_M_TIMER 7 +#define IRQ_U_EXT 8 +#define IRQ_S_EXT 9 +#define IRQ_H_EXT 10 /* reserved */ +#define IRQ_M_EXT 11 + +/* mip masks */ +#define MIP_USIP (1 << IRQ_U_SOFT) +#define MIP_SSIP (1 << IRQ_S_SOFT) +#define MIP_HSIP (1 << IRQ_H_SOFT) +#define MIP_MSIP (1 << IRQ_M_SOFT) +#define MIP_UTIP (1 << IRQ_U_TIMER) +#define MIP_STIP (1 << IRQ_S_TIMER) +#define MIP_HTIP (1 << IRQ_H_TIMER) +#define MIP_MTIP (1 << IRQ_M_TIMER) +#define MIP_UEIP (1 << IRQ_U_EXT) +#define MIP_SEIP (1 << IRQ_S_EXT) +#define MIP_HEIP (1 << IRQ_H_EXT) +#define MIP_MEIP (1 << IRQ_M_EXT) + +/* sip masks */ +#define SIP_SSIP MIP_SSIP +#define SIP_STIP MIP_STIP +#define SIP_SEIP MIP_SEIP diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 495390ab1c3..3726299d4a4 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -90,7 +90,7 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, target_ulong csrno) { #ifndef CONFIG_USER_ONLY - uint64_t delegable_ints = MIP_SSIP | MIP_STIP | MIP_SEIP | (1 << IRQ_X_COP); + uint64_t delegable_ints = MIP_SSIP | MIP_STIP | MIP_SEIP; uint64_t all_ints = delegable_ints | MIP_MSIP | MIP_MTIP; #endif From c1c269684bf6067d5d6c52b361b3c09e2bbde8af Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sun, 8 Apr 2018 13:08:13 +1200 Subject: [PATCH 38/76] RISC-V: Implement modular CSR helper interface Previous CSR code uses csr_read_helper and csr_write_helper to update CSR registers however this interface prevents atomic read/modify/write CSR operations; in addition there is no trap-free method to access to CSRs due to the monolithic CSR functions call longjmp. The current iCSR interface is not safe to be called by target/riscv/gdbstub.c as privilege checks or missing CSRs may call longjmp to generate exceptions. It needs to indicate existence so traps can be generated in the CSR instruction helpers. This commit moves CSR access from the monolithic switch statements in target/riscv/op_helper.c into modular read/write functions in target/riscv/csr.c using a new function pointer table for dispatch (which can later be used to allow CPUs to hook up model specific CSRs). A read/modify/write interface is added to support atomic CSR operations and a non-trapping interface is added to allow exception-free access to CSRs by the debugger. The CSR functions and CSR dispatch table are ordered to match The RISC-V Instruction Set Manual, Volume II: Privileged Architecture Version 1.10, 2.2 CSR Listing. An API is added to allow derived cpu instances to modify or implement new CSR operations. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- target/riscv/Makefile.objs | 2 +- target/riscv/cpu.h | 36 +- target/riscv/cpu_helper.c | 4 +- target/riscv/csr.c | 860 +++++++++++++++++++++++++++++++++++++ target/riscv/gdbstub.c | 10 +- target/riscv/op_helper.c | 613 +------------------------- 6 files changed, 919 insertions(+), 606 deletions(-) create mode 100644 target/riscv/csr.c diff --git a/target/riscv/Makefile.objs b/target/riscv/Makefile.objs index fcc5d34c1f2..4072abe3e45 100644 --- a/target/riscv/Makefile.objs +++ b/target/riscv/Makefile.objs @@ -1 +1 @@ -obj-y += translate.o op_helper.o cpu_helper.o cpu.o fpu_helper.o gdbstub.o pmp.o +obj-y += translate.o op_helper.o cpu_helper.o cpu.o csr.o fpu_helper.o gdbstub.o pmp.o diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index c5d485769cd..9168afd5d75 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -290,9 +290,39 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, #endif } -void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, - target_ulong csrno); -target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno); +int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, + target_ulong new_value, target_ulong write_mask); + +static inline void csr_write_helper(CPURISCVState *env, target_ulong val, + int csrno) +{ + riscv_csrrw(env, csrno, NULL, val, -1); +} + +static inline target_ulong csr_read_helper(CPURISCVState *env, int csrno) +{ + target_ulong val = 0; + riscv_csrrw(env, csrno, &val, 0, 0); + return val; +} + +typedef int (*riscv_csr_predicate_fn)(CPURISCVState *env, int csrno); +typedef int (*riscv_csr_read_fn)(CPURISCVState *env, int csrno, + target_ulong *ret_value); +typedef int (*riscv_csr_write_fn)(CPURISCVState *env, int csrno, + target_ulong new_value); +typedef int (*riscv_csr_op_fn)(CPURISCVState *env, int csrno, + target_ulong *ret_value, target_ulong new_value, target_ulong write_mask); + +typedef struct { + riscv_csr_predicate_fn predicate; + riscv_csr_read_fn read; + riscv_csr_write_fn write; + riscv_csr_op_fn op; +} riscv_csr_operations; + +void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops); +void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops); #include "exec/cpu-all.h" diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 86f9f4730c8..8e0b83c6ec9 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -526,7 +526,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) get_field(s, MSTATUS_SIE) : get_field(s, MSTATUS_UIE << env->priv)); s = set_field(s, MSTATUS_SPP, env->priv); s = set_field(s, MSTATUS_SIE, 0); - csr_write_helper(env, s, CSR_MSTATUS); + env->mstatus = s; riscv_set_mode(env, PRV_S); } else { /* No need to check MTVEC for misaligned - lower 2 bits cannot be set */ @@ -551,7 +551,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) get_field(s, MSTATUS_MIE) : get_field(s, MSTATUS_UIE << env->priv)); s = set_field(s, MSTATUS_MPP, env->priv); s = set_field(s, MSTATUS_MIE, 0); - csr_write_helper(env, s, CSR_MSTATUS); + env->mstatus = s; riscv_set_mode(env, PRV_M); } /* TODO yield load reservation */ diff --git a/target/riscv/csr.c b/target/riscv/csr.c new file mode 100644 index 00000000000..40158038eda --- /dev/null +++ b/target/riscv/csr.c @@ -0,0 +1,860 @@ +/* + * RISC-V Control and Status Registers. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "cpu.h" +#include "qemu/main-loop.h" +#include "exec/exec-all.h" + +/* CSR function table */ + +static riscv_csr_operations csr_ops[]; + +/* CSR function table constants */ + +enum { + CSR_TABLE_SIZE = 0x1000 +}; + +/* CSR function table public API */ + +void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) +{ + *ops = csr_ops[csrno & (CSR_TABLE_SIZE - 1)]; +} + +void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops) +{ + csr_ops[csrno & (CSR_TABLE_SIZE - 1)] = *ops; +} + +/* User Floating-Point CSRs */ + +static int read_fflags(CPURISCVState *env, int csrno, target_ulong *val) +{ +#if !defined(CONFIG_USER_ONLY) + if (!(env->mstatus & MSTATUS_FS)) { + return -1; + } +#endif + *val = cpu_riscv_get_fflags(env); + return 0; +} + +static int write_fflags(CPURISCVState *env, int csrno, target_ulong val) +{ +#if !defined(CONFIG_USER_ONLY) + if (!(env->mstatus & MSTATUS_FS)) { + return -1; + } + env->mstatus |= MSTATUS_FS; +#endif + cpu_riscv_set_fflags(env, val & (FSR_AEXC >> FSR_AEXC_SHIFT)); + return 0; +} + +static int read_frm(CPURISCVState *env, int csrno, target_ulong *val) +{ +#if !defined(CONFIG_USER_ONLY) + if (!(env->mstatus & MSTATUS_FS)) { + return -1; + } +#endif + *val = env->frm; + return 0; +} + +static int write_frm(CPURISCVState *env, int csrno, target_ulong val) +{ +#if !defined(CONFIG_USER_ONLY) + if (!(env->mstatus & MSTATUS_FS)) { + return -1; + } + env->mstatus |= MSTATUS_FS; +#endif + env->frm = val & (FSR_RD >> FSR_RD_SHIFT); + return 0; +} + +static int read_fcsr(CPURISCVState *env, int csrno, target_ulong *val) +{ +#if !defined(CONFIG_USER_ONLY) + if (!(env->mstatus & MSTATUS_FS)) { + return -1; + } +#endif + *val = (cpu_riscv_get_fflags(env) << FSR_AEXC_SHIFT) + | (env->frm << FSR_RD_SHIFT); + return 0; +} + +static int write_fcsr(CPURISCVState *env, int csrno, target_ulong val) +{ +#if !defined(CONFIG_USER_ONLY) + if (!(env->mstatus & MSTATUS_FS)) { + return -1; + } + env->mstatus |= MSTATUS_FS; +#endif + env->frm = (val & FSR_RD) >> FSR_RD_SHIFT; + cpu_riscv_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT); + return 0; +} + +/* User Timers and Counters */ + +static int counter_enabled(CPURISCVState *env, int csrno) +{ +#ifndef CONFIG_USER_ONLY + target_ulong ctr_en = env->priv == PRV_U ? env->scounteren : + env->priv == PRV_S ? env->mcounteren : -1U; +#else + target_ulong ctr_en = -1; +#endif + return (ctr_en >> (csrno & 31)) & 1; +} + +#if !defined(CONFIG_USER_ONLY) +static int read_zero_counter(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (!counter_enabled(env, csrno)) { + return -1; + } + *val = 0; + return 0; +} +#endif + +static int read_instret(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (!counter_enabled(env, csrno)) { + return -1; + } +#if !defined(CONFIG_USER_ONLY) + if (use_icount) { + *val = cpu_get_icount(); + } else { + *val = cpu_get_host_ticks(); + } +#else + *val = cpu_get_host_ticks(); +#endif + return 0; +} + +#if defined(TARGET_RISCV32) +static int read_instreth(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (!counter_enabled(env, csrno)) { + return -1; + } +#if !defined(CONFIG_USER_ONLY) + if (use_icount) { + *val = cpu_get_icount() >> 32; + } else { + *val = cpu_get_host_ticks() >> 32; + } +#else + *val = cpu_get_host_ticks() >> 32; +#endif + return 0; +} +#endif + +#if defined(CONFIG_USER_ONLY) + +static int read_time(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = cpu_get_host_ticks(); + return 0; +} + +#if defined(TARGET_RISCV32) +static int read_timeh(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = cpu_get_host_ticks() >> 32; + return 0; +} +#endif + +#else + +/* Machine constants */ + +#define M_MODE_INTERRUPTS (MIP_MSIP | MIP_MTIP | MIP_MEIP) +#define S_MODE_INTERRUPTS (MIP_SSIP | MIP_STIP | MIP_SEIP) + +static const target_ulong delegable_ints = S_MODE_INTERRUPTS; +static const target_ulong all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS; +static const target_ulong delegable_excps = + (1ULL << (RISCV_EXCP_INST_ADDR_MIS)) | + (1ULL << (RISCV_EXCP_INST_ACCESS_FAULT)) | + (1ULL << (RISCV_EXCP_ILLEGAL_INST)) | + (1ULL << (RISCV_EXCP_BREAKPOINT)) | + (1ULL << (RISCV_EXCP_LOAD_ADDR_MIS)) | + (1ULL << (RISCV_EXCP_LOAD_ACCESS_FAULT)) | + (1ULL << (RISCV_EXCP_STORE_AMO_ADDR_MIS)) | + (1ULL << (RISCV_EXCP_STORE_AMO_ACCESS_FAULT)) | + (1ULL << (RISCV_EXCP_U_ECALL)) | + (1ULL << (RISCV_EXCP_S_ECALL)) | + (1ULL << (RISCV_EXCP_H_ECALL)) | + (1ULL << (RISCV_EXCP_M_ECALL)) | + (1ULL << (RISCV_EXCP_INST_PAGE_FAULT)) | + (1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT)) | + (1ULL << (RISCV_EXCP_STORE_PAGE_FAULT)); +static const target_ulong sstatus_v1_9_mask = SSTATUS_SIE | SSTATUS_SPIE | + SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS | + SSTATUS_SUM | SSTATUS_SD; +static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE | + SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS | + SSTATUS_SUM | SSTATUS_MXR | SSTATUS_SD; + +#if defined(TARGET_RISCV32) +static const char valid_vm_1_09[16] = { + [VM_1_09_MBARE] = 1, + [VM_1_09_SV32] = 1, +}; +static const char valid_vm_1_10[16] = { + [VM_1_10_MBARE] = 1, + [VM_1_10_SV32] = 1 +}; +#elif defined(TARGET_RISCV64) +static const char valid_vm_1_09[16] = { + [VM_1_09_MBARE] = 1, + [VM_1_09_SV39] = 1, + [VM_1_09_SV48] = 1, +}; +static const char valid_vm_1_10[16] = { + [VM_1_10_MBARE] = 1, + [VM_1_10_SV39] = 1, + [VM_1_10_SV48] = 1, + [VM_1_10_SV57] = 1 +}; +#endif + +/* Machine Information Registers */ + +static int read_zero(CPURISCVState *env, int csrno, target_ulong *val) +{ + return *val = 0; +} + +static int read_mhartid(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mhartid; + return 0; +} + +/* Machine Trap Setup */ + +static int read_mstatus(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mstatus; + return 0; +} + +static int validate_vm(CPURISCVState *env, target_ulong vm) +{ + return (env->priv_ver >= PRIV_VERSION_1_10_0) ? + valid_vm_1_10[vm & 0xf] : valid_vm_1_09[vm & 0xf]; +} + +static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) +{ + target_ulong mstatus = env->mstatus; + target_ulong mask = 0; + target_ulong mpp = get_field(val, MSTATUS_MPP); + + /* flush tlb on mstatus fields that affect VM */ + if (env->priv_ver <= PRIV_VERSION_1_09_1) { + if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | + MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_VM)) { + tlb_flush(CPU(riscv_env_get_cpu(env))); + } + mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | + MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | + MSTATUS_MPP | MSTATUS_MXR | + (validate_vm(env, get_field(val, MSTATUS_VM)) ? + MSTATUS_VM : 0); + } + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | + MSTATUS_MPRV | MSTATUS_SUM)) { + tlb_flush(CPU(riscv_env_get_cpu(env))); + } + mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | + MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | + MSTATUS_MPP | MSTATUS_MXR; + } + + /* silenty discard mstatus.mpp writes for unsupported modes */ + if (mpp == PRV_H || + (!riscv_has_ext(env, RVS) && mpp == PRV_S) || + (!riscv_has_ext(env, RVU) && mpp == PRV_U)) { + mask &= ~MSTATUS_MPP; + } + + mstatus = (mstatus & ~mask) | (val & mask); + + /* Note: this is a workaround for an issue where mstatus.FS + does not report dirty after floating point operations + that modify floating point state. This workaround is + technically compliant with the RISC-V Privileged + specification as it is legal to return only off, or dirty. + at the expense of extra floating point save/restore. */ + + /* FP is always dirty or off */ + if (mstatus & MSTATUS_FS) { + mstatus |= MSTATUS_FS; + } + + int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | + ((mstatus & MSTATUS_XS) == MSTATUS_XS); + mstatus = set_field(mstatus, MSTATUS_SD, dirty); + env->mstatus = mstatus; + + return 0; +} + +static int read_misa(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->misa; + return 0; +} + +static int read_medeleg(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->medeleg; + return 0; +} + +static int write_medeleg(CPURISCVState *env, int csrno, target_ulong val) +{ + env->medeleg = (env->medeleg & ~delegable_excps) | (val & delegable_excps); + return 0; +} + +static int read_mideleg(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mideleg; + return 0; +} + +static int write_mideleg(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mideleg = (env->mideleg & ~delegable_ints) | (val & delegable_ints); + return 0; +} + +static int read_mie(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mie; + return 0; +} + +static int write_mie(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mie = (env->mie & ~all_ints) | (val & all_ints); + return 0; +} + +static int read_mtvec(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mtvec; + return 0; +} + +static int write_mtvec(CPURISCVState *env, int csrno, target_ulong val) +{ + /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ + if ((val & 3) == 0) { + env->mtvec = val >> 2 << 2; + } else { + qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: vectored traps not supported"); + } + return 0; +} + +static int read_mcounteren(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (env->priv_ver < PRIV_VERSION_1_10_0) { + return -1; + } + *val = env->mcounteren; + return 0; +} + +static int write_mcounteren(CPURISCVState *env, int csrno, target_ulong val) +{ + if (env->priv_ver < PRIV_VERSION_1_10_0) { + return -1; + } + env->mcounteren = val; + return 0; +} + +static int read_mscounteren(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (env->priv_ver > PRIV_VERSION_1_09_1) { + return -1; + } + *val = env->mcounteren; + return 0; +} + +static int write_mscounteren(CPURISCVState *env, int csrno, target_ulong val) +{ + if (env->priv_ver > PRIV_VERSION_1_09_1) { + return -1; + } + env->mcounteren = val; + return 0; +} + +static int read_mucounteren(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (env->priv_ver > PRIV_VERSION_1_09_1) { + return -1; + } + *val = env->scounteren; + return 0; +} + +static int write_mucounteren(CPURISCVState *env, int csrno, target_ulong val) +{ + if (env->priv_ver > PRIV_VERSION_1_09_1) { + return -1; + } + env->scounteren = val; + return 0; +} + +/* Machine Trap Handling */ + +static int read_mscratch(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mscratch; + return 0; +} + +static int write_mscratch(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mscratch = val; + return 0; +} + +static int read_mepc(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mepc; + return 0; +} + +static int write_mepc(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mepc = val; + return 0; +} + +static int read_mcause(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mcause; + return 0; +} + +static int write_mcause(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mcause = val; + return 0; +} + +static int read_mbadaddr(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mbadaddr; + return 0; +} + +static int write_mbadaddr(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mbadaddr = val; + return 0; +} + +static int read_mip(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = atomic_read(&env->mip); + return 0; +} + +static int write_mip(CPURISCVState *env, int csrno, target_ulong val) +{ + RISCVCPU *cpu = riscv_env_get_cpu(env); + + /* + * csrs, csrc on mip.SEIP is not decomposable into separate read and + * write steps, so a different implementation is needed + */ + + qemu_mutex_lock_iothread(); + riscv_cpu_update_mip(cpu, MIP_SSIP | MIP_STIP, + (val & (MIP_SSIP | MIP_STIP))); + qemu_mutex_unlock_iothread(); + + return 0; +} + +/* Supervisor Trap Setup */ + +static int read_sstatus(CPURISCVState *env, int csrno, target_ulong *val) +{ + target_ulong mask = ((env->priv_ver >= PRIV_VERSION_1_10_0) ? + sstatus_v1_10_mask : sstatus_v1_9_mask); + *val = env->mstatus & mask; + return 0; +} + +static int write_sstatus(CPURISCVState *env, int csrno, target_ulong val) +{ + target_ulong mask = ((env->priv_ver >= PRIV_VERSION_1_10_0) ? + sstatus_v1_10_mask : sstatus_v1_9_mask); + target_ulong newval = (env->mstatus & ~mask) | (val & mask); + return write_mstatus(env, CSR_MSTATUS, newval); +} + +static int read_sie(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mie & env->mideleg; + return 0; +} + +static int write_sie(CPURISCVState *env, int csrno, target_ulong val) +{ + target_ulong newval = (env->mie & ~env->mideleg) | (val & env->mideleg); + return write_mie(env, CSR_MIE, newval); +} + +static int read_stvec(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->stvec; + return 0; +} + +static int write_stvec(CPURISCVState *env, int csrno, target_ulong val) +{ + /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ + if ((val & 3) == 0) { + env->stvec = val >> 2 << 2; + } else { + qemu_log_mask(LOG_UNIMP, "CSR_STVEC: vectored traps not supported"); + } + return 0; +} + +static int read_scounteren(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (env->priv_ver < PRIV_VERSION_1_10_0) { + return -1; + } + *val = env->scounteren; + return 0; +} + +static int write_scounteren(CPURISCVState *env, int csrno, target_ulong val) +{ + if (env->priv_ver < PRIV_VERSION_1_10_0) { + return -1; + } + env->scounteren = val; + return 0; +} + +/* Supervisor Trap Handling */ + +static int read_sscratch(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->sscratch; + return 0; +} + +static int write_sscratch(CPURISCVState *env, int csrno, target_ulong val) +{ + env->sscratch = val; + return 0; +} + +static int read_sepc(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->sepc; + return 0; +} + +static int write_sepc(CPURISCVState *env, int csrno, target_ulong val) +{ + env->sepc = val; + return 0; +} + +static int read_scause(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->scause; + return 0; +} + +static int write_scause(CPURISCVState *env, int csrno, target_ulong val) +{ + env->scause = val; + return 0; +} + +static int read_sbadaddr(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->sbadaddr; + return 0; +} + +static int write_sbadaddr(CPURISCVState *env, int csrno, target_ulong val) +{ + env->sbadaddr = val; + return 0; +} + +static int read_sip(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = atomic_read(&env->mip) & env->mideleg; + return 0; +} + +static int write_sip(CPURISCVState *env, int csrno, target_ulong val) +{ + target_ulong newval = (atomic_read(&env->mip) & ~env->mideleg) + | (val & env->mideleg); + return write_mip(env, CSR_MIP, newval); +} + +/* Supervisor Protection and Translation */ + +static int read_satp(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (!riscv_feature(env, RISCV_FEATURE_MMU)) { + *val = 0; + } else if (env->priv_ver >= PRIV_VERSION_1_10_0) { + *val = env->satp; + } else { + *val = env->sptbr; + } + return 0; +} + +static int write_satp(CPURISCVState *env, int csrno, target_ulong val) +{ + if (!riscv_feature(env, RISCV_FEATURE_MMU)) { + return 0; + } + if (env->priv_ver <= PRIV_VERSION_1_09_1 && (val ^ env->sptbr)) { + tlb_flush(CPU(riscv_env_get_cpu(env))); + env->sptbr = val & (((target_ulong) + 1 << (TARGET_PHYS_ADDR_SPACE_BITS - PGSHIFT)) - 1); + } + if (env->priv_ver >= PRIV_VERSION_1_10_0 && + validate_vm(env, get_field(val, SATP_MODE)) && + ((val ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN))) + { + tlb_flush(CPU(riscv_env_get_cpu(env))); + env->satp = val; + } + return 0; +} + +/* Physical Memory Protection */ + +static int read_pmpcfg(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = pmpcfg_csr_read(env, csrno - CSR_PMPCFG0); + return 0; +} + +static int write_pmpcfg(CPURISCVState *env, int csrno, target_ulong val) +{ + pmpcfg_csr_write(env, csrno - CSR_PMPCFG0, val); + return 0; +} + +static int read_pmpaddr(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = pmpaddr_csr_read(env, csrno - CSR_PMPADDR0); + return 0; +} + +static int write_pmpaddr(CPURISCVState *env, int csrno, target_ulong val) +{ + pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val); + return 0; +} + +#endif + +/* + * riscv_csrrw - read and/or update control and status register + * + * csrr <-> riscv_csrrw(env, csrno, ret_value, 0, 0); + * csrrw <-> riscv_csrrw(env, csrno, ret_value, value, -1); + * csrrs <-> riscv_csrrw(env, csrno, ret_value, -1, value); + * csrrc <-> riscv_csrrw(env, csrno, ret_value, 0, value); + */ + +int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, + target_ulong new_value, target_ulong write_mask) +{ + int ret; + target_ulong old_value; + + /* check privileges and return -1 if check fails */ +#if !defined(CONFIG_USER_ONLY) + int csr_priv = get_field(csrno, 0x300); + int read_only = get_field(csrno, 0xC00) == 3; + if ((write_mask && read_only) || (env->priv < csr_priv)) { + return -1; + } +#endif + + /* execute combined read/write operation if it exists */ + if (csr_ops[csrno].op) { + return csr_ops[csrno].op(env, csrno, ret_value, new_value, write_mask); + } + + /* if no accessor exists then return failure */ + if (!csr_ops[csrno].read) { + return -1; + } + + /* read old value */ + ret = csr_ops[csrno].read(env, csrno, &old_value); + if (ret < 0) { + return ret; + } + + /* write value if writable and write mask set, otherwise drop writes */ + if (write_mask) { + new_value = (old_value & ~write_mask) | (new_value & write_mask); + if (csr_ops[csrno].write) { + ret = csr_ops[csrno].write(env, csrno, new_value); + if (ret < 0) { + return ret; + } + } + } + + /* return old value */ + if (ret_value) { + *ret_value = old_value; + } + + return 0; +} + +/* Control and Status Register function table */ + +static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { + /* User Floating-Point CSRs */ + [CSR_FFLAGS] = { read_fflags, write_fflags }, + [CSR_FRM] = { read_frm, write_frm }, + [CSR_FCSR] = { read_fcsr, write_fcsr }, + + /* User Timers and Counters */ + [CSR_CYCLE] = { read_instret }, + [CSR_INSTRET] = { read_instret }, +#if defined(TARGET_RISCV32) + [CSR_CYCLEH] = { read_instreth }, + [CSR_INSTRETH] = { read_instreth }, +#endif + + /* User-level time CSRs are only available in linux-user + * In privileged mode, the monitor emulates these CSRs */ +#if defined(CONFIG_USER_ONLY) + [CSR_TIME] = { read_time }, +#if defined(TARGET_RISCV32) + [CSR_TIMEH] = { read_timeh }, +#endif +#endif + +#if !defined(CONFIG_USER_ONLY) + /* Machine Timers and Counters */ + [CSR_MCYCLE] = { read_instret }, + [CSR_MINSTRET] = { read_instret }, +#if defined(TARGET_RISCV32) + [CSR_MCYCLEH] = { read_instreth }, + [CSR_MINSTRETH] = { read_instreth }, +#endif + + /* Machine Information Registers */ + [CSR_MVENDORID] = { read_zero }, + [CSR_MARCHID] = { read_zero }, + [CSR_MIMPID] = { read_zero }, + [CSR_MHARTID] = { read_mhartid }, + + /* Machine Trap Setup */ + [CSR_MSTATUS] = { read_mstatus, write_mstatus }, + [CSR_MISA] = { read_misa }, + [CSR_MIDELEG] = { read_mideleg, write_mideleg }, + [CSR_MEDELEG] = { read_medeleg, write_medeleg }, + [CSR_MIE] = { read_mie, write_mie }, + [CSR_MTVEC] = { read_mtvec, write_mtvec }, + [CSR_MCOUNTEREN] = { read_mcounteren, write_mcounteren }, + + /* Legacy Counter Setup (priv v1.9.1) */ + [CSR_MUCOUNTEREN] = { read_mucounteren, write_mucounteren }, + [CSR_MSCOUNTEREN] = { read_mscounteren, write_mscounteren }, + + /* Machine Trap Handling */ + [CSR_MSCRATCH] = { read_mscratch, write_mscratch }, + [CSR_MEPC] = { read_mepc, write_mepc }, + [CSR_MCAUSE] = { read_mcause, write_mcause }, + [CSR_MBADADDR] = { read_mbadaddr, write_mbadaddr }, + [CSR_MIP] = { read_mip, write_mip }, + + /* Supervisor Trap Setup */ + [CSR_SSTATUS] = { read_sstatus, write_sstatus }, + [CSR_SIE] = { read_sie, write_sie }, + [CSR_STVEC] = { read_stvec, write_stvec }, + [CSR_SCOUNTEREN] = { read_scounteren, write_scounteren }, + + /* Supervisor Trap Handling */ + [CSR_SSCRATCH] = { read_sscratch, write_sscratch }, + [CSR_SEPC] = { read_sepc, write_sepc }, + [CSR_SCAUSE] = { read_scause, write_scause }, + [CSR_SBADADDR] = { read_sbadaddr, write_sbadaddr }, + [CSR_SIP] = { read_sip, write_sip }, + + /* Supervisor Protection and Translation */ + [CSR_SATP] = { read_satp, write_satp }, + + /* Physical Memory Protection */ + [CSR_PMPCFG0 ... CSR_PMPADDR9] = { read_pmpcfg, write_pmpcfg }, + [CSR_PMPADDR0 ... CSR_PMPADDR15] = { read_pmpaddr, write_pmpaddr }, + + /* Performance Counters */ + [CSR_HPMCOUNTER3 ... CSR_HPMCOUNTER31] = { read_zero_counter }, + [CSR_MHPMCOUNTER3 ... CSR_MHPMCOUNTER31] = { read_zero }, + [CSR_MHPMEVENT3 ... CSR_MHPMEVENT31] = { read_zero }, +#if defined(TARGET_RISCV32) + [CSR_HPMCOUNTER3H ... CSR_HPMCOUNTER31H] = { read_zero_counter }, + [CSR_MHPMCOUNTER3H ... CSR_MHPMCOUNTER31H] = { read_zero }, +#endif +#endif +}; diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index 4f919b6c341..3cabb21cd01 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -33,7 +33,10 @@ int riscv_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) } else if (n < 65) { return gdb_get_reg64(mem_buf, env->fpr[n - 33]); } else if (n < 4096 + 65) { - return gdb_get_regl(mem_buf, csr_read_helper(env, n - 65)); + target_ulong val = 0; + if (riscv_csrrw(env, n - 65, &val, 0, 0) == 0) { + return gdb_get_regl(mem_buf, val); + } } return 0; } @@ -56,7 +59,10 @@ int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->fpr[n - 33] = ldq_p(mem_buf); /* always 64-bit */ return sizeof(uint64_t); } else if (n < 4096 + 65) { - csr_write_helper(env, ldtul_p(mem_buf), n - 65); + target_ulong val = ldtul_p(mem_buf); + if (riscv_csrrw(env, n - 65, NULL, val, -1) == 0) { + return sizeof(target_ulong); + } } return 0; } diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 3726299d4a4..81bd1a77ea9 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -24,39 +24,6 @@ #include "exec/exec-all.h" #include "exec/helper-proto.h" -#ifndef CONFIG_USER_ONLY - -#if defined(TARGET_RISCV32) -static const char valid_vm_1_09[16] = { - [VM_1_09_MBARE] = 1, - [VM_1_09_SV32] = 1, -}; -static const char valid_vm_1_10[16] = { - [VM_1_10_MBARE] = 1, - [VM_1_10_SV32] = 1 -}; -#elif defined(TARGET_RISCV64) -static const char valid_vm_1_09[16] = { - [VM_1_09_MBARE] = 1, - [VM_1_09_SV39] = 1, - [VM_1_09_SV48] = 1, -}; -static const char valid_vm_1_10[16] = { - [VM_1_10_MBARE] = 1, - [VM_1_10_SV39] = 1, - [VM_1_10_SV48] = 1, - [VM_1_10_SV57] = 1 -}; -#endif - -static int validate_vm(CPURISCVState *env, target_ulong vm) -{ - return (env->priv_ver >= PRIV_VERSION_1_10_0) ? - valid_vm_1_10[vm & 0xf] : valid_vm_1_09[vm & 0xf]; -} - -#endif - /* Exceptions processing helpers */ void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env, uint32_t exception, uintptr_t pc) @@ -72,584 +39,34 @@ void helper_raise_exception(CPURISCVState *env, uint32_t exception) do_raise_exception_err(env, exception, 0); } -static void validate_mstatus_fs(CPURISCVState *env, uintptr_t ra) -{ -#ifndef CONFIG_USER_ONLY - if (!(env->mstatus & MSTATUS_FS)) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, ra); - } -#endif -} - -/* - * Handle writes to CSRs and any resulting special behavior - * - * Adapted from Spike's processor_t::set_csr - */ -void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, - target_ulong csrno) -{ -#ifndef CONFIG_USER_ONLY - uint64_t delegable_ints = MIP_SSIP | MIP_STIP | MIP_SEIP; - uint64_t all_ints = delegable_ints | MIP_MSIP | MIP_MTIP; -#endif - - switch (csrno) { - case CSR_FFLAGS: - validate_mstatus_fs(env, GETPC()); - cpu_riscv_set_fflags(env, val_to_write & (FSR_AEXC >> FSR_AEXC_SHIFT)); - break; - case CSR_FRM: - validate_mstatus_fs(env, GETPC()); - env->frm = val_to_write & (FSR_RD >> FSR_RD_SHIFT); - break; - case CSR_FCSR: - validate_mstatus_fs(env, GETPC()); - env->frm = (val_to_write & FSR_RD) >> FSR_RD_SHIFT; - cpu_riscv_set_fflags(env, (val_to_write & FSR_AEXC) >> FSR_AEXC_SHIFT); - break; -#ifndef CONFIG_USER_ONLY - case CSR_MSTATUS: { - target_ulong mstatus = env->mstatus; - target_ulong mask = 0; - target_ulong mpp = get_field(val_to_write, MSTATUS_MPP); - - /* flush tlb on mstatus fields that affect VM */ - if (env->priv_ver <= PRIV_VERSION_1_09_1) { - if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | - MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_VM)) { - helper_tlb_flush(env); - } - mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | - MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | - MSTATUS_MPP | MSTATUS_MXR | - (validate_vm(env, get_field(val_to_write, MSTATUS_VM)) ? - MSTATUS_VM : 0); - } - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | - MSTATUS_MPRV | MSTATUS_SUM)) { - helper_tlb_flush(env); - } - mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | - MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | - MSTATUS_MPP | MSTATUS_MXR; - } - - /* silenty discard mstatus.mpp writes for unsupported modes */ - if (mpp == PRV_H || - (!riscv_has_ext(env, RVS) && mpp == PRV_S) || - (!riscv_has_ext(env, RVU) && mpp == PRV_U)) { - mask &= ~MSTATUS_MPP; - } - - mstatus = (mstatus & ~mask) | (val_to_write & mask); - - /* Note: this is a workaround for an issue where mstatus.FS - does not report dirty after floating point operations - that modify floating point state. This workaround is - technically compliant with the RISC-V Privileged - specification as it is legal to return only off, or dirty. - at the expense of extra floating point save/restore. */ - - /* FP is always dirty or off */ - if (mstatus & MSTATUS_FS) { - mstatus |= MSTATUS_FS; - } - - int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | - ((mstatus & MSTATUS_XS) == MSTATUS_XS); - mstatus = set_field(mstatus, MSTATUS_SD, dirty); - env->mstatus = mstatus; - break; - } - case CSR_MIP: { - /* - * Since the writeable bits in MIP are not set asynchrously by the - * CLINT, no additional locking is needed for read-modifiy-write - * CSR operations - */ - qemu_mutex_lock_iothread(); - RISCVCPU *cpu = riscv_env_get_cpu(env); - riscv_cpu_update_mip(cpu, MIP_SSIP | MIP_STIP, - (val_to_write & (MIP_SSIP | MIP_STIP))); - /* - * csrs, csrc on mip.SEIP is not decomposable into separate read and - * write steps, so a different implementation is needed - */ - qemu_mutex_unlock_iothread(); - break; - } - case CSR_MIE: { - env->mie = (env->mie & ~all_ints) | - (val_to_write & all_ints); - break; - } - case CSR_MIDELEG: - env->mideleg = (env->mideleg & ~delegable_ints) - | (val_to_write & delegable_ints); - break; - case CSR_MEDELEG: { - target_ulong mask = 0; - mask |= 1ULL << (RISCV_EXCP_INST_ADDR_MIS); - mask |= 1ULL << (RISCV_EXCP_INST_ACCESS_FAULT); - mask |= 1ULL << (RISCV_EXCP_ILLEGAL_INST); - mask |= 1ULL << (RISCV_EXCP_BREAKPOINT); - mask |= 1ULL << (RISCV_EXCP_LOAD_ADDR_MIS); - mask |= 1ULL << (RISCV_EXCP_LOAD_ACCESS_FAULT); - mask |= 1ULL << (RISCV_EXCP_STORE_AMO_ADDR_MIS); - mask |= 1ULL << (RISCV_EXCP_STORE_AMO_ACCESS_FAULT); - mask |= 1ULL << (RISCV_EXCP_U_ECALL); - mask |= 1ULL << (RISCV_EXCP_S_ECALL); - mask |= 1ULL << (RISCV_EXCP_H_ECALL); - mask |= 1ULL << (RISCV_EXCP_M_ECALL); - mask |= 1ULL << (RISCV_EXCP_INST_PAGE_FAULT); - mask |= 1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT); - mask |= 1ULL << (RISCV_EXCP_STORE_PAGE_FAULT); - env->medeleg = (env->medeleg & ~mask) - | (val_to_write & mask); - break; - } - case CSR_MINSTRET: - /* minstret is WARL so unsupported writes are ignored */ - break; - case CSR_MCYCLE: - /* mcycle is WARL so unsupported writes are ignored */ - break; -#if defined(TARGET_RISCV32) - case CSR_MINSTRETH: - /* minstreth is WARL so unsupported writes are ignored */ - break; - case CSR_MCYCLEH: - /* mcycleh is WARL so unsupported writes are ignored */ - break; -#endif - case CSR_MUCOUNTEREN: - if (env->priv_ver <= PRIV_VERSION_1_09_1) { - env->scounteren = val_to_write; - break; - } else { - goto do_illegal; - } - case CSR_MSCOUNTEREN: - if (env->priv_ver <= PRIV_VERSION_1_09_1) { - env->mcounteren = val_to_write; - break; - } else { - goto do_illegal; - } - case CSR_SSTATUS: { - target_ulong ms = env->mstatus; - target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE - | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS - | SSTATUS_SUM | SSTATUS_SD; - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - mask |= SSTATUS_MXR; - } - ms = (ms & ~mask) | (val_to_write & mask); - csr_write_helper(env, ms, CSR_MSTATUS); - break; - } - case CSR_SIP: { - qemu_mutex_lock_iothread(); - target_ulong next_mip = (env->mip & ~env->mideleg) - | (val_to_write & env->mideleg); - qemu_mutex_unlock_iothread(); - csr_write_helper(env, next_mip, CSR_MIP); - break; - } - case CSR_SIE: { - target_ulong next_mie = (env->mie & ~env->mideleg) - | (val_to_write & env->mideleg); - csr_write_helper(env, next_mie, CSR_MIE); - break; - } - case CSR_SATP: /* CSR_SPTBR */ { - if (!riscv_feature(env, RISCV_FEATURE_MMU)) { - break; - } - if (env->priv_ver <= PRIV_VERSION_1_09_1 && (val_to_write ^ env->sptbr)) - { - helper_tlb_flush(env); - env->sptbr = val_to_write & (((target_ulong) - 1 << (TARGET_PHYS_ADDR_SPACE_BITS - PGSHIFT)) - 1); - } - if (env->priv_ver >= PRIV_VERSION_1_10_0 && - validate_vm(env, get_field(val_to_write, SATP_MODE)) && - ((val_to_write ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN))) - { - helper_tlb_flush(env); - env->satp = val_to_write; - } - break; - } - case CSR_SEPC: - env->sepc = val_to_write; - break; - case CSR_STVEC: - /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ - if ((val_to_write & 3) == 0) { - env->stvec = val_to_write >> 2 << 2; - } else { - qemu_log_mask(LOG_UNIMP, - "CSR_STVEC: vectored traps not supported\n"); - } - break; - case CSR_SCOUNTEREN: - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - env->scounteren = val_to_write; - break; - } else { - goto do_illegal; - } - case CSR_SSCRATCH: - env->sscratch = val_to_write; - break; - case CSR_SCAUSE: - env->scause = val_to_write; - break; - case CSR_SBADADDR: - env->sbadaddr = val_to_write; - break; - case CSR_MEPC: - env->mepc = val_to_write; - break; - case CSR_MTVEC: - /* bits [1:0] indicate mode; 0 = direct, 1 = vectored, 2 >= reserved */ - if ((val_to_write & 3) == 0) { - env->mtvec = val_to_write >> 2 << 2; - } else { - qemu_log_mask(LOG_UNIMP, - "CSR_MTVEC: vectored traps not supported\n"); - } - break; - case CSR_MCOUNTEREN: - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - env->mcounteren = val_to_write; - break; - } else { - goto do_illegal; - } - case CSR_MSCRATCH: - env->mscratch = val_to_write; - break; - case CSR_MCAUSE: - env->mcause = val_to_write; - break; - case CSR_MBADADDR: - env->mbadaddr = val_to_write; - break; - case CSR_MISA: - /* misa is WARL so unsupported writes are ignored */ - break; - case CSR_PMPCFG0: - case CSR_PMPCFG1: - case CSR_PMPCFG2: - case CSR_PMPCFG3: - pmpcfg_csr_write(env, csrno - CSR_PMPCFG0, val_to_write); - break; - case CSR_PMPADDR0: - case CSR_PMPADDR1: - case CSR_PMPADDR2: - case CSR_PMPADDR3: - case CSR_PMPADDR4: - case CSR_PMPADDR5: - case CSR_PMPADDR6: - case CSR_PMPADDR7: - case CSR_PMPADDR8: - case CSR_PMPADDR9: - case CSR_PMPADDR10: - case CSR_PMPADDR11: - case CSR_PMPADDR12: - case CSR_PMPADDR13: - case CSR_PMPADDR14: - case CSR_PMPADDR15: - pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val_to_write); - break; -#endif -#if !defined(CONFIG_USER_ONLY) - do_illegal: -#endif - default: - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); - } -} - -/* - * Handle reads to CSRs and any resulting special behavior - * - * Adapted from Spike's processor_t::get_csr - */ -target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) -{ -#ifndef CONFIG_USER_ONLY - target_ulong ctr_en = env->priv == PRV_U ? env->scounteren : - env->priv == PRV_S ? env->mcounteren : -1U; -#else - target_ulong ctr_en = -1; -#endif - target_ulong ctr_ok = (ctr_en >> (csrno & 31)) & 1; - - if (csrno >= CSR_HPMCOUNTER3 && csrno <= CSR_HPMCOUNTER31) { - if (ctr_ok) { - return 0; - } - } -#if defined(TARGET_RISCV32) - if (csrno >= CSR_HPMCOUNTER3H && csrno <= CSR_HPMCOUNTER31H) { - if (ctr_ok) { - return 0; - } - } -#endif - if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) { - return 0; - } -#if defined(TARGET_RISCV32) - if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) { - return 0; - } -#endif - if (csrno >= CSR_MHPMEVENT3 && csrno <= CSR_MHPMEVENT31) { - return 0; - } - - switch (csrno) { - case CSR_FFLAGS: - validate_mstatus_fs(env, GETPC()); - return cpu_riscv_get_fflags(env); - case CSR_FRM: - validate_mstatus_fs(env, GETPC()); - return env->frm; - case CSR_FCSR: - validate_mstatus_fs(env, GETPC()); - return (cpu_riscv_get_fflags(env) << FSR_AEXC_SHIFT) - | (env->frm << FSR_RD_SHIFT); - /* rdtime/rdtimeh is trapped and emulated by bbl in system mode */ -#ifdef CONFIG_USER_ONLY - case CSR_TIME: - return cpu_get_host_ticks(); -#if defined(TARGET_RISCV32) - case CSR_TIMEH: - return cpu_get_host_ticks() >> 32; -#endif -#endif - case CSR_INSTRET: - case CSR_CYCLE: - if (ctr_ok) { -#if !defined(CONFIG_USER_ONLY) - if (use_icount) { - return cpu_get_icount(); - } else { - return cpu_get_host_ticks(); - } -#else - return cpu_get_host_ticks(); -#endif - } - break; -#if defined(TARGET_RISCV32) - case CSR_INSTRETH: - case CSR_CYCLEH: - if (ctr_ok) { -#if !defined(CONFIG_USER_ONLY) - if (use_icount) { - return cpu_get_icount() >> 32; - } else { - return cpu_get_host_ticks() >> 32; - } -#else - return cpu_get_host_ticks() >> 32; -#endif - } - break; -#endif -#ifndef CONFIG_USER_ONLY - case CSR_MINSTRET: - case CSR_MCYCLE: - if (use_icount) { - return cpu_get_icount(); - } else { - return cpu_get_host_ticks(); - } - case CSR_MINSTRETH: - case CSR_MCYCLEH: -#if defined(TARGET_RISCV32) - if (use_icount) { - return cpu_get_icount() >> 32; - } else { - return cpu_get_host_ticks() >> 32; - } -#endif - break; - case CSR_MUCOUNTEREN: - if (env->priv_ver <= PRIV_VERSION_1_09_1) { - return env->scounteren; - } else { - break; /* illegal instruction */ - } - case CSR_MSCOUNTEREN: - if (env->priv_ver <= PRIV_VERSION_1_09_1) { - return env->mcounteren; - } else { - break; /* illegal instruction */ - } - case CSR_SSTATUS: { - target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE - | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS - | SSTATUS_SUM | SSTATUS_SD; - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - mask |= SSTATUS_MXR; - } - return env->mstatus & mask; - } - case CSR_SIP: { - qemu_mutex_lock_iothread(); - target_ulong tmp = env->mip & env->mideleg; - qemu_mutex_unlock_iothread(); - return tmp; - } - case CSR_SIE: - return env->mie & env->mideleg; - case CSR_SEPC: - return env->sepc; - case CSR_SBADADDR: - return env->sbadaddr; - case CSR_STVEC: - return env->stvec; - case CSR_SCOUNTEREN: - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - return env->scounteren; - } else { - break; /* illegal instruction */ - } - case CSR_SCAUSE: - return env->scause; - case CSR_SATP: /* CSR_SPTBR */ - if (!riscv_feature(env, RISCV_FEATURE_MMU)) { - return 0; - } - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - return env->satp; - } else { - return env->sptbr; - } - case CSR_SSCRATCH: - return env->sscratch; - case CSR_MSTATUS: - return env->mstatus; - case CSR_MIP: { - qemu_mutex_lock_iothread(); - target_ulong tmp = env->mip; - qemu_mutex_unlock_iothread(); - return tmp; - } - case CSR_MIE: - return env->mie; - case CSR_MEPC: - return env->mepc; - case CSR_MSCRATCH: - return env->mscratch; - case CSR_MCAUSE: - return env->mcause; - case CSR_MBADADDR: - return env->mbadaddr; - case CSR_MISA: - return env->misa; - case CSR_MARCHID: - return 0; /* as spike does */ - case CSR_MIMPID: - return 0; /* as spike does */ - case CSR_MVENDORID: - return 0; /* as spike does */ - case CSR_MHARTID: - return env->mhartid; - case CSR_MTVEC: - return env->mtvec; - case CSR_MCOUNTEREN: - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - return env->mcounteren; - } else { - break; /* illegal instruction */ - } - case CSR_MEDELEG: - return env->medeleg; - case CSR_MIDELEG: - return env->mideleg; - case CSR_PMPCFG0: - case CSR_PMPCFG1: - case CSR_PMPCFG2: - case CSR_PMPCFG3: - return pmpcfg_csr_read(env, csrno - CSR_PMPCFG0); - case CSR_PMPADDR0: - case CSR_PMPADDR1: - case CSR_PMPADDR2: - case CSR_PMPADDR3: - case CSR_PMPADDR4: - case CSR_PMPADDR5: - case CSR_PMPADDR6: - case CSR_PMPADDR7: - case CSR_PMPADDR8: - case CSR_PMPADDR9: - case CSR_PMPADDR10: - case CSR_PMPADDR11: - case CSR_PMPADDR12: - case CSR_PMPADDR13: - case CSR_PMPADDR14: - case CSR_PMPADDR15: - return pmpaddr_csr_read(env, csrno - CSR_PMPADDR0); -#endif - } - /* used by e.g. MTIME read */ - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); -} - -/* - * Check that CSR access is allowed. - * - * Adapted from Spike's decode.h:validate_csr - */ -static void validate_csr(CPURISCVState *env, uint64_t which, - uint64_t write, uintptr_t ra) -{ -#ifndef CONFIG_USER_ONLY - unsigned csr_priv = get_field((which), 0x300); - unsigned csr_read_only = get_field((which), 0xC00) == 3; - if (((write) && csr_read_only) || (env->priv < csr_priv)) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, ra); - } -#endif -} - target_ulong helper_csrrw(CPURISCVState *env, target_ulong src, target_ulong csr) { - validate_csr(env, csr, 1, GETPC()); - uint64_t csr_backup = csr_read_helper(env, csr); - csr_write_helper(env, src, csr); - return csr_backup; + target_ulong val = 0; + if (riscv_csrrw(env, csr, &val, src, -1) < 0) { + do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } + return val; } target_ulong helper_csrrs(CPURISCVState *env, target_ulong src, target_ulong csr, target_ulong rs1_pass) { - validate_csr(env, csr, rs1_pass != 0, GETPC()); - uint64_t csr_backup = csr_read_helper(env, csr); - if (rs1_pass != 0) { - csr_write_helper(env, src | csr_backup, csr); + target_ulong val = 0; + if (riscv_csrrw(env, csr, &val, -1, rs1_pass ? src : 0) < 0) { + do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - return csr_backup; + return val; } target_ulong helper_csrrc(CPURISCVState *env, target_ulong src, target_ulong csr, target_ulong rs1_pass) { - validate_csr(env, csr, rs1_pass != 0, GETPC()); - uint64_t csr_backup = csr_read_helper(env, csr); - if (rs1_pass != 0) { - csr_write_helper(env, (~src) & csr_backup, csr); + target_ulong val = 0; + if (riscv_csrrw(env, csr, &val, 0, rs1_pass ? src : 0) < 0) { + do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - return csr_backup; + return val; } #ifndef CONFIG_USER_ONLY @@ -674,7 +91,7 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) mstatus = set_field(mstatus, MSTATUS_SPIE, 0); mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); riscv_set_mode(env, prev_priv); - csr_write_helper(env, mstatus, CSR_MSTATUS); + env->mstatus = mstatus; return retpc; } @@ -699,7 +116,7 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) mstatus = set_field(mstatus, MSTATUS_MPIE, 0); mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); riscv_set_mode(env, prev_priv); - csr_write_helper(env, mstatus, CSR_MSTATUS); + env->mstatus = mstatus; return retpc; } From 0d5f558d8ce3509545eb2af3489e34bbdf0f1512 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Tue, 10 Apr 2018 12:49:51 +1200 Subject: [PATCH 39/76] RISC-V: Implement atomic mip/sip CSR updates Use the new CSR read/modify/write interface to implement atomic updates to mip/sip. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- target/riscv/csr.c | 56 +++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 40158038eda..c91a55607b3 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -496,25 +496,31 @@ static int write_mbadaddr(CPURISCVState *env, int csrno, target_ulong val) return 0; } -static int read_mip(CPURISCVState *env, int csrno, target_ulong *val) -{ - *val = atomic_read(&env->mip); - return 0; -} - -static int write_mip(CPURISCVState *env, int csrno, target_ulong val) +static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value, + target_ulong new_value, target_ulong write_mask) { RISCVCPU *cpu = riscv_env_get_cpu(env); + target_ulong mask = write_mask & delegable_ints; + uint32_t old_mip; + + /* We can't allow the supervisor to control SEIP as this would allow the + * supervisor to clear a pending external interrupt which will result in + * lost a interrupt in the case a PLIC is attached. The SEIP bit must be + * hardware controlled when a PLIC is attached. This should be an option + * for CPUs with software-delegated Supervisor External Interrupts. */ + mask &= ~MIP_SEIP; + + if (mask) { + qemu_mutex_lock_iothread(); + old_mip = riscv_cpu_update_mip(cpu, mask, (new_value & mask)); + qemu_mutex_unlock_iothread(); + } else { + old_mip = atomic_read(&env->mip); + } - /* - * csrs, csrc on mip.SEIP is not decomposable into separate read and - * write steps, so a different implementation is needed - */ - - qemu_mutex_lock_iothread(); - riscv_cpu_update_mip(cpu, MIP_SSIP | MIP_STIP, - (val & (MIP_SSIP | MIP_STIP))); - qemu_mutex_unlock_iothread(); + if (ret_value) { + *ret_value = old_mip; + } return 0; } @@ -634,17 +640,11 @@ static int write_sbadaddr(CPURISCVState *env, int csrno, target_ulong val) return 0; } -static int read_sip(CPURISCVState *env, int csrno, target_ulong *val) -{ - *val = atomic_read(&env->mip) & env->mideleg; - return 0; -} - -static int write_sip(CPURISCVState *env, int csrno, target_ulong val) +static int rmw_sip(CPURISCVState *env, int csrno, target_ulong *ret_value, + target_ulong new_value, target_ulong write_mask) { - target_ulong newval = (atomic_read(&env->mip) & ~env->mideleg) - | (val & env->mideleg); - return write_mip(env, CSR_MIP, newval); + return rmw_mip(env, CSR_MSTATUS, ret_value, new_value, + write_mask & env->mideleg); } /* Supervisor Protection and Translation */ @@ -826,7 +826,7 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MEPC] = { read_mepc, write_mepc }, [CSR_MCAUSE] = { read_mcause, write_mcause }, [CSR_MBADADDR] = { read_mbadaddr, write_mbadaddr }, - [CSR_MIP] = { read_mip, write_mip }, + [CSR_MIP] = { NULL, NULL, rmw_mip }, /* Supervisor Trap Setup */ [CSR_SSTATUS] = { read_sstatus, write_sstatus }, @@ -839,7 +839,7 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_SEPC] = { read_sepc, write_sepc }, [CSR_SCAUSE] = { read_scause, write_scause }, [CSR_SBADADDR] = { read_sbadaddr, write_sbadaddr }, - [CSR_SIP] = { read_sip, write_sip }, + [CSR_SIP] = { NULL, NULL, rmw_sip }, /* Supervisor Protection and Translation */ [CSR_SATP] = { read_satp, write_satp }, From c8972234e791a340953c5b2316e4427963654b7c Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Thu, 12 Apr 2018 08:29:24 +1200 Subject: [PATCH 40/76] RISC-V: Implement existential predicates for CSRs CSR predicate functions are added to the CSR table. mstatus.FS and counter enable checks are moved to predicate functions and two new predicates are added to check misa.S for s* CSRs and a new PMP CPU feature for pmp* CSRs. Processors that don't implement S-mode will trap on access to s* CSRs and processors that don't implement PMP will trap on accesses to pmp* CSRs. PMP checks are disabled in riscv_cpu_handle_mmu_fault when the PMP CPU feature is not present. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- target/riscv/cpu.c | 6 ++ target/riscv/cpu.h | 5 +- target/riscv/cpu_helper.c | 3 +- target/riscv/csr.c | 170 +++++++++++++++++++++----------------- 4 files changed, 105 insertions(+), 79 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index a025a0a3baa..e558aa8d367 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -126,6 +126,7 @@ static void rv32gcsu_priv1_09_1_cpu_init(Object *obj) set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); + set_feature(env, RISCV_FEATURE_PMP); } static void rv32gcsu_priv1_10_0_cpu_init(Object *obj) @@ -135,6 +136,7 @@ static void rv32gcsu_priv1_10_0_cpu_init(Object *obj) set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); + set_feature(env, RISCV_FEATURE_PMP); } static void rv32imacu_nommu_cpu_init(Object *obj) @@ -143,6 +145,7 @@ static void rv32imacu_nommu_cpu_init(Object *obj) set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU); set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); + set_feature(env, RISCV_FEATURE_PMP); } #elif defined(TARGET_RISCV64) @@ -154,6 +157,7 @@ static void rv64gcsu_priv1_09_1_cpu_init(Object *obj) set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); + set_feature(env, RISCV_FEATURE_PMP); } static void rv64gcsu_priv1_10_0_cpu_init(Object *obj) @@ -163,6 +167,7 @@ static void rv64gcsu_priv1_10_0_cpu_init(Object *obj) set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); + set_feature(env, RISCV_FEATURE_PMP); } static void rv64imacu_nommu_cpu_init(Object *obj) @@ -171,6 +176,7 @@ static void rv64imacu_nommu_cpu_init(Object *obj) set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU); set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); + set_feature(env, RISCV_FEATURE_PMP); } #endif diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 9168afd5d75..e1d0fe9f286 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -83,9 +83,10 @@ /* S extension denotes that Supervisor mode exists, however it is possible to have a core that support S mode but does not have an MMU and there is currently no bit in misa to indicate whether an MMU exists or not - so a cpu features bitfield is required */ + so a cpu features bitfield is required, likewise for optional PMP support */ enum { - RISCV_FEATURE_MMU + RISCV_FEATURE_MMU, + RISCV_FEATURE_PMP }; #define USER_VERSION_2_02_0 0x00020200 diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 8e0b83c6ec9..5a101ef0c55 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -404,7 +404,8 @@ int riscv_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, qemu_log_mask(CPU_LOG_MMU, "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, pa, prot); - if (!pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) { + if (riscv_feature(env, RISCV_FEATURE_PMP) && + !pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) { ret = TRANSLATE_FAIL; } if (ret == TRANSLATE_SUCCESS) { diff --git a/target/riscv/csr.c b/target/riscv/csr.c index c91a55607b3..bcb7326fcc0 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -45,6 +45,47 @@ void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops) csr_ops[csrno & (CSR_TABLE_SIZE - 1)] = *ops; } +/* Predicates */ + +static int fs(CPURISCVState *env, int csrno) +{ +#if !defined(CONFIG_USER_ONLY) + if (!(env->mstatus & MSTATUS_FS)) { + return -1; + } +#endif + return 0; +} + +static int ctr(CPURISCVState *env, int csrno) +{ +#if !defined(CONFIG_USER_ONLY) + target_ulong ctr_en = env->priv == PRV_U ? env->scounteren : + env->priv == PRV_S ? env->mcounteren : -1U; + if (!(ctr_en & (1 << (csrno & 31)))) { + return -1; + } +#endif + return 0; +} + +#if !defined(CONFIG_USER_ONLY) +static int any(CPURISCVState *env, int csrno) +{ + return 0; +} + +static int smode(CPURISCVState *env, int csrno) +{ + return -!riscv_has_ext(env, RVS); +} + +static int pmp(CPURISCVState *env, int csrno) +{ + return -!riscv_feature(env, RISCV_FEATURE_PMP); +} +#endif + /* User Floating-Point CSRs */ static int read_fflags(CPURISCVState *env, int csrno, target_ulong *val) @@ -120,33 +161,8 @@ static int write_fcsr(CPURISCVState *env, int csrno, target_ulong val) /* User Timers and Counters */ -static int counter_enabled(CPURISCVState *env, int csrno) -{ -#ifndef CONFIG_USER_ONLY - target_ulong ctr_en = env->priv == PRV_U ? env->scounteren : - env->priv == PRV_S ? env->mcounteren : -1U; -#else - target_ulong ctr_en = -1; -#endif - return (ctr_en >> (csrno & 31)) & 1; -} - -#if !defined(CONFIG_USER_ONLY) -static int read_zero_counter(CPURISCVState *env, int csrno, target_ulong *val) -{ - if (!counter_enabled(env, csrno)) { - return -1; - } - *val = 0; - return 0; -} -#endif - static int read_instret(CPURISCVState *env, int csrno, target_ulong *val) { - if (!counter_enabled(env, csrno)) { - return -1; - } #if !defined(CONFIG_USER_ONLY) if (use_icount) { *val = cpu_get_icount(); @@ -162,9 +178,6 @@ static int read_instret(CPURISCVState *env, int csrno, target_ulong *val) #if defined(TARGET_RISCV32) static int read_instreth(CPURISCVState *env, int csrno, target_ulong *val) { - if (!counter_enabled(env, csrno)) { - return -1; - } #if !defined(CONFIG_USER_ONLY) if (use_icount) { *val = cpu_get_icount() >> 32; @@ -733,6 +746,11 @@ int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, } #endif + /* check predicate */ + if (!csr_ops[csrno].predicate || csr_ops[csrno].predicate(env, csrno) < 0) { + return -1; + } + /* execute combined read/write operation if it exists */ if (csr_ops[csrno].op) { return csr_ops[csrno].op(env, csrno, ret_value, new_value, write_mask); @@ -772,89 +790,89 @@ int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* User Floating-Point CSRs */ - [CSR_FFLAGS] = { read_fflags, write_fflags }, - [CSR_FRM] = { read_frm, write_frm }, - [CSR_FCSR] = { read_fcsr, write_fcsr }, + [CSR_FFLAGS] = { fs, read_fflags, write_fflags }, + [CSR_FRM] = { fs, read_frm, write_frm }, + [CSR_FCSR] = { fs, read_fcsr, write_fcsr }, /* User Timers and Counters */ - [CSR_CYCLE] = { read_instret }, - [CSR_INSTRET] = { read_instret }, + [CSR_CYCLE] = { ctr, read_instret }, + [CSR_INSTRET] = { ctr, read_instret }, #if defined(TARGET_RISCV32) - [CSR_CYCLEH] = { read_instreth }, - [CSR_INSTRETH] = { read_instreth }, + [CSR_CYCLEH] = { ctr, read_instreth }, + [CSR_INSTRETH] = { ctr, read_instreth }, #endif /* User-level time CSRs are only available in linux-user * In privileged mode, the monitor emulates these CSRs */ #if defined(CONFIG_USER_ONLY) - [CSR_TIME] = { read_time }, + [CSR_TIME] = { ctr, read_time }, #if defined(TARGET_RISCV32) - [CSR_TIMEH] = { read_timeh }, + [CSR_TIMEH] = { ctr, read_timeh }, #endif #endif #if !defined(CONFIG_USER_ONLY) /* Machine Timers and Counters */ - [CSR_MCYCLE] = { read_instret }, - [CSR_MINSTRET] = { read_instret }, + [CSR_MCYCLE] = { any, read_instret }, + [CSR_MINSTRET] = { any, read_instret }, #if defined(TARGET_RISCV32) - [CSR_MCYCLEH] = { read_instreth }, - [CSR_MINSTRETH] = { read_instreth }, + [CSR_MCYCLEH] = { any, read_instreth }, + [CSR_MINSTRETH] = { any, read_instreth }, #endif /* Machine Information Registers */ - [CSR_MVENDORID] = { read_zero }, - [CSR_MARCHID] = { read_zero }, - [CSR_MIMPID] = { read_zero }, - [CSR_MHARTID] = { read_mhartid }, + [CSR_MVENDORID] = { any, read_zero }, + [CSR_MARCHID] = { any, read_zero }, + [CSR_MIMPID] = { any, read_zero }, + [CSR_MHARTID] = { any, read_mhartid }, /* Machine Trap Setup */ - [CSR_MSTATUS] = { read_mstatus, write_mstatus }, - [CSR_MISA] = { read_misa }, - [CSR_MIDELEG] = { read_mideleg, write_mideleg }, - [CSR_MEDELEG] = { read_medeleg, write_medeleg }, - [CSR_MIE] = { read_mie, write_mie }, - [CSR_MTVEC] = { read_mtvec, write_mtvec }, - [CSR_MCOUNTEREN] = { read_mcounteren, write_mcounteren }, + [CSR_MSTATUS] = { any, read_mstatus, write_mstatus }, + [CSR_MISA] = { any, read_misa }, + [CSR_MIDELEG] = { any, read_mideleg, write_mideleg }, + [CSR_MEDELEG] = { any, read_medeleg, write_medeleg }, + [CSR_MIE] = { any, read_mie, write_mie }, + [CSR_MTVEC] = { any, read_mtvec, write_mtvec }, + [CSR_MCOUNTEREN] = { any, read_mcounteren, write_mcounteren }, /* Legacy Counter Setup (priv v1.9.1) */ - [CSR_MUCOUNTEREN] = { read_mucounteren, write_mucounteren }, - [CSR_MSCOUNTEREN] = { read_mscounteren, write_mscounteren }, + [CSR_MUCOUNTEREN] = { any, read_mucounteren, write_mucounteren }, + [CSR_MSCOUNTEREN] = { any, read_mscounteren, write_mscounteren }, /* Machine Trap Handling */ - [CSR_MSCRATCH] = { read_mscratch, write_mscratch }, - [CSR_MEPC] = { read_mepc, write_mepc }, - [CSR_MCAUSE] = { read_mcause, write_mcause }, - [CSR_MBADADDR] = { read_mbadaddr, write_mbadaddr }, - [CSR_MIP] = { NULL, NULL, rmw_mip }, + [CSR_MSCRATCH] = { any, read_mscratch, write_mscratch }, + [CSR_MEPC] = { any, read_mepc, write_mepc }, + [CSR_MCAUSE] = { any, read_mcause, write_mcause }, + [CSR_MBADADDR] = { any, read_mbadaddr, write_mbadaddr }, + [CSR_MIP] = { any, NULL, NULL, rmw_mip }, /* Supervisor Trap Setup */ - [CSR_SSTATUS] = { read_sstatus, write_sstatus }, - [CSR_SIE] = { read_sie, write_sie }, - [CSR_STVEC] = { read_stvec, write_stvec }, - [CSR_SCOUNTEREN] = { read_scounteren, write_scounteren }, + [CSR_SSTATUS] = { smode, read_sstatus, write_sstatus }, + [CSR_SIE] = { smode, read_sie, write_sie }, + [CSR_STVEC] = { smode, read_stvec, write_stvec }, + [CSR_SCOUNTEREN] = { smode, read_scounteren, write_scounteren }, /* Supervisor Trap Handling */ - [CSR_SSCRATCH] = { read_sscratch, write_sscratch }, - [CSR_SEPC] = { read_sepc, write_sepc }, - [CSR_SCAUSE] = { read_scause, write_scause }, - [CSR_SBADADDR] = { read_sbadaddr, write_sbadaddr }, - [CSR_SIP] = { NULL, NULL, rmw_sip }, + [CSR_SSCRATCH] = { smode, read_sscratch, write_sscratch }, + [CSR_SEPC] = { smode, read_sepc, write_sepc }, + [CSR_SCAUSE] = { smode, read_scause, write_scause }, + [CSR_SBADADDR] = { smode, read_sbadaddr, write_sbadaddr }, + [CSR_SIP] = { smode, NULL, NULL, rmw_sip }, /* Supervisor Protection and Translation */ - [CSR_SATP] = { read_satp, write_satp }, + [CSR_SATP] = { smode, read_satp, write_satp }, /* Physical Memory Protection */ - [CSR_PMPCFG0 ... CSR_PMPADDR9] = { read_pmpcfg, write_pmpcfg }, - [CSR_PMPADDR0 ... CSR_PMPADDR15] = { read_pmpaddr, write_pmpaddr }, + [CSR_PMPCFG0 ... CSR_PMPADDR9] = { pmp, read_pmpcfg, write_pmpcfg }, + [CSR_PMPADDR0 ... CSR_PMPADDR15] = { pmp, read_pmpaddr, write_pmpaddr }, /* Performance Counters */ - [CSR_HPMCOUNTER3 ... CSR_HPMCOUNTER31] = { read_zero_counter }, - [CSR_MHPMCOUNTER3 ... CSR_MHPMCOUNTER31] = { read_zero }, - [CSR_MHPMEVENT3 ... CSR_MHPMEVENT31] = { read_zero }, + [CSR_HPMCOUNTER3 ... CSR_HPMCOUNTER31] = { ctr, read_zero }, + [CSR_MHPMCOUNTER3 ... CSR_MHPMCOUNTER31] = { any, read_zero }, + [CSR_MHPMEVENT3 ... CSR_MHPMEVENT31] = { any, read_zero }, #if defined(TARGET_RISCV32) - [CSR_HPMCOUNTER3H ... CSR_HPMCOUNTER31H] = { read_zero_counter }, - [CSR_MHPMCOUNTER3H ... CSR_MHPMCOUNTER31H] = { read_zero }, + [CSR_HPMCOUNTER3H ... CSR_HPMCOUNTER31H] = { ctr, read_zero }, + [CSR_MHPMCOUNTER3H ... CSR_MHPMCOUNTER31H] = { any, read_zero }, #endif #endif }; From d3dd429448ba5ef2bfbc430f428f4fabcfce09e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 28 Mar 2018 10:13:22 -0700 Subject: [PATCH 41/76] RISC-V: Split out mstatus_fs from tb_flags Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Cc: Richard Henderson Signed-off-by: Michael Clark Reviewed-by: Michael Clark --- target/riscv/cpu.h | 6 +++--- target/riscv/translate.c | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index e1d0fe9f286..1ade90d23bb 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -276,8 +276,8 @@ void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env, target_ulong cpu_riscv_get_fflags(CPURISCVState *env); void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong); -#define TB_FLAGS_MMU_MASK 3 -#define TB_FLAGS_FP_ENABLE MSTATUS_FS +#define TB_FLAGS_MMU_MASK 3 +#define TB_FLAGS_MSTATUS_FS MSTATUS_FS static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) @@ -285,7 +285,7 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, *pc = env->pc; *cs_base = 0; #ifdef CONFIG_USER_ONLY - *flags = TB_FLAGS_FP_ENABLE; + *flags = TB_FLAGS_MSTATUS_FS; #else *flags = cpu_mmu_index(env, 0) | (env->mstatus & MSTATUS_FS); #endif diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 0b6be74f2dd..a6822b8359d 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -44,7 +44,7 @@ typedef struct DisasContext { /* pc_succ_insn points to the instruction following base.pc_next */ target_ulong pc_succ_insn; uint32_t opcode; - uint32_t flags; + uint32_t mstatus_fs; uint32_t mem_idx; /* Remember the rounding mode encoded in the previous fp instruction, which we have already installed into env->fp_status. Or -1 for @@ -656,7 +656,7 @@ static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd, { TCGv t0; - if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { + if (ctx->mstatus_fs == 0) { gen_exception_illegal(ctx); return; } @@ -686,7 +686,7 @@ static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1, { TCGv t0; - if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { + if (ctx->mstatus_fs == 0) { gen_exception_illegal(ctx); return; } @@ -945,7 +945,7 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, { TCGv t0 = NULL; - if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { + if (ctx->mstatus_fs == 0) { goto do_illegal; } @@ -1810,8 +1810,8 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) DisasContext *ctx = container_of(dcbase, DisasContext, base); ctx->pc_succ_insn = ctx->base.pc_first; - ctx->flags = ctx->base.tb->flags; ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK; + ctx->mstatus_fs = ctx->base.tb->flags & TB_FLAGS_MSTATUS_FS; ctx->frm = -1; /* unknown rounding mode */ } From 04a5c790e6de737bac42036446d957577a1480eb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 28 Mar 2018 10:14:44 -0700 Subject: [PATCH 42/76] RISC-V: Mark mstatus.fs dirty Modifed from Richard Henderson's patch [1] to integrate with the new control and status register implementation. [1] https://lists.nongnu.org/archive/html/qemu-devel/2018-03/msg07034.html Note: the f* CSRs already mark mstatus.FS dirty using env->mstatus |= mstatus.FS so the bug in the first spin of this patch has been fixed in a prior commit. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Cc: Richard Henderson Signed-off-by: Michael Clark Reviewed-by: Michael Clark Co-authored-by: Richard Henderson Co-authored-by: Michael Clark --- target/riscv/csr.c | 12 ------------ target/riscv/translate.c | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index bcb7326fcc0..05f79c1d098 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -326,18 +326,6 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) mstatus = (mstatus & ~mask) | (val & mask); - /* Note: this is a workaround for an issue where mstatus.FS - does not report dirty after floating point operations - that modify floating point state. This workaround is - technically compliant with the RISC-V Privileged - specification as it is legal to return only off, or dirty. - at the expense of extra floating point save/restore. */ - - /* FP is always dirty or off */ - if (mstatus & MSTATUS_FS) { - mstatus |= MSTATUS_FS; - } - int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | ((mstatus & MSTATUS_XS) == MSTATUS_XS); mstatus = set_field(mstatus, MSTATUS_SD, dirty); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index a6822b8359d..cc0927074c6 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -651,6 +651,31 @@ static void gen_store(DisasContext *ctx, uint32_t opc, int rs1, int rs2, tcg_temp_free(dat); } +#ifndef CONFIG_USER_ONLY +/* The states of mstatus_fs are: + * 0 = disabled, 1 = initial, 2 = clean, 3 = dirty + * We will have already diagnosed disabled state, + * and need to turn initial/clean into dirty. + */ +static void mark_fs_dirty(DisasContext *ctx) +{ + TCGv tmp; + if (ctx->mstatus_fs == MSTATUS_FS) { + return; + } + /* Remember the state change for the rest of the TB. */ + ctx->mstatus_fs = MSTATUS_FS; + + tmp = tcg_temp_new(); + tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); + tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); + tcg_temp_free(tmp); +} +#else +static inline void mark_fs_dirty(DisasContext *ctx) { } +#endif + static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd, int rs1, target_long imm) { @@ -679,6 +704,8 @@ static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd, break; } tcg_temp_free(t0); + + mark_fs_dirty(ctx); } static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1, @@ -944,6 +971,7 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, int rs2, int rm) { TCGv t0 = NULL; + bool fp_output = true; if (ctx->mstatus_fs == 0) { goto do_illegal; @@ -1006,6 +1034,7 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, } gen_set_gpr(rd, t0); tcg_temp_free(t0); + fp_output = false; break; case OPC_RISC_FCVT_W_S: @@ -1035,6 +1064,7 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, } gen_set_gpr(rd, t0); tcg_temp_free(t0); + fp_output = false; break; case OPC_RISC_FCVT_S_W: @@ -1085,6 +1115,7 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, } gen_set_gpr(rd, t0); tcg_temp_free(t0); + fp_output = false; break; case OPC_RISC_FMV_S_X: @@ -1177,6 +1208,7 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, } gen_set_gpr(rd, t0); tcg_temp_free(t0); + fp_output = false; break; case OPC_RISC_FCVT_W_D: @@ -1206,6 +1238,7 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, } gen_set_gpr(rd, t0); tcg_temp_free(t0); + fp_output = false; break; case OPC_RISC_FCVT_D_W: @@ -1253,6 +1286,7 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, default: goto do_illegal; } + fp_output = false; break; case OPC_RISC_FMV_D_X: @@ -1269,7 +1303,11 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, tcg_temp_free(t0); } gen_exception_illegal(ctx); - break; + return; + } + + if (fp_output) { + mark_fs_dirty(ctx); } } From dd1fe62d8ada75c81a2af92f2de370e2f515525f Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 16 Apr 2018 11:39:35 +1200 Subject: [PATCH 43/76] RISC-V: Implement mstatus.TSR/TW/TVM This adds the necessary minimum to support S-mode virtualization for priv ISA >= v1.10 Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Cc: Matthew Suozzo Signed-off-by: Michael Clark Co-authored-by: Matthew Suozzo Co-authored-by: Michael Clark --- target/riscv/csr.c | 17 +++++++++++++---- target/riscv/op_helper.c | 25 +++++++++++++++++++++---- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 05f79c1d098..14d447c2d39 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -314,7 +314,8 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) } mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | - MSTATUS_MPP | MSTATUS_MXR; + MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TVM | MSTATUS_TSR | + MSTATUS_TW; } /* silenty discard mstatus.mpp writes for unsupported modes */ @@ -655,7 +656,11 @@ static int read_satp(CPURISCVState *env, int csrno, target_ulong *val) if (!riscv_feature(env, RISCV_FEATURE_MMU)) { *val = 0; } else if (env->priv_ver >= PRIV_VERSION_1_10_0) { - *val = env->satp; + if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) { + return -1; + } else { + *val = env->satp; + } } else { *val = env->sptbr; } @@ -676,8 +681,12 @@ static int write_satp(CPURISCVState *env, int csrno, target_ulong val) validate_vm(env, get_field(val, SATP_MODE)) && ((val ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN))) { - tlb_flush(CPU(riscv_env_get_cpu(env))); - env->satp = val; + if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) { + return -1; + } else { + tlb_flush(CPU(riscv_env_get_cpu(env))); + env->satp = val; + } } return 0; } diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 81bd1a77ea9..77c79ba36e0 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -82,6 +82,11 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); } + if (env->priv_ver >= PRIV_VERSION_1_10_0 && + get_field(env->mstatus, MSTATUS_TSR)) { + do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } + target_ulong mstatus = env->mstatus; target_ulong prev_priv = get_field(mstatus, MSTATUS_SPP); mstatus = set_field(mstatus, @@ -125,16 +130,28 @@ void helper_wfi(CPURISCVState *env) { CPUState *cs = CPU(riscv_env_get_cpu(env)); - cs->halted = 1; - cs->exception_index = EXCP_HLT; - cpu_loop_exit(cs); + if (env->priv == PRV_S && + env->priv_ver >= PRIV_VERSION_1_10_0 && + get_field(env->mstatus, MSTATUS_TW)) { + do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } else { + cs->halted = 1; + cs->exception_index = EXCP_HLT; + cpu_loop_exit(cs); + } } void helper_tlb_flush(CPURISCVState *env) { RISCVCPU *cpu = riscv_env_get_cpu(env); CPUState *cs = CPU(cpu); - tlb_flush(cs); + if (env->priv == PRV_S && + env->priv_ver >= PRIV_VERSION_1_10_0 && + get_field(env->mstatus, MSTATUS_TVM)) { + do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } else { + tlb_flush(cs); + } } #endif /* !CONFIG_USER_ONLY */ From 4ea99959858c30a33aab3908028b8a43b388256a Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 23 Apr 2018 05:57:26 +1200 Subject: [PATCH 44/76] RISC-V: Add hartid and \n to interrupt logging Add carriage return that was erroneously removed when converting to qemu_log. Change hard coded core number to the actual hartid. Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- target/riscv/cpu_helper.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 5a101ef0c55..f257050f128 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -446,11 +446,13 @@ void riscv_cpu_do_interrupt(CPUState *cs) if (RISCV_DEBUG_INTERRUPT) { int log_cause = cs->exception_index & RISCV_EXCP_INT_MASK; if (cs->exception_index & RISCV_EXCP_INT_FLAG) { - qemu_log_mask(LOG_TRACE, "core 0: trap %s, epc 0x" TARGET_FMT_lx, - riscv_intr_names[log_cause], env->pc); + qemu_log_mask(LOG_TRACE, "core " + TARGET_FMT_ld ": trap %s, epc 0x" TARGET_FMT_lx "\n", + env->mhartid, riscv_intr_names[log_cause], env->pc); } else { - qemu_log_mask(LOG_TRACE, "core 0: intr %s, epc 0x" TARGET_FMT_lx, - riscv_excp_names[log_cause], env->pc); + qemu_log_mask(LOG_TRACE, "core " + TARGET_FMT_ld ": intr %s, epc 0x" TARGET_FMT_lx "\n", + env->mhartid, riscv_excp_names[log_cause], env->pc); } } @@ -512,8 +514,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) if (hasbadaddr) { if (RISCV_DEBUG_INTERRUPT) { - qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld - ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr); + qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld ": badaddr 0x" + TARGET_FMT_lx "\n", env->mhartid, env->badaddr); } env->sbadaddr = env->badaddr; } else { @@ -537,8 +539,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) if (hasbadaddr) { if (RISCV_DEBUG_INTERRUPT) { - qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld - ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr); + qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld ": badaddr 0x" + TARGET_FMT_lx "\n", env->mhartid, env->badaddr); } env->mbadaddr = env->badaddr; } else { From 48b610f670f159f88b828d3eb1bc9fac5d3fbe6b Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Tue, 24 Apr 2018 10:12:27 +1200 Subject: [PATCH 45/76] RISC-V: Use riscv prefix consistently on cpu helpers * Add riscv prefix to raise_exception function * Add riscv prefix to CSR read/write functions * Add riscv prefix to signal handler function * Add riscv prefix to get fflags function * Remove redundant declaration of riscv_cpu_init and rename cpu_riscv_init to riscv_cpu_init * rename riscv_set_mode to riscv_cpu_set_mode Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- linux-user/riscv/signal.c | 4 ++-- target/riscv/cpu.h | 21 ++++++++++----------- target/riscv/cpu_helper.c | 10 +++++----- target/riscv/csr.c | 8 ++++---- target/riscv/fpu_helper.c | 6 +++--- target/riscv/op_helper.c | 28 ++++++++++++++-------------- 6 files changed, 38 insertions(+), 39 deletions(-) diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c index f598d41891c..83ecc6f799a 100644 --- a/linux-user/riscv/signal.c +++ b/linux-user/riscv/signal.c @@ -83,7 +83,7 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env) __put_user(env->fpr[i], &sc->fpr[i]); } - uint32_t fcsr = csr_read_helper(env, CSR_FCSR); /*riscv_get_fcsr(env);*/ + uint32_t fcsr = riscv_csr_read(env, CSR_FCSR); __put_user(fcsr, &sc->fcsr); } @@ -159,7 +159,7 @@ static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc) uint32_t fcsr; __get_user(fcsr, &sc->fcsr); - csr_write_helper(env, fcsr, CSR_FCSR); + riscv_csr_write(env, CSR_FCSR, fcsr); } static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 1ade90d23bb..d6bb3136db1 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -257,7 +257,7 @@ char *riscv_isa_string(RISCVCPU *cpu); void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf); #define cpu_init(cpu_model) cpu_generic_init(TYPE_RISCV_CPU, cpu_model) -#define cpu_signal_handler cpu_riscv_signal_handler +#define cpu_signal_handler riscv_cpu_signal_handler #define cpu_list riscv_cpu_list #define cpu_mmu_index riscv_cpu_mmu_index @@ -265,16 +265,15 @@ void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf); uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ #endif -void riscv_set_mode(CPURISCVState *env, target_ulong newpriv); +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv); void riscv_translate_init(void); -RISCVCPU *cpu_riscv_init(const char *cpu_model); -int cpu_riscv_signal_handler(int host_signum, void *pinfo, void *puc); -void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env, - uint32_t exception, uintptr_t pc); +int riscv_cpu_signal_handler(int host_signum, void *pinfo, void *puc); +void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env, + uint32_t exception, uintptr_t pc); -target_ulong cpu_riscv_get_fflags(CPURISCVState *env); -void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong); +target_ulong riscv_cpu_get_fflags(CPURISCVState *env); +void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong); #define TB_FLAGS_MMU_MASK 3 #define TB_FLAGS_MSTATUS_FS MSTATUS_FS @@ -294,13 +293,13 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, target_ulong write_mask); -static inline void csr_write_helper(CPURISCVState *env, target_ulong val, - int csrno) +static inline void riscv_csr_write(CPURISCVState *env, int csrno, + target_ulong val) { riscv_csrrw(env, csrno, NULL, val, -1); } -static inline target_ulong csr_read_helper(CPURISCVState *env, int csrno) +static inline target_ulong riscv_csr_read(CPURISCVState *env, int csrno) { target_ulong val = 0; riscv_csrrw(env, csrno, &val, 0, 0); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index f257050f128..f49e98ed594 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -93,7 +93,7 @@ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) return old; } -void riscv_set_mode(CPURISCVState *env, target_ulong newpriv) +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) { if (newpriv > PRV_M) { g_assert_not_reached(); @@ -366,7 +366,7 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, g_assert_not_reached(); } env->badaddr = addr; - do_raise_exception_err(env, cs->exception_index, retaddr); + riscv_raise_exception(env, cs->exception_index, retaddr); } /* called by qemu's softmmu to fill the qemu tlb */ @@ -378,7 +378,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size, if (ret == TRANSLATE_FAIL) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - do_raise_exception_err(env, cs->exception_index, retaddr); + riscv_raise_exception(env, cs->exception_index, retaddr); } } @@ -530,7 +530,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) s = set_field(s, MSTATUS_SPP, env->priv); s = set_field(s, MSTATUS_SIE, 0); env->mstatus = s; - riscv_set_mode(env, PRV_S); + riscv_cpu_set_mode(env, PRV_S); } else { /* No need to check MTVEC for misaligned - lower 2 bits cannot be set */ env->pc = env->mtvec; @@ -555,7 +555,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) s = set_field(s, MSTATUS_MPP, env->priv); s = set_field(s, MSTATUS_MIE, 0); env->mstatus = s; - riscv_set_mode(env, PRV_M); + riscv_cpu_set_mode(env, PRV_M); } /* TODO yield load reservation */ #endif diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 14d447c2d39..3d930c8b392 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -95,7 +95,7 @@ static int read_fflags(CPURISCVState *env, int csrno, target_ulong *val) return -1; } #endif - *val = cpu_riscv_get_fflags(env); + *val = riscv_cpu_get_fflags(env); return 0; } @@ -107,7 +107,7 @@ static int write_fflags(CPURISCVState *env, int csrno, target_ulong val) } env->mstatus |= MSTATUS_FS; #endif - cpu_riscv_set_fflags(env, val & (FSR_AEXC >> FSR_AEXC_SHIFT)); + riscv_cpu_set_fflags(env, val & (FSR_AEXC >> FSR_AEXC_SHIFT)); return 0; } @@ -141,7 +141,7 @@ static int read_fcsr(CPURISCVState *env, int csrno, target_ulong *val) return -1; } #endif - *val = (cpu_riscv_get_fflags(env) << FSR_AEXC_SHIFT) + *val = (riscv_cpu_get_fflags(env) << FSR_AEXC_SHIFT) | (env->frm << FSR_RD_SHIFT); return 0; } @@ -155,7 +155,7 @@ static int write_fcsr(CPURISCVState *env, int csrno, target_ulong val) env->mstatus |= MSTATUS_FS; #endif env->frm = (val & FSR_RD) >> FSR_RD_SHIFT; - cpu_riscv_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT); + riscv_cpu_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT); return 0; } diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index fdb87d8d82c..1452a153f26 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -23,7 +23,7 @@ #include "exec/exec-all.h" #include "exec/helper-proto.h" -target_ulong cpu_riscv_get_fflags(CPURISCVState *env) +target_ulong riscv_cpu_get_fflags(CPURISCVState *env) { int soft = get_float_exception_flags(&env->fp_status); target_ulong hard = 0; @@ -37,7 +37,7 @@ target_ulong cpu_riscv_get_fflags(CPURISCVState *env) return hard; } -void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong hard) +void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong hard) { int soft = 0; @@ -74,7 +74,7 @@ void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm) softrm = float_round_ties_away; break; default: - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } set_float_rounding_mode(softrm, &env->fp_status); diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 77c79ba36e0..b7dc18a41e2 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -25,7 +25,7 @@ #include "exec/helper-proto.h" /* Exceptions processing helpers */ -void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env, +void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env, uint32_t exception, uintptr_t pc) { CPUState *cs = CPU(riscv_env_get_cpu(env)); @@ -36,7 +36,7 @@ void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env, void helper_raise_exception(CPURISCVState *env, uint32_t exception) { - do_raise_exception_err(env, exception, 0); + riscv_raise_exception(env, exception, 0); } target_ulong helper_csrrw(CPURISCVState *env, target_ulong src, @@ -44,7 +44,7 @@ target_ulong helper_csrrw(CPURISCVState *env, target_ulong src, { target_ulong val = 0; if (riscv_csrrw(env, csr, &val, src, -1) < 0) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } return val; } @@ -54,7 +54,7 @@ target_ulong helper_csrrs(CPURISCVState *env, target_ulong src, { target_ulong val = 0; if (riscv_csrrw(env, csr, &val, -1, rs1_pass ? src : 0) < 0) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } return val; } @@ -64,7 +64,7 @@ target_ulong helper_csrrc(CPURISCVState *env, target_ulong src, { target_ulong val = 0; if (riscv_csrrw(env, csr, &val, 0, rs1_pass ? src : 0) < 0) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } return val; } @@ -74,17 +74,17 @@ target_ulong helper_csrrc(CPURISCVState *env, target_ulong src, target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) { if (!(env->priv >= PRV_S)) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } target_ulong retpc = env->sepc; if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { - do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); } if (env->priv_ver >= PRIV_VERSION_1_10_0 && get_field(env->mstatus, MSTATUS_TSR)) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } target_ulong mstatus = env->mstatus; @@ -95,7 +95,7 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) get_field(mstatus, MSTATUS_SPIE)); mstatus = set_field(mstatus, MSTATUS_SPIE, 0); mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); - riscv_set_mode(env, prev_priv); + riscv_cpu_set_mode(env, prev_priv); env->mstatus = mstatus; return retpc; @@ -104,12 +104,12 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) { if (!(env->priv >= PRV_M)) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } target_ulong retpc = env->mepc; if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { - do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); } target_ulong mstatus = env->mstatus; @@ -120,7 +120,7 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) get_field(mstatus, MSTATUS_MPIE)); mstatus = set_field(mstatus, MSTATUS_MPIE, 0); mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); - riscv_set_mode(env, prev_priv); + riscv_cpu_set_mode(env, prev_priv); env->mstatus = mstatus; return retpc; @@ -133,7 +133,7 @@ void helper_wfi(CPURISCVState *env) if (env->priv == PRV_S && env->priv_ver >= PRIV_VERSION_1_10_0 && get_field(env->mstatus, MSTATUS_TW)) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } else { cs->halted = 1; cs->exception_index = EXCP_HLT; @@ -148,7 +148,7 @@ void helper_tlb_flush(CPURISCVState *env) if (env->priv == PRV_S && env->priv_ver >= PRIV_VERSION_1_10_0 && get_field(env->mstatus, MSTATUS_TVM)) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } else { tlb_flush(cs); } From 48cbe9eeb0eaea22bc0d5857b13cb1de8d51a248 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 30 Apr 2018 11:42:41 +1200 Subject: [PATCH 46/76] RISC-V: Replace __builtin_popcount with ctpop8 in PLIC The mode variable only uses the lower 4-bits (M,H,S,U) so replace the GCC specific __builtin_popcount with ctpop8. Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Alistair Francis Signed-off-by: Michael Clark --- hw/riscv/sifive_plic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c index 9cf9a1f9864..ad91299584f 100644 --- a/hw/riscv/sifive_plic.c +++ b/hw/riscv/sifive_plic.c @@ -383,7 +383,7 @@ static void parse_hart_config(SiFivePLICState *plic) p = plic->hart_config; while ((c = *p++)) { if (c == ',') { - addrid += __builtin_popcount(modes); + addrid += ctpop8(modes); modes = 0; hartid++; } else { @@ -397,7 +397,7 @@ static void parse_hart_config(SiFivePLICState *plic) } } if (modes) { - addrid += __builtin_popcount(modes); + addrid += ctpop8(modes); } hartid++; From 773e7f7c03b869629cf0e26a742ca16a03a8b67f Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 30 Apr 2018 12:29:34 +1200 Subject: [PATCH 47/76] RISC-V: Add missing free for plic_hart_config Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Alistair Francis Signed-off-by: Michael Clark --- hw/riscv/virt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 248bbdffd3a..da6d24287e4 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -385,6 +385,8 @@ static void riscv_virt_board_init(MachineState *machine) serial_mm_init(system_memory, memmap[VIRT_UART0].base, 0, qdev_get_gpio_in(DEVICE(s->plic), UART0_IRQ), 399193, serial_hd(0), DEVICE_LITTLE_ENDIAN); + + g_free(plic_hart_config); } static void riscv_virt_board_machine_init(MachineClass *mc) From bf9ddaaafded961867574dbb938d305c995c59c6 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 7 May 2018 10:08:25 +1200 Subject: [PATCH 48/76] RISC-V: Allow interrupt controllers to claim interrupts We can't allow the supervisor to control SEIP as this would allow the supervisor to clear a pending external interrupt which will result in lost a interrupt in the case a PLIC is attached. The SEIP bit must be hardware controlled when a PLIC is attached. This logic was previously hard-coded so SEIP was always masked even if no PLIC was attached. This patch adds riscv_cpu_claim_interrupts so that the PLIC can register control of SEIP. In the case of models without a PLIC (spike), the SEIP bit remains software controlled. This interface allows for hardware control of supervisor timer and software interrupts by other interrupt controller models. Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Alistair Francis Signed-off-by: Michael Clark --- hw/riscv/sifive_plic.c | 14 ++++++++++++++ target/riscv/cpu.h | 2 ++ target/riscv/cpu_helper.c | 11 +++++++++++ target/riscv/csr.c | 12 ++++-------- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c index ad91299584f..b9095bfde4f 100644 --- a/hw/riscv/sifive_plic.c +++ b/hw/riscv/sifive_plic.c @@ -23,6 +23,7 @@ #include "qemu/error-report.h" #include "hw/sysbus.h" #include "target/riscv/cpu.h" +#include "sysemu/sysemu.h" #include "hw/riscv/sifive_plic.h" #define RISCV_DEBUG_PLIC 0 @@ -431,6 +432,7 @@ static void sifive_plic_irq_request(void *opaque, int irq, int level) static void sifive_plic_realize(DeviceState *dev, Error **errp) { SiFivePLICState *plic = SIFIVE_PLIC(dev); + int i; memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic, TYPE_SIFIVE_PLIC, plic->aperture_size); @@ -443,6 +445,18 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio); qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources); + + /* We can't allow the supervisor to control SEIP as this would allow the + * supervisor to clear a pending external interrupt which will result in + * lost a interrupt in the case a PLIC is attached. The SEIP bit must be + * hardware controlled when a PLIC is attached. */ + for (i = 0; i < smp_cpus; i++) { + RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i)); + if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) { + error_report("sifive_plic_realize: SEIP already claimed"); + exit(1); + } + } } static void sifive_plic_class_init(ObjectClass *klass, void *data) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index d6bb3136db1..ae0e3f6a544 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -138,6 +138,7 @@ struct CPURISCVState { * mip is 32-bits to allow atomic_read on 32-bit hosts. */ uint32_t mip; + uint32_t miclaim; target_ulong mie; target_ulong mideleg; @@ -262,6 +263,7 @@ void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf); #define cpu_mmu_index riscv_cpu_mmu_index #ifndef CONFIG_USER_ONLY +int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts); uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ #endif diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index f49e98ed594..555756d40cf 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -72,6 +72,17 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) #if !defined(CONFIG_USER_ONLY) +int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts) +{ + CPURISCVState *env = &cpu->env; + if (env->miclaim & interrupts) { + return -1; + } else { + env->miclaim |= interrupts; + return 0; + } +} + /* iothread_mutex must be held */ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) { diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 3d930c8b392..85454f1d4cf 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -502,15 +502,11 @@ static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, target_ulong write_mask) { RISCVCPU *cpu = riscv_env_get_cpu(env); - target_ulong mask = write_mask & delegable_ints; - uint32_t old_mip; - /* We can't allow the supervisor to control SEIP as this would allow the - * supervisor to clear a pending external interrupt which will result in - * lost a interrupt in the case a PLIC is attached. The SEIP bit must be - * hardware controlled when a PLIC is attached. This should be an option - * for CPUs with software-delegated Supervisor External Interrupts. */ - mask &= ~MIP_SEIP; + /* Allow software control of delegable interrupts not claimed by hardware */ + target_ulong mask = write_mask & delegable_ints & ~env->miclaim; + + uint32_t old_mip; if (mask) { qemu_mutex_lock_iothread(); From aa213c85ebb32fb054ef77bf5d66ce2b447766a2 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 12 May 2018 14:07:44 +1200 Subject: [PATCH 49/76] RISC-V: Add misa to DisasContext gen methods should access state from DisasContext. Add misa field to the DisasContext struct and remove CPURISCVState argument from all gen methods. Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Alistair Francis Cc: Emilio G. Cota Signed-off-by: Michael Clark Reviewed-by: Richard Henderson --- target/riscv/translate.c | 78 +++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index cc0927074c6..c199cd56c4b 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -45,6 +45,7 @@ typedef struct DisasContext { target_ulong pc_succ_insn; uint32_t opcode; uint32_t mstatus_fs; + uint32_t misa; uint32_t mem_idx; /* Remember the rounding mode encoded in the previous fp instruction, which we have already installed into env->fp_status. Or -1 for @@ -74,6 +75,11 @@ static const int tcg_memop_lookup[8] = { #define CASE_OP_32_64(X) case X #endif +static inline bool has_ext(DisasContext *ctx, uint32_t ext) +{ + return ctx->misa & ext; +} + static void generate_exception(DisasContext *ctx, int excp) { tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); @@ -505,14 +511,13 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, int rd, tcg_temp_free(source1); } -static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd, - target_ulong imm) +static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) { target_ulong next_pc; /* check misaligned: */ next_pc = ctx->base.pc_next + imm; - if (!riscv_has_ext(env, RVC)) { + if (!has_ext(ctx, RVC)) { if ((next_pc & 0x3) != 0) { gen_exception_inst_addr_mis(ctx); return; @@ -526,8 +531,8 @@ static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd, ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, - int rd, int rs1, target_long imm) +static void gen_jalr(DisasContext *ctx, uint32_t opc, int rd, int rs1, + target_long imm) { /* no chaining with JALR */ TCGLabel *misaligned = NULL; @@ -539,7 +544,7 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, tcg_gen_addi_tl(cpu_pc, cpu_pc, imm); tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2); - if (!riscv_has_ext(env, RVC)) { + if (!has_ext(ctx, RVC)) { misaligned = gen_new_label(); tcg_gen_andi_tl(t0, cpu_pc, 0x2); tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned); @@ -564,8 +569,8 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, tcg_temp_free(t0); } -static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc, - int rs1, int rs2, target_long bimm) +static void gen_branch(DisasContext *ctx, uint32_t opc, int rs1, int rs2, + target_long bimm) { TCGLabel *l = gen_new_label(); TCGv source1, source2; @@ -602,7 +607,7 @@ static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc, gen_goto_tb(ctx, 1, ctx->pc_succ_insn); gen_set_label(l); /* branch taken */ - if (!riscv_has_ext(env, RVC) && ((ctx->base.pc_next + bimm) & 0x3)) { + if (!has_ext(ctx, RVC) && ((ctx->base.pc_next + bimm) & 0x3)) { /* misaligned */ gen_exception_inst_addr_mis(ctx); } else { @@ -1311,8 +1316,8 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, } } -static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, - int rd, int rs1, int csr) +static void gen_system(DisasContext *ctx, uint32_t opc, int rd, int rs1, + int csr) { TCGv source1, csr_store, dest, rs1_pass, imm_rs1; source1 = tcg_temp_new(); @@ -1354,7 +1359,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, gen_exception_illegal(ctx); break; case 0x102: /* SRET */ - if (riscv_has_ext(env, RVS)) { + if (has_ext(ctx, RVS)) { gen_helper_sret(cpu_pc, cpu_env, cpu_pc); tcg_gen_exit_tb(NULL, 0); /* no chaining */ ctx->base.is_jmp = DISAS_NORETURN; @@ -1495,7 +1500,7 @@ static void decode_RV32_64C0(DisasContext *ctx) } } -static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx) +static void decode_RV32_64C1(DisasContext *ctx) { uint8_t funct3 = extract32(ctx->opcode, 13, 3); uint8_t rd_rs1 = GET_C_RS1(ctx->opcode); @@ -1515,7 +1520,7 @@ static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx) GET_C_IMM(ctx->opcode)); #else /* C.JAL(RV32) -> jal x1, offset[11:1] */ - gen_jal(env, ctx, 1, GET_C_J_IMM(ctx->opcode)); + gen_jal(ctx, 1, GET_C_J_IMM(ctx->opcode)); #endif break; case 2: @@ -1594,22 +1599,22 @@ static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx) break; case 5: /* C.J -> jal x0, offset[11:1]*/ - gen_jal(env, ctx, 0, GET_C_J_IMM(ctx->opcode)); + gen_jal(ctx, 0, GET_C_J_IMM(ctx->opcode)); break; case 6: /* C.BEQZ -> beq rs1', x0, offset[8:1]*/ rs1s = GET_C_RS1S(ctx->opcode); - gen_branch(env, ctx, OPC_RISC_BEQ, rs1s, 0, GET_C_B_IMM(ctx->opcode)); + gen_branch(ctx, OPC_RISC_BEQ, rs1s, 0, GET_C_B_IMM(ctx->opcode)); break; case 7: /* C.BNEZ -> bne rs1', x0, offset[8:1]*/ rs1s = GET_C_RS1S(ctx->opcode); - gen_branch(env, ctx, OPC_RISC_BNE, rs1s, 0, GET_C_B_IMM(ctx->opcode)); + gen_branch(ctx, OPC_RISC_BNE, rs1s, 0, GET_C_B_IMM(ctx->opcode)); break; } } -static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx) +static void decode_RV32_64C2(DisasContext *ctx) { uint8_t rd, rs2; uint8_t funct3 = extract32(ctx->opcode, 13, 3); @@ -1643,7 +1648,7 @@ static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx) if (extract32(ctx->opcode, 12, 1) == 0) { if (rs2 == 0) { /* C.JR -> jalr x0, rs1, 0*/ - gen_jalr(env, ctx, OPC_RISC_JALR, 0, rd, 0); + gen_jalr(ctx, OPC_RISC_JALR, 0, rd, 0); } else { /* C.MV -> add rd, x0, rs2 */ gen_arith(ctx, OPC_RISC_ADD, rd, 0, rs2); @@ -1651,11 +1656,11 @@ static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx) } else { if (rd == 0) { /* C.EBREAK -> ebreak*/ - gen_system(env, ctx, OPC_RISC_ECALL, 0, 0, 0x1); + gen_system(ctx, OPC_RISC_ECALL, 0, 0, 0x1); } else { if (rs2 == 0) { /* C.JALR -> jalr x1, rs1, 0*/ - gen_jalr(env, ctx, OPC_RISC_JALR, 1, rd, 0); + gen_jalr(ctx, OPC_RISC_JALR, 1, rd, 0); } else { /* C.ADD -> add rd, rd, rs2 */ gen_arith(ctx, OPC_RISC_ADD, rd, rd, rs2); @@ -1687,7 +1692,7 @@ static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx) } } -static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx) +static void decode_RV32_64C(DisasContext *ctx) { uint8_t op = extract32(ctx->opcode, 0, 2); @@ -1696,15 +1701,15 @@ static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx) decode_RV32_64C0(ctx); break; case 1: - decode_RV32_64C1(env, ctx); + decode_RV32_64C1(ctx); break; case 2: - decode_RV32_64C2(env, ctx); + decode_RV32_64C2(ctx); break; } } -static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) +static void decode_RV32_64G(DisasContext *ctx) { int rs1; int rs2; @@ -1739,13 +1744,13 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) break; case OPC_RISC_JAL: imm = GET_JAL_IMM(ctx->opcode); - gen_jal(env, ctx, rd, imm); + gen_jal(ctx, rd, imm); break; case OPC_RISC_JALR: - gen_jalr(env, ctx, MASK_OP_JALR(ctx->opcode), rd, rs1, imm); + gen_jalr(ctx, MASK_OP_JALR(ctx->opcode), rd, rs1, imm); break; case OPC_RISC_BRANCH: - gen_branch(env, ctx, MASK_OP_BRANCH(ctx->opcode), rs1, rs2, + gen_branch(ctx, MASK_OP_BRANCH(ctx->opcode), rs1, rs2, GET_B_IMM(ctx->opcode)); break; case OPC_RISC_LOAD: @@ -1818,7 +1823,7 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) #endif break; case OPC_RISC_SYSTEM: - gen_system(env, ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1, + gen_system(ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1, (ctx->opcode & 0xFFF00000) >> 20); break; default: @@ -1827,29 +1832,31 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) } } -static void decode_opc(CPURISCVState *env, DisasContext *ctx) +static void decode_opc(DisasContext *ctx) { /* check for compressed insn */ if (extract32(ctx->opcode, 0, 2) != 3) { - if (!riscv_has_ext(env, RVC)) { + if (!has_ext(ctx, RVC)) { gen_exception_illegal(ctx); } else { ctx->pc_succ_insn = ctx->base.pc_next + 2; - decode_RV32_64C(env, ctx); + decode_RV32_64C(ctx); } } else { ctx->pc_succ_insn = ctx->base.pc_next + 4; - decode_RV32_64G(env, ctx); + decode_RV32_64G(ctx); } } -static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) +static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + CPURISCVState *env = cpu->env_ptr; ctx->pc_succ_insn = ctx->base.pc_first; ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK; ctx->mstatus_fs = ctx->base.tb->flags & TB_FLAGS_MSTATUS_FS; + ctx->misa = env->misa; ctx->frm = -1; /* unknown rounding mode */ } @@ -1880,14 +1887,13 @@ static bool riscv_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, return true; } - static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPURISCVState *env = cpu->env_ptr; ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); - decode_opc(env, ctx); + decode_opc(ctx); ctx->base.pc_next = ctx->pc_succ_insn; if (ctx->base.is_jmp == DISAS_NEXT) { From 8f8022608ce9fdeb3336b57a96b8d0a5eab9ba02 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Thu, 10 May 2018 10:16:49 +1200 Subject: [PATCH 50/76] RISC-V: Add misa.MAFD checks to translate Add misa checks for M, A, F and D extensions and if they are not present generate illegal instructions. This improves emulation accurary for harts with a limited set of extensions. Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Alistair Francis Cc: Emilio G. Cota Signed-off-by: Michael Clark --- target/riscv/translate.c | 158 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index c199cd56c4b..b98fd64132b 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -290,24 +290,42 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, tcg_gen_and_tl(source1, source1, source2); break; CASE_OP_32_64(OPC_RISC_MUL): + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_mul_tl(source1, source1, source2); break; case OPC_RISC_MULH: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_muls2_tl(source2, source1, source1, source2); break; case OPC_RISC_MULHSU: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } gen_mulhsu(source1, source1, source2); break; case OPC_RISC_MULHU: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_mulu2_tl(source2, source1, source1, source2); break; #if defined(TARGET_RISCV64) case OPC_RISC_DIVW: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_ext32s_tl(source1, source1); tcg_gen_ext32s_tl(source2, source2); /* fall through to DIV */ #endif case OPC_RISC_DIV: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } /* Handle by altering args to tcg_gen_div to produce req'd results: * For overflow: want source1 in source1 and 1 in source2 * For div by zero: want -1 in source1 and 1 in source2 -> -1 result */ @@ -339,11 +357,17 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, break; #if defined(TARGET_RISCV64) case OPC_RISC_DIVUW: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_ext32u_tl(source1, source1); tcg_gen_ext32u_tl(source2, source2); /* fall through to DIVU */ #endif case OPC_RISC_DIVU: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } cond1 = tcg_temp_new(); zeroreg = tcg_const_tl(0); resultopt1 = tcg_temp_new(); @@ -363,11 +387,17 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, break; #if defined(TARGET_RISCV64) case OPC_RISC_REMW: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_ext32s_tl(source1, source1); tcg_gen_ext32s_tl(source2, source2); /* fall through to REM */ #endif case OPC_RISC_REM: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } cond1 = tcg_temp_new(); cond2 = tcg_temp_new(); zeroreg = tcg_const_tl(0); @@ -395,11 +425,17 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, break; #if defined(TARGET_RISCV64) case OPC_RISC_REMUW: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } tcg_gen_ext32u_tl(source1, source1); tcg_gen_ext32u_tl(source2, source2); /* fall through to REMU */ #endif case OPC_RISC_REMU: + if (!has_ext(ctx, RVM)) { + goto do_illegal; + } cond1 = tcg_temp_new(); zeroreg = tcg_const_tl(0); resultopt1 = tcg_temp_new(); @@ -417,6 +453,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, tcg_temp_free(zeroreg); tcg_temp_free(resultopt1); break; + do_illegal: default: gen_exception_illegal(ctx); return; @@ -697,13 +734,20 @@ static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd, switch (opc) { case OPC_RISC_FLW: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEUL); /* RISC-V requires NaN-boxing of narrower width floating point values */ tcg_gen_ori_i64(cpu_fpr[rd], cpu_fpr[rd], 0xffffffff00000000ULL); break; case OPC_RISC_FLD: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEQ); break; + do_illegal: default: gen_exception_illegal(ctx); break; @@ -729,11 +773,18 @@ static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1, switch (opc) { case OPC_RISC_FSW: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEUL); break; case OPC_RISC_FSD: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEQ); break; + do_illegal: default: gen_exception_illegal(ctx); break; @@ -897,15 +948,22 @@ static void gen_fp_fmadd(DisasContext *ctx, uint32_t opc, int rd, { switch (opc) { case OPC_RISC_FMADD_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; case OPC_RISC_FMADD_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; + do_illegal: default: gen_exception_illegal(ctx); break; @@ -917,15 +975,22 @@ static void gen_fp_fmsub(DisasContext *ctx, uint32_t opc, int rd, { switch (opc) { case OPC_RISC_FMSUB_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; case OPC_RISC_FMSUB_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; + do_illegal: default: gen_exception_illegal(ctx); break; @@ -937,15 +1002,22 @@ static void gen_fp_fnmsub(DisasContext *ctx, uint32_t opc, int rd, { switch (opc) { case OPC_RISC_FNMSUB_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fnmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; case OPC_RISC_FNMSUB_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fnmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; + do_illegal: default: gen_exception_illegal(ctx); break; @@ -957,15 +1029,22 @@ static void gen_fp_fnmadd(DisasContext *ctx, uint32_t opc, int rd, { switch (opc) { case OPC_RISC_FNMADD_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fnmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; case OPC_RISC_FNMADD_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fnmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], cpu_fpr[rs3]); break; + do_illegal: default: gen_exception_illegal(ctx); break; @@ -984,30 +1063,51 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, switch (opc) { case OPC_RISC_FADD_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FSUB_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FMUL_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fmul_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FDIV_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fdiv_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FSQRT_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fsqrt_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); break; case OPC_RISC_FSGNJ_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } gen_fsgnj(ctx, rd, rs1, rs2, rm, INT32_MIN); break; case OPC_RISC_FMIN_S: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } /* also handles: OPC_RISC_FMAX_S */ switch (rm) { case 0x0: @@ -1023,6 +1123,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, case OPC_RISC_FEQ_S: /* also handles: OPC_RISC_FLT_S, OPC_RISC_FLE_S */ + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } t0 = tcg_temp_new(); switch (rm) { case 0x0: @@ -1044,6 +1147,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, case OPC_RISC_FCVT_W_S: /* also OPC_RISC_FCVT_WU_S, OPC_RISC_FCVT_L_S, OPC_RISC_FCVT_LU_S */ + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } t0 = tcg_temp_new(); switch (rs2) { case 0: /* FCVT_W_S */ @@ -1074,6 +1180,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, case OPC_RISC_FCVT_S_W: /* also OPC_RISC_FCVT_S_WU, OPC_RISC_FCVT_S_L, OPC_RISC_FCVT_S_LU */ + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } t0 = tcg_temp_new(); gen_get_gpr(t0, rs1); switch (rs2) { @@ -1103,6 +1212,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, case OPC_RISC_FMV_X_S: /* also OPC_RISC_FCLASS_S */ + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } t0 = tcg_temp_new(); switch (rm) { case 0: /* FMV */ @@ -1124,6 +1236,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, break; case OPC_RISC_FMV_S_X: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } t0 = tcg_temp_new(); gen_get_gpr(t0, rs1); #if defined(TARGET_RISCV64) @@ -1136,22 +1251,37 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, /* double */ case OPC_RISC_FADD_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FSUB_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FMUL_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fmul_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FDIV_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fdiv_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); break; case OPC_RISC_FSQRT_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } gen_set_rm(ctx, rm); gen_helper_fsqrt_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); break; @@ -1161,6 +1291,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, case OPC_RISC_FMIN_D: /* also OPC_RISC_FMAX_D */ + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } switch (rm) { case 0: gen_helper_fmin_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); @@ -1174,6 +1307,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, break; case OPC_RISC_FCVT_S_D: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } switch (rs2) { case 1: gen_set_rm(ctx, rm); @@ -1185,6 +1321,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, break; case OPC_RISC_FCVT_D_S: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } switch (rs2) { case 0: gen_set_rm(ctx, rm); @@ -1197,6 +1336,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, case OPC_RISC_FEQ_D: /* also OPC_RISC_FLT_D, OPC_RISC_FLE_D */ + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } t0 = tcg_temp_new(); switch (rm) { case 0: @@ -1218,6 +1360,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, case OPC_RISC_FCVT_W_D: /* also OPC_RISC_FCVT_WU_D, OPC_RISC_FCVT_L_D, OPC_RISC_FCVT_LU_D */ + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } t0 = tcg_temp_new(); switch (rs2) { case 0: @@ -1248,6 +1393,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, case OPC_RISC_FCVT_D_W: /* also OPC_RISC_FCVT_D_WU, OPC_RISC_FCVT_D_L, OPC_RISC_FCVT_D_LU */ + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } t0 = tcg_temp_new(); gen_get_gpr(t0, rs1); switch (rs2) { @@ -1278,6 +1426,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, #if defined(TARGET_RISCV64) case OPC_RISC_FMV_X_D: /* also OPC_RISC_FCLASS_D */ + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } switch (rm) { case 0: /* FMV */ gen_set_gpr(rd, cpu_fpr[rs1]); @@ -1295,6 +1446,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, break; case OPC_RISC_FMV_D_X: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } t0 = tcg_temp_new(); gen_get_gpr(t0, rs1); tcg_gen_mov_tl(cpu_fpr[rd], t0); @@ -1786,6 +1940,9 @@ static void decode_RV32_64G(DisasContext *ctx) GET_STORE_IMM(ctx->opcode)); break; case OPC_RISC_ATOMIC: + if (!has_ext(ctx, RVA)) { + goto do_illegal; + } gen_atomic(ctx, MASK_OP_ATOMIC(ctx->opcode), rd, rs1, rs2); break; case OPC_RISC_FMADD: @@ -1826,6 +1983,7 @@ static void decode_RV32_64G(DisasContext *ctx) gen_system(ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1, (ctx->opcode & 0xFFF00000) >> 20); break; + do_illegal: default: gen_exception_illegal(ctx); break; From b616b912a250b46c0aa2c4fb601ec2736ecc8e8b Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Thu, 10 May 2018 10:52:34 +1200 Subject: [PATCH 51/76] RISC-V: Add misa runtime write support This patch adds support for writing misa. misa is validated based on rules in the ISA specification. 'E' is mutually exclusive with all other extensions. 'D' depends on 'F' so 'D' bit is dropped if 'F' is not present. A conservative approach to consistency is taken by flushing the translation cache on misa writes. misa_mask is added to the CPU struct to store the original set of extensions. Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Alistair Francis Signed-off-by: Michael Clark --- target/riscv/cpu.c | 2 +- target/riscv/cpu.h | 4 +++- target/riscv/cpu_bits.h | 11 +++++++++ target/riscv/csr.c | 52 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index e558aa8d367..1ecd6d47e36 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -88,7 +88,7 @@ typedef struct RISCVCPUInfo { static void set_misa(CPURISCVState *env, target_ulong misa) { - env->misa = misa; + env->misa_mask = env->misa = misa; } static void set_versions(CPURISCVState *env, int user_ver, int priv_ver) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index ae0e3f6a544..830a9d476dc 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -86,7 +86,8 @@ so a cpu features bitfield is required, likewise for optional PMP support */ enum { RISCV_FEATURE_MMU, - RISCV_FEATURE_PMP + RISCV_FEATURE_PMP, + RISCV_FEATURE_MISA_RW }; #define USER_VERSION_2_02_0 0x00020200 @@ -118,6 +119,7 @@ struct CPURISCVState { target_ulong user_ver; target_ulong priv_ver; target_ulong misa; + target_ulong misa_mask; uint32_t features; diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 5439f4719ee..7afcb2468d8 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -311,10 +311,21 @@ #define MSTATUS32_SD 0x80000000 #define MSTATUS64_SD 0x8000000000000000ULL +#define MISA32_MXL 0xC0000000 +#define MISA64_MXL 0xC000000000000000ULL + +#define MXL_RV32 1 +#define MXL_RV64 2 +#define MXL_RV128 3 + #if defined(TARGET_RISCV32) #define MSTATUS_SD MSTATUS32_SD +#define MISA_MXL MISA32_MXL +#define MXL_VAL MXL_RV32 #elif defined(TARGET_RISCV64) #define MSTATUS_SD MSTATUS64_SD +#define MISA_MXL MISA64_MXL +#define MXL_VAL MXL_RV64 #endif /* sstatus CSR bits */ diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 85454f1d4cf..a798b82d8b0 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -341,6 +341,56 @@ static int read_misa(CPURISCVState *env, int csrno, target_ulong *val) return 0; } +static int write_misa(CPURISCVState *env, int csrno, target_ulong val) +{ + if (!riscv_feature(env, RISCV_FEATURE_MISA_RW)) { + /* drop write to misa */ + return 0; + } + + /* 'I' or 'E' must be present */ + if (!(val & (RVI | RVE))) { + /* it not, drop write to misa */ + return 0; + } + + /* 'E' excludes all other extensions */ + if (val & RVE) { + /* when we support 'E' we can do "val = RVE;" however + * for now we just drop writes if 'E' is present */ + return 0; + } + + /* Mask extensions that are not supported by this hart */ + val &= env->misa_mask; + + /* Mask extensions that are not supported by QEMU */ + val &= (RVI | RVE | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + + /* 'D' depends on 'F', so clear 'D' if 'F' is not present */ + if ((val & RVD) && !(val & RVF)) { + val &= ~RVD; + } + + /* Suppress 'C' if next instruction is not aligned + TODO: this should check next_pc */ + if ((val & RVC) && (GETPC() & ~3) != 0) { + val &= ~RVC; + } + + /* misa.MXL writes are not supported by QEMU */ + val = (env->misa & MISA_MXL) | (val & ~MISA_MXL); + + /* flush translation cache */ + if (val != env->misa) { + tb_flush(CPU(riscv_env_get_cpu(env))); + } + + env->misa = val; + + return 0; +} + static int read_medeleg(CPURISCVState *env, int csrno, target_ulong *val) { *val = env->medeleg; @@ -821,7 +871,7 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* Machine Trap Setup */ [CSR_MSTATUS] = { any, read_mstatus, write_mstatus }, - [CSR_MISA] = { any, read_misa }, + [CSR_MISA] = { any, read_misa, write_misa }, [CSR_MIDELEG] = { any, read_mideleg, write_mideleg }, [CSR_MEDELEG] = { any, read_medeleg, write_medeleg }, [CSR_MIE] = { any, read_mie, write_mie }, From 68350686f5ee40c6f143b8bb435508c1ac2923d1 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 12 May 2018 13:29:26 +1200 Subject: [PATCH 52/76] RISC-V: Fix CLINT timecmp low 32-bit writes A missing shift made updates to the low order bits of timecmp erroneously copy the old low order bits into the high order bits of the 64-bit timecmp register. Add the missing shift and rename timecmp local variables to timecmp_hi and timecmp_lo. This bug didn't show up as the low order bits are usually written first followed by the high order bits meaning the high order bits contained an invalid value between the timecmp_lo and timecmp_hi update. Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Alistair Francis Co-Authored-by: Johannes Haring Signed-off-by: Michael Clark --- hw/riscv/sifive_clint.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c index 0d2fd52487e..d4c159e9373 100644 --- a/hw/riscv/sifive_clint.c +++ b/hw/riscv/sifive_clint.c @@ -146,15 +146,15 @@ static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value, error_report("clint: invalid timecmp hartid: %zu", hartid); } else if ((addr & 0x7) == 0) { /* timecmp_lo */ - uint64_t timecmp = env->timecmp; + uint64_t timecmp_hi = env->timecmp >> 32; sifive_clint_write_timecmp(RISCV_CPU(cpu), - timecmp << 32 | (value & 0xFFFFFFFF)); + timecmp_hi << 32 | (value & 0xFFFFFFFF)); return; } else if ((addr & 0x7) == 4) { /* timecmp_hi */ - uint64_t timecmp = env->timecmp; + uint64_t timecmp_lo = env->timecmp; sifive_clint_write_timecmp(RISCV_CPU(cpu), - value << 32 | (timecmp & 0xFFFFFFFF)); + value << 32 | (timecmp_lo & 0xFFFFFFFF)); } else { error_report("clint: invalid timecmp write: %08x", (uint32_t)addr); } From 38824c15ba375d82bd36034f93129471b638ed72 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 12 May 2018 14:50:00 +1200 Subject: [PATCH 53/76] RISC-V: Fix PLIC pending bitfield reads The address calculation for the pending bitfield had a copy paste bug. This bug went unnoticed because the Linux PLIC driver does not read the pending bitfield, rather it reads pending interrupt numbers from the claim register and writes acknowledgements back to the claim register. Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Alistair Francis Reported-by: Vincent Siles Signed-off-by: Michael Clark --- hw/riscv/sifive_plic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c index b9095bfde4f..bcde8cc7585 100644 --- a/hw/riscv/sifive_plic.c +++ b/hw/riscv/sifive_plic.c @@ -215,7 +215,7 @@ static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size) } else if (addr >= plic->pending_base && /* 1 bit per source */ addr < plic->pending_base + (plic->num_sources >> 3)) { - uint32_t word = (addr - plic->priority_base) >> 2; + uint32_t word = (addr - plic->pending_base) >> 2; if (RISCV_DEBUG_PLIC) { qemu_log("plic: read pending: word=%d value=%d\n", word, plic->pending[word]); From 3ad4698a29becc95bc0a827a4cc65779a8248b52 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 12 May 2018 20:45:17 +1200 Subject: [PATCH 54/76] RISC-V: Enable second UART on sifive_e and sifive_u Previously the second UARTs on the sifive_e and sifive_u machines where disabled due to check-qtest-riscv32 and check-qtest-riscv64 failures. Recent changes in the QEMU core serial code have resolved these failures so the second UARTs can be instantiated. Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Alistair Francis Signed-off-by: Michael Clark --- hw/riscv/sifive_e.c | 5 ++--- hw/riscv/sifive_u.c | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 4577d720372..a927b1263a0 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -192,9 +192,8 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) memmap[SIFIVE_E_QSPI0].base, memmap[SIFIVE_E_QSPI0].size); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm0", memmap[SIFIVE_E_PWM0].base, memmap[SIFIVE_E_PWM0].size); - /* sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base, - serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), - SIFIVE_E_UART1_IRQ)); */ + sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base, + serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_UART1_IRQ)); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi1", memmap[SIFIVE_E_QSPI1].base, memmap[SIFIVE_E_QSPI1].size); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm1", diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 59ae1ce24a0..b819e68e510 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -348,9 +348,8 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) memmap[SIFIVE_U_PLIC].size); sifive_uart_create(system_memory, memmap[SIFIVE_U_UART0].base, serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART0_IRQ)); - /* sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base, - serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), - SIFIVE_U_UART1_IRQ)); */ + sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base, + serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART1_IRQ)); sifive_clint_create(memmap[SIFIVE_U_CLINT].base, memmap[SIFIVE_U_CLINT].size, smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); From fb092b0463ea08682fb7446899abf2c9e52f583e Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Wed, 16 May 2018 21:47:36 +1200 Subject: [PATCH 55/76] RISC-V: Remove unnecessary disassembler constraints Remove machine generated constraints that are not referenced by the pseudo-instruction constraints. Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Alistair Francis Signed-off-by: Michael Clark --- disas/riscv.c | 138 -------------------------------------------------- 1 file changed, 138 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 7fd1019623e..27546dd7902 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -87,33 +87,10 @@ typedef enum { typedef enum { rvc_end, - rvc_simm_6, - rvc_imm_6, - rvc_imm_7, - rvc_imm_8, - rvc_imm_9, - rvc_imm_10, - rvc_imm_12, - rvc_imm_18, - rvc_imm_nz, - rvc_imm_x2, - rvc_imm_x4, - rvc_imm_x8, - rvc_imm_x16, - rvc_rd_b3, - rvc_rs1_b3, - rvc_rs2_b3, - rvc_rd_eq_rs1, rvc_rd_eq_ra, - rvc_rd_eq_sp, rvc_rd_eq_x0, - rvc_rs1_eq_sp, rvc_rs1_eq_x0, rvc_rs2_eq_x0, - rvc_rd_ne_x0_x2, - rvc_rd_ne_x0, - rvc_rs1_ne_x0, - rvc_rs2_ne_x0, rvc_rs2_eq_rs1, rvc_rs1_eq_ra, rvc_imm_eq_zero, @@ -2522,111 +2499,16 @@ static bool check_constraints(rv_decode *dec, const rvc_constraint *c) uint8_t rd = dec->rd, rs1 = dec->rs1, rs2 = dec->rs2; while (*c != rvc_end) { switch (*c) { - case rvc_simm_6: - if (!(imm >= -32 && imm < 32)) { - return false; - } - break; - case rvc_imm_6: - if (!(imm <= 63)) { - return false; - } - break; - case rvc_imm_7: - if (!(imm <= 127)) { - return false; - } - break; - case rvc_imm_8: - if (!(imm <= 255)) { - return false; - } - break; - case rvc_imm_9: - if (!(imm <= 511)) { - return false; - } - break; - case rvc_imm_10: - if (!(imm <= 1023)) { - return false; - } - break; - case rvc_imm_12: - if (!(imm <= 4095)) { - return false; - } - break; - case rvc_imm_18: - if (!(imm <= 262143)) { - return false; - } - break; - case rvc_imm_nz: - if (!(imm != 0)) { - return false; - } - break; - case rvc_imm_x2: - if (!((imm & 0b1) == 0)) { - return false; - } - break; - case rvc_imm_x4: - if (!((imm & 0b11) == 0)) { - return false; - } - break; - case rvc_imm_x8: - if (!((imm & 0b111) == 0)) { - return false; - } - break; - case rvc_imm_x16: - if (!((imm & 0b1111) == 0)) { - return false; - } - break; - case rvc_rd_b3: - if (!(rd >= 8 && rd <= 15)) { - return false; - } - break; - case rvc_rs1_b3: - if (!(rs1 >= 8 && rs1 <= 15)) { - return false; - } - break; - case rvc_rs2_b3: - if (!(rs2 >= 8 && rs2 <= 15)) { - return false; - } - break; - case rvc_rd_eq_rs1: - if (!(rd == rs1)) { - return false; - } - break; case rvc_rd_eq_ra: if (!(rd == 1)) { return false; } break; - case rvc_rd_eq_sp: - if (!(rd == 2)) { - return false; - } - break; case rvc_rd_eq_x0: if (!(rd == 0)) { return false; } break; - case rvc_rs1_eq_sp: - if (!(rs1 == 2)) { - return false; - } - break; case rvc_rs1_eq_x0: if (!(rs1 == 0)) { return false; @@ -2637,26 +2519,6 @@ static bool check_constraints(rv_decode *dec, const rvc_constraint *c) return false; } break; - case rvc_rd_ne_x0_x2: - if (!(rd != 0 && rd != 2)) { - return false; - } - break; - case rvc_rd_ne_x0: - if (!(rd != 0)) { - return false; - } - break; - case rvc_rs1_ne_x0: - if (!(rs1 != 0)) { - return false; - } - break; - case rvc_rs2_ne_x0: - if (!(rs2 != 0)) { - return false; - } - break; case rvc_rs2_eq_rs1: if (!(rs2 == rs1)) { return false; From 4ab48e566a62d7d18ab9e95e0ff8f21ebbf3b463 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Thu, 17 May 2018 19:15:45 +1200 Subject: [PATCH 56/76] elf: Add RISC-V PSABI ELF header defines Refer to the RISC-V PSABI specification for details: - https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md Cc: Michael Tokarev Cc: Richard Henderson Cc: Alistair Francis Reviewed-by: Laurent Vivier Signed-off-by: Michael Clark --- include/elf.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/elf.h b/include/elf.h index 934dbbd6b3a..217c8ff9ca7 100644 --- a/include/elf.h +++ b/include/elf.h @@ -1285,6 +1285,16 @@ typedef struct { #define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ #define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ +/* RISC-V ELF Flags. */ +#define EF_RISCV_RVC 0x0001 +#define EF_RISCV_FLOAT_ABI 0x0006 +#define EF_RISCV_FLOAT_ABI_SOFT 0x0000 +#define EF_RISCV_FLOAT_ABI_SINGLE 0x0002 +#define EF_RISCV_FLOAT_ABI_DOUBLE 0x0004 +#define EF_RISCV_FLOAT_ABI_QUAD 0x0006 +#define EF_RISCV_RVE 0x0008 +#define EF_RISCV_TSO 0x0010 + typedef struct elf32_rel { Elf32_Addr r_offset; Elf32_Word r_info; From 6175fc31dcc2d980ea27340aec1daca134c79105 Mon Sep 17 00:00:00 2001 From: Kito Cheng Date: Fri, 16 Jun 2017 16:17:12 +0800 Subject: [PATCH 57/76] RISC-V: linux-user support for RVE ABI This change checks elf_flags for EF_RISCV_RVE and if present uses the RVE linux syscall ABI which uses t0 for the syscall number instead of a7. Warn and exit if a non-RVE ABI binary is run on a cpu with the RVE extension as it is incompatible. Cc: Palmer Dabbelt Cc: Sagar Karandikar Cc: Bastian Koppelmann Cc: Alistair Francis Co-authored-by: Kito Cheng Co-authored-by: Michael Clark Signed-off-by: Michael Clark --- linux-user/riscv/cpu_loop.c | 14 +++++++++++++- target/riscv/cpu.h | 4 ++++ target/riscv/cpu_user.h | 3 ++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index f137d39d7e8..5f6a941c6c1 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "cpu_loop-common.h" +#include "elf.h" void cpu_loop(CPURISCVState *env) { @@ -53,7 +54,8 @@ void cpu_loop(CPURISCVState *env) ret = 0; } else { ret = do_syscall(env, - env->gpr[xA7], + env->gpr[(env->elf_flags & EF_RISCV_RVE) + ? xT0 : xA7], env->gpr[xA0], env->gpr[xA1], env->gpr[xA2], @@ -113,6 +115,16 @@ void cpu_loop(CPURISCVState *env) void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + CPUState *cpu = ENV_GET_CPU(env); + TaskState *ts = cpu->opaque; + struct image_info *info = ts->info; + env->pc = regs->sepc; env->gpr[xSP] = regs->sp; + env->elf_flags = info->elf_flags; + + if ((env->misa & RVE) && !(env->elf_flags & EF_RISCV_RVE)) { + fprintf(stderr, "Incompatible ELF: RVE cpu requires RVE ABI binary\n"); + exit(EXIT_FAILURE); + } } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 830a9d476dc..0823461ae9f 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -123,6 +123,10 @@ struct CPURISCVState { uint32_t features; +#ifdef CONFIG_USER_ONLY + uint32_t elf_flags; +#endif + #ifndef CONFIG_USER_ONLY target_ulong priv; target_ulong resetvec; diff --git a/target/riscv/cpu_user.h b/target/riscv/cpu_user.h index c2199610abf..52d380aa98c 100644 --- a/target/riscv/cpu_user.h +++ b/target/riscv/cpu_user.h @@ -10,4 +10,5 @@ #define xA4 14 #define xA5 15 #define xA6 16 -#define xA7 17 /* syscall number goes here */ +#define xA7 17 /* syscall number for RVI ABI */ +#define xT0 5 /* syscall number for RVE ABI */ From 5b69297cfdc00741973373b06e942bfd81752f30 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Tue, 22 May 2018 13:33:28 +1200 Subject: [PATCH 58/76] RISC-V: Don't add NULL bootargs to device-tree Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- hw/riscv/sifive_u.c | 4 +++- hw/riscv/spike.c | 6 ++++-- hw/riscv/virt.c | 4 +++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index b819e68e510..d6cb149f145 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -230,7 +230,9 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_add_subnode(fdt, "/chosen"); qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); - qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + if (cmdline) { + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + } g_free(nodename); } diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index c8c056c50b0..b0f0e97a1dd 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -156,8 +156,10 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap, g_free(cells); g_free(nodename); - qemu_fdt_add_subnode(fdt, "/chosen"); - qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + if (cmdline) { + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + } } static void spike_v1_10_0_board_init(MachineState *machine) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index da6d24287e4..90e7ef30c17 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -254,7 +254,9 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, qemu_fdt_add_subnode(fdt, "/chosen"); qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); - qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + if (cmdline) { + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + } g_free(nodename); return fdt; From d45ae034099e3d18ba3e824e1d5077f6d53333d5 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sun, 20 May 2018 09:03:10 +1200 Subject: [PATCH 59/76] RISC-V: Support separate firmware and kernel payload Support for separate firmware and kernel payload is added by updating BBL to read optional preloaded kernel address attributes from device-tree using a similar mechanism to that used to pass init ramdisk addresses to linux kernel. chosen { riscv,kernel-start = <0x00000000 0x80200000>; riscv,kernel-end = <0x00000000 0x80590634>; }; These attributes are added by QEMU and read by BBL when combining -bios and -kernel options. e.g. $ qemu-system-riscv64 -machine virt -bios bbl -kernel vmlinux With this change, bbl can be compiled without --with-payload and the dummy payload alignment is altered to make the memory footprint of the firmware-only bbl smaller. The dummy payload message is updated to indicate the alternative load method. This load method could also be supported by a first stage boot loader that reads seperate firmware and kernel from SPI flash. The main advantage of this new mechanism is that it eases kernel development by avoiding the riscv-pk packaging step after kernel builds, makes building per repository artefacts for CI simpler, and mimics bootloaders on other platforms that can load a kernel image file directly. Ultimately BBL should use an SPI driver to load the kernel image however this mechanism supports use cases such such as QEMU's -bios, -kernel and -initrd options following examples from other platforms that pass kernel entry to firmware via device-tree. The board is also changed to use the firmware address from the loaded firmware or combined firmware+kernel. This is normally equal to the DRAM base address of 0x8000_0000, however now it is possible to boot firmware at different load addresses because the reset code jumps to the actual firmware entry address. Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- hw/riscv/Makefile.objs | 1 + hw/riscv/boot.c | 172 ++++++++++++++++++++++++++++++++++++++++ hw/riscv/virt.c | 67 +++------------- include/hw/riscv/boot.h | 30 +++++++ 4 files changed, 213 insertions(+), 57 deletions(-) create mode 100644 hw/riscv/boot.c create mode 100644 include/hw/riscv/boot.h diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs index 1dde01d39dc..d36b004ab0f 100644 --- a/hw/riscv/Makefile.objs +++ b/hw/riscv/Makefile.objs @@ -1,3 +1,4 @@ +obj-y += boot.o obj-y += riscv_htif.o obj-y += riscv_hart.o obj-y += sifive_e.o diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c new file mode 100644 index 00000000000..cf4e5d59463 --- /dev/null +++ b/hw/riscv/boot.c @@ -0,0 +1,172 @@ +/* + * QEMU RISCV firmware and kernel loader + * + * Copyright (c) 2017-2018 SiFive, Inc. + * + * Holds the state of a heterogenous array of RISC-V harts + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "hw/loader.h" +#include "hw/boards.h" +#include "sysemu/device_tree.h" +#include "elf.h" +#include "hw/riscv/boot.h" + +#define RISCV_BOOT_DEBUG 0 + +#define boot_debug(fs, ...) \ + if (RISCV_BOOT_DEBUG) { \ + fprintf(stderr, "boot: %s: "fs, __func__, ##__VA_ARGS__); \ + } + +static uint64_t kernel_offset; + +static uint64_t kernel_translate(void *opaque, uint64_t addr) +{ + /* mask kernel virtual address and offset by load address */ + if (kernel_offset) { + return (addr & 0x7fffffff) + kernel_offset; + } else { + return addr; + } +} + +hwaddr riscv_load_firmware(const char *filename) +{ + uint64_t firmware_entry, firmware_start, firmware_end; + + if (load_elf(filename, NULL, NULL, + &firmware_entry, &firmware_start, &firmware_end, + 0, EM_RISCV, 1, 0) < 0) { + error_report("riscv_boot: could not load firmware '%s'", filename); + exit(1); + } + + /* align kernel load address to the megapage after the firmware */ +#if defined(TARGET_RISCV32) + kernel_offset = (firmware_end + 0x3fffff) & ~0x3fffff; +#else + kernel_offset = (firmware_end + 0x1fffff) & ~0x1fffff; +#endif + + boot_debug("entry=0x" TARGET_FMT_plx " start=0x" TARGET_FMT_plx " " + "end=0x" TARGET_FMT_plx " kernel_offset=0x" TARGET_FMT_plx "\n", + firmware_entry, firmware_start, firmware_end, kernel_offset); + + return firmware_entry; +} + +hwaddr riscv_load_kernel(const char *filename, void *fdt) +{ + uint64_t kernel_entry, kernel_start, kernel_end; + + if (load_elf(filename, kernel_translate, NULL, + &kernel_entry, &kernel_start, &kernel_end, + 0, EM_RISCV, 1, 0) < 0) { + error_report("riscv_boot: could not load kernel '%s'", filename); + exit(1); + } + + boot_debug("entry=0x" TARGET_FMT_plx " start=0x" TARGET_FMT_plx " " + "end=0x" TARGET_FMT_plx "\n", kernel_entry, kernel_start, + kernel_end); + + /* + * pass kernel load address via device-tree to firmware + * + * BBL reads the kernel address from device-tree + */ + if (fdt) { + qemu_fdt_setprop_cells(fdt, "/chosen", "riscv,kernel-end", + kernel_end >> 32, kernel_end); + qemu_fdt_setprop_cells(fdt, "/chosen", "riscv,kernel-start", + kernel_start >> 32, kernel_start); + } + + return kernel_entry; +} + +void riscv_load_initrd(const char *filename, uint64_t mem_size, + hwaddr firmware_entry, void *fdt) +{ + uint64_t start, size; + + /* We want to put the initrd far enough into RAM that when the + * kernel is uncompressed it will not clobber the initrd. However + * on boards without much RAM we must ensure that we still leave + * enough room for a decent sized initrd, and on boards with large + * amounts of RAM we must avoid the initrd being so far up in RAM + * that it is outside lowmem and inaccessible to the kernel. + * So for boards with less than 256MB of RAM we put the initrd + * halfway into RAM, and for boards with 256MB of RAM or more we put + * the initrd at 128MB. + */ + start = firmware_entry + MIN(mem_size / 2, 128 * 1024 * 1024); + + size = load_ramdisk(filename, start, mem_size - start); + if (size == -1) { + size = load_image_targphys(filename, start, mem_size - start); + if (size == -1) { + error_report("riscv_boot: could not load ramdisk '%s'", filename); + exit(1); + } + } + + boot_debug("start=0x" TARGET_FMT_plx " end=0x" TARGET_FMT_plx "\n", + start, start + size); + + /* + * pass initrd load address via device-tree to kernel + * + * linux-kernel reads the initrd address from device-tree + */ + if (fdt) { + qemu_fdt_setprop_cells(fdt, "/chosen", "linux,initrd-end", + (start + size) >> 32, start + size); + qemu_fdt_setprop_cells(fdt, "/chosen", "linux,initrd-start", + start >> 32, start); + } +} + +hwaddr riscv_load_firmware_kernel_initrd(MachineState *machine, void *fdt) +{ + hwaddr firmware_entry = 0; + + /* load firmware e.g. -bios bbl */ + if (machine->firmware) { + firmware_entry = riscv_load_firmware(machine->firmware); + } + + /* load combined bbl+kernel or separate kernel */ + if (machine->kernel_filename) { + if (machine->firmware) { + /* load separate bios and kernel e.g. -bios bbl -kernel vmlinux */ + riscv_load_kernel(machine->kernel_filename, fdt); + } else { + /* load traditional combined bbl+kernel e.g. -kernel bbl_vmlimux */ + firmware_entry = riscv_load_kernel(machine->kernel_filename, NULL); + } + if (machine->initrd_filename) { + /* load separate initrd */ + riscv_load_initrd(machine->initrd_filename, machine->ram_size, + firmware_entry, fdt); + } + } + + return firmware_entry; +} diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 90e7ef30c17..66dc4e07c3f 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -34,6 +34,7 @@ #include "hw/riscv/sifive_plic.h" #include "hw/riscv/sifive_clint.h" #include "hw/riscv/sifive_test.h" +#include "hw/riscv/boot.h" #include "hw/riscv/virt.h" #include "chardev/char.h" #include "sysemu/arch_init.h" @@ -57,47 +58,6 @@ static const struct MemmapEntry { [VIRT_DRAM] = { 0x80000000, 0x0 }, }; -static uint64_t load_kernel(const char *kernel_filename) -{ - uint64_t kernel_entry, kernel_high; - - if (load_elf(kernel_filename, NULL, NULL, - &kernel_entry, NULL, &kernel_high, - 0, EM_RISCV, 1, 0) < 0) { - error_report("qemu: could not load kernel '%s'", kernel_filename); - exit(1); - } - return kernel_entry; -} - -static hwaddr load_initrd(const char *filename, uint64_t mem_size, - uint64_t kernel_entry, hwaddr *start) -{ - int size; - - /* We want to put the initrd far enough into RAM that when the - * kernel is uncompressed it will not clobber the initrd. However - * on boards without much RAM we must ensure that we still leave - * enough room for a decent sized initrd, and on boards with large - * amounts of RAM we must avoid the initrd being so far up in RAM - * that it is outside lowmem and inaccessible to the kernel. - * So for boards with less than 256MB of RAM we put the initrd - * halfway into RAM, and for boards with 256MB of RAM or more we put - * the initrd at 128MB. - */ - *start = kernel_entry + MIN(mem_size / 2, 128 * MiB); - - size = load_ramdisk(filename, *start, mem_size - *start); - if (size == -1) { - size = load_image_targphys(filename, *start, mem_size - *start); - if (size == -1) { - error_report("qemu: could not load ramdisk '%s'", filename); - exit(1); - } - } - return *start + size; -} - static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, uint64_t mem_size, const char *cmdline) { @@ -274,6 +234,7 @@ static void riscv_virt_board_init(MachineState *machine) size_t plic_hart_config_len; int i; void *fdt; + hwaddr firmware_entry; /* Initialize SOC */ object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc), @@ -300,20 +261,12 @@ static void riscv_virt_board_init(MachineState *machine) memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base, mask_rom); - if (machine->kernel_filename) { - uint64_t kernel_entry = load_kernel(machine->kernel_filename); - - if (machine->initrd_filename) { - hwaddr start; - hwaddr end = load_initrd(machine->initrd_filename, - machine->ram_size, kernel_entry, - &start); - qemu_fdt_setprop_cell(fdt, "/chosen", - "linux,initrd-start", start); - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", - end); - } - } + /* + * combined firmware and kernel: -kernel bbl_vmlimux + * separate firmware and kernel: -bios bbl -kernel vmlinux + * firmware, kernel and ramdisk: -bios bbl -kernel vmlinux -initrd initramfs + */ + firmware_entry = riscv_load_firmware_kernel_initrd(machine, fdt); /* reset vector */ uint32_t reset_vec[8] = { @@ -327,8 +280,8 @@ static void riscv_virt_board_init(MachineState *machine) #endif 0x00028067, /* jr t0 */ 0x00000000, - memmap[VIRT_DRAM].base, /* start: .dword memmap[VIRT_DRAM].base */ - 0x00000000, + firmware_entry, /* .word firmware_entry */ + firmware_entry >> 32, /* dtb: */ }; diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h new file mode 100644 index 00000000000..aa30bf1c45b --- /dev/null +++ b/include/hw/riscv/boot.h @@ -0,0 +1,30 @@ +/* + * QEMU RISCV firmware and kernel loader interface + * + * Copyright (c) 2017-2018 SiFive, Inc. + * + * Holds the state of a heterogenous array of RISC-V harts + * + * 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 . + */ + +#ifndef HW_RISCV_BOOT_H +#define HW_RISCV_BOOT_H + +hwaddr riscv_load_firmware(const char *filename); +hwaddr riscv_load_kernel(const char *filename, void *fdt); +void riscv_load_initrd(const char *filename, uint64_t mem_size, + hwaddr firmware_entry, void *fdt); +hwaddr riscv_load_firmware_kernel_initrd(MachineState *machine, void *fdt); + +#endif From f37c96845e31389d70e57d9a2cfdd96ed8853055 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Sat, 26 May 2018 10:10:08 +1200 Subject: [PATCH 60/76] RISC-V: Change local interrupts from edge to level This effectively changes riscv_cpu_update_mip from edge to level. i.e. cpu_interrupt or cpu_reset_interrupt are called regardless of the current interrupt level. Fixes WFI doesn't return when a IPI is issued: - https://github.com/riscv/riscv-qemu/issues/132 To test: 1) Apply RISC-V Linux CPU hotplug patch: - http://lists.infradead.org/pipermail/linux-riscv/2018-May/000603.html 2) Enable CONFIG_CPU_HOTPLUG in linux .config 3) Try to offline and online cpus: echo 1 > /sys/devices/system/cpu/cpu2/online echo 0 > /sys/devices/system/cpu/cpu2/online echo 1 > /sys/devices/system/cpu/cpu2/online Reported-by: Atish Patra Cc: Atish Patra Cc: Alistair Francis Signed-off-by: Michael Clark --- target/riscv/cpu_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 555756d40cf..073bdcfe741 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -95,9 +95,9 @@ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) cmp = atomic_cmpxchg(&env->mip, old, new); } while (old != cmp); - if (new && !old) { + if (new) { cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); - } else if (!new && old) { + } else { cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); } From b885d0380e031c8c971587f8ceafc4f705c0eed4 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Tue, 19 Jun 2018 13:17:50 -0700 Subject: [PATCH 61/76] RISC-V: Add SiFive Test device to E and U series machines Add the SiFive Test device to 'sifive_e' and 'sifive_u' machines so that test cases can shutdown QEMU and signal an exit code. The SiFive Test Finisher is a real device that exists in the RTL to allow exiting simulations. Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- hw/riscv/sifive_e.c | 3 +++ hw/riscv/sifive_u.c | 11 +++++++++++ include/hw/riscv/sifive_e.h | 1 + include/hw/riscv/sifive_u.h | 1 + 4 files changed, 16 insertions(+) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index a927b1263a0..e7413f5f750 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -41,6 +41,7 @@ #include "hw/riscv/riscv_hart.h" #include "hw/riscv/sifive_plic.h" #include "hw/riscv/sifive_clint.h" +#include "hw/riscv/sifive_test.h" #include "hw/riscv/sifive_prci.h" #include "hw/riscv/sifive_uart.h" #include "hw/riscv/sifive_e.h" @@ -56,6 +57,7 @@ static const struct MemmapEntry { [SIFIVE_E_DEBUG] = { 0x0, 0x100 }, [SIFIVE_E_MROM] = { 0x1000, 0x2000 }, [SIFIVE_E_OTP] = { 0x20000, 0x2000 }, + [SIFIVE_E_TEST] = { 0x100000, 0x1000 }, [SIFIVE_E_CLINT] = { 0x2000000, 0x10000 }, [SIFIVE_E_PLIC] = { 0xc000000, 0x4000000 }, [SIFIVE_E_AON] = { 0x10000000, 0x8000 }, @@ -181,6 +183,7 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) sifive_clint_create(memmap[SIFIVE_E_CLINT].base, memmap[SIFIVE_E_CLINT].size, smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + sifive_test_create(memmap[SIFIVE_E_TEST].base); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.aon", memmap[SIFIVE_E_AON].base, memmap[SIFIVE_E_AON].size); sifive_prci_create(memmap[SIFIVE_E_PRCI].base); diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index d6cb149f145..69ba408a333 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -38,6 +38,7 @@ #include "hw/riscv/riscv_hart.h" #include "hw/riscv/sifive_plic.h" #include "hw/riscv/sifive_clint.h" +#include "hw/riscv/sifive_test.h" #include "hw/riscv/sifive_uart.h" #include "hw/riscv/sifive_prci.h" #include "hw/riscv/sifive_u.h" @@ -55,6 +56,7 @@ static const struct MemmapEntry { } sifive_u_memmap[] = { [SIFIVE_U_DEBUG] = { 0x0, 0x100 }, [SIFIVE_U_MROM] = { 0x1000, 0x11000 }, + [SIFIVE_U_TEST] = { 0x100000, 0x1000 }, [SIFIVE_U_CLINT] = { 0x2000000, 0x10000 }, [SIFIVE_U_PLIC] = { 0xc000000, 0x4000000 }, [SIFIVE_U_UART0] = { 0x10013000, 0x1000 }, @@ -218,6 +220,14 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0); g_free(nodename); + nodename = g_strdup_printf("/soc/test@%lx", + (long)memmap[SIFIVE_U_TEST].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,test0"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[SIFIVE_U_TEST].base, + 0x0, memmap[SIFIVE_U_TEST].size); + nodename = g_strdup_printf("/soc/uart@%lx", (long)memmap[SIFIVE_U_UART0].base); qemu_fdt_add_subnode(fdt, nodename); @@ -374,6 +384,7 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem), 0, memmap[SIFIVE_U_GEM].base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem), 0, plic_gpios[SIFIVE_U_GEM_IRQ]); + sifive_test_create(memmap[SIFIVE_U_TEST].base); } static void riscv_sifive_u_machine_init(MachineClass *mc) diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h index 7b6d8aed968..c856b99d14e 100644 --- a/include/hw/riscv/sifive_e.h +++ b/include/hw/riscv/sifive_e.h @@ -44,6 +44,7 @@ enum { SIFIVE_E_DEBUG, SIFIVE_E_MROM, SIFIVE_E_OTP, + SIFIVE_E_TEST, SIFIVE_E_CLINT, SIFIVE_E_PLIC, SIFIVE_E_AON, diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index e8b4d9ffa3f..d2ac08269c6 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -48,6 +48,7 @@ typedef struct SiFiveUState { enum { SIFIVE_U_DEBUG, SIFIVE_U_MROM, + SIFIVE_U_TEST, SIFIVE_U_CLINT, SIFIVE_U_PLIC, SIFIVE_U_UART0, From 3cb31abcbb9562ef254779fec786bd06d41ad768 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Wed, 20 Jun 2018 17:33:13 -0700 Subject: [PATCH 62/76] RISC-V: Add support for vectored interrupts If vectored interrupts are enabled (bits[1:0] of mtvec/stvec == 1) then use the following logic for trap entry address calculation: pc = mtvec + cause * 4 In addition to adding support for vectored interrupts this patch simplifies the interrupt delivery logic by making sync/async cause decoding and encoding steps distinct. The cause code and the sign bit indicating sync/async is split at the beginning of the function and fixed cause is renamed to cause. The MSB setting for async traps is delayed until setting mcause/scause to allow redundant variables to be eliminated. Some variables are renamed for conciseness and moved so that decls are at the start of the block. Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- target/riscv/cpu_helper.c | 144 ++++++++++++++------------------------ target/riscv/csr.c | 12 ++-- 2 files changed, 59 insertions(+), 97 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 073bdcfe741..f78295390ed 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -454,118 +454,80 @@ void riscv_cpu_do_interrupt(CPUState *cs) RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - if (RISCV_DEBUG_INTERRUPT) { - int log_cause = cs->exception_index & RISCV_EXCP_INT_MASK; - if (cs->exception_index & RISCV_EXCP_INT_FLAG) { - qemu_log_mask(LOG_TRACE, "core " - TARGET_FMT_ld ": trap %s, epc 0x" TARGET_FMT_lx "\n", - env->mhartid, riscv_intr_names[log_cause], env->pc); - } else { - qemu_log_mask(LOG_TRACE, "core " - TARGET_FMT_ld ": intr %s, epc 0x" TARGET_FMT_lx "\n", - env->mhartid, riscv_excp_names[log_cause], env->pc); + /* cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide + so we mask off the MSB and separate into trap type and cause */ + bool async = !!(cs->exception_index & RISCV_EXCP_INT_FLAG); + target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK; + target_ulong deleg = async ? deleg = env->mideleg : env->medeleg; + target_ulong tval = 0; + + static const int ecall_cause_map[] = { + [PRV_U] = RISCV_EXCP_U_ECALL, + [PRV_S] = RISCV_EXCP_S_ECALL, + [PRV_H] = RISCV_EXCP_H_ECALL, + [PRV_M] = RISCV_EXCP_M_ECALL + }; + + if (!async) { + /* set tval to badaddr for traps with address information */ + switch (cause) { + case RISCV_EXCP_INST_ADDR_MIS: + case RISCV_EXCP_INST_ACCESS_FAULT: + case RISCV_EXCP_LOAD_ADDR_MIS: + case RISCV_EXCP_STORE_AMO_ADDR_MIS: + case RISCV_EXCP_LOAD_ACCESS_FAULT: + case RISCV_EXCP_STORE_AMO_ACCESS_FAULT: + case RISCV_EXCP_INST_PAGE_FAULT: + case RISCV_EXCP_LOAD_PAGE_FAULT: + case RISCV_EXCP_STORE_PAGE_FAULT: + tval = env->badaddr; + break; + default: + break; } - } - - target_ulong fixed_cause = 0; - if (cs->exception_index & (RISCV_EXCP_INT_FLAG)) { - /* hacky for now. the MSB (bit 63) indicates interrupt but cs->exception - index is only 32 bits wide */ - fixed_cause = cs->exception_index & RISCV_EXCP_INT_MASK; - fixed_cause |= ((target_ulong)1) << (TARGET_LONG_BITS - 1); - } else { - /* fixup User ECALL -> correct priv ECALL */ - if (cs->exception_index == RISCV_EXCP_U_ECALL) { - switch (env->priv) { - case PRV_U: - fixed_cause = RISCV_EXCP_U_ECALL; - break; - case PRV_S: - fixed_cause = RISCV_EXCP_S_ECALL; - break; - case PRV_H: - fixed_cause = RISCV_EXCP_H_ECALL; - break; - case PRV_M: - fixed_cause = RISCV_EXCP_M_ECALL; - break; - } - } else { - fixed_cause = cs->exception_index; + /* ecall is dispatched as one cause so translate based on mode */ + if (cause == RISCV_EXCP_U_ECALL) { + assert(env->priv <= 3); + cause = ecall_cause_map[env->priv]; } } - target_ulong backup_epc = env->pc; - - target_ulong bit = fixed_cause; - target_ulong deleg = env->medeleg; - - int hasbadaddr = - (fixed_cause == RISCV_EXCP_INST_ADDR_MIS) || - (fixed_cause == RISCV_EXCP_INST_ACCESS_FAULT) || - (fixed_cause == RISCV_EXCP_LOAD_ADDR_MIS) || - (fixed_cause == RISCV_EXCP_STORE_AMO_ADDR_MIS) || - (fixed_cause == RISCV_EXCP_LOAD_ACCESS_FAULT) || - (fixed_cause == RISCV_EXCP_STORE_AMO_ACCESS_FAULT) || - (fixed_cause == RISCV_EXCP_INST_PAGE_FAULT) || - (fixed_cause == RISCV_EXCP_LOAD_PAGE_FAULT) || - (fixed_cause == RISCV_EXCP_STORE_PAGE_FAULT); - - if (bit & ((target_ulong)1 << (TARGET_LONG_BITS - 1))) { - deleg = env->mideleg; - bit &= ~((target_ulong)1 << (TARGET_LONG_BITS - 1)); + if (RISCV_DEBUG_INTERRUPT) { + qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld ": %s %s, " + "epc 0x" TARGET_FMT_lx ": tval 0x" TARGET_FMT_lx "\n", + env->mhartid, async ? "intr" : "trap", + (async ? riscv_intr_names : riscv_excp_names)[cause], + env->pc, tval); } - if (env->priv <= PRV_S && bit < 64 && ((deleg >> bit) & 1)) { + if (env->priv <= PRV_S && + cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) { /* handle the trap in S-mode */ - /* No need to check STVEC for misaligned - lower 2 bits cannot be set */ - env->pc = env->stvec; - env->scause = fixed_cause; - env->sepc = backup_epc; - - if (hasbadaddr) { - if (RISCV_DEBUG_INTERRUPT) { - qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld ": badaddr 0x" - TARGET_FMT_lx "\n", env->mhartid, env->badaddr); - } - env->sbadaddr = env->badaddr; - } else { - /* otherwise we must clear sbadaddr/stval - * todo: support populating stval on illegal instructions */ - env->sbadaddr = 0; - } - target_ulong s = env->mstatus; s = set_field(s, MSTATUS_SPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ? get_field(s, MSTATUS_SIE) : get_field(s, MSTATUS_UIE << env->priv)); s = set_field(s, MSTATUS_SPP, env->priv); s = set_field(s, MSTATUS_SIE, 0); env->mstatus = s; + env->scause = cause | ~(((target_ulong)-1) >> async); + env->sepc = env->pc; + env->sbadaddr = tval; + env->pc = (env->stvec >> 2 << 2) + + ((async && (env->stvec & 3) == 1) ? cause * 4 : 0); riscv_cpu_set_mode(env, PRV_S); } else { - /* No need to check MTVEC for misaligned - lower 2 bits cannot be set */ - env->pc = env->mtvec; - env->mepc = backup_epc; - env->mcause = fixed_cause; - - if (hasbadaddr) { - if (RISCV_DEBUG_INTERRUPT) { - qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld ": badaddr 0x" - TARGET_FMT_lx "\n", env->mhartid, env->badaddr); - } - env->mbadaddr = env->badaddr; - } else { - /* otherwise we must clear mbadaddr/mtval - * todo: support populating mtval on illegal instructions */ - env->mbadaddr = 0; - } - + /* handle the trap in M-mode */ target_ulong s = env->mstatus; s = set_field(s, MSTATUS_MPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ? get_field(s, MSTATUS_MIE) : get_field(s, MSTATUS_UIE << env->priv)); s = set_field(s, MSTATUS_MPP, env->priv); s = set_field(s, MSTATUS_MIE, 0); env->mstatus = s; + env->mcause = cause | ~(((target_ulong)-1) >> async); + env->mepc = env->pc; + env->mbadaddr = tval; + env->pc = (env->mtvec >> 2 << 2) + + ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); riscv_cpu_set_mode(env, PRV_M); } /* TODO yield load reservation */ diff --git a/target/riscv/csr.c b/target/riscv/csr.c index a798b82d8b0..b40d01f2090 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -436,10 +436,10 @@ static int read_mtvec(CPURISCVState *env, int csrno, target_ulong *val) static int write_mtvec(CPURISCVState *env, int csrno, target_ulong val) { /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ - if ((val & 3) == 0) { - env->mtvec = val >> 2 << 2; + if ((val & 3) < 2) { + env->mtvec = val; } else { - qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: vectored traps not supported"); + qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: reserved mode not supported\n"); } return 0; } @@ -612,10 +612,10 @@ static int read_stvec(CPURISCVState *env, int csrno, target_ulong *val) static int write_stvec(CPURISCVState *env, int csrno, target_ulong val) { /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ - if ((val & 3) == 0) { - env->stvec = val >> 2 << 2; + if ((val & 3) < 2) { + env->stvec = val; } else { - qemu_log_mask(LOG_UNIMP, "CSR_STVEC: vectored traps not supported"); + qemu_log_mask(LOG_UNIMP, "CSR_STVEC: reserved mode not supported\n"); } return 0; } From 7f3f89d11385ebf86c94567d8a6bd1e596b3cbe8 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Thu, 21 Jun 2018 13:40:46 -0700 Subject: [PATCH 63/76] RISC-V: Convert trap debugging to trace events Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- Makefile.objs | 1 + target/riscv/cpu_helper.c | 12 +++--------- target/riscv/trace-events | 2 ++ 3 files changed, 6 insertions(+), 9 deletions(-) create mode 100644 target/riscv/trace-events diff --git a/Makefile.objs b/Makefile.objs index 7a9828da282..adbba37d65c 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -253,6 +253,7 @@ trace-events-subdirs += target/arm trace-events-subdirs += target/i386 trace-events-subdirs += target/mips trace-events-subdirs += target/ppc +trace-events-subdirs += target/riscv trace-events-subdirs += target/s390x trace-events-subdirs += target/sparc trace-events-subdirs += ui diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index f78295390ed..bf21192f929 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -22,8 +22,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "tcg-op.h" - -#define RISCV_DEBUG_INTERRUPT 0 +#include "trace.h" int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) { @@ -492,13 +491,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) } } - if (RISCV_DEBUG_INTERRUPT) { - qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld ": %s %s, " - "epc 0x" TARGET_FMT_lx ": tval 0x" TARGET_FMT_lx "\n", - env->mhartid, async ? "intr" : "trap", - (async ? riscv_intr_names : riscv_excp_names)[cause], - env->pc, tval); - } + trace_riscv_trap(env->mhartid, async, cause, env->pc, tval, cause < 16 ? + (async ? riscv_intr_names : riscv_excp_names)[cause] : "(unknown)"); if (env->priv <= PRV_S && cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) { diff --git a/target/riscv/trace-events b/target/riscv/trace-events new file mode 100644 index 00000000000..48af0373df6 --- /dev/null +++ b/target/riscv/trace-events @@ -0,0 +1,2 @@ +# target/riscv/cpu_helper.c +riscv_trap(uint64_t hartid, bool async, uint64_t cause, uint64_t epc, uint64_t tval, const char *desc) "hart:%"PRId64", async:%d, cause:%"PRId64", epc:0x%"PRIx64", tval:0x%"PRIx64", desc=%s" From 51a00050ee798b41901d649744515b4a33ac1a8f Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Fri, 22 Jun 2018 12:35:09 -0700 Subject: [PATCH 64/76] RISC-V: Update load reservation comment in do_interrupt Cc: Palmer Dabbelt Cc: Alistair Francis Signed-off-by: Michael Clark --- target/riscv/cpu_helper.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index bf21192f929..c6f3d57b49f 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -524,7 +524,12 @@ void riscv_cpu_do_interrupt(CPUState *cs) ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); riscv_cpu_set_mode(env, PRV_M); } - /* TODO yield load reservation */ + + /* NOTE: it is not necessary to yield load reservations here. It is only + necessary for an SC from "another hart" to cause a load reservation + to be yielded. Refer to the memory consistency model section of the + RISC-V ISA Specification. */ + #endif cs->exception_index = EXCP_NONE; /* mark handled to qemu */ } From c2c2112be0c7be81633880c1ae8ecb71ec499f94 Mon Sep 17 00:00:00 2001 From: Nathaniel Graff Date: Tue, 24 Jul 2018 09:52:46 -0700 Subject: [PATCH 65/76] sifive_prci: Read and write PRCI registers Writes to the SiFive PRCI registers are preserved while leaving the ready bits set for the HFX/HFR oscillators and the lock bit set for the PLL. Signed-off-by: Nathaniel Graff Reviewed-by: Michael Clark --- hw/riscv/sifive_prci.c | 51 ++++++++++++++++++++++++++++------ include/hw/riscv/sifive_prci.h | 32 +++++++++++++++++++++ 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/hw/riscv/sifive_prci.c b/hw/riscv/sifive_prci.c index 0910ea32c1a..1435423a23e 100644 --- a/hw/riscv/sifive_prci.c +++ b/hw/riscv/sifive_prci.c @@ -23,15 +23,19 @@ #include "target/riscv/cpu.h" #include "hw/riscv/sifive_prci.h" -/* currently implements enough to mock freedom-e-sdk BSP clock programming */ - static uint64_t sifive_prci_read(void *opaque, hwaddr addr, unsigned int size) { - if (addr == 0 /* PRCI_HFROSCCFG */) { - return 1 << 31; /* ROSC_RDY */ - } - if (addr == 8 /* PRCI_PLLCFG */) { - return 1 << 31; /* PLL_LOCK */ + SiFivePRCIState *s = opaque; + switch(addr) + { + case SIFIVE_PRCI_HFROSCCFG: + return s->hfrosccfg; + case SIFIVE_PRCI_HFXOSCCFG: + return s->hfxosccfg; + case SIFIVE_PRCI_PLLCFG: + return s->pllcfg; + case SIFIVE_PRCI_PLLOUTDIV: + return s->plloutdiv; } hw_error("%s: read: addr=0x%x\n", __func__, (int)addr); return 0; @@ -40,7 +44,31 @@ static uint64_t sifive_prci_read(void *opaque, hwaddr addr, unsigned int size) static void sifive_prci_write(void *opaque, hwaddr addr, uint64_t val64, unsigned int size) { - /* discard writes */ + SiFivePRCIState *s = opaque; + switch(addr) + { + case SIFIVE_PRCI_HFROSCCFG: + s->hfrosccfg = (uint32_t) val64; + /* OSC stays ready */ + s->hfrosccfg |= SIFIVE_PRCI_HFROSCCFG_RDY; + break; + case SIFIVE_PRCI_HFXOSCCFG: + s->hfxosccfg = (uint32_t) val64; + /* OSC stays ready */ + s->hfxosccfg |= SIFIVE_PRCI_HFXOSCCFG_RDY; + break; + case SIFIVE_PRCI_PLLCFG: + s->pllcfg = (uint32_t) val64; + /* PLL stays locked */ + s->pllcfg |= SIFIVE_PRCI_PLLCFG_LOCK; + break; + case SIFIVE_PRCI_PLLOUTDIV: + s->plloutdiv = (uint32_t) val64; + break; + default: + hw_error("%s: bad write: addr=0x%x v=0x%x\n", + __func__, (int)addr, (int)val64); + } } static const MemoryRegionOps sifive_prci_ops = { @@ -60,6 +88,13 @@ static void sifive_prci_init(Object *obj) memory_region_init_io(&s->mmio, obj, &sifive_prci_ops, s, TYPE_SIFIVE_PRCI, 0x8000); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + s->hfrosccfg = (SIFIVE_PRCI_HFROSCCFG_RDY | SIFIVE_PRCI_HFROSCCFG_EN); + s->hfxosccfg = (SIFIVE_PRCI_HFROSCCFG_RDY | SIFIVE_PRCI_HFROSCCFG_EN); + s->pllcfg = (SIFIVE_PRCI_PLLCFG_REFSEL | SIFIVE_PRCI_PLLCFG_BYPASS | + SIFIVE_PRCI_PLLCFG_LOCK); + s->plloutdiv = SIFIVE_PRCI_PLLOUTDIV_DIV1; + } static const TypeInfo sifive_prci_info = { diff --git a/include/hw/riscv/sifive_prci.h b/include/hw/riscv/sifive_prci.h index b6f4c486cc1..bd51c4af3c1 100644 --- a/include/hw/riscv/sifive_prci.h +++ b/include/hw/riscv/sifive_prci.h @@ -19,6 +19,34 @@ #ifndef HW_SIFIVE_PRCI_H #define HW_SIFIVE_PRCI_H +enum { + SIFIVE_PRCI_HFROSCCFG = 0x0, + SIFIVE_PRCI_HFXOSCCFG = 0x4, + SIFIVE_PRCI_PLLCFG = 0x8, + SIFIVE_PRCI_PLLOUTDIV = 0xC +}; + +enum { + SIFIVE_PRCI_HFROSCCFG_RDY = (1 << 31), + SIFIVE_PRCI_HFROSCCFG_EN = (1 << 30) +}; + +enum { + SIFIVE_PRCI_HFXOSCCFG_RDY = (1 << 31), + SIFIVE_PRCI_HFXOSCCFG_EN = (1 << 30) +}; + +enum { + SIFIVE_PRCI_PLLCFG_PLLSEL = (1 << 16), + SIFIVE_PRCI_PLLCFG_REFSEL = (1 << 17), + SIFIVE_PRCI_PLLCFG_BYPASS = (1 << 18), + SIFIVE_PRCI_PLLCFG_LOCK = (1 << 31) +}; + +enum { + SIFIVE_PRCI_PLLOUTDIV_DIV1 = (1 << 8) +}; + #define TYPE_SIFIVE_PRCI "riscv.sifive.prci" #define SIFIVE_PRCI(obj) \ @@ -30,6 +58,10 @@ typedef struct SiFivePRCIState { /*< public >*/ MemoryRegion mmio; + uint32_t hfrosccfg; + uint32_t hfxosccfg; + uint32_t pllcfg; + uint32_t plloutdiv; } SiFivePRCIState; DeviceState *sifive_prci_create(hwaddr addr); From 72f4001617f59130c6a08c29648fde50bbbc2100 Mon Sep 17 00:00:00 2001 From: Nathaniel Graff Date: Tue, 24 Jul 2018 09:53:09 -0700 Subject: [PATCH 66/76] sifive_uart: Implement interrupt pending register The watermark bits are set in the interrupt pending register according to the configuration of txcnt and rxcnt in the txctrl and rxctrl registers. Since the UART TX does not implement a FIFO, the txwm bit is set as long as the TX watermark level is greater than zero. Signed-off-by: Nathaniel Graff Reviewed-by: Michael Clark --- hw/riscv/sifive_uart.c | 22 +++++++++++++++++----- include/hw/riscv/sifive_uart.h | 3 +++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c index b0c3798cf27..e87937bc6bf 100644 --- a/hw/riscv/sifive_uart.c +++ b/hw/riscv/sifive_uart.c @@ -28,12 +28,24 @@ * Not yet implemented: * * Transmit FIFO using "qemu/fifo8.h" - * SIFIVE_UART_IE_TXWM interrupts - * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark - * Rx FIFO watermark interrupt trigger threshold - * Tx FIFO watermark interrupt trigger threshold. */ +/* Returns the state of the IP (interrupt pending) register */ +static uint64_t uart_ip(SiFiveUARTState *s) +{ + uint64_t ret = 0; + + uint64_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl); + uint64_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl); + + if(txcnt != 0) + ret |= SIFIVE_UART_IP_TXWM; + if(s->rx_fifo_len > rxcnt) + ret |= SIFIVE_UART_IP_RXWM; + + return ret; +} + static void update_irq(SiFiveUARTState *s) { int cond = 0; @@ -69,7 +81,7 @@ uart_read(void *opaque, hwaddr addr, unsigned int size) case SIFIVE_UART_IE: return s->ie; case SIFIVE_UART_IP: - return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0; + return uart_ip(s); case SIFIVE_UART_TXCTRL: return s->txctrl; case SIFIVE_UART_RXCTRL: diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h index 504f18a60f1..c8dc1c57fd0 100644 --- a/include/hw/riscv/sifive_uart.h +++ b/include/hw/riscv/sifive_uart.h @@ -43,6 +43,9 @@ enum { SIFIVE_UART_IP_RXWM = 2 /* Receive watermark interrupt pending */ }; +#define SIFIVE_UART_GET_TXCNT(txctrl) ((txctrl >> 16) & 0x7) +#define SIFIVE_UART_GET_RXCNT(rxctrl) ((rxctrl >> 16) & 0x7) + #define TYPE_SIFIVE_UART "riscv.sifive.uart" #define SIFIVE_UART(obj) \ From 34298446855cb6dc1f6107fa859962de9929f79c Mon Sep 17 00:00:00 2001 From: Dayeol Lee Date: Tue, 17 Jul 2018 17:28:32 +0000 Subject: [PATCH 67/76] target/riscv/pmp.c: Fix PMP NAPOT decoding bug According to the RISC-V priv. v1.10 ISA document, pmpaddr register stores (base_addr | (size/2 - 1)) >> 2 for a NAPOT-encoded address. However, the current code decodes (base_addr | (size - 1)) >> 3 which leads to a wrong base address and size. Signed-off-by: Dayeol Lee Reviewed-by: Alistair Francis Reviewed-by: Michael Clark --- target/riscv/pmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index f432f3b7594..c4c6b09fd9a 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -138,7 +138,7 @@ static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea) return; } else { target_ulong t1 = ctz64(~a); - target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 3; + target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 2; target_ulong range = ((target_ulong)1 << (t1 + 3)) - 1; *sa = base; *ea = base + range; From 5e346b9047f7be71a3761ec4296c32eb658fd6e0 Mon Sep 17 00:00:00 2001 From: Dayeol Lee Date: Tue, 17 Jul 2018 21:10:05 +0000 Subject: [PATCH 68/76] target/riscv/pmp.c: Fix PMP range boundary address bug A wrong address is passed to `pmp_is_in_range` while checking if a memory access is within a PMP range. Since the ending address of the pmp range (i.e., pmp_state.addr[i].ea) is set to the last address in the range (i.e., pmp base + pmp size - 1), memory accesses containg the last address in the range will always fail. For example, assume that a PMP range is 4KB from 0x87654000 such that the last address within the range is 0x87654fff. 1-byte access to 0x87654fff should be considered to be fully inside the PMP range. However the access now fails and complains partial inclusion because pmp_is_in_range(env, i, addr + size) returns 0 whereas pmp_is_in_range(env, i, addr) returns 1. Signed-off-by: Dayeol Lee Reviewed-by: Alistair Francis Reviewed-by: Michael Clark --- target/riscv/pmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index c4c6b09fd9a..459e5564c10 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -245,7 +245,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, from low to high */ for (i = 0; i < MAX_RISCV_PMPS; i++) { s = pmp_is_in_range(env, i, addr); - e = pmp_is_in_range(env, i, addr + size); + e = pmp_is_in_range(env, i, addr + size - 1); /* partially inside */ if ((s + e) == 1) { From f816b312795f4918be095cfb8c74daf98501ef4f Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Tue, 15 May 2018 18:00:00 +0200 Subject: [PATCH 69/76] riscv: remove define cpu_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_init() was removed since 2.12, so drop the define that is now unused. Signed-off-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Clark --- target/riscv/cpu.h | 1 - 1 file changed, 1 deletion(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 0823461ae9f..c2db0b3d686 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -263,7 +263,6 @@ int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, char *riscv_isa_string(RISCVCPU *cpu); void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf); -#define cpu_init(cpu_model) cpu_generic_init(TYPE_RISCV_CPU, cpu_model) #define cpu_signal_handler riscv_cpu_signal_handler #define cpu_list riscv_cpu_list #define cpu_mmu_index riscv_cpu_mmu_index From 32ad714e0e25ba025fec4de73af756a090c43d42 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 6 Aug 2018 17:11:50 +1200 Subject: [PATCH 70/76] hw/riscv: flatten SiFive machine bus topology An SOC child object was added in a previous commit which created a static toplogy of the SOC and apparently external periphery, however, the intention is to make the peripheral configuration of the 'sifive_e' and 'sifive_u' machines dynamic so any static structs need to be removed. The first step towards creating dynamic topology is to remove the static topology, which in effect, created a wrapper around riscv_hart_array. Signed-off-by: Michael Clark --- hw/riscv/sifive_e.c | 77 ++++++------------------ hw/riscv/sifive_u.c | 115 +++++++++++------------------------- include/hw/riscv/sifive_e.h | 14 +---- include/hw/riscv/sifive_u.h | 16 +---- 4 files changed, 53 insertions(+), 169 deletions(-) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index e7413f5f750..16b547eec81 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -100,21 +100,27 @@ static void sifive_mmio_emulate(MemoryRegion *parent, const char *name, static void riscv_sifive_e_init(MachineState *machine) { const struct MemmapEntry *memmap = sifive_e_memmap; - SiFiveEState *s = g_new0(SiFiveEState, 1); MemoryRegion *sys_mem = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); + MemoryRegion *xip_mem = g_new(MemoryRegion, 1); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + int i; - /* Initialize SoC */ - object_initialize_child(OBJECT(machine), "soc", &s->soc, - sizeof(s->soc), TYPE_RISCV_E_SOC, - &error_abort, NULL); - object_property_set_bool(OBJECT(&s->soc), true, "realized", + /* Initialize harts */ + object_initialize(&s->cpus, sizeof(s->cpus), TYPE_RISCV_HART_ARRAY); + object_property_add_child(OBJECT(machine), "cpus", OBJECT(&s->cpus), + &error_abort); + object_property_set_str(OBJECT(&s->cpus), SIFIVE_E_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts", + &error_abort); + object_property_set_bool(OBJECT(&s->cpus), true, "realized", &error_abort); /* Data Tightly Integrated Memory */ - memory_region_init_ram(main_mem, NULL, "riscv.sifive.e.ram", + memory_region_init_ram(main_mem, NULL, "riscv.sifive.e.dtim", memmap[SIFIVE_E_DTIM].size, &error_fatal); memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_DTIM].base, main_mem); @@ -135,32 +141,6 @@ static void riscv_sifive_e_init(MachineState *machine) if (machine->kernel_filename) { load_kernel(machine->kernel_filename); } -} - -static void riscv_sifive_e_soc_init(Object *obj) -{ - SiFiveESoCState *s = RISCV_E_SOC(obj); - - object_initialize_child(obj, "cpus", &s->cpus, - sizeof(s->cpus), TYPE_RISCV_HART_ARRAY, - &error_abort, NULL); - object_property_set_str(OBJECT(&s->cpus), SIFIVE_E_CPU, "cpu-type", - &error_abort); - object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts", - &error_abort); -} - -static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) -{ - const struct MemmapEntry *memmap = sifive_e_memmap; - - SiFiveESoCState *s = RISCV_E_SOC(dev); - MemoryRegion *sys_mem = get_system_memory(); - MemoryRegion *xip_mem = g_new(MemoryRegion, 1); - MemoryRegion *mask_rom = g_new(MemoryRegion, 1); - - object_property_set_bool(OBJECT(&s->cpus), true, "realized", - &error_abort); /* Mask ROM */ memory_region_init_rom(mask_rom, NULL, "riscv.sifive.e.mrom", @@ -181,8 +161,11 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) SIFIVE_E_PLIC_CONTEXT_STRIDE, memmap[SIFIVE_E_PLIC].size); sifive_clint_create(memmap[SIFIVE_E_CLINT].base, - memmap[SIFIVE_E_CLINT].size, smp_cpus, - SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + memmap[SIFIVE_E_CLINT].size, + smp_cpus, + SIFIVE_SIP_BASE, + SIFIVE_TIMECMP_BASE, + SIFIVE_TIME_BASE); sifive_test_create(memmap[SIFIVE_E_TEST].base); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.aon", memmap[SIFIVE_E_AON].base, memmap[SIFIVE_E_AON].size); @@ -221,27 +204,3 @@ static void riscv_sifive_e_machine_init(MachineClass *mc) } DEFINE_MACHINE("sifive_e", riscv_sifive_e_machine_init) - -static void riscv_sifive_e_soc_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = riscv_sifive_e_soc_realize; - /* Reason: Uses serial_hds in realize function, thus can't be used twice */ - dc->user_creatable = false; -} - -static const TypeInfo riscv_sifive_e_soc_type_info = { - .name = TYPE_RISCV_E_SOC, - .parent = TYPE_DEVICE, - .instance_size = sizeof(SiFiveESoCState), - .instance_init = riscv_sifive_e_soc_init, - .class_init = riscv_sifive_e_soc_class_init, -}; - -static void riscv_sifive_e_soc_register_types(void) -{ - type_register_static(&riscv_sifive_e_soc_type_info); -} - -type_init(riscv_sifive_e_soc_register_types) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 69ba408a333..fc84ab42e91 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -34,6 +34,7 @@ #include "hw/loader.h" #include "hw/sysbus.h" #include "hw/char/serial.h" +#include "hw/net/cadence_gem.h" #include "target/riscv/cpu.h" #include "hw/riscv/riscv_hart.h" #include "hw/riscv/sifive_plic.h" @@ -121,10 +122,10 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); - for (cpu = s->soc.cpus.num_harts - 1; cpu >= 0; cpu--) { + for (cpu = s->cpus.num_harts - 1; cpu >= 0; cpu--) { nodename = g_strdup_printf("/cpus/cpu@%d", cpu); char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); - char *isa = riscv_isa_string(&s->soc.cpus.harts[cpu]); + char *isa = riscv_isa_string(&s->cpus.harts[cpu]); qemu_fdt_add_subnode(fdt, nodename); qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", SIFIVE_U_CLOCK_FREQ); @@ -145,8 +146,8 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, g_free(nodename); } - cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4); - for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) { + cells = g_new0(uint32_t, s->cpus.num_harts * 4); + for (cpu = 0; cpu < s->cpus.num_harts; cpu++) { nodename = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); @@ -164,12 +165,12 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, 0x0, memmap[SIFIVE_U_CLINT].base, 0x0, memmap[SIFIVE_U_CLINT].size); qemu_fdt_setprop(fdt, nodename, "interrupts-extended", - cells, s->soc.cpus.num_harts * sizeof(uint32_t) * 4); + cells, s->cpus.num_harts * sizeof(uint32_t) * 4); g_free(cells); g_free(nodename); - cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4); - for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) { + cells = g_new0(uint32_t, s->cpus.num_harts * 4); + for (cpu = 0; cpu < s->cpus.num_harts; cpu++) { nodename = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); @@ -186,7 +187,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0"); qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); qemu_fdt_setprop(fdt, nodename, "interrupts-extended", - cells, s->soc.cpus.num_harts * sizeof(uint32_t) * 4); + cells, s->cpus.num_harts * sizeof(uint32_t) * 4); qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0, memmap[SIFIVE_U_PLIC].base, 0x0, memmap[SIFIVE_U_PLIC].size); @@ -249,17 +250,23 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, static void riscv_sifive_u_init(MachineState *machine) { const struct MemmapEntry *memmap = sifive_u_memmap; - SiFiveUState *s = g_new0(SiFiveUState, 1); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + Error *err = NULL; + int i; - /* Initialize SoC */ - object_initialize_child(OBJECT(machine), "soc", &s->soc, - sizeof(s->soc), TYPE_RISCV_U_SOC, - &error_abort, NULL); - object_property_set_bool(OBJECT(&s->soc), true, "realized", + /* Initialize harts */ + object_initialize(&s->cpus, sizeof(s->cpus), TYPE_RISCV_HART_ARRAY); + object_property_add_child(OBJECT(machine), "cpus", OBJECT(&s->cpus), + &error_abort); + object_property_set_str(OBJECT(&s->cpus), SIFIVE_U_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts", + &error_abort); + object_property_set_bool(OBJECT(&s->cpus), true, "realized", &error_abort); /* register RAM */ @@ -309,36 +316,6 @@ static void riscv_sifive_u_init(MachineState *machine) rom_add_blob_fixed_as("mrom.fdt", s->fdt, fdt_totalsize(s->fdt), memmap[SIFIVE_U_MROM].base + sizeof(reset_vec), &address_space_memory); -} - -static void riscv_sifive_u_soc_init(Object *obj) -{ - SiFiveUSoCState *s = RISCV_U_SOC(obj); - - object_initialize_child(obj, "cpus", &s->cpus, sizeof(s->cpus), - TYPE_RISCV_HART_ARRAY, &error_abort, NULL); - object_property_set_str(OBJECT(&s->cpus), SIFIVE_U_CPU, "cpu-type", - &error_abort); - object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts", - &error_abort); - - sysbus_init_child_obj(obj, "gem", &s->gem, sizeof(s->gem), - TYPE_CADENCE_GEM); -} - -static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) -{ - SiFiveUSoCState *s = RISCV_U_SOC(dev); - const struct MemmapEntry *memmap = sifive_u_memmap; - MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *mask_rom = g_new(MemoryRegion, 1); - qemu_irq plic_gpios[SIFIVE_U_PLIC_NUM_SOURCES]; - int i; - Error *err = NULL; - NICInfo *nd = &nd_table[0]; - - object_property_set_bool(OBJECT(&s->cpus), true, "realized", - &error_abort); /* boot rom */ memory_region_init_rom(mask_rom, NULL, "riscv.sifive.u.mrom", @@ -364,27 +341,25 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART1_IRQ)); sifive_clint_create(memmap[SIFIVE_U_CLINT].base, memmap[SIFIVE_U_CLINT].size, smp_cpus, - SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); - - for (i = 0; i < SIFIVE_U_PLIC_NUM_SOURCES; i++) { - plic_gpios[i] = qdev_get_gpio_in(DEVICE(s->plic), i); - } + SIFIVE_SIP_BASE, + SIFIVE_TIMECMP_BASE, + SIFIVE_TIME_BASE); + sifive_test_create(memmap[SIFIVE_U_TEST].base); - if (nd->used) { - qemu_check_nic_model(nd, TYPE_CADENCE_GEM); - qdev_set_nic_properties(DEVICE(&s->gem), nd); + /* Initialize gem ethernet */ + object_initialize(&s->gem, sizeof(s->gem), TYPE_CADENCE_GEM); + object_property_add_child(OBJECT(machine), "gem", OBJECT(&s->gem), + &error_abort); + if (nd_table[0].used) { + qemu_check_nic_model(nd_table, TYPE_CADENCE_GEM); + qdev_set_nic_properties(DEVICE(&s->gem), nd_table); } object_property_set_int(OBJECT(&s->gem), GEM_REVISION, "revision", &error_abort); object_property_set_bool(OBJECT(&s->gem), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem), 0, memmap[SIFIVE_U_GEM].base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem), 0, - plic_gpios[SIFIVE_U_GEM_IRQ]); - sifive_test_create(memmap[SIFIVE_U_TEST].base); + qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_GEM_IRQ)); } static void riscv_sifive_u_machine_init(MachineClass *mc) @@ -395,27 +370,3 @@ static void riscv_sifive_u_machine_init(MachineClass *mc) } DEFINE_MACHINE("sifive_u", riscv_sifive_u_machine_init) - -static void riscv_sifive_u_soc_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = riscv_sifive_u_soc_realize; - /* Reason: Uses serial_hds in realize function, thus can't be used twice */ - dc->user_creatable = false; -} - -static const TypeInfo riscv_sifive_u_soc_type_info = { - .name = TYPE_RISCV_U_SOC, - .parent = TYPE_DEVICE, - .instance_size = sizeof(SiFiveUSoCState), - .instance_init = riscv_sifive_u_soc_init, - .class_init = riscv_sifive_u_soc_class_init, -}; - -static void riscv_sifive_u_soc_register_types(void) -{ - type_register_static(&riscv_sifive_u_soc_type_info); -} - -type_init(riscv_sifive_u_soc_register_types) diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h index c856b99d14e..0d3666bb124 100644 --- a/include/hw/riscv/sifive_e.h +++ b/include/hw/riscv/sifive_e.h @@ -19,25 +19,13 @@ #ifndef HW_SIFIVE_E_H #define HW_SIFIVE_E_H -#define TYPE_RISCV_E_SOC "riscv.sifive.e.soc" -#define RISCV_E_SOC(obj) \ - OBJECT_CHECK(SiFiveESoCState, (obj), TYPE_RISCV_E_SOC) - -typedef struct SiFiveESoCState { +typedef struct SiFiveEState { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ RISCVHartArrayState cpus; DeviceState *plic; -} SiFiveESoCState; - -typedef struct SiFiveEState { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - SiFiveESoCState soc; } SiFiveEState; enum { diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index d2ac08269c6..5aab5351c60 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -19,13 +19,7 @@ #ifndef HW_SIFIVE_U_H #define HW_SIFIVE_U_H -#include "hw/net/cadence_gem.h" - -#define TYPE_RISCV_U_SOC "riscv.sifive.u.soc" -#define RISCV_U_SOC(obj) \ - OBJECT_CHECK(SiFiveUSoCState, (obj), TYPE_RISCV_U_SOC) - -typedef struct SiFiveUSoCState { +typedef struct SiFiveUState { /*< private >*/ SysBusDevice parent_obj; @@ -33,14 +27,6 @@ typedef struct SiFiveUSoCState { RISCVHartArrayState cpus; DeviceState *plic; CadenceGEMState gem; -} SiFiveUSoCState; - -typedef struct SiFiveUState { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - SiFiveUSoCState soc; void *fdt; int fdt_size; } SiFiveUState; From 2ddf36fcf8e3caed10e08a60d86fafd50996b09f Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 6 Aug 2018 17:21:00 +1200 Subject: [PATCH 71/76] target/riscv: rename RISCV_FEATURE_MISA Renamed RISCV_FEATURE_MISA_RW to RISCV_FEATURE_MISA Signed-off-by: Michael Clark --- target/riscv/cpu.h | 2 +- target/riscv/csr.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index c2db0b3d686..ee678900f19 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -87,7 +87,7 @@ enum { RISCV_FEATURE_MMU, RISCV_FEATURE_PMP, - RISCV_FEATURE_MISA_RW + RISCV_FEATURE_MISA }; #define USER_VERSION_2_02_0 0x00020200 diff --git a/target/riscv/csr.c b/target/riscv/csr.c index b40d01f2090..90f68663f3c 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -343,7 +343,7 @@ static int read_misa(CPURISCVState *env, int csrno, target_ulong *val) static int write_misa(CPURISCVState *env, int csrno, target_ulong val) { - if (!riscv_feature(env, RISCV_FEATURE_MISA_RW)) { + if (!riscv_feature(env, RISCV_FEATURE_MISA)) { /* drop write to misa */ return 0; } From f37056ebd04899256112aa39478c8ff65f50778f Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Tue, 11 Sep 2018 13:50:03 +1200 Subject: [PATCH 72/76] RISC-V - Disassemble reserved compressed encodings as illegal - Due to the design of the disassembler, the immediate is not known during decoding of the opcode; so to handle compressed encodings with reserved immediate values (non-zero), we need to add an additional check during decompression to match reserved encodings with zero immediates and translate them into the illegal instruction. The following compressed opcodes have reserved encodings with zero immediates: c.addi4spn, c.addi, c.lui, c.addi16sp, c.srli, c.srai, c.andi and c.slli Signed-off-by: Michael Clark --- disas/riscv.c | 51 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 27546dd7902..1fe4eff82a9 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -504,14 +504,19 @@ typedef struct { const rvc_constraint *constraints; } rv_comp_data; +enum { + rvcd_imm_nz = 0x1 +}; + typedef struct { const char * const name; const rv_codec codec; const char * const format; const rv_comp_data *pseudo; - const int decomp_rv32; - const int decomp_rv64; - const int decomp_rv128; + const short decomp_rv32; + const short decomp_rv64; + const short decomp_rv128; + const short decomp_data; } rv_opcode_data; /* register names */ @@ -1011,7 +1016,7 @@ const rv_opcode_data opcode_data[] = { { "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, { "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, - { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi, rvcd_imm_nz }, { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, 0 }, { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw }, { "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 }, @@ -1019,14 +1024,14 @@ const rv_opcode_data opcode_data[] = { { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw }, { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 }, { "c.nop", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, - { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi, rvcd_imm_nz }, { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, NULL, rv_op_jal, 0, 0 }, { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, - { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, - { "c.lui", rv_codec_ci_lui, rv_fmt_rd_imm, NULL, rv_op_lui, rv_op_lui, rv_op_lui }, - { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, rv_op_srli, rv_op_srli }, - { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, rv_op_srai, rv_op_srai }, - { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, rv_op_andi, rv_op_andi }, + { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi, rvcd_imm_nz }, + { "c.lui", rv_codec_ci_lui, rv_fmt_rd_imm, NULL, rv_op_lui, rv_op_lui, rv_op_lui, rvcd_imm_nz }, + { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, rv_op_srli, rv_op_srli, rvcd_imm_nz }, + { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, rv_op_srai, rv_op_srai, rvcd_imm_nz }, + { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, rv_op_andi, rv_op_andi, rvcd_imm_nz }, { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, rv_op_sub }, { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, rv_op_xor }, { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, rv_op_or }, @@ -1036,7 +1041,7 @@ const rv_opcode_data opcode_data[] = { { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal, rv_op_jal, rv_op_jal }, { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq, rv_op_beq, rv_op_beq }, { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, rv_op_bne }, - { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, rv_op_slli, rv_op_slli }, + { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, rv_op_slli, rv_op_slli, rvcd_imm_nz }, { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, rv_op_fld }, { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw }, { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 }, @@ -2795,8 +2800,12 @@ static void decode_inst_decompress_rv32(rv_decode *dec) { int decomp_op = opcode_data[dec->op].decomp_rv32; if (decomp_op != rv_op_illegal) { - dec->op = decomp_op; - dec->codec = opcode_data[decomp_op].codec; + if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) && dec->imm == 0) { + dec->op = rv_op_illegal; + } else { + dec->op = decomp_op; + dec->codec = opcode_data[decomp_op].codec; + } } } @@ -2804,8 +2813,12 @@ static void decode_inst_decompress_rv64(rv_decode *dec) { int decomp_op = opcode_data[dec->op].decomp_rv64; if (decomp_op != rv_op_illegal) { - dec->op = decomp_op; - dec->codec = opcode_data[decomp_op].codec; + if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) && dec->imm == 0) { + dec->op = rv_op_illegal; + } else { + dec->op = decomp_op; + dec->codec = opcode_data[decomp_op].codec; + } } } @@ -2813,8 +2826,12 @@ static void decode_inst_decompress_rv128(rv_decode *dec) { int decomp_op = opcode_data[dec->op].decomp_rv128; if (decomp_op != rv_op_illegal) { - dec->op = decomp_op; - dec->codec = opcode_data[decomp_op].codec; + if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) && dec->imm == 0) { + dec->op = rv_op_illegal; + } else { + dec->op = decomp_op; + dec->codec = opcode_data[decomp_op].codec; + } } } From 9358b911255f3fa9bcec07fb4431858eb45aa7f7 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 17 Sep 2018 10:53:17 +1200 Subject: [PATCH 73/76] disas/riscv: Use constant strings instead of #define for formats Signed-off-by: Michael Clark --- disas/riscv.c | 70 +++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 1fe4eff82a9..30df6b9f8eb 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -537,41 +537,41 @@ static const char rv_freg_name_sym[32][5] = { /* instruction formats */ -#define rv_fmt_none "O\t" -#define rv_fmt_rs1 "O\t1" -#define rv_fmt_offset "O\to" -#define rv_fmt_pred_succ "O\tp,s" -#define rv_fmt_rs1_rs2 "O\t1,2" -#define rv_fmt_rd_imm "O\t0,i" -#define rv_fmt_rd_offset "O\t0,o" -#define rv_fmt_rd_rs1_rs2 "O\t0,1,2" -#define rv_fmt_frd_rs1 "O\t3,1" -#define rv_fmt_rd_frs1 "O\t0,4" -#define rv_fmt_rd_frs1_frs2 "O\t0,4,5" -#define rv_fmt_frd_frs1_frs2 "O\t3,4,5" -#define rv_fmt_rm_frd_frs1 "O\tr,3,4" -#define rv_fmt_rm_frd_rs1 "O\tr,3,1" -#define rv_fmt_rm_rd_frs1 "O\tr,0,4" -#define rv_fmt_rm_frd_frs1_frs2 "O\tr,3,4,5" -#define rv_fmt_rm_frd_frs1_frs2_frs3 "O\tr,3,4,5,6" -#define rv_fmt_rd_rs1_imm "O\t0,1,i" -#define rv_fmt_rd_rs1_offset "O\t0,1,i" -#define rv_fmt_rd_offset_rs1 "O\t0,i(1)" -#define rv_fmt_frd_offset_rs1 "O\t3,i(1)" -#define rv_fmt_rd_csr_rs1 "O\t0,c,1" -#define rv_fmt_rd_csr_zimm "O\t0,c,7" -#define rv_fmt_rs2_offset_rs1 "O\t2,i(1)" -#define rv_fmt_frs2_offset_rs1 "O\t5,i(1)" -#define rv_fmt_rs1_rs2_offset "O\t1,2,o" -#define rv_fmt_rs2_rs1_offset "O\t2,1,o" -#define rv_fmt_aqrl_rd_rs2_rs1 "OAR\t0,2,(1)" -#define rv_fmt_aqrl_rd_rs1 "OAR\t0,(1)" -#define rv_fmt_rd "O\t0" -#define rv_fmt_rd_zimm "O\t0,7" -#define rv_fmt_rd_rs1 "O\t0,1" -#define rv_fmt_rd_rs2 "O\t0,2" -#define rv_fmt_rs1_offset "O\t1,o" -#define rv_fmt_rs2_offset "O\t2,o" +static const char rv_fmt_none[] = "O\t"; +static const char rv_fmt_rs1[] = "O\t1"; +static const char rv_fmt_offset[] = "O\to"; +static const char rv_fmt_pred_succ[] = "O\tp,s"; +static const char rv_fmt_rs1_rs2[] = "O\t1,2"; +static const char rv_fmt_rd_imm[] = "O\t0,i"; +static const char rv_fmt_rd_offset[] = "O\t0,o"; +static const char rv_fmt_rd_rs1_rs2[] = "O\t0,1,2"; +static const char rv_fmt_frd_rs1[] = "O\t3,1"; +static const char rv_fmt_rd_frs1[] = "O\t0,4"; +static const char rv_fmt_rd_frs1_frs2[] = "O\t0,4,5"; +static const char rv_fmt_frd_frs1_frs2[] = "O\t3,4,5"; +static const char rv_fmt_rm_frd_frs1[] = "O\tr,3,4"; +static const char rv_fmt_rm_frd_rs1[] = "O\tr,3,1"; +static const char rv_fmt_rm_rd_frs1[] = "O\tr,0,4"; +static const char rv_fmt_rm_frd_frs1_frs2[] = "O\tr,3,4,5"; +static const char rv_fmt_rm_frd_frs1_frs2_frs3[] = "O\tr,3,4,5,6"; +static const char rv_fmt_rd_rs1_imm[] = "O\t0,1,i"; +static const char rv_fmt_rd_rs1_offset[] = "O\t0,1,i"; +static const char rv_fmt_rd_offset_rs1[] = "O\t0,i(1)"; +static const char rv_fmt_frd_offset_rs1[] = "O\t3,i(1)"; +static const char rv_fmt_rd_csr_rs1[] = "O\t0,c,1"; +static const char rv_fmt_rd_csr_zimm[] = "O\t0,c,7"; +static const char rv_fmt_rs2_offset_rs1[] = "O\t2,i(1)"; +static const char rv_fmt_frs2_offset_rs1[] = "O\t5,i(1)"; +static const char rv_fmt_rs1_rs2_offset[] = "O\t1,2,o"; +static const char rv_fmt_rs2_rs1_offset[] = "O\t2,1,o"; +static const char rv_fmt_aqrl_rd_rs2_rs1[] = "OAR\t0,2,(1)"; +static const char rv_fmt_aqrl_rd_rs1[] = "OAR\t0,(1)"; +static const char rv_fmt_rd[] = "O\t0"; +static const char rv_fmt_rd_zimm[] = "O\t0,7"; +static const char rv_fmt_rd_rs1[] = "O\t0,1"; +static const char rv_fmt_rd_rs2[] = "O\t0,2"; +static const char rv_fmt_rs1_offset[] = "O\t1,o"; +static const char rv_fmt_rs2_offset[] = "O\t2,o"; /* pseudo-instruction constraints */ From 5565d26fdd08b0482be8717a19c7248d53eec07e Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 17 Sep 2018 10:54:27 +1200 Subject: [PATCH 74/76] disas/riscv: Fix `rdinstreth` constraint The constraint for `rdinstreth` was comparing the csr number to 0xc80, which is `cycleh` instead. Fix this. Author: Wladimir J. van der Laan Signed-off-by: Michael Clark --- disas/riscv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disas/riscv.c b/disas/riscv.c index 30df6b9f8eb..cd4f377ed9d 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -614,7 +614,7 @@ static const rvc_constraint rvcc_rdtime[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc01, r static const rvc_constraint rvcc_rdinstret[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc02, rvc_end }; static const rvc_constraint rvcc_rdcycleh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end }; static const rvc_constraint rvcc_rdtimeh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc81, rvc_end }; -static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end }; +static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc82, rvc_end }; static const rvc_constraint rvcc_frcsr[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x003, rvc_end }; static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, rvc_end }; static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, rvc_end }; From 741cee4b350568adfe382b7397be7c063430a6f9 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Wed, 19 Sep 2018 15:06:30 +1200 Subject: [PATCH 75/76] RISC-V: Add cpu_abort unimplemented for unassigned access During submission of the RISC-V port, cpu_unassigned_access was removed because the port was not allowed to contain printf and exit and should not abort during normal operation. Interestingly the semantics of an unimplemented cpu_unassigned_access were unknown, and suprisingly when the method is not implemented then behaviour is undefined and memory accesses succeed. This is potentially a core QEMU bug, or something that should be documented for port maintainers. This patch adds back a cpu_abort, as a short term workaround, similar to the pre-submission behaviour except using the preferred cpu_abort API vs printf and exit. Signed-off-by: Michael Clark --- target/riscv/cpu.c | 1 + target/riscv/cpu.h | 2 ++ target/riscv/cpu_helper.c | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 1ecd6d47e36..832fb3cd567 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -357,6 +357,7 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) #ifdef CONFIG_USER_ONLY cc->handle_mmu_fault = riscv_cpu_handle_mmu_fault; #else + cc->do_unassigned_access = riscv_cpu_unassigned_access; cc->do_unaligned_access = riscv_cpu_do_unaligned_access; cc->get_phys_page_debug = riscv_cpu_get_phys_page_debug; #endif diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index ee678900f19..a269c07e3b7 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -260,6 +260,8 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, uintptr_t retaddr); int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw, int mmu_idx); +void riscv_cpu_unassigned_access(CPUState *cpu, hwaddr addr, bool is_write, + bool is_exec, int unused, unsigned size); char *riscv_isa_string(RISCVCPU *cpu); void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index c6f3d57b49f..4b3d75cc9ce 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -356,6 +356,12 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) return phys_addr; } +void riscv_cpu_unassigned_access(CPUState *cpu, hwaddr addr, bool is_write, + bool is_exec, int unused, unsigned size) +{ + cpu_abort(cpu, "%s: unimplemented, addr=" TARGET_FMT_plx, __func__, addr); +} + void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) From 2b5e1a779c35350b2888511573ddc2caaa47eead Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Wed, 19 Sep 2018 20:39:35 +1200 Subject: [PATCH 76/76] RISC-V: Implement riscv_cpu_unassigned_access The previous patch in this series adds riscv_cpu_unassigned_access and restores the previous behavior of cpu exit only using the correct cpu_abort API. This patch removes the unimplemented code and replaces it with code to raise a load or store access fault. Signed-off-by: Michael Clark --- target/riscv/cpu_helper.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 4b3d75cc9ce..61b2a3e1ae1 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -356,10 +356,16 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) return phys_addr; } -void riscv_cpu_unassigned_access(CPUState *cpu, hwaddr addr, bool is_write, +void riscv_cpu_unassigned_access(CPUState *cs, hwaddr addr, bool is_write, bool is_exec, int unused, unsigned size) { - cpu_abort(cpu, "%s: unimplemented, addr=" TARGET_FMT_plx, __func__, addr); + RISCVCPU *cpu = RISCV_CPU(cs); + if (is_write) { + cs->exception_index = RISCV_EXCP_STORE_AMO_ACCESS_FAULT; + } else { + cs->exception_index = RISCV_EXCP_LOAD_ACCESS_FAULT; + } + riscv_raise_exception(&cpu->env, cs->exception_index, GETPC()); } void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,