aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--MAINTAINERS10
-rw-r--r--Makefile2
-rw-r--r--audio/Makefile.objs6
-rw-r--r--chardev/Makefile.objs1
-rwxr-xr-xconfigure38
-rw-r--r--default-configs/arm-softmmu.mak1
-rw-r--r--default-configs/sparc64-softmmu.mak1
-rw-r--r--hmp.c14
-rw-r--r--hw/arm/Makefile.objs1
-rw-r--r--hw/arm/msf2-soc.c238
-rw-r--r--hw/arm/msf2-som.c105
-rw-r--r--hw/arm/omap2.c49
-rw-r--r--hw/arm/palm.c30
-rw-r--r--hw/block/xen_disk.c2
-rw-r--r--hw/gpio/omap_gpio.c26
-rw-r--r--hw/i2c/omap_i2c.c44
-rw-r--r--hw/ide/ahci.c2
-rw-r--r--hw/ide/core.c2
-rw-r--r--hw/intc/armv7m_nvic.c913
-rw-r--r--hw/intc/trace-events13
-rw-r--r--hw/mips/Makefile.objs2
-rw-r--r--hw/mips/cps.c2
-rw-r--r--hw/mips/mips_fulong2e.c2
-rw-r--r--hw/mips/mips_jazz.c2
-rw-r--r--hw/mips/mips_malta.c2
-rw-r--r--hw/mips/mips_mipssim.c2
-rw-r--r--hw/mips/mips_r4k.c2
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/msf2-sysreg.c160
-rw-r--r--hw/misc/trace-events5
-rw-r--r--hw/net/Makefile.objs1
-rw-r--r--hw/net/sunhme.c978
-rw-r--r--hw/net/trace-events29
-rw-r--r--hw/sparc64/sun4u.c25
-rw-r--r--hw/ssi/Makefile.objs1
-rw-r--r--hw/ssi/mss-spi.c404
-rw-r--r--hw/timer/Makefile.objs1
-rw-r--r--hw/timer/mss-timer.c289
-rw-r--r--hw/timer/omap_gptimer.c49
-rw-r--r--hw/timer/omap_synctimer.c35
-rw-r--r--hw/usb/Makefile.objs13
-rw-r--r--hw/xen/xen_pt.h1
-rw-r--r--hw/xen/xen_pt_config_init.c20
-rw-r--r--hw/xen/xen_pt_msi.c13
-rw-r--r--include/glib-compat.h2
-rw-r--r--include/hw/arm/msf2-soc.h67
-rw-r--r--include/hw/ide/internal.h3
-rw-r--r--include/hw/intc/armv7m_nvic.h33
-rw-r--r--include/hw/misc/msf2-sysreg.h77
-rw-r--r--include/hw/net/mii.h4
-rw-r--r--include/hw/pci/pci_ids.h1
-rw-r--r--include/hw/ssi/mss-spi.h58
-rw-r--r--include/hw/timer/mss-timer.h64
-rw-r--r--include/migration/register.h1
-rw-r--r--include/qemu/bitmap.h17
-rw-r--r--include/sysemu/seccomp.h2
-rw-r--r--migration/Makefile.objs1
-rw-r--r--migration/channel.c18
-rw-r--r--migration/exec.c2
-rw-r--r--migration/fd.c2
-rw-r--r--migration/migration.c176
-rw-r--r--migration/migration.h9
-rw-r--r--migration/postcopy-ram.c100
-rw-r--r--migration/postcopy-ram.h2
-rw-r--r--migration/ram.c216
-rw-r--r--migration/ram.h5
-rw-r--r--migration/savevm.c56
-rw-r--r--migration/socket.c10
-rw-r--r--net/Makefile.objs2
-rw-r--r--qapi/migration.json39
-rwxr-xr-xscripts/archive-source.sh51
-rw-r--r--scripts/qemu.py10
-rw-r--r--target/arm/cpu.c7
-rw-r--r--target/arm/cpu.h62
-rw-r--r--target/arm/helper.c142
-rw-r--r--target/arm/translate-a64.c227
-rw-r--r--target/mips/Makefile.objs2
-rw-r--r--target/mips/cp0_timer.c (renamed from hw/mips/cputimer.c)2
-rw-r--r--target/mips/cpu-qom.h1
-rw-r--r--target/mips/cpu.c54
-rw-r--r--target/mips/cpu.h357
-rw-r--r--target/mips/dsp_helper.c56
-rw-r--r--target/mips/gdbstub.c1
-rw-r--r--target/mips/helper.c1
-rw-r--r--target/mips/internal.h422
-rw-r--r--target/mips/kvm.c1
-rw-r--r--target/mips/machine.c1
-rw-r--r--target/mips/msa_helper.c1
-rw-r--r--target/mips/op_helper.c1
-rw-r--r--target/mips/translate.c23
-rw-r--r--target/mips/translate_init.c58
-rw-r--r--target/s390x/kvm.c9
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/docker/Makefile.include16
-rwxr-xr-xtests/docker/common.rc20
-rwxr-xr-xtests/docker/docker.py3
-rw-r--r--tests/docker/dockerfiles/fedora.docker1
-rw-r--r--tests/docker/dockerfiles/travis.docker6
-rw-r--r--tests/docker/dockerfiles/ubuntu.docker11
-rwxr-xr-xtests/docker/run18
-rwxr-xr-xtests/docker/test-block21
-rwxr-xr-xtests/docker/test-full82
-rw-r--r--tests/keys/README6
-rw-r--r--tests/keys/id_rsa27
-rw-r--r--tests/keys/id_rsa.pub1
-rw-r--r--tests/vm/Makefile.include42
-rw-r--r--tests/vm/README89
-rwxr-xr-xtests/vm/basevm.py262
-rwxr-xr-xtests/vm/freebsd42
-rwxr-xr-xtests/vm/netbsd42
-rwxr-xr-xtests/vm/openbsd43
-rwxr-xr-xtests/vm/ubuntu.i38689
-rw-r--r--ui/Makefile.objs1
-rw-r--r--util/bitmap.c47
-rw-r--r--util/bitops.c6
-rw-r--r--vl.c4
117 files changed, 5899 insertions, 975 deletions
diff --git a/.gitignore b/.gitignore
index cf65316863..40acfcb9e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,7 @@
/vscclient
/vhost-user-scsi
/fsdev/virtfs-proxy-helper
+*.tmp
*.[1-9]
*.a
*.aux
diff --git a/MAINTAINERS b/MAINTAINERS
index e511ba780f..ffcd25bf1f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1403,6 +1403,14 @@ S: Maintained
F: include/sysemu/cryptodev*.h
F: backends/cryptodev*.c
+Python scripts
+M: Eduardo Habkost <ehabkost@redhat.com>
+M: Cleber Rosa <crosa@redhat.com>
+S: Odd fixes
+F: scripts/qmp/*
+F: scripts/*.py
+F: tests/*.py
+
QAPI
M: Markus Armbruster <armbru@redhat.com>
M: Michael Roth <mdroth@linux.vnet.ibm.com>
@@ -1896,6 +1904,7 @@ F: docs/block-replication.txt
Build and test automation
-------------------------
+Build and test automation
M: Alex Bennée <alex.bennee@linaro.org>
M: Fam Zheng <famz@redhat.com>
R: Philippe Mathieu-Daudé <f4bug@amsat.org>
@@ -1904,6 +1913,7 @@ S: Maintained
F: .travis.yml
F: .shippable.yml
F: tests/docker/
+F: tests/vm/
W: https://travis-ci.org/qemu/qemu
W: https://app.shippable.com/github/qemu/qemu
W: http://patchew.org/QEMU/
diff --git a/Makefile b/Makefile
index 4eb40376d2..2be61fcf1c 100644
--- a/Makefile
+++ b/Makefile
@@ -816,6 +816,7 @@ endif
-include $(wildcard *.d tests/*.d)
include $(SRC_PATH)/tests/docker/Makefile.include
+include $(SRC_PATH)/tests/vm/Makefile.include
.PHONY: help
help:
@@ -839,6 +840,7 @@ help:
@echo 'Test targets:'
@echo ' check - Run all tests (check-help for details)'
@echo ' docker - Help about targets running tests inside Docker containers'
+ @echo ' vm-test - Help about targets running tests inside VM'
@echo ''
@echo 'Documentation targets:'
@echo ' html info pdf txt'
diff --git a/audio/Makefile.objs b/audio/Makefile.objs
index 481d1aa30e..8a5ede6e2b 100644
--- a/audio/Makefile.objs
+++ b/audio/Makefile.objs
@@ -11,3 +11,9 @@ common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
common-obj-y += wavcapture.o
sdlaudio.o-cflags := $(SDL_CFLAGS)
+sdlaudio.o-libs := $(SDL_LIBS)
+alsaaudio.o-libs := $(ALSA_LIBS)
+paaudio.o-libs := $(PULSE_LIBS)
+coreaudio.o-libs := $(COREAUDIO_LIBS)
+dsoundaudio.o-libs := $(DSOUND_LIBS)
+ossaudio.o-libs := $(OSS_LIBS)
diff --git a/chardev/Makefile.objs b/chardev/Makefile.objs
index 52a8127606..d68e1347f9 100644
--- a/chardev/Makefile.objs
+++ b/chardev/Makefile.objs
@@ -20,5 +20,6 @@ chardev-obj-$(CONFIG_WIN32) += char-win-stdio.o
common-obj-y += msmouse.o wctablet.o testdev.o
common-obj-$(CONFIG_BRLAPI) += baum.o
baum.o-cflags := $(SDL_CFLAGS)
+baum.o-libs := $(BRLAPI_LIBS)
common-obj-$(CONFIG_SPICE) += spice.o
diff --git a/configure b/configure
index f6edc2a33f..133a5757ef 100755
--- a/configure
+++ b/configure
@@ -2794,7 +2794,6 @@ EOF
sdl_cflags="$sdl_cflags $x11_cflags"
sdl_libs="$sdl_libs $x11_libs"
fi
- libs_softmmu="$sdl_libs $libs_softmmu"
fi
##########################################
@@ -2807,7 +2806,6 @@ EOF
rdma_libs="-lrdmacm -libverbs"
if compile_prog "" "$rdma_libs" ; then
rdma="yes"
- libs_softmmu="$libs_softmmu $rdma_libs"
else
if test "$rdma" = "yes" ; then
error_exit \
@@ -2952,8 +2950,6 @@ int main(void)
EOF
if compile_prog "" "$vde_libs" ; then
vde=yes
- libs_softmmu="$vde_libs $libs_softmmu"
- libs_tools="$vde_libs $libs_tools"
else
if test "$vde" = "yes" ; then
feature_not_found "vde" "Install vde (Virtual Distributed Ethernet) devel"
@@ -3041,13 +3037,13 @@ for drv in $audio_drv_list; do
alsa)
audio_drv_probe $drv alsa/asoundlib.h -lasound \
"return snd_pcm_close((snd_pcm_t *)0);"
- libs_softmmu="-lasound $libs_softmmu"
+ alsa_libs="-lasound"
;;
pa)
audio_drv_probe $drv pulse/pulseaudio.h "-lpulse" \
"pa_context_set_source_output_volume(NULL, 0, NULL, NULL, NULL); return 0;"
- libs_softmmu="-lpulse $libs_softmmu"
+ pulse_libs="-lpulse"
audio_pt_int="yes"
;;
@@ -3058,16 +3054,16 @@ for drv in $audio_drv_list; do
;;
coreaudio)
- libs_softmmu="-framework CoreAudio $libs_softmmu"
+ coreaudio_libs="-framework CoreAudio"
;;
dsound)
- libs_softmmu="-lole32 -ldxguid $libs_softmmu"
+ dsound_libs="-lole32 -ldxguid"
audio_win_int="yes"
;;
oss)
- libs_softmmu="$oss_lib $libs_softmmu"
+ oss_libs="$oss_lib"
;;
wav)
@@ -3095,7 +3091,6 @@ int main( void ) { return brlapi__openConnection (NULL, NULL, NULL); }
EOF
if compile_prog "" "$brlapi_libs" ; then
brlapi=yes
- libs_softmmu="$brlapi_libs $libs_softmmu"
else
if test "$brlapi" = "yes" ; then
feature_not_found "brlapi" "Install brlapi devel"
@@ -4239,8 +4234,6 @@ if test "$smartcard" != "no"; then
if $pkg_config libcacard; then
libcacard_cflags=$($pkg_config --cflags libcacard)
libcacard_libs=$($pkg_config --libs libcacard)
- QEMU_CFLAGS="$QEMU_CFLAGS $libcacard_cflags"
- libs_softmmu="$libs_softmmu $libcacard_libs"
smartcard="yes"
else
if test "$smartcard" = "yes"; then
@@ -4256,8 +4249,6 @@ if test "$libusb" != "no" ; then
libusb="yes"
libusb_cflags=$($pkg_config --cflags libusb-1.0)
libusb_libs=$($pkg_config --libs libusb-1.0)
- QEMU_CFLAGS="$QEMU_CFLAGS $libusb_cflags"
- libs_softmmu="$libs_softmmu $libusb_libs"
else
if test "$libusb" = "yes"; then
feature_not_found "libusb" "Install libusb devel >= 1.0.13"
@@ -4272,8 +4263,6 @@ if test "$usb_redir" != "no" ; then
usb_redir="yes"
usb_redir_cflags=$($pkg_config --cflags libusbredirparser-0.5)
usb_redir_libs=$($pkg_config --libs libusbredirparser-0.5)
- QEMU_CFLAGS="$QEMU_CFLAGS $usb_redir_cflags"
- libs_softmmu="$libs_softmmu $usb_redir_libs"
else
if test "$usb_redir" = "yes"; then
feature_not_found "usb-redir" "Install usbredir devel"
@@ -5548,6 +5537,7 @@ if test "$slirp" = "yes" ; then
fi
if test "$vde" = "yes" ; then
echo "CONFIG_VDE=y" >> $config_host_mak
+ echo "VDE_LIBS=$vde_libs" >> $config_host_mak
fi
if test "$netmap" = "yes" ; then
echo "CONFIG_NETMAP=y" >> $config_host_mak
@@ -5563,6 +5553,11 @@ for drv in $audio_drv_list; do
def=CONFIG_$(echo $drv | LC_ALL=C tr '[a-z]' '[A-Z]')
echo "$def=y" >> $config_host_mak
done
+echo "ALSA_LIBS=$alsa_libs" >> $config_host_mak
+echo "PULSE_LIBS=$pulse_libs" >> $config_host_mak
+echo "COREAUDIO_LIBS=$coreaudio_libs" >> $config_host_mak
+echo "DSOUND_LIBS=$dsound_libs" >> $config_host_mak
+echo "OSS_LIBS=$oss_libs" >> $config_host_mak
if test "$audio_pt_int" = "yes" ; then
echo "CONFIG_AUDIO_PT_INT=y" >> $config_host_mak
fi
@@ -5607,6 +5602,7 @@ if test "$sdl" = "yes" ; then
echo "CONFIG_SDL=y" >> $config_host_mak
echo "CONFIG_SDLABI=$sdlabi" >> $config_host_mak
echo "SDL_CFLAGS=$sdl_cflags" >> $config_host_mak
+ echo "SDL_LIBS=$sdl_libs" >> $config_host_mak
fi
if test "$cocoa" = "yes" ; then
echo "CONFIG_COCOA=y" >> $config_host_mak
@@ -5696,6 +5692,7 @@ if test "$curl" = "yes" ; then
fi
if test "$brlapi" = "yes" ; then
echo "CONFIG_BRLAPI=y" >> $config_host_mak
+ echo "BRLAPI_LIBS=$brlapi_libs" >> $config_host_mak
fi
if test "$bluez" = "yes" ; then
echo "CONFIG_BLUEZ=y" >> $config_host_mak
@@ -5833,14 +5830,20 @@ fi
if test "$smartcard" = "yes" ; then
echo "CONFIG_SMARTCARD=y" >> $config_host_mak
+ echo "SMARTCARD_CFLAGS=$libcacard_cflags" >> $config_host_mak
+ echo "SMARTCARD_LIBS=$libcacard_libs" >> $config_host_mak
fi
if test "$libusb" = "yes" ; then
echo "CONFIG_USB_LIBUSB=y" >> $config_host_mak
+ echo "LIBUSB_CFLAGS=$libusb_cflags" >> $config_host_mak
+ echo "LIBUSB_LIBS=$libusb_libs" >> $config_host_mak
fi
if test "$usb_redir" = "yes" ; then
echo "CONFIG_USB_REDIR=y" >> $config_host_mak
+ echo "USB_REDIR_CFLAGS=$usb_redir_cflags" >> $config_host_mak
+ echo "USB_REDIR_LIBS=$usb_redir_libs" >> $config_host_mak
fi
if test "$opengl" = "yes" ; then
@@ -6036,6 +6039,7 @@ echo "CONFIG_TRACE_FILE=$trace_file" >> $config_host_mak
if test "$rdma" = "yes" ; then
echo "CONFIG_RDMA=y" >> $config_host_mak
+ echo "RDMA_LIBS=$rdma_libs" >> $config_host_mak
fi
if test "$have_rtnetlink" = "yes" ; then
@@ -6557,7 +6561,7 @@ if test "$ccache_cpp2" = "yes"; then
fi
# build tree in object directory in case the source is not in the current directory
-DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
+DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests tests/vm"
DIRS="$DIRS docs docs/interop fsdev scsi"
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
DIRS="$DIRS roms/seabios roms/vgabios"
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index bbdd3c1d8b..5059d134c8 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -129,3 +129,4 @@ CONFIG_ACPI=y
CONFIG_SMBIOS=y
CONFIG_ASPEED_SOC=y
CONFIG_GPIO_KEY=y
+CONFIG_MSF2=y
diff --git a/default-configs/sparc64-softmmu.mak b/default-configs/sparc64-softmmu.mak
index d07876ab97..3e177bbd7b 100644
--- a/default-configs/sparc64-softmmu.mak
+++ b/default-configs/sparc64-softmmu.mak
@@ -12,6 +12,7 @@ CONFIG_FDC=y
CONFIG_IDE_ISA=y
CONFIG_IDE_CMD646=y
CONFIG_PCI_APB=y
+CONFIG_SUNHME=y
CONFIG_MC146818RTC=y
CONFIG_ISA_TESTDEV=y
CONFIG_EMPTY_SLOT=y
diff --git a/hmp.c b/hmp.c
index 0fb2bc7043..ace729d03f 100644
--- a/hmp.c
+++ b/hmp.c
@@ -336,6 +336,12 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "%s: %s\n",
MigrationParameter_str(MIGRATION_PARAMETER_BLOCK_INCREMENTAL),
params->block_incremental ? "on" : "off");
+ monitor_printf(mon, "%s: %" PRId64 "\n",
+ MigrationParameter_str(MIGRATION_PARAMETER_X_MULTIFD_CHANNELS),
+ params->x_multifd_channels);
+ monitor_printf(mon, "%s: %" PRId64 "\n",
+ MigrationParameter_str(MIGRATION_PARAMETER_X_MULTIFD_PAGE_COUNT),
+ params->x_multifd_page_count);
}
qapi_free_MigrationParameters(params);
@@ -1621,6 +1627,14 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
p->has_block_incremental = true;
visit_type_bool(v, param, &p->block_incremental, &err);
break;
+ case MIGRATION_PARAMETER_X_MULTIFD_CHANNELS:
+ p->has_x_multifd_channels = true;
+ visit_type_int(v, param, &p->x_multifd_channels, &err);
+ break;
+ case MIGRATION_PARAMETER_X_MULTIFD_PAGE_COUNT:
+ p->has_x_multifd_page_count = true;
+ visit_type_int(v, param, &p->x_multifd_page_count, &err);
+ break;
default:
assert(0);
}
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 5ee6f7da5b..2794e086d6 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -19,3 +19,4 @@ obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o sabrelite.o
obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o
obj-$(CONFIG_MPS2) += mps2.o
+obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c
new file mode 100644
index 0000000000..6f97fa9fe3
--- /dev/null
+++ b/hw/arm/msf2-soc.c
@@ -0,0 +1,238 @@
+/*
+ * SmartFusion2 SoC emulation.
+ *
+ * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "hw/arm/arm.h"
+#include "exec/address-spaces.h"
+#include "hw/char/serial.h"
+#include "hw/boards.h"
+#include "sysemu/block-backend.h"
+#include "qemu/cutils.h"
+#include "hw/arm/msf2-soc.h"
+#include "hw/misc/unimp.h"
+
+#define MSF2_TIMER_BASE 0x40004000
+#define MSF2_SYSREG_BASE 0x40038000
+
+#define ENVM_BASE_ADDRESS 0x60000000
+
+#define SRAM_BASE_ADDRESS 0x20000000
+
+#define MSF2_ENVM_MAX_SIZE (512 * K_BYTE)
+
+/*
+ * eSRAM max size is 80k without SECDED(Single error correction and
+ * dual error detection) feature and 64k with SECDED.
+ * We do not support SECDED now.
+ */
+#define MSF2_ESRAM_MAX_SIZE (80 * K_BYTE)
+
+static const uint32_t spi_addr[MSF2_NUM_SPIS] = { 0x40001000 , 0x40011000 };
+static const uint32_t uart_addr[MSF2_NUM_UARTS] = { 0x40000000 , 0x40010000 };
+
+static const int spi_irq[MSF2_NUM_SPIS] = { 2, 3 };
+static const int uart_irq[MSF2_NUM_UARTS] = { 10, 11 };
+static const int timer_irq[MSF2_NUM_TIMERS] = { 14, 15 };
+
+static void m2sxxx_soc_initfn(Object *obj)
+{
+ MSF2State *s = MSF2_SOC(obj);
+ int i;
+
+ object_initialize(&s->armv7m, sizeof(s->armv7m), TYPE_ARMV7M);
+ qdev_set_parent_bus(DEVICE(&s->armv7m), sysbus_get_default());
+
+ object_initialize(&s->sysreg, sizeof(s->sysreg), TYPE_MSF2_SYSREG);
+ qdev_set_parent_bus(DEVICE(&s->sysreg), sysbus_get_default());
+
+ object_initialize(&s->timer, sizeof(s->timer), TYPE_MSS_TIMER);
+ qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default());
+
+ for (i = 0; i < MSF2_NUM_SPIS; i++) {
+ object_initialize(&s->spi[i], sizeof(s->spi[i]),
+ TYPE_MSS_SPI);
+ qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
+ }
+}
+
+static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
+{
+ MSF2State *s = MSF2_SOC(dev_soc);
+ DeviceState *dev, *armv7m;
+ SysBusDevice *busdev;
+ Error *err = NULL;
+ int i;
+
+ MemoryRegion *system_memory = get_system_memory();
+ MemoryRegion *nvm = g_new(MemoryRegion, 1);
+ MemoryRegion *nvm_alias = g_new(MemoryRegion, 1);
+ MemoryRegion *sram = g_new(MemoryRegion, 1);
+
+ memory_region_init_rom(nvm, NULL, "MSF2.eNVM", s->envm_size,
+ &error_fatal);
+ /*
+ * On power-on, the eNVM region 0x60000000 is automatically
+ * remapped to the Cortex-M3 processor executable region
+ * start address (0x0). We do not support remapping other eNVM,
+ * eSRAM and DDR regions by guest(via Sysreg) currently.
+ */
+ memory_region_init_alias(nvm_alias, NULL, "MSF2.eNVM",
+ nvm, 0, s->envm_size);
+
+ memory_region_add_subregion(system_memory, ENVM_BASE_ADDRESS, nvm);
+ memory_region_add_subregion(system_memory, 0, nvm_alias);
+
+ memory_region_init_ram(sram, NULL, "MSF2.eSRAM", s->esram_size,
+ &error_fatal);
+ memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram);
+
+ armv7m = DEVICE(&s->armv7m);
+ qdev_prop_set_uint32(armv7m, "num-irq", 81);
+ qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type);
+ object_property_set_link(OBJECT(&s->armv7m), OBJECT(get_system_memory()),
+ "memory", &error_abort);
+ object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ if (!s->m3clk) {
+ error_setg(errp, "Invalid m3clk value");
+ error_append_hint(errp, "m3clk can not be zero\n");
+ return;
+ }
+ system_clock_scale = NANOSECONDS_PER_SECOND / s->m3clk;
+
+ for (i = 0; i < MSF2_NUM_UARTS; i++) {
+ if (serial_hds[i]) {
+ serial_mm_init(get_system_memory(), uart_addr[i], 2,
+ qdev_get_gpio_in(armv7m, uart_irq[i]),
+ 115200, serial_hds[i], DEVICE_NATIVE_ENDIAN);
+ }
+ }
+
+ dev = DEVICE(&s->timer);
+ /* APB0 clock is the timer input clock */
+ qdev_prop_set_uint32(dev, "clock-frequency", s->m3clk / s->apb0div);
+ object_property_set_bool(OBJECT(&s->timer), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, MSF2_TIMER_BASE);
+ sysbus_connect_irq(busdev, 0,
+ qdev_get_gpio_in(armv7m, timer_irq[0]));
+ sysbus_connect_irq(busdev, 1,
+ qdev_get_gpio_in(armv7m, timer_irq[1]));
+
+ dev = DEVICE(&s->sysreg);
+ qdev_prop_set_uint32(dev, "apb0divisor", s->apb0div);
+ qdev_prop_set_uint32(dev, "apb1divisor", s->apb1div);
+ object_property_set_bool(OBJECT(&s->sysreg), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, MSF2_SYSREG_BASE);
+
+ for (i = 0; i < MSF2_NUM_SPIS; i++) {
+ gchar *bus_name;
+
+ object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_addr[i]);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0,
+ qdev_get_gpio_in(armv7m, spi_irq[i]));
+
+ /* Alias controller SPI bus to the SoC itself */
+ bus_name = g_strdup_printf("spi%d", i);
+ object_property_add_alias(OBJECT(s), bus_name,
+ OBJECT(&s->spi[i]), "spi",
+ &error_abort);
+ g_free(bus_name);
+ }
+
+ /* Below devices are not modelled yet. */
+ create_unimplemented_device("i2c_0", 0x40002000, 0x1000);
+ create_unimplemented_device("dma", 0x40003000, 0x1000);
+ create_unimplemented_device("watchdog", 0x40005000, 0x1000);
+ create_unimplemented_device("i2c_1", 0x40012000, 0x1000);
+ create_unimplemented_device("gpio", 0x40013000, 0x1000);
+ create_unimplemented_device("hs-dma", 0x40014000, 0x1000);
+ create_unimplemented_device("can", 0x40015000, 0x1000);
+ create_unimplemented_device("rtc", 0x40017000, 0x1000);
+ create_unimplemented_device("apb_config", 0x40020000, 0x10000);
+ create_unimplemented_device("emac", 0x40041000, 0x1000);
+ create_unimplemented_device("usb", 0x40043000, 0x1000);
+}
+
+static Property m2sxxx_soc_properties[] = {
+ /*
+ * part name specifies the type of SmartFusion2 device variant(this
+ * property is for information purpose only.
+ */
+ DEFINE_PROP_STRING("cpu-type", MSF2State, cpu_type),
+ DEFINE_PROP_STRING("part-name", MSF2State, part_name),
+ DEFINE_PROP_UINT64("eNVM-size", MSF2State, envm_size, MSF2_ENVM_MAX_SIZE),
+ DEFINE_PROP_UINT64("eSRAM-size", MSF2State, esram_size,
+ MSF2_ESRAM_MAX_SIZE),
+ /* Libero GUI shows 100Mhz as default for clocks */
+ DEFINE_PROP_UINT32("m3clk", MSF2State, m3clk, 100 * 1000000),
+ /* default divisors in Libero GUI */
+ DEFINE_PROP_UINT8("apb0div", MSF2State, apb0div, 2),
+ DEFINE_PROP_UINT8("apb1div", MSF2State, apb1div, 2),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void m2sxxx_soc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = m2sxxx_soc_realize;
+ dc->props = m2sxxx_soc_properties;
+}
+
+static const TypeInfo m2sxxx_soc_info = {
+ .name = TYPE_MSF2_SOC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MSF2State),
+ .instance_init = m2sxxx_soc_initfn,
+ .class_init = m2sxxx_soc_class_init,
+};
+
+static void m2sxxx_soc_types(void)
+{
+ type_register_static(&m2sxxx_soc_info);
+}
+
+type_init(m2sxxx_soc_types)
diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c
new file mode 100644
index 0000000000..0795a3a3a1
--- /dev/null
+++ b/hw/arm/msf2-som.c
@@ -0,0 +1,105 @@
+/*
+ * SmartFusion2 SOM starter kit(from Emcraft) emulation.
+ *
+ * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "hw/boards.h"
+#include "hw/arm/arm.h"
+#include "exec/address-spaces.h"
+#include "qemu/cutils.h"
+#include "hw/arm/msf2-soc.h"
+#include "cpu.h"
+
+#define DDR_BASE_ADDRESS 0xA0000000
+#define DDR_SIZE (64 * M_BYTE)
+
+#define M2S010_ENVM_SIZE (256 * K_BYTE)
+#define M2S010_ESRAM_SIZE (64 * K_BYTE)
+
+static void emcraft_sf2_s2s010_init(MachineState *machine)
+{
+ DeviceState *dev;
+ DeviceState *spi_flash;
+ MSF2State *soc;
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ DriveInfo *dinfo = drive_get_next(IF_MTD);
+ qemu_irq cs_line;
+ SSIBus *spi_bus;
+ MemoryRegion *sysmem = get_system_memory();
+ MemoryRegion *ddr = g_new(MemoryRegion, 1);
+
+ if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) {
+ error_report("This board can only be used with CPU %s",
+ mc->default_cpu_type);
+ }
+
+ memory_region_init_ram(ddr, NULL, "ddr-ram", DDR_SIZE,
+ &error_fatal);
+ memory_region_add_subregion(sysmem, DDR_BASE_ADDRESS, ddr);
+
+ dev = qdev_create(NULL, TYPE_MSF2_SOC);
+ qdev_prop_set_string(dev, "part-name", "M2S010");
+ qdev_prop_set_string(dev, "cpu-type", mc->default_cpu_type);
+
+ qdev_prop_set_uint64(dev, "eNVM-size", M2S010_ENVM_SIZE);
+ qdev_prop_set_uint64(dev, "eSRAM-size", M2S010_ESRAM_SIZE);
+
+ /*
+ * CPU clock and peripheral clocks(APB0, APB1)are configurable
+ * in Libero. CPU clock is divided by APB0 and APB1 divisors for
+ * peripherals. Emcraft's SoM kit comes with these settings by default.
+ */
+ qdev_prop_set_uint32(dev, "m3clk", 142 * 1000000);
+ qdev_prop_set_uint32(dev, "apb0div", 2);
+ qdev_prop_set_uint32(dev, "apb1div", 2);
+
+ object_property_set_bool(OBJECT(dev), true, "realized", &error_fatal);
+
+ soc = MSF2_SOC(dev);
+
+ /* Attach SPI flash to SPI0 controller */
+ spi_bus = (SSIBus *)qdev_get_child_bus(dev, "spi0");
+ spi_flash = ssi_create_slave_no_init(spi_bus, "s25sl12801");
+ qdev_prop_set_uint8(spi_flash, "spansion-cr2nv", 1);
+ if (dinfo) {
+ qdev_prop_set_drive(spi_flash, "drive", blk_by_legacy_dinfo(dinfo),
+ &error_fatal);
+ }
+ qdev_init_nofail(spi_flash);
+ cs_line = qdev_get_gpio_in_named(spi_flash, SSI_GPIO_CS, 0);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&soc->spi[0]), 1, cs_line);
+
+ armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename,
+ soc->envm_size);
+}
+
+static void emcraft_sf2_machine_init(MachineClass *mc)
+{
+ mc->desc = "SmartFusion2 SOM kit from Emcraft (M2S010)";
+ mc->init = emcraft_sf2_s2s010_init;
+ mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3");
+}
+
+DEFINE_MACHINE("emcraft-sf2", emcraft_sf2_machine_init)
diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c
index 3f6076ede8..f5b148881c 100644
--- a/hw/arm/omap2.c
+++ b/hw/arm/omap2.c
@@ -2087,19 +2087,44 @@ static void omap_sysctl_write(void *opaque, hwaddr addr,
}
}
+static uint64_t omap_sysctl_readfn(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ switch (size) {
+ case 1:
+ return omap_sysctl_read8(opaque, addr);
+ case 2:
+ return omap_badwidth_read32(opaque, addr); /* TODO */
+ case 4:
+ return omap_sysctl_read(opaque, addr);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void omap_sysctl_writefn(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ switch (size) {
+ case 1:
+ omap_sysctl_write8(opaque, addr, value);
+ break;
+ case 2:
+ omap_badwidth_write32(opaque, addr, value); /* TODO */
+ break;
+ case 4:
+ omap_sysctl_write(opaque, addr, value);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
static const MemoryRegionOps omap_sysctl_ops = {
- .old_mmio = {
- .read = {
- omap_sysctl_read8,
- omap_badwidth_read32, /* TODO */
- omap_sysctl_read,
- },
- .write = {
- omap_sysctl_write8,
- omap_badwidth_write32, /* TODO */
- omap_sysctl_write,
- },
- },
+ .read = omap_sysctl_readfn,
+ .write = omap_sysctl_writefn,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
diff --git a/hw/arm/palm.c b/hw/arm/palm.c
index b8753e2b5c..a1f55d79b4 100644
--- a/hw/arm/palm.c
+++ b/hw/arm/palm.c
@@ -31,26 +31,16 @@
#include "exec/address-spaces.h"
#include "cpu.h"
-static uint32_t static_readb(void *opaque, hwaddr offset)
+static uint64_t static_read(void *opaque, hwaddr offset, unsigned size)
{
- uint32_t *val = (uint32_t *) opaque;
- return *val >> ((offset & 3) << 3);
-}
+ uint32_t *val = (uint32_t *)opaque;
+ uint32_t sizemask = 7 >> size;
-static uint32_t static_readh(void *opaque, hwaddr offset)
-{
- uint32_t *val = (uint32_t *) opaque;
- return *val >> ((offset & 1) << 3);
-}
-
-static uint32_t static_readw(void *opaque, hwaddr offset)
-{
- uint32_t *val = (uint32_t *) opaque;
- return *val >> ((offset & 0) << 3);
+ return *val >> ((offset & sizemask) << 3);
}
-static void static_write(void *opaque, hwaddr offset,
- uint32_t value)
+static void static_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
{
#ifdef SPY
printf("%s: value %08lx written at " PA_FMT "\n",
@@ -59,10 +49,10 @@ static void static_write(void *opaque, hwaddr offset,
}
static const MemoryRegionOps static_ops = {
- .old_mmio = {
- .read = { static_readb, static_readh, static_readw, },
- .write = { static_write, static_write, static_write, },
- },
+ .read = static_read,
+ .write = static_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
index d42ed7070d..536e2ee735 100644
--- a/hw/block/xen_disk.c
+++ b/hw/block/xen_disk.c
@@ -1232,7 +1232,7 @@ static int blk_connect(struct XenDevice *xendev)
return -1;
}
- domids = g_malloc0_n(blkdev->nr_ring_ref, sizeof(uint32_t));
+ domids = g_new0(uint32_t, blkdev->nr_ring_ref);
for (i = 0; i < blkdev->nr_ring_ref; i++) {
domids[i] = blkdev->xendev.dom;
}
diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c
index 1df394eb12..17891e2d0f 100644
--- a/hw/gpio/omap_gpio.c
+++ b/hw/gpio/omap_gpio.c
@@ -525,17 +525,23 @@ static void omap2_gpio_module_write(void *opaque, hwaddr addr,
}
}
-static uint32_t omap2_gpio_module_readp(void *opaque, hwaddr addr)
+static uint64_t omap2_gpio_module_readp(void *opaque, hwaddr addr,
+ unsigned size)
{
return omap2_gpio_module_read(opaque, addr & ~3) >> ((addr & 3) << 3);
}
static void omap2_gpio_module_writep(void *opaque, hwaddr addr,
- uint32_t value)
+ uint64_t value, unsigned size)
{
uint32_t cur = 0;
uint32_t mask = 0xffff;
+ if (size == 4) {
+ omap2_gpio_module_write(opaque, addr, value);
+ return;
+ }
+
switch (addr & ~3) {
case 0x00: /* GPIO_REVISION */
case 0x14: /* GPIO_SYSSTATUS */
@@ -581,18 +587,10 @@ static void omap2_gpio_module_writep(void *opaque, hwaddr addr,
}
static const MemoryRegionOps omap2_gpio_module_ops = {
- .old_mmio = {
- .read = {
- omap2_gpio_module_readp,
- omap2_gpio_module_readp,
- omap2_gpio_module_read,
- },
- .write = {
- omap2_gpio_module_writep,
- omap2_gpio_module_writep,
- omap2_gpio_module_write,
- },
- },
+ .read = omap2_gpio_module_readp,
+ .write = omap2_gpio_module_writep,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c
index f6e80bee25..12264ee0f5 100644
--- a/hw/i2c/omap_i2c.c
+++ b/hw/i2c/omap_i2c.c
@@ -430,19 +430,39 @@ static void omap_i2c_writeb(void *opaque, hwaddr addr,
}
}
+static uint64_t omap_i2c_readfn(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ switch (size) {
+ case 2:
+ return omap_i2c_read(opaque, addr);
+ default:
+ return omap_badwidth_read16(opaque, addr);
+ }
+}
+
+static void omap_i2c_writefn(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ switch (size) {
+ case 1:
+ /* Only the last fifo write can be 8 bit. */
+ omap_i2c_writeb(opaque, addr, value);
+ break;
+ case 2:
+ omap_i2c_write(opaque, addr, value);
+ break;
+ default:
+ omap_badwidth_write16(opaque, addr, value);
+ break;
+ }
+}
+
static const MemoryRegionOps omap_i2c_ops = {
- .old_mmio = {
- .read = {
- omap_badwidth_read16,
- omap_i2c_read,
- omap_badwidth_read16,
- },
- .write = {
- omap_i2c_writeb, /* Only the last fifo write can be 8 bit. */
- omap_i2c_write,
- omap_badwidth_write16,
- },
- },
+ .read = omap_i2c_readfn,
+ .write = omap_i2c_writefn,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 24c65dfab3..32d1296a64 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -184,7 +184,7 @@ static void ahci_check_irq(AHCIState *s)
static void ahci_trigger_irq(AHCIState *s, AHCIDevice *d,
enum AHCIPortIRQ irqbit)
{
- g_assert(irqbit >= 0 && irqbit < 32);
+ g_assert((unsigned)irqbit < 32);
uint32_t irq = 1U << irqbit;
uint32_t irqstat = d->port_regs.irq_stat | irq;
diff --git a/hw/ide/core.c b/hw/ide/core.c
index a19bd9011c..d63eb4a72e 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -68,7 +68,7 @@ const char *IDE_DMA_CMD_lookup[IDE_DMA__COUNT] = {
static const char *IDE_DMA_CMD_str(enum ide_dma_cmd enval)
{
- if (enval >= IDE_DMA__BEGIN && enval < IDE_DMA__COUNT) {
+ if ((unsigned)enval < IDE_DMA__COUNT) {
return IDE_DMA_CMD_lookup[enval];
}
return "DMA UNKNOWN CMD";
diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c
index d3e20561c7..d90d8d0784 100644
--- a/hw/intc/armv7m_nvic.c
+++ b/hw/intc/armv7m_nvic.c
@@ -47,13 +47,15 @@
* For historical reasons QEMU tends to use "interrupt" and
* "exception" more or less interchangeably.
*/
-#define NVIC_FIRST_IRQ 16
+#define NVIC_FIRST_IRQ NVIC_INTERNAL_VECTORS
#define NVIC_MAX_IRQ (NVIC_MAX_VECTORS - NVIC_FIRST_IRQ)
/* Effective running priority of the CPU when no exception is active
* (higher than the highest possible priority value)
*/
#define NVIC_NOEXC_PRIO 0x100
+/* Maximum priority of non-secure exceptions when AIRCR.PRIS is set */
+#define NVIC_NS_PRIO_LIMIT 0x80
static const uint8_t nvic_id[] = {
0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1
@@ -61,10 +63,10 @@ static const uint8_t nvic_id[] = {
static int nvic_pending_prio(NVICState *s)
{
- /* return the priority of the current pending interrupt,
+ /* return the group priority of the current pending interrupt,
* or NVIC_NOEXC_PRIO if no interrupt is pending
*/
- return s->vectpending ? s->vectors[s->vectpending].prio : NVIC_NOEXC_PRIO;
+ return s->vectpending_prio;
}
/* Return the value of the ISCR RETTOBASE bit:
@@ -84,9 +86,12 @@ static int nvic_pending_prio(NVICState *s)
static bool nvic_rettobase(NVICState *s)
{
int irq, nhand = 0;
+ bool check_sec = arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY);
for (irq = ARMV7M_EXCP_RESET; irq < s->num_irq; irq++) {
- if (s->vectors[irq].active) {
+ if (s->vectors[irq].active ||
+ (check_sec && irq < NVIC_INTERNAL_VECTORS &&
+ s->sec_vectors[irq].active)) {
nhand++;
if (nhand == 2) {
return 0;
@@ -123,13 +128,139 @@ static bool nvic_isrpending(NVICState *s)
return false;
}
+static bool exc_is_banked(int exc)
+{
+ /* Return true if this is one of the limited set of exceptions which
+ * are banked (and thus have state in sec_vectors[])
+ */
+ return exc == ARMV7M_EXCP_HARD ||
+ exc == ARMV7M_EXCP_MEM ||
+ exc == ARMV7M_EXCP_USAGE ||
+ exc == ARMV7M_EXCP_SVC ||
+ exc == ARMV7M_EXCP_PENDSV ||
+ exc == ARMV7M_EXCP_SYSTICK;
+}
+
/* Return a mask word which clears the subpriority bits from
* a priority value for an M-profile exception, leaving only
* the group priority.
*/
-static inline uint32_t nvic_gprio_mask(NVICState *s)
+static inline uint32_t nvic_gprio_mask(NVICState *s, bool secure)
+{
+ return ~0U << (s->prigroup[secure] + 1);
+}
+
+static bool exc_targets_secure(NVICState *s, int exc)
+{
+ /* Return true if this non-banked exception targets Secure state. */
+ if (!arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY)) {
+ return false;
+ }
+
+ if (exc >= NVIC_FIRST_IRQ) {
+ return !s->itns[exc];
+ }
+
+ /* Function shouldn't be called for banked exceptions. */
+ assert(!exc_is_banked(exc));
+
+ switch (exc) {
+ case ARMV7M_EXCP_NMI:
+ case ARMV7M_EXCP_BUS:
+ return !(s->cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK);
+ case ARMV7M_EXCP_SECURE:
+ return true;
+ case ARMV7M_EXCP_DEBUG:
+ /* TODO: controlled by DEMCR.SDME, which we don't yet implement */
+ return false;
+ default:
+ /* reset, and reserved (unused) low exception numbers.
+ * We'll get called by code that loops through all the exception
+ * numbers, but it doesn't matter what we return here as these
+ * non-existent exceptions will never be pended or active.
+ */
+ return true;
+ }
+}
+
+static int exc_group_prio(NVICState *s, int rawprio, bool targets_secure)
+{
+ /* Return the group priority for this exception, given its raw
+ * (group-and-subgroup) priority value and whether it is targeting
+ * secure state or not.
+ */
+ if (rawprio < 0) {
+ return rawprio;
+ }
+ rawprio &= nvic_gprio_mask(s, targets_secure);
+ /* AIRCR.PRIS causes us to squash all NS priorities into the
+ * lower half of the total range
+ */
+ if (!targets_secure &&
+ (s->cpu->env.v7m.aircr & R_V7M_AIRCR_PRIS_MASK)) {
+ rawprio = (rawprio >> 1) + NVIC_NS_PRIO_LIMIT;
+ }
+ return rawprio;
+}
+
+/* Recompute vectpending and exception_prio for a CPU which implements
+ * the Security extension
+ */
+static void nvic_recompute_state_secure(NVICState *s)
{
- return ~0U << (s->prigroup + 1);
+ int i, bank;
+ int pend_prio = NVIC_NOEXC_PRIO;
+ int active_prio = NVIC_NOEXC_PRIO;
+ int pend_irq = 0;
+ bool pending_is_s_banked = false;
+
+ /* R_CQRV: precedence is by:
+ * - lowest group priority; if both the same then
+ * - lowest subpriority; if both the same then
+ * - lowest exception number; if both the same (ie banked) then
+ * - secure exception takes precedence
+ * Compare pseudocode RawExecutionPriority.
+ * Annoyingly, now we have two prigroup values (for S and NS)
+ * we can't do the loop comparison on raw priority values.
+ */
+ for (i = 1; i < s->num_irq; i++) {
+ for (bank = M_REG_S; bank >= M_REG_NS; bank--) {
+ VecInfo *vec;
+ int prio;
+ bool targets_secure;
+
+ if (bank == M_REG_S) {
+ if (!exc_is_banked(i)) {
+ continue;
+ }
+ vec = &s->sec_vectors[i];
+ targets_secure = true;
+ } else {
+ vec = &s->vectors[i];
+ targets_secure = !exc_is_banked(i) && exc_targets_secure(s, i);
+ }
+
+ prio = exc_group_prio(s, vec->prio, targets_secure);
+ if (vec->enabled && vec->pending && prio < pend_prio) {
+ pend_prio = prio;
+ pend_irq = i;
+ pending_is_s_banked = (bank == M_REG_S);
+ }
+ if (vec->active && prio < active_prio) {
+ active_prio = prio;
+ }
+ }
+ }
+
+ s->vectpending_is_s_banked = pending_is_s_banked;
+ s->vectpending = pend_irq;
+ s->vectpending_prio = pend_prio;
+ s->exception_prio = active_prio;
+
+ trace_nvic_recompute_state_secure(s->vectpending,
+ s->vectpending_is_s_banked,
+ s->vectpending_prio,
+ s->exception_prio);
}
/* Recompute vectpending and exception_prio */
@@ -140,6 +271,18 @@ static void nvic_recompute_state(NVICState *s)
int active_prio = NVIC_NOEXC_PRIO;
int pend_irq = 0;
+ /* In theory we could write one function that handled both
+ * the "security extension present" and "not present"; however
+ * the security related changes significantly complicate the
+ * recomputation just by themselves and mixing both cases together
+ * would be even worse, so we retain a separate non-secure-only
+ * version for CPUs which don't implement the security extension.
+ */
+ if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY)) {
+ nvic_recompute_state_secure(s);
+ return;
+ }
+
for (i = 1; i < s->num_irq; i++) {
VecInfo *vec = &s->vectors[i];
@@ -153,13 +296,20 @@ static void nvic_recompute_state(NVICState *s)
}
if (active_prio > 0) {
- active_prio &= nvic_gprio_mask(s);
+ active_prio &= nvic_gprio_mask(s, false);
+ }
+
+ if (pend_prio > 0) {
+ pend_prio &= nvic_gprio_mask(s, false);
}
s->vectpending = pend_irq;
+ s->vectpending_prio = pend_prio;
s->exception_prio = active_prio;
- trace_nvic_recompute_state(s->vectpending, s->exception_prio);
+ trace_nvic_recompute_state(s->vectpending,
+ s->vectpending_prio,
+ s->exception_prio);
}
/* Return the current execution priority of the CPU
@@ -169,21 +319,84 @@ static void nvic_recompute_state(NVICState *s)
static inline int nvic_exec_prio(NVICState *s)
{
CPUARMState *env = &s->cpu->env;
- int running;
+ int running = NVIC_NOEXC_PRIO;
+
+ if (env->v7m.basepri[M_REG_NS] > 0) {
+ running = exc_group_prio(s, env->v7m.basepri[M_REG_NS], M_REG_NS);
+ }
+
+ if (env->v7m.basepri[M_REG_S] > 0) {
+ int basepri = exc_group_prio(s, env->v7m.basepri[M_REG_S], M_REG_S);
+ if (running > basepri) {
+ running = basepri;
+ }
+ }
- if (env->v7m.faultmask[env->v7m.secure]) {
- running = -1;
- } else if (env->v7m.primask[env->v7m.secure]) {
+ if (env->v7m.primask[M_REG_NS]) {
+ if (env->v7m.aircr & R_V7M_AIRCR_PRIS_MASK) {
+ if (running > NVIC_NS_PRIO_LIMIT) {
+ running = NVIC_NS_PRIO_LIMIT;
+ }
+ } else {
+ running = 0;
+ }
+ }
+
+ if (env->v7m.primask[M_REG_S]) {
running = 0;
- } else if (env->v7m.basepri[env->v7m.secure] > 0) {
- running = env->v7m.basepri[env->v7m.secure] & nvic_gprio_mask(s);
- } else {
- running = NVIC_NOEXC_PRIO; /* lower than any possible priority */
}
+
+ if (env->v7m.faultmask[M_REG_NS]) {
+ if (env->v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK) {
+ running = -1;
+ } else {
+ if (env->v7m.aircr & R_V7M_AIRCR_PRIS_MASK) {
+ if (running > NVIC_NS_PRIO_LIMIT) {
+ running = NVIC_NS_PRIO_LIMIT;
+ }
+ } else {
+ running = 0;
+ }
+ }
+ }
+
+ if (env->v7m.faultmask[M_REG_S]) {
+ running = (env->v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK) ? -3 : -1;
+ }
+
/* consider priority of active handler */
return MIN(running, s->exception_prio);
}
+bool armv7m_nvic_neg_prio_requested(void *opaque, bool secure)
+{
+ /* Return true if the requested execution priority is negative
+ * for the specified security state, ie that security state
+ * has an active NMI or HardFault or has set its FAULTMASK.
+ * Note that this is not the same as whether the execution
+ * priority is actually negative (for instance AIRCR.PRIS may
+ * mean we don't allow FAULTMASK_NS to actually make the execution
+ * priority negative). Compare pseudocode IsReqExcPriNeg().
+ */
+ NVICState *s = opaque;
+
+ if (s->cpu->env.v7m.faultmask[secure]) {
+ return true;
+ }
+
+ if (secure ? s->sec_vectors[ARMV7M_EXCP_HARD].active :
+ s->vectors[ARMV7M_EXCP_HARD].active) {
+ return true;
+ }
+
+ if (s->vectors[ARMV7M_EXCP_NMI].active &&
+ exc_targets_secure(s, ARMV7M_EXCP_NMI) == secure) {
+ return true;
+ }
+
+ return false;
+}
+
bool armv7m_nvic_can_take_pending_exception(void *opaque)
{
NVICState *s = opaque;
@@ -198,15 +411,40 @@ int armv7m_nvic_raw_execution_priority(void *opaque)
return s->exception_prio;
}
-/* caller must call nvic_irq_update() after this */
-static void set_prio(NVICState *s, unsigned irq, uint8_t prio)
+/* caller must call nvic_irq_update() after this.
+ * secure indicates the bank to use for banked exceptions (we assert if
+ * we are passed secure=true for a non-banked exception).
+ */
+static void set_prio(NVICState *s, unsigned irq, bool secure, uint8_t prio)
{
assert(irq > ARMV7M_EXCP_NMI); /* only use for configurable prios */
assert(irq < s->num_irq);
- s->vectors[irq].prio = prio;
+ if (secure) {
+ assert(exc_is_banked(irq));
+ s->sec_vectors[irq].prio = prio;
+ } else {
+ s->vectors[irq].prio = prio;
+ }
+
+ trace_nvic_set_prio(irq, secure, prio);
+}
+
+/* Return the current raw priority register value.
+ * secure indicates the bank to use for banked exceptions (we assert if
+ * we are passed secure=true for a non-banked exception).
+ */
+static int get_prio(NVICState *s, unsigned irq, bool secure)
+{
+ assert(irq > ARMV7M_EXCP_NMI); /* only use for configurable prios */
+ assert(irq < s->num_irq);
- trace_nvic_set_prio(irq, prio);
+ if (secure) {
+ assert(exc_is_banked(irq));
+ return s->sec_vectors[irq].prio;
+ } else {
+ return s->vectors[irq].prio;
+ }
}
/* Recompute state and assert irq line accordingly.
@@ -233,31 +471,50 @@ static void nvic_irq_update(NVICState *s)
qemu_set_irq(s->excpout, lvl);
}
-static void armv7m_nvic_clear_pending(void *opaque, int irq)
+/**
+ * armv7m_nvic_clear_pending: mark the specified exception as not pending
+ * @opaque: the NVIC
+ * @irq: the exception number to mark as not pending
+ * @secure: false for non-banked exceptions or for the nonsecure
+ * version of a banked exception, true for the secure version of a banked
+ * exception.
+ *
+ * Marks the specified exception as not pending. Note that we will assert()
+ * if @secure is true and @irq does not specify one of the fixed set
+ * of architecturally banked exceptions.
+ */
+static void armv7m_nvic_clear_pending(void *opaque, int irq, bool secure)
{
NVICState *s = (NVICState *)opaque;
VecInfo *vec;
assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq);
- vec = &s->vectors[irq];
- trace_nvic_clear_pending(irq, vec->enabled, vec->prio);
+ if (secure) {
+ assert(exc_is_banked(irq));
+ vec = &s->sec_vectors[irq];
+ } else {
+ vec = &s->vectors[irq];
+ }
+ trace_nvic_clear_pending(irq, secure, vec->enabled, vec->prio);
if (vec->pending) {
vec->pending = 0;
nvic_irq_update(s);
}
}
-void armv7m_nvic_set_pending(void *opaque, int irq)
+void armv7m_nvic_set_pending(void *opaque, int irq, bool secure)
{
NVICState *s = (NVICState *)opaque;
+ bool banked = exc_is_banked(irq);
VecInfo *vec;
assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq);
+ assert(!secure || banked);
- vec = &s->vectors[irq];
- trace_nvic_set_pending(irq, vec->enabled, vec->prio);
+ vec = (banked && secure) ? &s->sec_vectors[irq] : &s->vectors[irq];
+ trace_nvic_set_pending(irq, secure, vec->enabled, vec->prio);
if (irq >= ARMV7M_EXCP_HARD && irq < ARMV7M_EXCP_PENDSV) {
/* If a synchronous exception is pending then it may be
@@ -283,7 +540,7 @@ void armv7m_nvic_set_pending(void *opaque, int irq)
int running = nvic_exec_prio(s);
bool escalate = false;
- if (vec->prio >= running) {
+ if (exc_group_prio(s, vec->prio, secure) >= running) {
trace_nvic_escalate_prio(irq, vec->prio, running);
escalate = true;
} else if (!vec->enabled) {
@@ -292,8 +549,22 @@ void armv7m_nvic_set_pending(void *opaque, int irq)
}
if (escalate) {
- if (running < 0) {
- /* We want to escalate to HardFault but we can't take a
+
+ /* We need to escalate this exception to a synchronous HardFault.
+ * If BFHFNMINS is set then we escalate to the banked HF for
+ * the target security state of the original exception; otherwise
+ * we take a Secure HardFault.
+ */
+ irq = ARMV7M_EXCP_HARD;
+ if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY) &&
+ (secure ||
+ !(s->cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK))) {
+ vec = &s->sec_vectors[irq];
+ } else {
+ vec = &s->vectors[irq];
+ }
+ if (running <= vec->prio) {
+ /* We want to escalate to HardFault but we can't take the
* synchronous HardFault at this point either. This is a
* Lockup condition due to a guest bug. We don't model
* Lockup, so report via cpu_abort() instead.
@@ -303,9 +574,7 @@ void armv7m_nvic_set_pending(void *opaque, int irq)
"(current priority %d)\n", irq, running);
}
- /* We can do the escalation, so we take HardFault instead */
- irq = ARMV7M_EXCP_HARD;
- vec = &s->vectors[irq];
+ /* HF may be banked but there is only one shared HFSR */
s->cpu->env.v7m.hfsr |= R_V7M_HFSR_FORCED_MASK;
}
}
@@ -317,29 +586,32 @@ void armv7m_nvic_set_pending(void *opaque, int irq)
}
/* Make pending IRQ active. */
-void armv7m_nvic_acknowledge_irq(void *opaque)
+bool armv7m_nvic_acknowledge_irq(void *opaque)
{
NVICState *s = (NVICState *)opaque;
CPUARMState *env = &s->cpu->env;
const int pending = s->vectpending;
const int running = nvic_exec_prio(s);
- int pendgroupprio;
VecInfo *vec;
+ bool targets_secure;
assert(pending > ARMV7M_EXCP_RESET && pending < s->num_irq);
- vec = &s->vectors[pending];
+ if (s->vectpending_is_s_banked) {
+ vec = &s->sec_vectors[pending];
+ targets_secure = true;
+ } else {
+ vec = &s->vectors[pending];
+ targets_secure = !exc_is_banked(s->vectpending) &&
+ exc_targets_secure(s, s->vectpending);
+ }
assert(vec->enabled);
assert(vec->pending);
- pendgroupprio = vec->prio;
- if (pendgroupprio > 0) {
- pendgroupprio &= nvic_gprio_mask(s);
- }
- assert(pendgroupprio < running);
+ assert(s->vectpending_prio < running);
- trace_nvic_acknowledge_irq(pending, vec->prio);
+ trace_nvic_acknowledge_irq(pending, s->vectpending_prio, targets_secure);
vec->active = 1;
vec->pending = 0;
@@ -347,9 +619,11 @@ void armv7m_nvic_acknowledge_irq(void *opaque)
env->v7m.exception = s->vectpending;
nvic_irq_update(s);
+
+ return targets_secure;
}
-int armv7m_nvic_complete_irq(void *opaque, int irq)
+int armv7m_nvic_complete_irq(void *opaque, int irq, bool secure)
{
NVICState *s = (NVICState *)opaque;
VecInfo *vec;
@@ -357,9 +631,13 @@ int armv7m_nvic_complete_irq(void *opaque, int irq)
assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq);
- vec = &s->vectors[irq];
+ if (secure && exc_is_banked(irq)) {
+ vec = &s->sec_vectors[irq];
+ } else {
+ vec = &s->vectors[irq];
+ }
- trace_nvic_complete_irq(irq);
+ trace_nvic_complete_irq(irq, secure);
if (!vec->active) {
/* Tell the caller this was an illegal exception return */
@@ -405,7 +683,7 @@ static void set_irq_level(void *opaque, int n, int level)
if (level != vec->level) {
vec->level = level;
if (level) {
- armv7m_nvic_set_pending(s, n);
+ armv7m_nvic_set_pending(s, n, false);
}
}
}
@@ -418,9 +696,28 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs)
switch (offset) {
case 4: /* Interrupt Control Type. */
return ((s->num_irq - NVIC_FIRST_IRQ) / 32) - 1;
+ case 0x380 ... 0x3bf: /* NVIC_ITNS<n> */
+ {
+ int startvec = 32 * (offset - 0x380) + NVIC_FIRST_IRQ;
+ int i;
+
+ if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
+ goto bad_offset;
+ }
+ if (!attrs.secure) {
+ return 0;
+ }
+ val = 0;
+ for (i = 0; i < 32 && startvec + i < s->num_irq; i++) {
+ if (s->itns[startvec + i]) {
+ val |= (1 << i);
+ }
+ }
+ return val;
+ }
case 0xd00: /* CPUID Base. */
return cpu->midr;
- case 0xd04: /* Interrupt Control State. */
+ case 0xd04: /* Interrupt Control State (ICSR) */
/* VECTACTIVE */
val = cpu->env.v7m.exception;
/* VECTPENDING */
@@ -433,24 +730,50 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs)
if (nvic_rettobase(s)) {
val |= (1 << 11);
}
- /* PENDSTSET */
- if (s->vectors[ARMV7M_EXCP_SYSTICK].pending) {
- val |= (1 << 26);
- }
- /* PENDSVSET */
- if (s->vectors[ARMV7M_EXCP_PENDSV].pending) {
- val |= (1 << 28);
+ if (attrs.secure) {
+ /* PENDSTSET */
+ if (s->sec_vectors[ARMV7M_EXCP_SYSTICK].pending) {
+ val |= (1 << 26);
+ }
+ /* PENDSVSET */
+ if (s->sec_vectors[ARMV7M_EXCP_PENDSV].pending) {
+ val |= (1 << 28);
+ }
+ } else {
+ /* PENDSTSET */
+ if (s->vectors[ARMV7M_EXCP_SYSTICK].pending) {
+ val |= (1 << 26);
+ }
+ /* PENDSVSET */
+ if (s->vectors[ARMV7M_EXCP_PENDSV].pending) {
+ val |= (1 << 28);
+ }
}
/* NMIPENDSET */
- if (s->vectors[ARMV7M_EXCP_NMI].pending) {
+ if ((cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK) &&
+ s->vectors[ARMV7M_EXCP_NMI].pending) {
val |= (1 << 31);
}
- /* ISRPREEMPT not implemented */
+ /* ISRPREEMPT: RES0 when halting debug not implemented */
+ /* STTNS: RES0 for the Main Extension */
return val;
case 0xd08: /* Vector Table Offset. */
return cpu->env.v7m.vecbase[attrs.secure];
- case 0xd0c: /* Application Interrupt/Reset Control. */
- return 0xfa050000 | (s->prigroup << 8);
+ case 0xd0c: /* Application Interrupt/Reset Control (AIRCR) */
+ val = 0xfa050000 | (s->prigroup[attrs.secure] << 8);
+ if (attrs.secure) {
+ /* s->aircr stores PRIS, BFHFNMINS, SYSRESETREQS */
+ val |= cpu->env.v7m.aircr;
+ } else {
+ if (arm_feature(&cpu->env, ARM_FEATURE_V8)) {
+ /* BFHFNMINS is R/O from NS; other bits are RAZ/WI. If
+ * security isn't supported then BFHFNMINS is RAO (and
+ * the bit in env.v7m.aircr is always set).
+ */
+ val |= cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK;
+ }
+ }
+ return val;
case 0xd10: /* System Control. */
/* TODO: Implement SLEEPONEXIT. */
return 0;
@@ -461,50 +784,117 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs)
val = cpu->env.v7m.ccr[attrs.secure];
val |= cpu->env.v7m.ccr[M_REG_NS] & R_V7M_CCR_BFHFNMIGN_MASK;
return val;
- case 0xd24: /* System Handler Status. */
+ case 0xd24: /* System Handler Control and State (SHCSR) */
val = 0;
- if (s->vectors[ARMV7M_EXCP_MEM].active) {
- val |= (1 << 0);
- }
- if (s->vectors[ARMV7M_EXCP_BUS].active) {
- val |= (1 << 1);
- }
- if (s->vectors[ARMV7M_EXCP_USAGE].active) {
- val |= (1 << 3);
+ if (attrs.secure) {
+ if (s->sec_vectors[ARMV7M_EXCP_MEM].active) {
+ val |= (1 << 0);
+ }
+ if (s->sec_vectors[ARMV7M_EXCP_HARD].active) {
+ val |= (1 << 2);
+ }
+ if (s->sec_vectors[ARMV7M_EXCP_USAGE].active) {
+ val |= (1 << 3);
+ }
+ if (s->sec_vectors[ARMV7M_EXCP_SVC].active) {
+ val |= (1 << 7);
+ }
+ if (s->sec_vectors[ARMV7M_EXCP_PENDSV].active) {
+ val |= (1 << 10);
+ }
+ if (s->sec_vectors[ARMV7M_EXCP_SYSTICK].active) {
+ val |= (1 << 11);
+ }
+ if (s->sec_vectors[ARMV7M_EXCP_USAGE].pending) {
+ val |= (1 << 12);
+ }
+ if (s->sec_vectors[ARMV7M_EXCP_MEM].pending) {
+ val |= (1 << 13);
+ }
+ if (s->sec_vectors[ARMV7M_EXCP_SVC].pending) {
+ val |= (1 << 15);
+ }
+ if (s->sec_vectors[ARMV7M_EXCP_MEM].enabled) {
+ val |= (1 << 16);
+ }
+ if (s->sec_vectors[ARMV7M_EXCP_USAGE].enabled) {
+ val |= (1 << 18);
+ }
+ if (s->sec_vectors[ARMV7M_EXCP_HARD].pending) {
+ val |= (1 << 21);
+ }
+ /* SecureFault is not banked but is always RAZ/WI to NS */
+ if (s->vectors[ARMV7M_EXCP_SECURE].active) {
+ val |= (1 << 4);
+ }
+ if (s->vectors[ARMV7M_EXCP_SECURE].enabled) {
+ val |= (1 << 19);
+ }
+ if (s->vectors[ARMV7M_EXCP_SECURE].pending) {
+ val |= (1 << 20);
+ }
+ } else {
+ if (s->vectors[ARMV7M_EXCP_MEM].active) {
+ val |= (1 << 0);
+ }
+ if (arm_feature(&cpu->env, ARM_FEATURE_V8)) {
+ /* HARDFAULTACT, HARDFAULTPENDED not present in v7M */
+ if (s->vectors[ARMV7M_EXCP_HARD].active) {
+ val |= (1 << 2);
+ }
+ if (s->vectors[ARMV7M_EXCP_HARD].pending) {
+ val |= (1 << 21);
+ }
+ }
+ if (s->vectors[ARMV7M_EXCP_USAGE].active) {
+ val |= (1 << 3);
+ }
+ if (s->vectors[ARMV7M_EXCP_SVC].active) {
+ val |= (1 << 7);
+ }
+ if (s->vectors[ARMV7M_EXCP_PENDSV].active) {
+ val |= (1 << 10);
+ }
+ if (s->vectors[ARMV7M_EXCP_SYSTICK].active) {
+ val |= (1 << 11);
+ }
+ if (s->vectors[ARMV7M_EXCP_USAGE].pending) {
+ val |= (1 << 12);
+ }
+ if (s->vectors[ARMV7M_EXCP_MEM].pending) {
+ val |= (1 << 13);
+ }
+ if (s->vectors[ARMV7M_EXCP_SVC].pending) {
+ val |= (1 << 15);
+ }
+ if (s->vectors[ARMV7M_EXCP_MEM].enabled) {
+ val |= (1 << 16);
+ }
+ if (s->vectors[ARMV7M_EXCP_USAGE].enabled) {
+ val |= (1 << 18);
+ }
}
- if (s->vectors[ARMV7M_EXCP_SVC].active) {
- val |= (1 << 7);
+ if (attrs.secure || (cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK)) {
+ if (s->vectors[ARMV7M_EXCP_BUS].active) {
+ val |= (1 << 1);
+ }
+ if (s->vectors[ARMV7M_EXCP_BUS].pending) {
+ val |= (1 << 14);
+ }
+ if (s->vectors[ARMV7M_EXCP_BUS].enabled) {
+ val |= (1 << 17);
+ }
+ if (arm_feature(&cpu->env, ARM_FEATURE_V8) &&
+ s->vectors[ARMV7M_EXCP_NMI].active) {
+ /* NMIACT is not present in v7M */
+ val |= (1 << 5);
+ }
}
+
+ /* TODO: this is RAZ/WI from NS if DEMCR.SDME is set */
if (s->vectors[ARMV7M_EXCP_DEBUG].active) {
val |= (1 << 8);
}
- if (s->vectors[ARMV7M_EXCP_PENDSV].active) {
- val |= (1 << 10);
- }
- if (s->vectors[ARMV7M_EXCP_SYSTICK].active) {
- val |= (1 << 11);
- }
- if (s->vectors[ARMV7M_EXCP_USAGE].pending) {
- val |= (1 << 12);
- }
- if (s->vectors[ARMV7M_EXCP_MEM].pending) {
- val |= (1 << 13);
- }
- if (s->vectors[ARMV7M_EXCP_BUS].pending) {
- val |= (1 << 14);
- }
- if (s->vectors[ARMV7M_EXCP_SVC].pending) {
- val |= (1 << 15);
- }
- if (s->vectors[ARMV7M_EXCP_MEM].enabled) {
- val |= (1 << 16);
- }
- if (s->vectors[ARMV7M_EXCP_BUS].enabled) {
- val |= (1 << 17);
- }
- if (s->vectors[ARMV7M_EXCP_USAGE].enabled) {
- val |= (1 << 18);
- }
return val;
case 0xd28: /* Configurable Fault Status. */
/* The BFSR bits [15:8] are shared between security states
@@ -640,40 +1030,87 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value,
ARMCPU *cpu = s->cpu;
switch (offset) {
- case 0xd04: /* Interrupt Control State. */
- if (value & (1 << 31)) {
- armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI);
+ case 0x380 ... 0x3bf: /* NVIC_ITNS<n> */
+ {
+ int startvec = 32 * (offset - 0x380) + NVIC_FIRST_IRQ;
+ int i;
+
+ if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
+ goto bad_offset;
+ }
+ if (!attrs.secure) {
+ break;
+ }
+ for (i = 0; i < 32 && startvec + i < s->num_irq; i++) {
+ s->itns[startvec + i] = (value >> i) & 1;
+ }
+ nvic_irq_update(s);
+ break;
+ }
+ case 0xd04: /* Interrupt Control State (ICSR) */
+ if (cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK) {
+ if (value & (1 << 31)) {
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI, false);
+ } else if (value & (1 << 30) &&
+ arm_feature(&cpu->env, ARM_FEATURE_V8)) {
+ /* PENDNMICLR didn't exist in v7M */
+ armv7m_nvic_clear_pending(s, ARMV7M_EXCP_NMI, false);
+ }
}
if (value & (1 << 28)) {
- armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV);
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV, attrs.secure);
} else if (value & (1 << 27)) {
- armv7m_nvic_clear_pending(s, ARMV7M_EXCP_PENDSV);
+ armv7m_nvic_clear_pending(s, ARMV7M_EXCP_PENDSV, attrs.secure);
}
if (value & (1 << 26)) {
- armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK, attrs.secure);
} else if (value & (1 << 25)) {
- armv7m_nvic_clear_pending(s, ARMV7M_EXCP_SYSTICK);
+ armv7m_nvic_clear_pending(s, ARMV7M_EXCP_SYSTICK, attrs.secure);
}
break;
case 0xd08: /* Vector Table Offset. */
cpu->env.v7m.vecbase[attrs.secure] = value & 0xffffff80;
break;
- case 0xd0c: /* Application Interrupt/Reset Control. */
- if ((value >> 16) == 0x05fa) {
- if (value & 4) {
- qemu_irq_pulse(s->sysresetreq);
+ case 0xd0c: /* Application Interrupt/Reset Control (AIRCR) */
+ if ((value >> R_V7M_AIRCR_VECTKEY_SHIFT) == 0x05fa) {
+ if (value & R_V7M_AIRCR_SYSRESETREQ_MASK) {
+ if (attrs.secure ||
+ !(cpu->env.v7m.aircr & R_V7M_AIRCR_SYSRESETREQS_MASK)) {
+ qemu_irq_pulse(s->sysresetreq);
+ }
}
- if (value & 2) {
+ if (value & R_V7M_AIRCR_VECTCLRACTIVE_MASK) {
qemu_log_mask(LOG_GUEST_ERROR,
"Setting VECTCLRACTIVE when not in DEBUG mode "
"is UNPREDICTABLE\n");
}
- if (value & 1) {
+ if (value & R_V7M_AIRCR_VECTRESET_MASK) {
+ /* NB: this bit is RES0 in v8M */
qemu_log_mask(LOG_GUEST_ERROR,
"Setting VECTRESET when not in DEBUG mode "
"is UNPREDICTABLE\n");
}
- s->prigroup = extract32(value, 8, 3);
+ s->prigroup[attrs.secure] = extract32(value,
+ R_V7M_AIRCR_PRIGROUP_SHIFT,
+ R_V7M_AIRCR_PRIGROUP_LENGTH);
+ if (attrs.secure) {
+ /* These bits are only writable by secure */
+ cpu->env.v7m.aircr = value &
+ (R_V7M_AIRCR_SYSRESETREQS_MASK |
+ R_V7M_AIRCR_BFHFNMINS_MASK |
+ R_V7M_AIRCR_PRIS_MASK);
+ /* BFHFNMINS changes the priority of Secure HardFault, and
+ * allows a pending Non-secure HardFault to preempt (which
+ * we implement by marking it enabled).
+ */
+ if (cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK) {
+ s->sec_vectors[ARMV7M_EXCP_HARD].prio = -3;
+ s->vectors[ARMV7M_EXCP_HARD].enabled = 1;
+ } else {
+ s->sec_vectors[ARMV7M_EXCP_HARD].prio = -1;
+ s->vectors[ARMV7M_EXCP_HARD].enabled = 0;
+ }
+ }
nvic_irq_update(s);
}
break;
@@ -705,21 +1142,71 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value,
cpu->env.v7m.ccr[attrs.secure] = value;
break;
- case 0xd24: /* System Handler Control. */
- s->vectors[ARMV7M_EXCP_MEM].active = (value & (1 << 0)) != 0;
- s->vectors[ARMV7M_EXCP_BUS].active = (value & (1 << 1)) != 0;
- s->vectors[ARMV7M_EXCP_USAGE].active = (value & (1 << 3)) != 0;
- s->vectors[ARMV7M_EXCP_SVC].active = (value & (1 << 7)) != 0;
+ case 0xd24: /* System Handler Control and State (SHCSR) */
+ if (attrs.secure) {
+ s->sec_vectors[ARMV7M_EXCP_MEM].active = (value & (1 << 0)) != 0;
+ /* Secure HardFault active bit cannot be written */
+ s->sec_vectors[ARMV7M_EXCP_USAGE].active = (value & (1 << 3)) != 0;
+ s->sec_vectors[ARMV7M_EXCP_SVC].active = (value & (1 << 7)) != 0;
+ s->sec_vectors[ARMV7M_EXCP_PENDSV].active =
+ (value & (1 << 10)) != 0;
+ s->sec_vectors[ARMV7M_EXCP_SYSTICK].active =
+ (value & (1 << 11)) != 0;
+ s->sec_vectors[ARMV7M_EXCP_USAGE].pending =
+ (value & (1 << 12)) != 0;
+ s->sec_vectors[ARMV7M_EXCP_MEM].pending = (value & (1 << 13)) != 0;
+ s->sec_vectors[ARMV7M_EXCP_SVC].pending = (value & (1 << 15)) != 0;
+ s->sec_vectors[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
+ s->sec_vectors[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
+ s->sec_vectors[ARMV7M_EXCP_USAGE].enabled =
+ (value & (1 << 18)) != 0;
+ /* SecureFault not banked, but RAZ/WI to NS */
+ s->vectors[ARMV7M_EXCP_SECURE].active = (value & (1 << 4)) != 0;
+ s->vectors[ARMV7M_EXCP_SECURE].enabled = (value & (1 << 19)) != 0;
+ s->vectors[ARMV7M_EXCP_SECURE].pending = (value & (1 << 20)) != 0;
+ } else {
+ s->vectors[ARMV7M_EXCP_MEM].active = (value & (1 << 0)) != 0;
+ if (arm_feature(&cpu->env, ARM_FEATURE_V8)) {
+ /* HARDFAULTPENDED is not present in v7M */
+ s->vectors[ARMV7M_EXCP_HARD].pending = (value & (1 << 21)) != 0;
+ }
+ s->vectors[ARMV7M_EXCP_USAGE].active = (value & (1 << 3)) != 0;
+ s->vectors[ARMV7M_EXCP_SVC].active = (value & (1 << 7)) != 0;
+ s->vectors[ARMV7M_EXCP_PENDSV].active = (value & (1 << 10)) != 0;
+ s->vectors[ARMV7M_EXCP_SYSTICK].active = (value & (1 << 11)) != 0;
+ s->vectors[ARMV7M_EXCP_USAGE].pending = (value & (1 << 12)) != 0;
+ s->vectors[ARMV7M_EXCP_MEM].pending = (value & (1 << 13)) != 0;
+ s->vectors[ARMV7M_EXCP_SVC].pending = (value & (1 << 15)) != 0;
+ s->vectors[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
+ s->vectors[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
+ }
+ if (attrs.secure || (cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK)) {
+ s->vectors[ARMV7M_EXCP_BUS].active = (value & (1 << 1)) != 0;
+ s->vectors[ARMV7M_EXCP_BUS].pending = (value & (1 << 14)) != 0;
+ s->vectors[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
+ }
+ /* NMIACT can only be written if the write is of a zero, with
+ * BFHFNMINS 1, and by the CPU in secure state via the NS alias.
+ */
+ if (!attrs.secure && cpu->env.v7m.secure &&
+ (cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK) &&
+ (value & (1 << 5)) == 0) {
+ s->vectors[ARMV7M_EXCP_NMI].active = 0;
+ }
+ /* HARDFAULTACT can only be written if the write is of a zero
+ * to the non-secure HardFault state by the CPU in secure state.
+ * The only case where we can be targeting the non-secure HF state
+ * when in secure state is if this is a write via the NS alias
+ * and BFHFNMINS is 1.
+ */
+ if (!attrs.secure && cpu->env.v7m.secure &&
+ (cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK) &&
+ (value & (1 << 2)) == 0) {
+ s->vectors[ARMV7M_EXCP_HARD].active = 0;
+ }
+
+ /* TODO: this is RAZ/WI from NS if DEMCR.SDME is set */
s->vectors[ARMV7M_EXCP_DEBUG].active = (value & (1 << 8)) != 0;
- s->vectors[ARMV7M_EXCP_PENDSV].active = (value & (1 << 10)) != 0;
- s->vectors[ARMV7M_EXCP_SYSTICK].active = (value & (1 << 11)) != 0;
- s->vectors[ARMV7M_EXCP_USAGE].pending = (value & (1 << 12)) != 0;
- s->vectors[ARMV7M_EXCP_MEM].pending = (value & (1 << 13)) != 0;
- s->vectors[ARMV7M_EXCP_BUS].pending = (value & (1 << 14)) != 0;
- s->vectors[ARMV7M_EXCP_SVC].pending = (value & (1 << 15)) != 0;
- s->vectors[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
- s->vectors[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
- s->vectors[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
nvic_irq_update(s);
break;
case 0xd28: /* Configurable Fault Status. */
@@ -885,7 +1372,7 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value,
{
int excnum = (value & 0x1ff) + NVIC_FIRST_IRQ;
if (excnum < s->num_irq) {
- armv7m_nvic_set_pending(s, excnum);
+ armv7m_nvic_set_pending(s, excnum, false);
}
break;
}
@@ -911,6 +1398,47 @@ static bool nvic_user_access_ok(NVICState *s, hwaddr offset, MemTxAttrs attrs)
}
}
+static int shpr_bank(NVICState *s, int exc, MemTxAttrs attrs)
+{
+ /* Behaviour for the SHPR register field for this exception:
+ * return M_REG_NS to use the nonsecure vector (including for
+ * non-banked exceptions), M_REG_S for the secure version of
+ * a banked exception, and -1 if this field should RAZ/WI.
+ */
+ switch (exc) {
+ case ARMV7M_EXCP_MEM:
+ case ARMV7M_EXCP_USAGE:
+ case ARMV7M_EXCP_SVC:
+ case ARMV7M_EXCP_PENDSV:
+ case ARMV7M_EXCP_SYSTICK:
+ /* Banked exceptions */
+ return attrs.secure;
+ case ARMV7M_EXCP_BUS:
+ /* Not banked, RAZ/WI from nonsecure if BFHFNMINS is zero */
+ if (!attrs.secure &&
+ !(s->cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK)) {
+ return -1;
+ }
+ return M_REG_NS;
+ case ARMV7M_EXCP_SECURE:
+ /* Not banked, RAZ/WI from nonsecure */
+ if (!attrs.secure) {
+ return -1;
+ }
+ return M_REG_NS;
+ case ARMV7M_EXCP_DEBUG:
+ /* Not banked. TODO should RAZ/WI if DEMCR.SDME is set */
+ return M_REG_NS;
+ case 8 ... 10:
+ case 13:
+ /* RES0 */
+ return -1;
+ default:
+ /* Not reachable due to decode of SHPR register addresses */
+ g_assert_not_reached();
+ }
+}
+
static MemTxResult nvic_sysreg_read(void *opaque, hwaddr addr,
uint64_t *data, unsigned size,
MemTxAttrs attrs)
@@ -935,7 +1463,8 @@ static MemTxResult nvic_sysreg_read(void *opaque, hwaddr addr,
startvec = offset - 0x180 + NVIC_FIRST_IRQ; /* vector # */
for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) {
- if (s->vectors[startvec + i].enabled) {
+ if (s->vectors[startvec + i].enabled &&
+ (attrs.secure || s->itns[startvec + i])) {
val |= (1 << i);
}
}
@@ -947,7 +1476,8 @@ static MemTxResult nvic_sysreg_read(void *opaque, hwaddr addr,
val = 0;
startvec = offset - 0x280 + NVIC_FIRST_IRQ; /* vector # */
for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) {
- if (s->vectors[startvec + i].pending) {
+ if (s->vectors[startvec + i].pending &&
+ (attrs.secure || s->itns[startvec + i])) {
val |= (1 << i);
}
}
@@ -957,7 +1487,8 @@ static MemTxResult nvic_sysreg_read(void *opaque, hwaddr addr,
startvec = offset - 0x300 + NVIC_FIRST_IRQ; /* vector # */
for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) {
- if (s->vectors[startvec + i].active) {
+ if (s->vectors[startvec + i].active &&
+ (attrs.secure || s->itns[startvec + i])) {
val |= (1 << i);
}
}
@@ -967,13 +1498,21 @@ static MemTxResult nvic_sysreg_read(void *opaque, hwaddr addr,
startvec = offset - 0x400 + NVIC_FIRST_IRQ; /* vector # */
for (i = 0; i < size && startvec + i < s->num_irq; i++) {
- val |= s->vectors[startvec + i].prio << (8 * i);
+ if (attrs.secure || s->itns[startvec + i]) {
+ val |= s->vectors[startvec + i].prio << (8 * i);
+ }
}
break;
- case 0xd18 ... 0xd23: /* System Handler Priority. */
+ case 0xd18 ... 0xd23: /* System Handler Priority (SHPR1, SHPR2, SHPR3) */
val = 0;
for (i = 0; i < size; i++) {
- val |= s->vectors[(offset - 0xd14) + i].prio << (i * 8);
+ unsigned hdlidx = (offset - 0xd14) + i;
+ int sbank = shpr_bank(s, hdlidx, attrs);
+
+ if (sbank < 0) {
+ continue;
+ }
+ val = deposit32(val, i * 8, 8, get_prio(s, hdlidx, sbank));
}
break;
case 0xfe0 ... 0xfff: /* ID. */
@@ -1024,7 +1563,8 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr,
startvec = 8 * (offset - 0x180) + NVIC_FIRST_IRQ;
for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) {
- if (value & (1 << i)) {
+ if (value & (1 << i) &&
+ (attrs.secure || s->itns[startvec + i])) {
s->vectors[startvec + i].enabled = setval;
}
}
@@ -1041,7 +1581,8 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr,
startvec = 8 * (offset - 0x280) + NVIC_FIRST_IRQ; /* vector # */
for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) {
- if (value & (1 << i)) {
+ if (value & (1 << i) &&
+ (attrs.secure || s->itns[startvec + i])) {
s->vectors[startvec + i].pending = setval;
}
}
@@ -1053,14 +1594,22 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr,
startvec = 8 * (offset - 0x400) + NVIC_FIRST_IRQ; /* vector # */
for (i = 0; i < size && startvec + i < s->num_irq; i++) {
- set_prio(s, startvec + i, (value >> (i * 8)) & 0xff);
+ if (attrs.secure || s->itns[startvec + i]) {
+ set_prio(s, startvec + i, false, (value >> (i * 8)) & 0xff);
+ }
}
nvic_irq_update(s);
return MEMTX_OK;
- case 0xd18 ... 0xd23: /* System Handler Priority. */
+ case 0xd18 ... 0xd23: /* System Handler Priority (SHPR1, SHPR2, SHPR3) */
for (i = 0; i < size; i++) {
unsigned hdlidx = (offset - 0xd14) + i;
- set_prio(s, hdlidx, (value >> (i * 8)) & 0xff);
+ int newprio = extract32(value, i * 8, 8);
+ int sbank = shpr_bank(s, hdlidx, attrs);
+
+ if (sbank < 0) {
+ continue;
+ }
+ set_prio(s, hdlidx, sbank, newprio);
}
nvic_irq_update(s);
return MEMTX_OK;
@@ -1126,9 +1675,12 @@ static int nvic_post_load(void *opaque, int version_id)
{
NVICState *s = opaque;
unsigned i;
+ int resetprio;
/* Check for out of range priority settings */
- if (s->vectors[ARMV7M_EXCP_RESET].prio != -3 ||
+ resetprio = arm_feature(&s->cpu->env, ARM_FEATURE_V8) ? -4 : -3;
+
+ if (s->vectors[ARMV7M_EXCP_RESET].prio != resetprio ||
s->vectors[ARMV7M_EXCP_NMI].prio != -2 ||
s->vectors[ARMV7M_EXCP_HARD].prio != -1) {
return 1;
@@ -1158,6 +1710,50 @@ static const VMStateDescription vmstate_VecInfo = {
}
};
+static bool nvic_security_needed(void *opaque)
+{
+ NVICState *s = opaque;
+
+ return arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY);
+}
+
+static int nvic_security_post_load(void *opaque, int version_id)
+{
+ NVICState *s = opaque;
+ int i;
+
+ /* Check for out of range priority settings */
+ if (s->sec_vectors[ARMV7M_EXCP_HARD].prio != -1
+ && s->sec_vectors[ARMV7M_EXCP_HARD].prio != -3) {
+ /* We can't cross-check against AIRCR.BFHFNMINS as we don't know
+ * if the CPU state has been migrated yet; a mismatch won't
+ * cause the emulation to blow up, though.
+ */
+ return 1;
+ }
+ for (i = ARMV7M_EXCP_MEM; i < ARRAY_SIZE(s->sec_vectors); i++) {
+ if (s->sec_vectors[i].prio & ~0xff) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_nvic_security = {
+ .name = "nvic/m-security",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = nvic_security_needed,
+ .post_load = &nvic_security_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(sec_vectors, NVICState, NVIC_INTERNAL_VECTORS, 1,
+ vmstate_VecInfo, VecInfo),
+ VMSTATE_UINT32(prigroup[M_REG_S], NVICState),
+ VMSTATE_BOOL_ARRAY(itns, NVICState, NVIC_MAX_VECTORS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_nvic = {
.name = "armv7m_nvic",
.version_id = 4,
@@ -1166,8 +1762,12 @@ static const VMStateDescription vmstate_nvic = {
.fields = (VMStateField[]) {
VMSTATE_STRUCT_ARRAY(vectors, NVICState, NVIC_MAX_VECTORS, 1,
vmstate_VecInfo, VecInfo),
- VMSTATE_UINT32(prigroup, NVICState),
+ VMSTATE_UINT32(prigroup[M_REG_NS], NVICState),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &vmstate_nvic_security,
+ NULL
}
};
@@ -1179,10 +1779,10 @@ static Property props_nvic[] = {
static void armv7m_nvic_reset(DeviceState *dev)
{
+ int resetprio;
NVICState *s = NVIC(dev);
s->vectors[ARMV7M_EXCP_NMI].enabled = 1;
- s->vectors[ARMV7M_EXCP_HARD].enabled = 1;
/* MEM, BUS, and USAGE are enabled through
* the System Handler Control register
*/
@@ -1191,10 +1791,25 @@ static void armv7m_nvic_reset(DeviceState *dev)
s->vectors[ARMV7M_EXCP_PENDSV].enabled = 1;
s->vectors[ARMV7M_EXCP_SYSTICK].enabled = 1;
- s->vectors[ARMV7M_EXCP_RESET].prio = -3;
+ resetprio = arm_feature(&s->cpu->env, ARM_FEATURE_V8) ? -4 : -3;
+ s->vectors[ARMV7M_EXCP_RESET].prio = resetprio;
s->vectors[ARMV7M_EXCP_NMI].prio = -2;
s->vectors[ARMV7M_EXCP_HARD].prio = -1;
+ if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY)) {
+ s->sec_vectors[ARMV7M_EXCP_HARD].enabled = 1;
+ s->sec_vectors[ARMV7M_EXCP_SVC].enabled = 1;
+ s->sec_vectors[ARMV7M_EXCP_PENDSV].enabled = 1;
+ s->sec_vectors[ARMV7M_EXCP_SYSTICK].enabled = 1;
+
+ /* AIRCR.BFHFNMINS resets to 0 so Secure HF is priority -1 (R_CMTC) */
+ s->sec_vectors[ARMV7M_EXCP_HARD].prio = -1;
+ /* If AIRCR.BFHFNMINS is 0 then NS HF is (effectively) disabled */
+ s->vectors[ARMV7M_EXCP_HARD].enabled = 0;
+ } else {
+ s->vectors[ARMV7M_EXCP_HARD].enabled = 1;
+ }
+
/* Strictly speaking the reset handler should be enabled.
* However, we don't simulate soft resets through the NVIC,
* and the reset vector should never be pended.
@@ -1203,6 +1818,22 @@ static void armv7m_nvic_reset(DeviceState *dev)
s->exception_prio = NVIC_NOEXC_PRIO;
s->vectpending = 0;
+ s->vectpending_is_s_banked = false;
+ s->vectpending_prio = NVIC_NOEXC_PRIO;
+
+ if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY)) {
+ memset(s->itns, 0, sizeof(s->itns));
+ } else {
+ /* This state is constant and not guest accessible in a non-security
+ * NVIC; we set the bits to true to avoid having to do a feature
+ * bit check in the NVIC enable/pend/etc register accessors.
+ */
+ int i;
+
+ for (i = NVIC_FIRST_IRQ; i < ARRAY_SIZE(s->itns); i++) {
+ s->itns[i] = true;
+ }
+ }
}
static void nvic_systick_trigger(void *opaque, int n, int level)
@@ -1213,8 +1844,10 @@ static void nvic_systick_trigger(void *opaque, int n, int level)
/* SysTick just asked us to pend its exception.
* (This is different from an external interrupt line's
* behaviour.)
+ * TODO: when we implement the banked systicks we must make
+ * this pend the correct banked exception.
*/
- armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK, false);
}
}
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 4762329482..b86f242b0f 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -167,16 +167,17 @@ gicv3_redist_set_irq(uint32_t cpu, int irq, int level) "GICv3 redistributor 0x%x
gicv3_redist_send_sgi(uint32_t cpu, int irq) "GICv3 redistributor 0x%x pending SGI %d"
# hw/intc/armv7m_nvic.c
-nvic_recompute_state(int vectpending, int exception_prio) "NVIC state recomputed: vectpending %d exception_prio %d"
-nvic_set_prio(int irq, uint8_t prio) "NVIC set irq %d priority %d"
+nvic_recompute_state(int vectpending, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d vectpending_prio %d exception_prio %d"
+nvic_recompute_state_secure(int vectpending, bool vectpending_is_s_banked, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d is_s_banked %d vectpending_prio %d exception_prio %d"
+nvic_set_prio(int irq, bool secure, uint8_t prio) "NVIC set irq %d secure-bank %d priority %d"
nvic_irq_update(int vectpending, int pendprio, int exception_prio, int level) "NVIC vectpending %d pending prio %d exception_prio %d: setting irq line to %d"
nvic_escalate_prio(int irq, int irqprio, int runprio) "NVIC escalating irq %d to HardFault: insufficient priority %d >= %d"
nvic_escalate_disabled(int irq) "NVIC escalating irq %d to HardFault: disabled"
-nvic_set_pending(int irq, int en, int prio) "NVIC set pending irq %d (enabled: %d priority %d)"
-nvic_clear_pending(int irq, int en, int prio) "NVIC clear pending irq %d (enabled: %d priority %d)"
+nvic_set_pending(int irq, bool secure, int en, int prio) "NVIC set pending irq %d secure-bank %d (enabled: %d priority %d)"
+nvic_clear_pending(int irq, bool secure, int en, int prio) "NVIC clear pending irq %d secure-bank %d (enabled: %d priority %d)"
nvic_set_pending_level(int irq) "NVIC set pending: irq %d higher prio than vectpending: setting irq line to 1"
-nvic_acknowledge_irq(int irq, int prio) "NVIC acknowledge IRQ: %d now active (prio %d)"
-nvic_complete_irq(int irq) "NVIC complete IRQ %d"
+nvic_acknowledge_irq(int irq, int prio, bool targets_secure) "NVIC acknowledge IRQ: %d now active (prio %d targets_secure %d)"
+nvic_complete_irq(int irq, bool secure) "NVIC complete IRQ %d (secure %d)"
nvic_set_irq_level(int irq, int level) "NVIC external irq %d level set to %d"
nvic_sysreg_read(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
nvic_sysreg_write(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
diff --git a/hw/mips/Makefile.objs b/hw/mips/Makefile.objs
index 48cd2ef50e..17a311aaba 100644
--- a/hw/mips/Makefile.objs
+++ b/hw/mips/Makefile.objs
@@ -1,5 +1,5 @@
obj-y += mips_r4k.o mips_malta.o mips_mipssim.o
-obj-y += addr.o cputimer.o mips_int.o
+obj-y += addr.o mips_int.o
obj-$(CONFIG_JAZZ) += mips_jazz.o
obj-$(CONFIG_FULONG) += mips_fulong2e.o
obj-y += gt64xxx_pci.o
diff --git a/hw/mips/cps.c b/hw/mips/cps.c
index 79d4c5e30a..fe5c630af6 100644
--- a/hw/mips/cps.c
+++ b/hw/mips/cps.c
@@ -71,7 +71,7 @@ static void mips_cps_realize(DeviceState *dev, Error **errp)
bool itu_present = false;
for (i = 0; i < s->num_vp; i++) {
- cpu = cpu_mips_init(s->cpu_model);
+ cpu = MIPS_CPU(cpu_generic_init(TYPE_MIPS_CPU, s->cpu_model));
/* Init internal devices */
cpu_mips_irq_init_cpu(cpu);
diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c
index 439a3d7a66..75318680e1 100644
--- a/hw/mips/mips_fulong2e.c
+++ b/hw/mips/mips_fulong2e.c
@@ -280,7 +280,7 @@ static void mips_fulong2e_init(MachineState *machine)
if (cpu_model == NULL) {
cpu_model = "Loongson-2E";
}
- cpu = cpu_mips_init(cpu_model);
+ cpu = MIPS_CPU(cpu_generic_init(TYPE_MIPS_CPU, cpu_model));
env = &cpu->env;
qemu_register_reset(main_cpu_reset, cpu);
diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c
index ae10670efd..7e6626dc88 100644
--- a/hw/mips/mips_jazz.c
+++ b/hw/mips/mips_jazz.c
@@ -151,7 +151,7 @@ static void mips_jazz_init(MachineState *machine,
if (cpu_model == NULL) {
cpu_model = "R4000";
}
- cpu = cpu_mips_init(cpu_model);
+ cpu = MIPS_CPU(cpu_generic_init(TYPE_MIPS_CPU, cpu_model));
env = &cpu->env;
qemu_register_reset(main_cpu_reset, cpu);
diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c
index e87cd3230b..2adb9bcf89 100644
--- a/hw/mips/mips_malta.c
+++ b/hw/mips/mips_malta.c
@@ -931,7 +931,7 @@ static void create_cpu_without_cps(const char *cpu_model,
int i;
for (i = 0; i < smp_cpus; i++) {
- cpu = cpu_mips_init(cpu_model);
+ cpu = MIPS_CPU(cpu_generic_init(TYPE_MIPS_CPU, cpu_model));
/* Init internal devices */
cpu_mips_irq_init_cpu(cpu);
diff --git a/hw/mips/mips_mipssim.c b/hw/mips/mips_mipssim.c
index 49cd38d680..a092072e2a 100644
--- a/hw/mips/mips_mipssim.c
+++ b/hw/mips/mips_mipssim.c
@@ -163,7 +163,7 @@ mips_mipssim_init(MachineState *machine)
cpu_model = "24Kf";
#endif
}
- cpu = cpu_mips_init(cpu_model);
+ cpu = MIPS_CPU(cpu_generic_init(TYPE_MIPS_CPU, cpu_model));
env = &cpu->env;
reset_info = g_malloc0(sizeof(ResetData));
diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c
index 7efee94431..1272d4ef9d 100644
--- a/hw/mips/mips_r4k.c
+++ b/hw/mips/mips_r4k.c
@@ -193,7 +193,7 @@ void mips_r4k_init(MachineState *machine)
cpu_model = "24Kf";
#endif
}
- cpu = cpu_mips_init(cpu_model);
+ cpu = MIPS_CPU(cpu_generic_init(TYPE_MIPS_CPU, cpu_model));
env = &cpu->env;
reset_info = g_malloc0(sizeof(ResetData));
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 29fb922cef..e8f0a02f35 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -59,3 +59,4 @@ obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
obj-$(CONFIG_AUX) += auxbus.o
obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o
obj-y += mmio_interface.o
+obj-$(CONFIG_MSF2) += msf2-sysreg.o
diff --git a/hw/misc/msf2-sysreg.c b/hw/misc/msf2-sysreg.c
new file mode 100644
index 0000000000..6eb501104b
--- /dev/null
+++ b/hw/misc/msf2-sysreg.c
@@ -0,0 +1,160 @@
+/*
+ * System Register block model of Microsemi SmartFusion2.
+ *
+ * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "hw/misc/msf2-sysreg.h"
+#include "qemu/error-report.h"
+#include "trace.h"
+
+static inline int msf2_divbits(uint32_t div)
+{
+ int r = ctz32(div);
+
+ return (div < 8) ? r : r + 1;
+}
+
+static void msf2_sysreg_reset(DeviceState *d)
+{
+ MSF2SysregState *s = MSF2_SYSREG(d);
+
+ s->regs[MSSDDR_PLL_STATUS_LOW_CR] = 0x021A2358;
+ s->regs[MSSDDR_PLL_STATUS] = 0x3;
+ s->regs[MSSDDR_FACC1_CR] = msf2_divbits(s->apb0div) << 5 |
+ msf2_divbits(s->apb1div) << 2;
+}
+
+static uint64_t msf2_sysreg_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ MSF2SysregState *s = opaque;
+ uint32_t ret = 0;
+
+ offset >>= 2;
+ if (offset < ARRAY_SIZE(s->regs)) {
+ ret = s->regs[offset];
+ trace_msf2_sysreg_read(offset << 2, ret);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%08" HWADDR_PRIx "\n", __func__,
+ offset << 2);
+ }
+
+ return ret;
+}
+
+static void msf2_sysreg_write(void *opaque, hwaddr offset,
+ uint64_t val, unsigned size)
+{
+ MSF2SysregState *s = opaque;
+ uint32_t newval = val;
+
+ offset >>= 2;
+
+ switch (offset) {
+ case MSSDDR_PLL_STATUS:
+ trace_msf2_sysreg_write_pll_status();
+ break;
+
+ case ESRAM_CR:
+ case DDR_CR:
+ case ENVM_REMAP_BASE_CR:
+ if (newval != s->regs[offset]) {
+ qemu_log_mask(LOG_UNIMP,
+ TYPE_MSF2_SYSREG": remapping not supported\n");
+ }
+ break;
+
+ default:
+ if (offset < ARRAY_SIZE(s->regs)) {
+ trace_msf2_sysreg_write(offset << 2, newval, s->regs[offset]);
+ s->regs[offset] = newval;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%08" HWADDR_PRIx "\n", __func__,
+ offset << 2);
+ }
+ break;
+ }
+}
+
+static const MemoryRegionOps sysreg_ops = {
+ .read = msf2_sysreg_read,
+ .write = msf2_sysreg_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void msf2_sysreg_init(Object *obj)
+{
+ MSF2SysregState *s = MSF2_SYSREG(obj);
+
+ memory_region_init_io(&s->iomem, obj, &sysreg_ops, s, TYPE_MSF2_SYSREG,
+ MSF2_SYSREG_MMIO_SIZE);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
+}
+
+static const VMStateDescription vmstate_msf2_sysreg = {
+ .name = TYPE_MSF2_SYSREG,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, MSF2SysregState, MSF2_SYSREG_MMIO_SIZE / 4),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property msf2_sysreg_properties[] = {
+ /* default divisors in Libero GUI */
+ DEFINE_PROP_UINT8("apb0divisor", MSF2SysregState, apb0div, 2),
+ DEFINE_PROP_UINT8("apb1divisor", MSF2SysregState, apb1div, 2),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void msf2_sysreg_realize(DeviceState *dev, Error **errp)
+{
+ MSF2SysregState *s = MSF2_SYSREG(dev);
+
+ if ((s->apb0div > 32 || !is_power_of_2(s->apb0div))
+ || (s->apb1div > 32 || !is_power_of_2(s->apb1div))) {
+ error_setg(errp, "Invalid apb divisor value");
+ error_append_hint(errp, "apb divisor must be a power of 2"
+ " and maximum value is 32\n");
+ }
+}
+
+static void msf2_sysreg_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_msf2_sysreg;
+ dc->reset = msf2_sysreg_reset;
+ dc->props = msf2_sysreg_properties;
+ dc->realize = msf2_sysreg_realize;
+}
+
+static const TypeInfo msf2_sysreg_info = {
+ .name = TYPE_MSF2_SYSREG,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .class_init = msf2_sysreg_class_init,
+ .instance_size = sizeof(MSF2SysregState),
+ .instance_init = msf2_sysreg_init,
+};
+
+static void msf2_sysreg_register_types(void)
+{
+ type_register_static(&msf2_sysreg_info);
+}
+
+type_init(msf2_sysreg_register_types)
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 3313585b12..616579a403 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -61,3 +61,8 @@ mps2_scc_reset(void) "MPS2 SCC: reset"
mps2_scc_leds(char led7, char led6, char led5, char led4, char led3, char led2, char led1, char led0) "MPS2 SCC LEDs: %c%c%c%c%c%c%c%c"
mps2_scc_cfg_write(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config write: function %d device %d data 0x%" PRIx32
mps2_scc_cfg_read(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config read: function %d device %d data 0x%" PRIx32
+
+# hw/misc/msf2-sysreg.c
+msf2_sysreg_write(uint64_t offset, uint32_t val, uint32_t prev) "msf2-sysreg write: addr 0x%08" HWADDR_PRIx " data 0x%" PRIx32 " prev 0x%" PRIx32
+msf2_sysreg_read(uint64_t offset, uint32_t val) "msf2-sysreg read: addr 0x%08" HWADDR_PRIx " data 0x%08" PRIx32
+msf2_sysreg_write_pll_status(void) "Invalid write to read only PLL status register"
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index 7e87d0176b..4171af0b5d 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -26,6 +26,7 @@ common-obj-$(CONFIG_IMX_FEC) += imx_fec.o
common-obj-$(CONFIG_CADENCE) += cadence_gem.o
common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o
common-obj-$(CONFIG_LANCE) += lance.o
+common-obj-$(CONFIG_SUNHME) += sunhme.o
common-obj-$(CONFIG_FTGMAC100) += ftgmac100.o
common-obj-$(CONFIG_SUNGEM) += sungem.o
diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c
new file mode 100644
index 0000000000..60277adcf1
--- /dev/null
+++ b/hw/net/sunhme.c
@@ -0,0 +1,978 @@
+/*
+ * QEMU Sun Happy Meal Ethernet emulation
+ *
+ * Copyright (c) 2017 Mark Cave-Ayland
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "hw/net/mii.h"
+#include "net/net.h"
+#include "net/checksum.h"
+#include "net/eth.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+
+#define HME_REG_SIZE 0x8000
+
+#define HME_SEB_REG_SIZE 0x2000
+
+#define HME_SEBI_RESET 0x0
+#define HME_SEB_RESET_ETX 0x1
+#define HME_SEB_RESET_ERX 0x2
+
+#define HME_SEBI_STAT 0x100
+#define HME_SEBI_STAT_LINUXBUG 0x108
+#define HME_SEB_STAT_RXTOHOST 0x10000
+#define HME_SEB_STAT_MIFIRQ 0x800000
+#define HME_SEB_STAT_HOSTTOTX 0x1000000
+#define HME_SEB_STAT_TXALL 0x2000000
+
+#define HME_SEBI_IMASK 0x104
+#define HME_SEBI_IMASK_LINUXBUG 0x10c
+
+#define HME_ETX_REG_SIZE 0x2000
+
+#define HME_ETXI_PENDING 0x0
+
+#define HME_ETXI_RING 0x8
+#define HME_ETXI_RING_ADDR 0xffffff00
+#define HME_ETXI_RING_OFFSET 0xff
+
+#define HME_ETXI_RSIZE 0x2c
+
+#define HME_ERX_REG_SIZE 0x2000
+
+#define HME_ERXI_CFG 0x0
+#define HME_ERX_CFG_RINGSIZE 0x600
+#define HME_ERX_CFG_RINGSIZE_SHIFT 9
+#define HME_ERX_CFG_BYTEOFFSET 0x38
+#define HME_ERX_CFG_BYTEOFFSET_SHIFT 3
+#define HME_ERX_CFG_CSUMSTART 0x7f0000
+#define HME_ERX_CFG_CSUMSHIFT 16
+
+#define HME_ERXI_RING 0x4
+#define HME_ERXI_RING_ADDR 0xffffff00
+#define HME_ERXI_RING_OFFSET 0xff
+
+#define HME_MAC_REG_SIZE 0x1000
+
+#define HME_MACI_TXCFG 0x20c
+#define HME_MAC_TXCFG_ENABLE 0x1
+
+#define HME_MACI_RXCFG 0x30c
+#define HME_MAC_RXCFG_ENABLE 0x1
+#define HME_MAC_RXCFG_PMISC 0x40
+#define HME_MAC_RXCFG_HENABLE 0x800
+
+#define HME_MACI_MACADDR2 0x318
+#define HME_MACI_MACADDR1 0x31c
+#define HME_MACI_MACADDR0 0x320
+
+#define HME_MACI_HASHTAB3 0x340
+#define HME_MACI_HASHTAB2 0x344
+#define HME_MACI_HASHTAB1 0x348
+#define HME_MACI_HASHTAB0 0x34c
+
+#define HME_MIF_REG_SIZE 0x20
+
+#define HME_MIFI_FO 0xc
+#define HME_MIF_FO_ST 0xc0000000
+#define HME_MIF_FO_ST_SHIFT 30
+#define HME_MIF_FO_OPC 0x30000000
+#define HME_MIF_FO_OPC_SHIFT 28
+#define HME_MIF_FO_PHYAD 0x0f800000
+#define HME_MIF_FO_PHYAD_SHIFT 23
+#define HME_MIF_FO_REGAD 0x007c0000
+#define HME_MIF_FO_REGAD_SHIFT 18
+#define HME_MIF_FO_TAMSB 0x20000
+#define HME_MIF_FO_TALSB 0x10000
+#define HME_MIF_FO_DATA 0xffff
+
+#define HME_MIFI_CFG 0x10
+#define HME_MIF_CFG_MDI0 0x100
+#define HME_MIF_CFG_MDI1 0x200
+
+#define HME_MIFI_IMASK 0x14
+
+#define HME_MIFI_STAT 0x18
+
+
+/* Wired HME PHY addresses */
+#define HME_PHYAD_INTERNAL 1
+#define HME_PHYAD_EXTERNAL 0
+
+#define MII_COMMAND_START 0x1
+#define MII_COMMAND_READ 0x2
+#define MII_COMMAND_WRITE 0x1
+
+#define TYPE_SUNHME "sunhme"
+#define SUNHME(obj) OBJECT_CHECK(SunHMEState, (obj), TYPE_SUNHME)
+
+/* Maximum size of buffer */
+#define HME_FIFO_SIZE 0x800
+
+/* Size of TX/RX descriptor */
+#define HME_DESC_SIZE 0x8
+
+#define HME_XD_OWN 0x80000000
+#define HME_XD_OFL 0x40000000
+#define HME_XD_SOP 0x40000000
+#define HME_XD_EOP 0x20000000
+#define HME_XD_RXLENMSK 0x3fff0000
+#define HME_XD_RXLENSHIFT 16
+#define HME_XD_RXCKSUM 0xffff
+#define HME_XD_TXLENMSK 0x00001fff
+#define HME_XD_TXCKSUM 0x10000000
+#define HME_XD_TXCSSTUFF 0xff00000
+#define HME_XD_TXCSSTUFFSHIFT 20
+#define HME_XD_TXCSSTART 0xfc000
+#define HME_XD_TXCSSTARTSHIFT 14
+
+#define HME_MII_REGS_SIZE 0x20
+
+typedef struct SunHMEState {
+ /*< private >*/
+ PCIDevice parent_obj;
+
+ NICState *nic;
+ NICConf conf;
+
+ MemoryRegion hme;
+ MemoryRegion sebreg;
+ MemoryRegion etxreg;
+ MemoryRegion erxreg;
+ MemoryRegion macreg;
+ MemoryRegion mifreg;
+
+ uint32_t sebregs[HME_SEB_REG_SIZE >> 2];
+ uint32_t etxregs[HME_ETX_REG_SIZE >> 2];
+ uint32_t erxregs[HME_ERX_REG_SIZE >> 2];
+ uint32_t macregs[HME_MAC_REG_SIZE >> 2];
+ uint32_t mifregs[HME_MIF_REG_SIZE >> 2];
+
+ uint16_t miiregs[HME_MII_REGS_SIZE];
+} SunHMEState;
+
+static Property sunhme_properties[] = {
+ DEFINE_NIC_PROPERTIES(SunHMEState, conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sunhme_reset_tx(SunHMEState *s)
+{
+ /* Indicate TX reset complete */
+ s->sebregs[HME_SEBI_RESET] &= ~HME_SEB_RESET_ETX;
+}
+
+static void sunhme_reset_rx(SunHMEState *s)
+{
+ /* Indicate RX reset complete */
+ s->sebregs[HME_SEBI_RESET] &= ~HME_SEB_RESET_ERX;
+}
+
+static void sunhme_update_irq(SunHMEState *s)
+{
+ PCIDevice *d = PCI_DEVICE(s);
+ int level;
+
+ /* MIF interrupt mask (16-bit) */
+ uint32_t mifmask = ~(s->mifregs[HME_MIFI_IMASK >> 2]) & 0xffff;
+ uint32_t mif = s->mifregs[HME_MIFI_STAT >> 2] & mifmask;
+
+ /* Main SEB interrupt mask (include MIF status from above) */
+ uint32_t sebmask = ~(s->sebregs[HME_SEBI_IMASK >> 2]) &
+ ~HME_SEB_STAT_MIFIRQ;
+ uint32_t seb = s->sebregs[HME_SEBI_STAT >> 2] & sebmask;
+ if (mif) {
+ seb |= HME_SEB_STAT_MIFIRQ;
+ }
+
+ level = (seb ? 1 : 0);
+ pci_set_irq(d, level);
+}
+
+static void sunhme_seb_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ SunHMEState *s = SUNHME(opaque);
+
+ trace_sunhme_seb_write(addr, val);
+
+ /* Handly buggy Linux drivers before 4.13 which have
+ the wrong offsets for HME_SEBI_STAT and HME_SEBI_IMASK */
+ switch (addr) {
+ case HME_SEBI_STAT_LINUXBUG:
+ addr = HME_SEBI_STAT;
+ break;
+ case HME_SEBI_IMASK_LINUXBUG:
+ addr = HME_SEBI_IMASK;
+ break;
+ default:
+ break;
+ }
+
+ switch (addr) {
+ case HME_SEBI_RESET:
+ if (val & HME_SEB_RESET_ETX) {
+ sunhme_reset_tx(s);
+ }
+ if (val & HME_SEB_RESET_ERX) {
+ sunhme_reset_rx(s);
+ }
+ val = s->sebregs[HME_SEBI_RESET >> 2];
+ break;
+ }
+
+ s->sebregs[addr >> 2] = val;
+}
+
+static uint64_t sunhme_seb_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ SunHMEState *s = SUNHME(opaque);
+ uint64_t val;
+
+ /* Handly buggy Linux drivers before 4.13 which have
+ the wrong offsets for HME_SEBI_STAT and HME_SEBI_IMASK */
+ switch (addr) {
+ case HME_SEBI_STAT_LINUXBUG:
+ addr = HME_SEBI_STAT;
+ break;
+ case HME_SEBI_IMASK_LINUXBUG:
+ addr = HME_SEBI_IMASK;
+ break;
+ default:
+ break;
+ }
+
+ val = s->sebregs[addr >> 2];
+
+ switch (addr) {
+ case HME_SEBI_STAT:
+ /* Autoclear status (except MIF) */
+ s->sebregs[HME_SEBI_STAT >> 2] &= HME_SEB_STAT_MIFIRQ;
+ sunhme_update_irq(s);
+ break;
+ }
+
+ trace_sunhme_seb_read(addr, val);
+
+ return val;
+}
+
+static const MemoryRegionOps sunhme_seb_ops = {
+ .read = sunhme_seb_read,
+ .write = sunhme_seb_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void sunhme_transmit(SunHMEState *s);
+
+static void sunhme_etx_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ SunHMEState *s = SUNHME(opaque);
+
+ trace_sunhme_etx_write(addr, val);
+
+ switch (addr) {
+ case HME_ETXI_PENDING:
+ if (val) {
+ sunhme_transmit(s);
+ }
+ break;
+ }
+
+ s->etxregs[addr >> 2] = val;
+}
+
+static uint64_t sunhme_etx_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ SunHMEState *s = SUNHME(opaque);
+ uint64_t val;
+
+ val = s->etxregs[addr >> 2];
+
+ trace_sunhme_etx_read(addr, val);
+
+ return val;
+}
+
+static const MemoryRegionOps sunhme_etx_ops = {
+ .read = sunhme_etx_read,
+ .write = sunhme_etx_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void sunhme_erx_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ SunHMEState *s = SUNHME(opaque);
+
+ trace_sunhme_erx_write(addr, val);
+
+ s->erxregs[addr >> 2] = val;
+}
+
+static uint64_t sunhme_erx_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ SunHMEState *s = SUNHME(opaque);
+ uint64_t val;
+
+ val = s->erxregs[addr >> 2];
+
+ trace_sunhme_erx_read(addr, val);
+
+ return val;
+}
+
+static const MemoryRegionOps sunhme_erx_ops = {
+ .read = sunhme_erx_read,
+ .write = sunhme_erx_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void sunhme_mac_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ SunHMEState *s = SUNHME(opaque);
+
+ trace_sunhme_mac_write(addr, val);
+
+ s->macregs[addr >> 2] = val;
+}
+
+static uint64_t sunhme_mac_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ SunHMEState *s = SUNHME(opaque);
+ uint64_t val;
+
+ val = s->macregs[addr >> 2];
+
+ trace_sunhme_mac_read(addr, val);
+
+ return val;
+}
+
+static const MemoryRegionOps sunhme_mac_ops = {
+ .read = sunhme_mac_read,
+ .write = sunhme_mac_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void sunhme_mii_write(SunHMEState *s, uint8_t reg, uint16_t data)
+{
+ trace_sunhme_mii_write(reg, data);
+
+ switch (reg) {
+ case MII_BMCR:
+ if (data & MII_BMCR_RESET) {
+ /* Autoclear reset bit, enable auto negotiation */
+ data &= ~MII_BMCR_RESET;
+ data |= MII_BMCR_AUTOEN;
+ }
+ if (data & MII_BMCR_ANRESTART) {
+ /* Autoclear auto negotiation restart */
+ data &= ~MII_BMCR_ANRESTART;
+
+ /* Indicate negotiation complete */
+ s->miiregs[MII_BMSR] |= MII_BMSR_AN_COMP;
+
+ if (!qemu_get_queue(s->nic)->link_down) {
+ s->miiregs[MII_ANLPAR] |= MII_ANLPAR_TXFD;
+ s->miiregs[MII_BMSR] |= MII_BMSR_LINK_ST;
+ }
+ }
+ break;
+ }
+
+ s->miiregs[reg] = data;
+}
+
+static uint16_t sunhme_mii_read(SunHMEState *s, uint8_t reg)
+{
+ uint16_t data = s->miiregs[reg];
+
+ trace_sunhme_mii_read(reg, data);
+
+ return data;
+}
+
+static void sunhme_mif_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ SunHMEState *s = SUNHME(opaque);
+ uint8_t cmd, reg;
+ uint16_t data;
+
+ trace_sunhme_mif_write(addr, val);
+
+ switch (addr) {
+ case HME_MIFI_CFG:
+ /* Mask the read-only bits */
+ val &= ~(HME_MIF_CFG_MDI0 | HME_MIF_CFG_MDI1);
+ val |= s->mifregs[HME_MIFI_CFG >> 2] &
+ (HME_MIF_CFG_MDI0 | HME_MIF_CFG_MDI1);
+ break;
+ case HME_MIFI_FO:
+ /* Detect start of MII command */
+ if ((val & HME_MIF_FO_ST) >> HME_MIF_FO_ST_SHIFT
+ != MII_COMMAND_START) {
+ val |= HME_MIF_FO_TALSB;
+ break;
+ }
+
+ /* Internal phy only */
+ if ((val & HME_MIF_FO_PHYAD) >> HME_MIF_FO_PHYAD_SHIFT
+ != HME_PHYAD_INTERNAL) {
+ val |= HME_MIF_FO_TALSB;
+ break;
+ }
+
+ cmd = (val & HME_MIF_FO_OPC) >> HME_MIF_FO_OPC_SHIFT;
+ reg = (val & HME_MIF_FO_REGAD) >> HME_MIF_FO_REGAD_SHIFT;
+ data = (val & HME_MIF_FO_DATA);
+
+ switch (cmd) {
+ case MII_COMMAND_WRITE:
+ sunhme_mii_write(s, reg, data);
+ break;
+
+ case MII_COMMAND_READ:
+ val &= ~HME_MIF_FO_DATA;
+ val |= sunhme_mii_read(s, reg);
+ break;
+ }
+
+ val |= HME_MIF_FO_TALSB;
+ break;
+ }
+
+ s->mifregs[addr >> 2] = val;
+}
+
+static uint64_t sunhme_mif_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ SunHMEState *s = SUNHME(opaque);
+ uint64_t val;
+
+ val = s->mifregs[addr >> 2];
+
+ switch (addr) {
+ case HME_MIFI_STAT:
+ /* Autoclear MIF interrupt status */
+ s->mifregs[HME_MIFI_STAT >> 2] = 0;
+ sunhme_update_irq(s);
+ break;
+ }
+
+ trace_sunhme_mif_read(addr, val);
+
+ return val;
+}
+
+static const MemoryRegionOps sunhme_mif_ops = {
+ .read = sunhme_mif_read,
+ .write = sunhme_mif_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void sunhme_transmit_frame(SunHMEState *s, uint8_t *buf, int size)
+{
+ qemu_send_packet(qemu_get_queue(s->nic), buf, size);
+}
+
+static inline int sunhme_get_tx_ring_count(SunHMEState *s)
+{
+ return (s->etxregs[HME_ETXI_RSIZE >> 2] + 1) << 4;
+}
+
+static inline int sunhme_get_tx_ring_nr(SunHMEState *s)
+{
+ return s->etxregs[HME_ETXI_RING >> 2] & HME_ETXI_RING_OFFSET;
+}
+
+static inline void sunhme_set_tx_ring_nr(SunHMEState *s, int i)
+{
+ uint32_t ring = s->etxregs[HME_ETXI_RING >> 2] & ~HME_ETXI_RING_OFFSET;
+ ring |= i & HME_ETXI_RING_OFFSET;
+
+ s->etxregs[HME_ETXI_RING >> 2] = ring;
+}
+
+static void sunhme_transmit(SunHMEState *s)
+{
+ PCIDevice *d = PCI_DEVICE(s);
+ dma_addr_t tb, addr;
+ uint32_t intstatus, status, buffer, sum = 0;
+ int cr, nr, len, xmit_pos, csum_offset = 0, csum_stuff_offset = 0;
+ uint16_t csum = 0;
+ uint8_t xmit_buffer[HME_FIFO_SIZE];
+
+ tb = s->etxregs[HME_ETXI_RING >> 2] & HME_ETXI_RING_ADDR;
+ nr = sunhme_get_tx_ring_count(s);
+ cr = sunhme_get_tx_ring_nr(s);
+
+ pci_dma_read(d, tb + cr * HME_DESC_SIZE, &status, 4);
+ pci_dma_read(d, tb + cr * HME_DESC_SIZE + 4, &buffer, 4);
+
+ xmit_pos = 0;
+ while (status & HME_XD_OWN) {
+ trace_sunhme_tx_desc(buffer, status, cr, nr);
+
+ /* Copy data into transmit buffer */
+ addr = buffer;
+ len = status & HME_XD_TXLENMSK;
+
+ if (xmit_pos + len > HME_FIFO_SIZE) {
+ len = HME_FIFO_SIZE - xmit_pos;
+ }
+
+ pci_dma_read(d, addr, &xmit_buffer[xmit_pos], len);
+ xmit_pos += len;
+
+ /* Detect start of packet for TX checksum */
+ if (status & HME_XD_SOP) {
+ sum = 0;
+ csum_offset = (status & HME_XD_TXCSSTART) >> HME_XD_TXCSSTARTSHIFT;
+ csum_stuff_offset = (status & HME_XD_TXCSSTUFF) >>
+ HME_XD_TXCSSTUFFSHIFT;
+ }
+
+ if (status & HME_XD_TXCKSUM) {
+ /* Only start calculation from csum_offset */
+ if (xmit_pos - len <= csum_offset && xmit_pos > csum_offset) {
+ sum += net_checksum_add(xmit_pos - csum_offset,
+ xmit_buffer + csum_offset);
+ trace_sunhme_tx_xsum_add(csum_offset, xmit_pos - csum_offset);
+ } else {
+ sum += net_checksum_add(len, xmit_buffer + xmit_pos - len);
+ trace_sunhme_tx_xsum_add(xmit_pos - len, len);
+ }
+ }
+
+ /* Detect end of packet for TX checksum */
+ if (status & HME_XD_EOP) {
+ /* Stuff the checksum if required */
+ if (status & HME_XD_TXCKSUM) {
+ csum = net_checksum_finish(sum);
+ stw_be_p(xmit_buffer + csum_stuff_offset, csum);
+ trace_sunhme_tx_xsum_stuff(csum, csum_stuff_offset);
+ }
+
+ if (s->macregs[HME_MACI_TXCFG >> 2] & HME_MAC_TXCFG_ENABLE) {
+ sunhme_transmit_frame(s, xmit_buffer, xmit_pos);
+ trace_sunhme_tx_done(xmit_pos);
+ }
+ }
+
+ /* Update status */
+ status &= ~HME_XD_OWN;
+ pci_dma_write(d, tb + cr * HME_DESC_SIZE, &status, 4);
+
+ /* Move onto next descriptor */
+ cr++;
+ if (cr >= nr) {
+ cr = 0;
+ }
+ sunhme_set_tx_ring_nr(s, cr);
+
+ pci_dma_read(d, tb + cr * HME_DESC_SIZE, &status, 4);
+ pci_dma_read(d, tb + cr * HME_DESC_SIZE + 4, &buffer, 4);
+
+ /* Indicate TX complete */
+ intstatus = s->sebregs[HME_SEBI_STAT >> 2];
+ intstatus |= HME_SEB_STAT_HOSTTOTX;
+ s->sebregs[HME_SEBI_STAT >> 2] = intstatus;
+
+ /* Autoclear TX pending */
+ s->etxregs[HME_ETXI_PENDING >> 2] = 0;
+
+ sunhme_update_irq(s);
+ }
+
+ /* TX FIFO now clear */
+ intstatus = s->sebregs[HME_SEBI_STAT >> 2];
+ intstatus |= HME_SEB_STAT_TXALL;
+ s->sebregs[HME_SEBI_STAT >> 2] = intstatus;
+ sunhme_update_irq(s);
+}
+
+static int sunhme_can_receive(NetClientState *nc)
+{
+ SunHMEState *s = qemu_get_nic_opaque(nc);
+
+ return s->macregs[HME_MAC_RXCFG_ENABLE >> 2] & HME_MAC_RXCFG_ENABLE;
+}
+
+static void sunhme_link_status_changed(NetClientState *nc)
+{
+ SunHMEState *s = qemu_get_nic_opaque(nc);
+
+ if (nc->link_down) {
+ s->miiregs[MII_ANLPAR] &= ~MII_ANLPAR_TXFD;
+ s->miiregs[MII_BMSR] &= ~MII_BMSR_LINK_ST;
+ } else {
+ s->miiregs[MII_ANLPAR] |= MII_ANLPAR_TXFD;
+ s->miiregs[MII_BMSR] |= MII_BMSR_LINK_ST;
+ }
+
+ /* Exact bits unknown */
+ s->mifregs[HME_MIFI_STAT >> 2] = 0xffff;
+ sunhme_update_irq(s);
+}
+
+static inline int sunhme_get_rx_ring_count(SunHMEState *s)
+{
+ uint32_t rings = (s->erxregs[HME_ERXI_CFG >> 2] & HME_ERX_CFG_RINGSIZE)
+ >> HME_ERX_CFG_RINGSIZE_SHIFT;
+
+ switch (rings) {
+ case 0:
+ return 32;
+ case 1:
+ return 64;
+ case 2:
+ return 128;
+ case 3:
+ return 256;
+ }
+
+ return 0;
+}
+
+static inline int sunhme_get_rx_ring_nr(SunHMEState *s)
+{
+ return s->erxregs[HME_ERXI_RING >> 2] & HME_ERXI_RING_OFFSET;
+}
+
+static inline void sunhme_set_rx_ring_nr(SunHMEState *s, int i)
+{
+ uint32_t ring = s->erxregs[HME_ERXI_RING >> 2] & ~HME_ERXI_RING_OFFSET;
+ ring |= i & HME_ERXI_RING_OFFSET;
+
+ s->erxregs[HME_ERXI_RING >> 2] = ring;
+}
+
+#define POLYNOMIAL_LE 0xedb88320
+static uint32_t sunhme_crc32_le(const uint8_t *p, int len)
+{
+ uint32_t crc;
+ int carry, i, j;
+ uint8_t b;
+
+ crc = 0xffffffff;
+ for (i = 0; i < len; i++) {
+ b = *p++;
+ for (j = 0; j < 8; j++) {
+ carry = (crc & 0x1) ^ (b & 0x01);
+ crc >>= 1;
+ b >>= 1;
+ if (carry) {
+ crc = crc ^ POLYNOMIAL_LE;
+ }
+ }
+ }
+
+ return crc;
+}
+
+#define MIN_BUF_SIZE 60
+
+static ssize_t sunhme_receive(NetClientState *nc, const uint8_t *buf,
+ size_t size)
+{
+ SunHMEState *s = qemu_get_nic_opaque(nc);
+ PCIDevice *d = PCI_DEVICE(s);
+ dma_addr_t rb, addr;
+ uint32_t intstatus, status, buffer, buffersize, sum;
+ uint16_t csum;
+ uint8_t buf1[60];
+ int nr, cr, len, rxoffset, csum_offset;
+
+ trace_sunhme_rx_incoming(size);
+
+ /* Do nothing if MAC RX disabled */
+ if (!(s->macregs[HME_MACI_RXCFG >> 2] & HME_MAC_RXCFG_ENABLE)) {
+ return -1;
+ }
+
+ trace_sunhme_rx_filter_destmac(buf[0], buf[1], buf[2],
+ buf[3], buf[4], buf[5]);
+
+ /* Check destination MAC address */
+ if (!(s->macregs[HME_MACI_RXCFG >> 2] & HME_MAC_RXCFG_PMISC)) {
+ /* Try and match local MAC address */
+ if (((s->macregs[HME_MACI_MACADDR0 >> 2] & 0xff00) >> 8) == buf[0] &&
+ (s->macregs[HME_MACI_MACADDR0 >> 2] & 0xff) == buf[1] &&
+ ((s->macregs[HME_MACI_MACADDR1 >> 2] & 0xff00) >> 8) == buf[2] &&
+ (s->macregs[HME_MACI_MACADDR1 >> 2] & 0xff) == buf[3] &&
+ ((s->macregs[HME_MACI_MACADDR2 >> 2] & 0xff00) >> 8) == buf[4] &&
+ (s->macregs[HME_MACI_MACADDR2 >> 2] & 0xff) == buf[5]) {
+ /* Matched local MAC address */
+ trace_sunhme_rx_filter_local_match();
+ } else if (buf[0] == 0xff && buf[1] == 0xff && buf[2] == 0xff &&
+ buf[3] == 0xff && buf[4] == 0xff && buf[5] == 0xff) {
+ /* Matched broadcast address */
+ trace_sunhme_rx_filter_bcast_match();
+ } else if (s->macregs[HME_MACI_RXCFG >> 2] & HME_MAC_RXCFG_HENABLE) {
+ /* Didn't match local address, check hash filter */
+ int mcast_idx = sunhme_crc32_le(buf, 6) >> 26;
+ if (!(s->macregs[(HME_MACI_HASHTAB0 >> 2) - (mcast_idx >> 4)] &
+ (1 << (mcast_idx & 0xf)))) {
+ /* Didn't match hash filter */
+ trace_sunhme_rx_filter_hash_nomatch();
+ trace_sunhme_rx_filter_reject();
+ return 0;
+ } else {
+ trace_sunhme_rx_filter_hash_match();
+ }
+ } else {
+ /* Not for us */
+ trace_sunhme_rx_filter_reject();
+ return 0;
+ }
+ } else {
+ trace_sunhme_rx_filter_promisc_match();
+ }
+
+ trace_sunhme_rx_filter_accept();
+
+ /* If too small buffer, then expand it */
+ if (size < MIN_BUF_SIZE) {
+ memcpy(buf1, buf, size);
+ memset(buf1 + size, 0, MIN_BUF_SIZE - size);
+ buf = buf1;
+ size = MIN_BUF_SIZE;
+ }
+
+ rb = s->erxregs[HME_ERXI_RING >> 2] & HME_ERXI_RING_ADDR;
+ nr = sunhme_get_rx_ring_count(s);
+ cr = sunhme_get_rx_ring_nr(s);
+
+ pci_dma_read(d, rb + cr * HME_DESC_SIZE, &status, 4);
+ pci_dma_read(d, rb + cr * HME_DESC_SIZE + 4, &buffer, 4);
+
+ rxoffset = (s->erxregs[HME_ERXI_CFG >> 2] & HME_ERX_CFG_BYTEOFFSET) >>
+ HME_ERX_CFG_BYTEOFFSET_SHIFT;
+
+ addr = buffer + rxoffset;
+ buffersize = (status & HME_XD_RXLENMSK) >> HME_XD_RXLENSHIFT;
+
+ /* Detect receive overflow */
+ len = size;
+ if (size > buffersize) {
+ status |= HME_XD_OFL;
+ len = buffersize;
+ }
+
+ pci_dma_write(d, addr, buf, len);
+
+ trace_sunhme_rx_desc(buffer, rxoffset, status, len, cr, nr);
+
+ /* Calculate the receive checksum */
+ csum_offset = (s->erxregs[HME_ERXI_CFG >> 2] & HME_ERX_CFG_CSUMSTART) >>
+ HME_ERX_CFG_CSUMSHIFT << 1;
+ sum = 0;
+ sum += net_checksum_add(len - csum_offset, (uint8_t *)buf + csum_offset);
+ csum = net_checksum_finish(sum);
+
+ trace_sunhme_rx_xsum_calc(csum);
+
+ /* Update status */
+ status &= ~HME_XD_OWN;
+ status &= ~HME_XD_RXLENMSK;
+ status |= len << HME_XD_RXLENSHIFT;
+ status &= ~HME_XD_RXCKSUM;
+ status |= csum;
+
+ pci_dma_write(d, rb + cr * HME_DESC_SIZE, &status, 4);
+
+ cr++;
+ if (cr >= nr) {
+ cr = 0;
+ }
+
+ sunhme_set_rx_ring_nr(s, cr);
+
+ /* Indicate RX complete */
+ intstatus = s->sebregs[HME_SEBI_STAT >> 2];
+ intstatus |= HME_SEB_STAT_RXTOHOST;
+ s->sebregs[HME_SEBI_STAT >> 2] = intstatus;
+
+ sunhme_update_irq(s);
+
+ return len;
+}
+
+static NetClientInfo net_sunhme_info = {
+ .type = NET_CLIENT_DRIVER_NIC,
+ .size = sizeof(NICState),
+ .can_receive = sunhme_can_receive,
+ .receive = sunhme_receive,
+ .link_status_changed = sunhme_link_status_changed,
+};
+
+static void sunhme_realize(PCIDevice *pci_dev, Error **errp)
+{
+ SunHMEState *s = SUNHME(pci_dev);
+ DeviceState *d = DEVICE(pci_dev);
+ uint8_t *pci_conf;
+
+ pci_conf = pci_dev->config;
+ pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */
+
+ memory_region_init(&s->hme, OBJECT(pci_dev), "sunhme", HME_REG_SIZE);
+ pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->hme);
+
+ memory_region_init_io(&s->sebreg, OBJECT(pci_dev), &sunhme_seb_ops, s,
+ "sunhme.seb", HME_SEB_REG_SIZE);
+ memory_region_add_subregion(&s->hme, 0, &s->sebreg);
+
+ memory_region_init_io(&s->etxreg, OBJECT(pci_dev), &sunhme_etx_ops, s,
+ "sunhme.etx", HME_ETX_REG_SIZE);
+ memory_region_add_subregion(&s->hme, 0x2000, &s->etxreg);
+
+ memory_region_init_io(&s->erxreg, OBJECT(pci_dev), &sunhme_erx_ops, s,
+ "sunhme.erx", HME_ERX_REG_SIZE);
+ memory_region_add_subregion(&s->hme, 0x4000, &s->erxreg);
+
+ memory_region_init_io(&s->macreg, OBJECT(pci_dev), &sunhme_mac_ops, s,
+ "sunhme.mac", HME_MAC_REG_SIZE);
+ memory_region_add_subregion(&s->hme, 0x6000, &s->macreg);
+
+ memory_region_init_io(&s->mifreg, OBJECT(pci_dev), &sunhme_mif_ops, s,
+ "sunhme.mif", HME_MIF_REG_SIZE);
+ memory_region_add_subregion(&s->hme, 0x7000, &s->mifreg);
+
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ s->nic = qemu_new_nic(&net_sunhme_info, &s->conf,
+ object_get_typename(OBJECT(d)), d->id, s);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+}
+
+static void sunhme_instance_init(Object *obj)
+{
+ SunHMEState *s = SUNHME(obj);
+
+ device_add_bootindex_property(obj, &s->conf.bootindex,
+ "bootindex", "/ethernet-phy@0",
+ DEVICE(obj), NULL);
+}
+
+static void sunhme_reset(DeviceState *ds)
+{
+ SunHMEState *s = SUNHME(ds);
+
+ /* Configure internal transceiver */
+ s->mifregs[HME_MIFI_CFG >> 2] |= HME_MIF_CFG_MDI0;
+
+ /* Advetise auto, 100Mbps FD */
+ s->miiregs[MII_ANAR] = MII_ANAR_TXFD;
+ s->miiregs[MII_BMSR] = MII_BMSR_AUTONEG | MII_BMSR_100TX_FD |
+ MII_BMSR_AN_COMP;
+
+ if (!qemu_get_queue(s->nic)->link_down) {
+ s->miiregs[MII_ANLPAR] |= MII_ANLPAR_TXFD;
+ s->miiregs[MII_BMSR] |= MII_BMSR_LINK_ST;
+ }
+
+ /* Set manufacturer */
+ s->miiregs[MII_PHYID1] = DP83840_PHYID1;
+ s->miiregs[MII_PHYID2] = DP83840_PHYID2;
+
+ /* Configure default interrupt mask */
+ s->mifregs[HME_MIFI_IMASK >> 2] = 0xffff;
+ s->sebregs[HME_SEBI_IMASK >> 2] = 0xff7fffff;
+}
+
+static const VMStateDescription vmstate_hme = {
+ .name = "sunhme",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(parent_obj, SunHMEState),
+ VMSTATE_MACADDR(conf.macaddr, SunHMEState),
+ VMSTATE_UINT32_ARRAY(sebregs, SunHMEState, (HME_SEB_REG_SIZE >> 2)),
+ VMSTATE_UINT32_ARRAY(etxregs, SunHMEState, (HME_ETX_REG_SIZE >> 2)),
+ VMSTATE_UINT32_ARRAY(erxregs, SunHMEState, (HME_ERX_REG_SIZE >> 2)),
+ VMSTATE_UINT32_ARRAY(macregs, SunHMEState, (HME_MAC_REG_SIZE >> 2)),
+ VMSTATE_UINT32_ARRAY(mifregs, SunHMEState, (HME_MIF_REG_SIZE >> 2)),
+ VMSTATE_UINT16_ARRAY(miiregs, SunHMEState, HME_MII_REGS_SIZE),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void sunhme_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = sunhme_realize;
+ k->vendor_id = PCI_VENDOR_ID_SUN;
+ k->device_id = PCI_DEVICE_ID_SUN_HME;
+ k->class_id = PCI_CLASS_NETWORK_ETHERNET;
+ dc->vmsd = &vmstate_hme;
+ dc->reset = sunhme_reset;
+ dc->props = sunhme_properties;
+ set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
+}
+
+static const TypeInfo sunhme_info = {
+ .name = TYPE_SUNHME,
+ .parent = TYPE_PCI_DEVICE,
+ .class_init = sunhme_class_init,
+ .instance_size = sizeof(SunHMEState),
+ .instance_init = sunhme_instance_init,
+};
+
+static void sunhme_register_types(void)
+{
+ type_register_static(&sunhme_info);
+}
+
+type_init(sunhme_register_types)
diff --git a/hw/net/trace-events b/hw/net/trace-events
index 5f816ef58e..45c4e9fba0 100644
--- a/hw/net/trace-events
+++ b/hw/net/trace-events
@@ -322,3 +322,32 @@ sungem_mmio_mif_write(uint64_t addr, uint64_t val) "MMIO mif write to 0x%"PRIx64
sungem_mmio_mif_read(uint64_t addr, uint64_t val) "MMIO mif read from 0x%"PRIx64" val=0x%"PRIx64
sungem_mmio_pcs_write(uint64_t addr, uint64_t val) "MMIO pcs write to 0x%"PRIx64" val=0x%"PRIx64
sungem_mmio_pcs_read(uint64_t addr, uint64_t val) "MMIO pcs read from 0x%"PRIx64" val=0x%"PRIx64
+
+# hw/net/sunhme.c
+sunhme_seb_write(uint64_t addr, uint64_t value) "addr 0x%"PRIx64" value 0x%"PRIx64
+sunhme_seb_read(uint64_t addr, uint64_t value) "addr 0x%"PRIx64" value 0x%"PRIx64
+sunhme_etx_write(uint64_t addr, uint64_t value) "addr 0x%"PRIx64" value 0x%"PRIx64
+sunhme_etx_read(uint64_t addr, uint64_t value) "addr 0x%"PRIx64" value 0x%"PRIx64
+sunhme_erx_write(uint64_t addr, uint64_t value) "addr 0x%"PRIx64" value 0x%"PRIx64
+sunhme_erx_read(uint64_t addr, uint64_t value) "addr 0x%"PRIx64" value 0x%"PRIx64
+sunhme_mac_write(uint64_t addr, uint64_t value) "addr 0x%"PRIx64" value 0x%"PRIx64
+sunhme_mac_read(uint64_t addr, uint64_t value) "addr 0x%"PRIx64" value 0x%"PRIx64
+sunhme_mii_write(uint64_t addr, uint64_t value) "addr 0x%"PRIx64" value 0x%"PRIx64
+sunhme_mii_read(uint8_t addr, uint16_t value) "addr 0x%x value 0x%x"
+sunhme_mif_write(uint8_t addr, uint16_t value) "addr 0x%x value 0x%x"
+sunhme_mif_read(uint64_t addr, uint64_t value) "addr 0x%"PRIx64" value 0x%"PRIx64
+sunhme_tx_desc(uint64_t buffer, uint32_t status, int cr, int nr) "addr 0x%"PRIx64" status 0x%"PRIx32 " (ring %d/%d)"
+sunhme_tx_xsum_add(int offset, int len) "adding xsum at offset %d, len %d"
+sunhme_tx_xsum_stuff(uint16_t xsum, int offset) "stuffing xsum 0x%x at offset %d"
+sunhme_tx_done(int len) "successfully transmitted frame with len %d"
+sunhme_rx_incoming(size_t len) "received incoming frame with len %zu"
+sunhme_rx_filter_destmac(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "received frame for MAC: %02x:%02x:%02x:%02x:%02x:%02x"
+sunhme_rx_filter_local_match(void) "incoming frame matches local MAC address"
+sunhme_rx_filter_bcast_match(void) "incoming frame matches broadcast MAC address"
+sunhme_rx_filter_hash_nomatch(void) "incoming MAC address not in hash table"
+sunhme_rx_filter_hash_match(void) "incoming MAC address found in hash table"
+sunhme_rx_filter_promisc_match(void) "incoming frame accepted due to promiscuous mode"
+sunhme_rx_filter_reject(void) "rejecting incoming frame"
+sunhme_rx_filter_accept(void) "accepting incoming frame"
+sunhme_rx_desc(uint32_t addr, int offset, uint32_t status, int len, int cr, int nr) "addr 0x%"PRIx32"(+0x%x) status 0x%"PRIx32 " len %d (ring %d/%d)"
+sunhme_rx_xsum_calc(uint16_t xsum) "calculated incoming xsum as 0x%x"
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c
index 5e59269adc..c3280aaf38 100644
--- a/hw/sparc64/sun4u.c
+++ b/hw/sparc64/sun4u.c
@@ -427,7 +427,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
unsigned int i;
uint64_t initrd_addr, initrd_size, kernel_addr, kernel_size, kernel_entry;
PCIBus *pci_bus, *pci_busA, *pci_busB;
- PCIDevice *ebus;
+ PCIDevice *ebus, *pci_dev;
ISABus *isa_bus;
SysBusDevice *s;
qemu_irq *ivec_irqs, *pbm_irqs;
@@ -435,6 +435,8 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
DriveInfo *fd[MAX_FD];
DeviceState *dev;
FWCfgState *fw_cfg;
+ NICInfo *nd;
+ int onboard_nic_idx;
/* init CPUs */
cpu = sparc64_cpu_devinit(machine->cpu_model, hwdef->default_cpu_model,
@@ -464,8 +466,23 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
serial_hds_isa_init(isa_bus, i, MAX_SERIAL_PORTS);
parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
- for(i = 0; i < nb_nics; i++)
- pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL);
+ onboard_nic_idx = -1;
+ for (i = 0; i < nb_nics; i++) {
+ nd = &nd_table[i];
+
+ if (onboard_nic_idx == -1 &&
+ (!nd->model || strcmp(nd->model, "sunhme") == 0)) {
+ pci_dev = pci_create(pci_bus, -1, "sunhme");
+ dev = &pci_dev->qdev;
+ qdev_set_nic_properties(dev, nd);
+ qdev_init_nofail(dev);
+
+ onboard_nic_idx = i;
+ } else {
+ pci_nic_init_nofail(nd, pci_bus, "ne2k_pci", NULL);
+ }
+ }
+ onboard_nic_idx = MAX(onboard_nic_idx, 0);
ide_drive_get(hd, ARRAY_SIZE(hd));
@@ -510,7 +527,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem,
/* XXX: need an option to load a NVRAM image */
0,
graphic_width, graphic_height, graphic_depth,
- (uint8_t *)&nd_table[0].macaddr);
+ (uint8_t *)&nd_table[onboard_nic_idx].macaddr);
dev = qdev_create(NULL, TYPE_FW_CFG_IO);
qdev_prop_set_bit(dev, "dma_enabled", false);
diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
index 487add2879..f5bcc65fe7 100644
--- a/hw/ssi/Makefile.objs
+++ b/hw/ssi/Makefile.objs
@@ -4,6 +4,7 @@ common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o
common-obj-$(CONFIG_ASPEED_SOC) += aspeed_smc.o
common-obj-$(CONFIG_STM32F2XX_SPI) += stm32f2xx_spi.o
+common-obj-$(CONFIG_MSF2) += mss-spi.o
obj-$(CONFIG_OMAP) += omap_spi.o
obj-$(CONFIG_IMX) += imx_spi.o
diff --git a/hw/ssi/mss-spi.c b/hw/ssi/mss-spi.c
new file mode 100644
index 0000000000..5a8e308e69
--- /dev/null
+++ b/hw/ssi/mss-spi.c
@@ -0,0 +1,404 @@
+/*
+ * Block model of SPI controller present in
+ * Microsemi's SmartFusion2 and SmartFusion SoCs.
+ *
+ * Copyright (C) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/ssi/mss-spi.h"
+#include "qemu/log.h"
+
+#ifndef MSS_SPI_ERR_DEBUG
+#define MSS_SPI_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT_L(lvl, fmt, args...) do { \
+ if (MSS_SPI_ERR_DEBUG >= lvl) { \
+ qemu_log("%s: " fmt "\n", __func__, ## args); \
+ } \
+} while (0);
+
+#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
+
+#define FIFO_CAPACITY 32
+
+#define R_SPI_CONTROL 0
+#define R_SPI_DFSIZE 1
+#define R_SPI_STATUS 2
+#define R_SPI_INTCLR 3
+#define R_SPI_RX 4
+#define R_SPI_TX 5
+#define R_SPI_CLKGEN 6
+#define R_SPI_SS 7
+#define R_SPI_MIS 8
+#define R_SPI_RIS 9
+
+#define S_TXDONE (1 << 0)
+#define S_RXRDY (1 << 1)
+#define S_RXCHOVRF (1 << 2)
+#define S_RXFIFOFUL (1 << 4)
+#define S_RXFIFOFULNXT (1 << 5)
+#define S_RXFIFOEMP (1 << 6)
+#define S_RXFIFOEMPNXT (1 << 7)
+#define S_TXFIFOFUL (1 << 8)
+#define S_TXFIFOFULNXT (1 << 9)
+#define S_TXFIFOEMP (1 << 10)
+#define S_TXFIFOEMPNXT (1 << 11)
+#define S_FRAMESTART (1 << 12)
+#define S_SSEL (1 << 13)
+#define S_ACTIVE (1 << 14)
+
+#define C_ENABLE (1 << 0)
+#define C_MODE (1 << 1)
+#define C_INTRXDATA (1 << 4)
+#define C_INTTXDATA (1 << 5)
+#define C_INTRXOVRFLO (1 << 6)
+#define C_SPS (1 << 26)
+#define C_BIGFIFO (1 << 29)
+#define C_RESET (1 << 31)
+
+#define FRAMESZ_MASK 0x1F
+#define FMCOUNT_MASK 0x00FFFF00
+#define FMCOUNT_SHIFT 8
+
+static void txfifo_reset(MSSSpiState *s)
+{
+ fifo32_reset(&s->tx_fifo);
+
+ s->regs[R_SPI_STATUS] &= ~S_TXFIFOFUL;
+ s->regs[R_SPI_STATUS] |= S_TXFIFOEMP;
+}
+
+static void rxfifo_reset(MSSSpiState *s)
+{
+ fifo32_reset(&s->rx_fifo);
+
+ s->regs[R_SPI_STATUS] &= ~S_RXFIFOFUL;
+ s->regs[R_SPI_STATUS] |= S_RXFIFOEMP;
+}
+
+static void set_fifodepth(MSSSpiState *s)
+{
+ unsigned int size = s->regs[R_SPI_DFSIZE] & FRAMESZ_MASK;
+
+ if (size <= 8) {
+ s->fifo_depth = 32;
+ } else if (size <= 16) {
+ s->fifo_depth = 16;
+ } else if (size <= 32) {
+ s->fifo_depth = 8;
+ } else {
+ s->fifo_depth = 4;
+ }
+}
+
+static void update_mis(MSSSpiState *s)
+{
+ uint32_t reg = s->regs[R_SPI_CONTROL];
+ uint32_t tmp;
+
+ /*
+ * form the Control register interrupt enable bits
+ * same as RIS, MIS and Interrupt clear registers for simplicity
+ */
+ tmp = ((reg & C_INTRXOVRFLO) >> 4) | ((reg & C_INTRXDATA) >> 3) |
+ ((reg & C_INTTXDATA) >> 5);
+ s->regs[R_SPI_MIS] |= tmp & s->regs[R_SPI_RIS];
+}
+
+static void spi_update_irq(MSSSpiState *s)
+{
+ int irq;
+
+ update_mis(s);
+ irq = !!(s->regs[R_SPI_MIS]);
+
+ qemu_set_irq(s->irq, irq);
+}
+
+static void mss_spi_reset(DeviceState *d)
+{
+ MSSSpiState *s = MSS_SPI(d);
+
+ memset(s->regs, 0, sizeof s->regs);
+ s->regs[R_SPI_CONTROL] = 0x80000102;
+ s->regs[R_SPI_DFSIZE] = 0x4;
+ s->regs[R_SPI_STATUS] = S_SSEL | S_TXFIFOEMP | S_RXFIFOEMP;
+ s->regs[R_SPI_CLKGEN] = 0x7;
+ s->regs[R_SPI_RIS] = 0x0;
+
+ s->fifo_depth = 4;
+ s->frame_count = 1;
+ s->enabled = false;
+
+ rxfifo_reset(s);
+ txfifo_reset(s);
+}
+
+static uint64_t
+spi_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ MSSSpiState *s = opaque;
+ uint32_t ret = 0;
+
+ addr >>= 2;
+ switch (addr) {
+ case R_SPI_RX:
+ s->regs[R_SPI_STATUS] &= ~S_RXFIFOFUL;
+ s->regs[R_SPI_STATUS] &= ~S_RXCHOVRF;
+ ret = fifo32_pop(&s->rx_fifo);
+ if (fifo32_is_empty(&s->rx_fifo)) {
+ s->regs[R_SPI_STATUS] |= S_RXFIFOEMP;
+ }
+ break;
+
+ case R_SPI_MIS:
+ update_mis(s);
+ ret = s->regs[R_SPI_MIS];
+ break;
+
+ default:
+ if (addr < ARRAY_SIZE(s->regs)) {
+ ret = s->regs[addr];
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__,
+ addr * 4);
+ return ret;
+ }
+ break;
+ }
+
+ DB_PRINT("addr=0x%" HWADDR_PRIx " = 0x%" PRIx32, addr * 4, ret);
+ spi_update_irq(s);
+ return ret;
+}
+
+static void assert_cs(MSSSpiState *s)
+{
+ qemu_set_irq(s->cs_line, 0);
+}
+
+static void deassert_cs(MSSSpiState *s)
+{
+ qemu_set_irq(s->cs_line, 1);
+}
+
+static void spi_flush_txfifo(MSSSpiState *s)
+{
+ uint32_t tx;
+ uint32_t rx;
+ bool sps = !!(s->regs[R_SPI_CONTROL] & C_SPS);
+
+ /*
+ * Chip Select(CS) is automatically controlled by this controller.
+ * If SPS bit is set in Control register then CS is asserted
+ * until all the frames set in frame count of Control register are
+ * transferred. If SPS is not set then CS pulses between frames.
+ * Note that Slave Select register specifies which of the CS line
+ * has to be controlled automatically by controller. Bits SS[7:1] are for
+ * masters in FPGA fabric since we model only Microcontroller subsystem
+ * of Smartfusion2 we control only one CS(SS[0]) line.
+ */
+ while (!fifo32_is_empty(&s->tx_fifo) && s->frame_count) {
+ assert_cs(s);
+
+ s->regs[R_SPI_STATUS] &= ~(S_TXDONE | S_RXRDY);
+
+ tx = fifo32_pop(&s->tx_fifo);
+ DB_PRINT("data tx:0x%" PRIx32, tx);
+ rx = ssi_transfer(s->spi, tx);
+ DB_PRINT("data rx:0x%" PRIx32, rx);
+
+ if (fifo32_num_used(&s->rx_fifo) == s->fifo_depth) {
+ s->regs[R_SPI_STATUS] |= S_RXCHOVRF;
+ s->regs[R_SPI_RIS] |= S_RXCHOVRF;
+ } else {
+ fifo32_push(&s->rx_fifo, rx);
+ s->regs[R_SPI_STATUS] &= ~S_RXFIFOEMP;
+ if (fifo32_num_used(&s->rx_fifo) == (s->fifo_depth - 1)) {
+ s->regs[R_SPI_STATUS] |= S_RXFIFOFULNXT;
+ } else if (fifo32_num_used(&s->rx_fifo) == s->fifo_depth) {
+ s->regs[R_SPI_STATUS] |= S_RXFIFOFUL;
+ }
+ }
+ s->frame_count--;
+ if (!sps) {
+ deassert_cs(s);
+ }
+ }
+
+ if (!s->frame_count) {
+ s->frame_count = (s->regs[R_SPI_CONTROL] & FMCOUNT_MASK) >>
+ FMCOUNT_SHIFT;
+ deassert_cs(s);
+ s->regs[R_SPI_RIS] |= S_TXDONE | S_RXRDY;
+ s->regs[R_SPI_STATUS] |= S_TXDONE | S_RXRDY;
+ }
+}
+
+static void spi_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ MSSSpiState *s = opaque;
+ uint32_t value = val64;
+
+ DB_PRINT("addr=0x%" HWADDR_PRIx " =0x%" PRIx32, addr, value);
+ addr >>= 2;
+
+ switch (addr) {
+ case R_SPI_TX:
+ /* adding to already full FIFO */
+ if (fifo32_num_used(&s->tx_fifo) == s->fifo_depth) {
+ break;
+ }
+ s->regs[R_SPI_STATUS] &= ~S_TXFIFOEMP;
+ fifo32_push(&s->tx_fifo, value);
+ if (fifo32_num_used(&s->tx_fifo) == (s->fifo_depth - 1)) {
+ s->regs[R_SPI_STATUS] |= S_TXFIFOFULNXT;
+ } else if (fifo32_num_used(&s->tx_fifo) == s->fifo_depth) {
+ s->regs[R_SPI_STATUS] |= S_TXFIFOFUL;
+ }
+ if (s->enabled) {
+ spi_flush_txfifo(s);
+ }
+ break;
+
+ case R_SPI_CONTROL:
+ s->regs[R_SPI_CONTROL] = value;
+ if (value & C_BIGFIFO) {
+ set_fifodepth(s);
+ } else {
+ s->fifo_depth = 4;
+ }
+ s->enabled = value & C_ENABLE;
+ s->frame_count = (value & FMCOUNT_MASK) >> FMCOUNT_SHIFT;
+ if (value & C_RESET) {
+ mss_spi_reset(DEVICE(s));
+ }
+ break;
+
+ case R_SPI_DFSIZE:
+ if (s->enabled) {
+ break;
+ }
+ s->regs[R_SPI_DFSIZE] = value;
+ break;
+
+ case R_SPI_INTCLR:
+ s->regs[R_SPI_INTCLR] = value;
+ if (value & S_TXDONE) {
+ s->regs[R_SPI_RIS] &= ~S_TXDONE;
+ }
+ if (value & S_RXRDY) {
+ s->regs[R_SPI_RIS] &= ~S_RXRDY;
+ }
+ if (value & S_RXCHOVRF) {
+ s->regs[R_SPI_RIS] &= ~S_RXCHOVRF;
+ }
+ break;
+
+ case R_SPI_MIS:
+ case R_SPI_STATUS:
+ case R_SPI_RIS:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Write to read only register 0x%" HWADDR_PRIx "\n",
+ __func__, addr * 4);
+ break;
+
+ default:
+ if (addr < ARRAY_SIZE(s->regs)) {
+ s->regs[addr] = value;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__,
+ addr * 4);
+ }
+ break;
+ }
+
+ spi_update_irq(s);
+}
+
+static const MemoryRegionOps spi_ops = {
+ .read = spi_read,
+ .write = spi_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4
+ }
+};
+
+static void mss_spi_realize(DeviceState *dev, Error **errp)
+{
+ MSSSpiState *s = MSS_SPI(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ s->spi = ssi_create_bus(dev, "spi");
+
+ sysbus_init_irq(sbd, &s->irq);
+ ssi_auto_connect_slaves(dev, &s->cs_line, s->spi);
+ sysbus_init_irq(sbd, &s->cs_line);
+
+ memory_region_init_io(&s->mmio, OBJECT(s), &spi_ops, s,
+ TYPE_MSS_SPI, R_SPI_MAX * 4);
+ sysbus_init_mmio(sbd, &s->mmio);
+
+ fifo32_create(&s->tx_fifo, FIFO_CAPACITY);
+ fifo32_create(&s->rx_fifo, FIFO_CAPACITY);
+}
+
+static const VMStateDescription vmstate_mss_spi = {
+ .name = TYPE_MSS_SPI,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_FIFO32(tx_fifo, MSSSpiState),
+ VMSTATE_FIFO32(rx_fifo, MSSSpiState),
+ VMSTATE_UINT32_ARRAY(regs, MSSSpiState, R_SPI_MAX),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void mss_spi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = mss_spi_realize;
+ dc->reset = mss_spi_reset;
+ dc->vmsd = &vmstate_mss_spi;
+}
+
+static const TypeInfo mss_spi_info = {
+ .name = TYPE_MSS_SPI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MSSSpiState),
+ .class_init = mss_spi_class_init,
+};
+
+static void mss_spi_register_types(void)
+{
+ type_register_static(&mss_spi_info);
+}
+
+type_init(mss_spi_register_types)
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 15cce1c531..8c19eac3b6 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -42,3 +42,4 @@ common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o
common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o
common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o
+common-obj-$(CONFIG_MSF2) += mss-timer.o
diff --git a/hw/timer/mss-timer.c b/hw/timer/mss-timer.c
new file mode 100644
index 0000000000..60f1213a3b
--- /dev/null
+++ b/hw/timer/mss-timer.c
@@ -0,0 +1,289 @@
+/*
+ * Block model of System timer present in
+ * Microsemi's SmartFusion2 and SmartFusion SoCs.
+ *
+ * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "qemu/log.h"
+#include "hw/timer/mss-timer.h"
+
+#ifndef MSS_TIMER_ERR_DEBUG
+#define MSS_TIMER_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT_L(lvl, fmt, args...) do { \
+ if (MSS_TIMER_ERR_DEBUG >= lvl) { \
+ qemu_log("%s: " fmt "\n", __func__, ## args); \
+ } \
+} while (0);
+
+#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
+
+#define R_TIM_VAL 0
+#define R_TIM_LOADVAL 1
+#define R_TIM_BGLOADVAL 2
+#define R_TIM_CTRL 3
+#define R_TIM_RIS 4
+#define R_TIM_MIS 5
+
+#define TIMER_CTRL_ENBL (1 << 0)
+#define TIMER_CTRL_ONESHOT (1 << 1)
+#define TIMER_CTRL_INTR (1 << 2)
+#define TIMER_RIS_ACK (1 << 0)
+#define TIMER_RST_CLR (1 << 6)
+#define TIMER_MODE (1 << 0)
+
+static void timer_update_irq(struct Msf2Timer *st)
+{
+ bool isr, ier;
+
+ isr = !!(st->regs[R_TIM_RIS] & TIMER_RIS_ACK);
+ ier = !!(st->regs[R_TIM_CTRL] & TIMER_CTRL_INTR);
+ qemu_set_irq(st->irq, (ier && isr));
+}
+
+static void timer_update(struct Msf2Timer *st)
+{
+ uint64_t count;
+
+ if (!(st->regs[R_TIM_CTRL] & TIMER_CTRL_ENBL)) {
+ ptimer_stop(st->ptimer);
+ return;
+ }
+
+ count = st->regs[R_TIM_LOADVAL];
+ ptimer_set_limit(st->ptimer, count, 1);
+ ptimer_run(st->ptimer, 1);
+}
+
+static uint64_t
+timer_read(void *opaque, hwaddr offset, unsigned int size)
+{
+ MSSTimerState *t = opaque;
+ hwaddr addr;
+ struct Msf2Timer *st;
+ uint32_t ret = 0;
+ int timer = 0;
+ int isr;
+ int ier;
+
+ addr = offset >> 2;
+ /*
+ * Two independent timers has same base address.
+ * Based on address passed figure out which timer is being used.
+ */
+ if ((addr >= R_TIM1_MAX) && (addr < NUM_TIMERS * R_TIM1_MAX)) {
+ timer = 1;
+ addr -= R_TIM1_MAX;
+ }
+
+ st = &t->timers[timer];
+
+ switch (addr) {
+ case R_TIM_VAL:
+ ret = ptimer_get_count(st->ptimer);
+ break;
+
+ case R_TIM_MIS:
+ isr = !!(st->regs[R_TIM_RIS] & TIMER_RIS_ACK);
+ ier = !!(st->regs[R_TIM_CTRL] & TIMER_CTRL_INTR);
+ ret = ier & isr;
+ break;
+
+ default:
+ if (addr < R_TIM1_MAX) {
+ ret = st->regs[addr];
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ TYPE_MSS_TIMER": 64-bit mode not supported\n");
+ return ret;
+ }
+ break;
+ }
+
+ DB_PRINT("timer=%d 0x%" HWADDR_PRIx "=0x%" PRIx32, timer, offset,
+ ret);
+ return ret;
+}
+
+static void
+timer_write(void *opaque, hwaddr offset,
+ uint64_t val64, unsigned int size)
+{
+ MSSTimerState *t = opaque;
+ hwaddr addr;
+ struct Msf2Timer *st;
+ int timer = 0;
+ uint32_t value = val64;
+
+ addr = offset >> 2;
+ /*
+ * Two independent timers has same base address.
+ * Based on addr passed figure out which timer is being used.
+ */
+ if ((addr >= R_TIM1_MAX) && (addr < NUM_TIMERS * R_TIM1_MAX)) {
+ timer = 1;
+ addr -= R_TIM1_MAX;
+ }
+
+ st = &t->timers[timer];
+
+ DB_PRINT("addr=0x%" HWADDR_PRIx " val=0x%" PRIx32 " (timer=%d)", offset,
+ value, timer);
+
+ switch (addr) {
+ case R_TIM_CTRL:
+ st->regs[R_TIM_CTRL] = value;
+ timer_update(st);
+ break;
+
+ case R_TIM_RIS:
+ if (value & TIMER_RIS_ACK) {
+ st->regs[R_TIM_RIS] &= ~TIMER_RIS_ACK;
+ }
+ break;
+
+ case R_TIM_LOADVAL:
+ st->regs[R_TIM_LOADVAL] = value;
+ if (st->regs[R_TIM_CTRL] & TIMER_CTRL_ENBL) {
+ timer_update(st);
+ }
+ break;
+
+ case R_TIM_BGLOADVAL:
+ st->regs[R_TIM_BGLOADVAL] = value;
+ st->regs[R_TIM_LOADVAL] = value;
+ break;
+
+ case R_TIM_VAL:
+ case R_TIM_MIS:
+ break;
+
+ default:
+ if (addr < R_TIM1_MAX) {
+ st->regs[addr] = value;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ TYPE_MSS_TIMER": 64-bit mode not supported\n");
+ return;
+ }
+ break;
+ }
+ timer_update_irq(st);
+}
+
+static const MemoryRegionOps timer_ops = {
+ .read = timer_read,
+ .write = timer_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4
+ }
+};
+
+static void timer_hit(void *opaque)
+{
+ struct Msf2Timer *st = opaque;
+
+ st->regs[R_TIM_RIS] |= TIMER_RIS_ACK;
+
+ if (!(st->regs[R_TIM_CTRL] & TIMER_CTRL_ONESHOT)) {
+ timer_update(st);
+ }
+ timer_update_irq(st);
+}
+
+static void mss_timer_init(Object *obj)
+{
+ MSSTimerState *t = MSS_TIMER(obj);
+ int i;
+
+ /* Init all the ptimers. */
+ for (i = 0; i < NUM_TIMERS; i++) {
+ struct Msf2Timer *st = &t->timers[i];
+
+ st->bh = qemu_bh_new(timer_hit, st);
+ st->ptimer = ptimer_init(st->bh, PTIMER_POLICY_DEFAULT);
+ ptimer_set_freq(st->ptimer, t->freq_hz);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &st->irq);
+ }
+
+ memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, TYPE_MSS_TIMER,
+ NUM_TIMERS * R_TIM1_MAX * 4);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &t->mmio);
+}
+
+static const VMStateDescription vmstate_timers = {
+ .name = "mss-timer-block",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PTIMER(ptimer, struct Msf2Timer),
+ VMSTATE_UINT32_ARRAY(regs, struct Msf2Timer, R_TIM1_MAX),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_mss_timer = {
+ .name = TYPE_MSS_TIMER,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(freq_hz, MSSTimerState),
+ VMSTATE_STRUCT_ARRAY(timers, MSSTimerState, NUM_TIMERS, 0,
+ vmstate_timers, struct Msf2Timer),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property mss_timer_properties[] = {
+ /* Libero GUI shows 100Mhz as default for clocks */
+ DEFINE_PROP_UINT32("clock-frequency", MSSTimerState, freq_hz,
+ 100 * 1000000),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mss_timer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = mss_timer_properties;
+ dc->vmsd = &vmstate_mss_timer;
+}
+
+static const TypeInfo mss_timer_info = {
+ .name = TYPE_MSS_TIMER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MSSTimerState),
+ .instance_init = mss_timer_init,
+ .class_init = mss_timer_class_init,
+};
+
+static void mss_timer_register_types(void)
+{
+ type_register_static(&mss_timer_info);
+}
+
+type_init(mss_timer_register_types)
diff --git a/hw/timer/omap_gptimer.c b/hw/timer/omap_gptimer.c
index 5e3e8a6d70..6d7c8a396f 100644
--- a/hw/timer/omap_gptimer.c
+++ b/hw/timer/omap_gptimer.c
@@ -450,19 +450,44 @@ static void omap_gp_timer_writeh(void *opaque, hwaddr addr,
s->writeh = (uint16_t) value;
}
+static uint64_t omap_gp_timer_readfn(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ switch (size) {
+ case 1:
+ return omap_badwidth_read32(opaque, addr);
+ case 2:
+ return omap_gp_timer_readh(opaque, addr);
+ case 4:
+ return omap_gp_timer_readw(opaque, addr);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void omap_gp_timer_writefn(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ switch (size) {
+ case 1:
+ omap_badwidth_write32(opaque, addr, value);
+ break;
+ case 2:
+ omap_gp_timer_writeh(opaque, addr, value);
+ break;
+ case 4:
+ omap_gp_timer_write(opaque, addr, value);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
static const MemoryRegionOps omap_gp_timer_ops = {
- .old_mmio = {
- .read = {
- omap_badwidth_read32,
- omap_gp_timer_readh,
- omap_gp_timer_readw,
- },
- .write = {
- omap_badwidth_write32,
- omap_gp_timer_writeh,
- omap_gp_timer_write,
- },
- },
+ .read = omap_gp_timer_readfn,
+ .write = omap_gp_timer_writefn,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
diff --git a/hw/timer/omap_synctimer.c b/hw/timer/omap_synctimer.c
index 9ee6519793..0d75a90f3a 100644
--- a/hw/timer/omap_synctimer.c
+++ b/hw/timer/omap_synctimer.c
@@ -68,25 +68,32 @@ static uint32_t omap_synctimer_readh(void *opaque, hwaddr addr)
}
}
-static void omap_synctimer_write(void *opaque, hwaddr addr,
- uint32_t value)
+static uint64_t omap_synctimer_readfn(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ switch (size) {
+ case 1:
+ return omap_badwidth_read32(opaque, addr);
+ case 2:
+ return omap_synctimer_readh(opaque, addr);
+ case 4:
+ return omap_synctimer_readw(opaque, addr);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void omap_synctimer_writefn(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
{
OMAP_BAD_REG(addr);
}
static const MemoryRegionOps omap_synctimer_ops = {
- .old_mmio = {
- .read = {
- omap_badwidth_read32,
- omap_synctimer_readh,
- omap_synctimer_readw,
- },
- .write = {
- omap_badwidth_write32,
- omap_synctimer_write,
- omap_synctimer_write,
- },
- },
+ .read = omap_synctimer_readfn,
+ .write = omap_synctimer_writefn,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index 757e365562..9255234c63 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -26,8 +26,10 @@ common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o
ifeq ($(CONFIG_USB_SMARTCARD),y)
common-obj-y += dev-smartcard-reader.o
-common-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o
-common-obj-$(CONFIG_SMARTCARD) += ccid-card-emulated.o
+common-obj-$(CONFIG_SMARTCARD) += smartcard.mo
+smartcard.mo-objs := ccid-card-passthru.o ccid-card-emulated.o
+smartcard.mo-cflags := $(SMARTCARD_CFLAGS)
+smartcard.mo-libs := $(SMARTCARD_LIBS)
endif
ifeq ($(CONFIG_POSIX),y)
@@ -36,6 +38,8 @@ endif
# usb redirection
common-obj-$(CONFIG_USB_REDIR) += redirect.o quirks.o
+redirect.o-cflags = $(USB_REDIR_CFLAGS)
+redirect.o-libs = $(USB_REDIR_LIBS)
# usb pass-through
ifeq ($(CONFIG_LIBUSB)$(CONFIG_USB),yy)
@@ -44,6 +48,11 @@ else
common-obj-y += host-stub.o
endif
+host-libusb.o-cflags := $(LIBUSB_CFLAGS)
+host-libusb.o-libs := $(LIBUSB_LIBS)
+
ifeq ($(CONFIG_USB_LIBUSB),y)
common-obj-$(CONFIG_XEN) += xen-usb.o
+xen-usb.o-cflags := $(LIBUSB_CFLAGS)
+xen-usb.o-libs := $(LIBUSB_LIBS)
endif
diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h
index 191d9caea1..aa39a9aa5f 100644
--- a/hw/xen/xen_pt.h
+++ b/hw/xen/xen_pt.h
@@ -180,6 +180,7 @@ typedef struct XenPTMSI {
uint32_t addr_hi; /* guest message upper address */
uint16_t data; /* guest message data */
uint32_t ctrl_offset; /* saved control offset */
+ uint32_t mask; /* guest mask bits */
int pirq; /* guest pirq corresponding */
bool initialized; /* when guest MSI is initialized */
bool mapped; /* when pirq is mapped */
diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c
index 1f04ec5eec..a3ce33e78b 100644
--- a/hw/xen/xen_pt_config_init.c
+++ b/hw/xen/xen_pt_config_init.c
@@ -1315,6 +1315,22 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s,
return 0;
}
+static int xen_pt_mask_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
+ uint32_t *val, uint32_t dev_value,
+ uint32_t valid_mask)
+{
+ int rc;
+
+ rc = xen_pt_long_reg_write(s, cfg_entry, val, dev_value, valid_mask);
+ if (rc) {
+ return rc;
+ }
+
+ s->msi->mask = *val;
+
+ return 0;
+}
+
/* MSI Capability Structure reg static information table */
static XenPTRegInfo xen_pt_emu_reg_msi[] = {
/* Next Pointer reg */
@@ -1393,7 +1409,7 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = {
.emu_mask = 0xFFFFFFFF,
.init = xen_pt_mask_reg_init,
.u.dw.read = xen_pt_long_reg_read,
- .u.dw.write = xen_pt_long_reg_write,
+ .u.dw.write = xen_pt_mask_reg_write,
},
/* Mask reg (if PCI_MSI_FLAGS_MASKBIT set, for 64-bit devices) */
{
@@ -1404,7 +1420,7 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] = {
.emu_mask = 0xFFFFFFFF,
.init = xen_pt_mask_reg_init,
.u.dw.read = xen_pt_long_reg_read,
- .u.dw.write = xen_pt_long_reg_write,
+ .u.dw.write = xen_pt_mask_reg_write,
},
/* Pending reg (if PCI_MSI_FLAGS_MASKBIT set, for 32-bit devices) */
{
diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c
index ff9a79f5d2..6d1e3bdeb4 100644
--- a/hw/xen/xen_pt_msi.c
+++ b/hw/xen/xen_pt_msi.c
@@ -24,6 +24,7 @@
#define XEN_PT_GFLAGS_SHIFT_DM 9
#define XEN_PT_GFLAGSSHIFT_DELIV_MODE 12
#define XEN_PT_GFLAGSSHIFT_TRG_MODE 15
+#define XEN_PT_GFLAGSSHIFT_UNMASKED 16
#define latch(fld) latch[PCI_MSIX_ENTRY_##fld / sizeof(uint32_t)]
@@ -155,7 +156,8 @@ static int msi_msix_update(XenPCIPassthroughState *s,
int pirq,
bool is_msix,
int msix_entry,
- int *old_pirq)
+ int *old_pirq,
+ bool masked)
{
PCIDevice *d = &s->dev;
uint8_t gvec = msi_vector(data);
@@ -171,6 +173,8 @@ static int msi_msix_update(XenPCIPassthroughState *s,
table_addr = s->msix->mmio_base_addr;
}
+ gflags |= masked ? 0 : (1u << XEN_PT_GFLAGSSHIFT_UNMASKED);
+
rc = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec,
pirq, gflags, table_addr);
@@ -273,8 +277,10 @@ int xen_pt_msi_setup(XenPCIPassthroughState *s)
int xen_pt_msi_update(XenPCIPassthroughState *s)
{
XenPTMSI *msi = s->msi;
+
+ /* Current MSI emulation in QEMU only supports 1 vector */
return msi_msix_update(s, msi_addr64(msi), msi->data, msi->pirq,
- false, 0, &msi->pirq);
+ false, 0, &msi->pirq, msi->mask & 1);
}
void xen_pt_msi_disable(XenPCIPassthroughState *s)
@@ -355,7 +361,8 @@ static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr,
}
rc = msi_msix_update(s, entry->addr, entry->data, pirq, true,
- entry_nr, &entry->pirq);
+ entry_nr, &entry->pirq,
+ vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT);
if (!rc) {
entry->updated = false;
diff --git a/include/glib-compat.h b/include/glib-compat.h
index fcffcd3f07..e15aca2d40 100644
--- a/include/glib-compat.h
+++ b/include/glib-compat.h
@@ -223,6 +223,8 @@ static inline gboolean g_hash_table_contains(GHashTable *hash_table,
{
return g_hash_table_lookup_extended(hash_table, key, NULL, NULL);
}
+#define G_SOURCE_CONTINUE TRUE
+#define G_SOURCE_REMOVE FALSE
#endif
#ifndef g_assert_true
diff --git a/include/hw/arm/msf2-soc.h b/include/hw/arm/msf2-soc.h
new file mode 100644
index 0000000000..3cfe5c76ee
--- /dev/null
+++ b/include/hw/arm/msf2-soc.h
@@ -0,0 +1,67 @@
+/*
+ * Microsemi Smartfusion2 SoC
+ *
+ * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_ARM_MSF2_SOC_H
+#define HW_ARM_MSF2_SOC_H
+
+#include "hw/arm/armv7m.h"
+#include "hw/timer/mss-timer.h"
+#include "hw/misc/msf2-sysreg.h"
+#include "hw/ssi/mss-spi.h"
+
+#define TYPE_MSF2_SOC "msf2-soc"
+#define MSF2_SOC(obj) OBJECT_CHECK(MSF2State, (obj), TYPE_MSF2_SOC)
+
+#define MSF2_NUM_SPIS 2
+#define MSF2_NUM_UARTS 2
+
+/*
+ * System timer consists of two programmable 32-bit
+ * decrementing counters that generate individual interrupts to
+ * the Cortex-M3 processor
+ */
+#define MSF2_NUM_TIMERS 2
+
+typedef struct MSF2State {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ ARMv7MState armv7m;
+
+ char *cpu_type;
+ char *part_name;
+ uint64_t envm_size;
+ uint64_t esram_size;
+
+ uint32_t m3clk;
+ uint8_t apb0div;
+ uint8_t apb1div;
+
+ MSF2SysregState sysreg;
+ MSSTimerState timer;
+ MSSSpiState spi[MSF2_NUM_SPIS];
+} MSF2State;
+
+#endif
diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h
index 180e00e32c..e641012b48 100644
--- a/include/hw/ide/internal.h
+++ b/include/hw/ide/internal.h
@@ -333,8 +333,7 @@ struct unreported_events {
};
enum ide_dma_cmd {
- IDE_DMA__BEGIN = 0,
- IDE_DMA_READ = IDE_DMA__BEGIN,
+ IDE_DMA_READ = 0,
IDE_DMA_WRITE,
IDE_DMA_TRIM,
IDE_DMA_ATAPI,
diff --git a/include/hw/intc/armv7m_nvic.h b/include/hw/intc/armv7m_nvic.h
index 1a4cce7442..ac7997ca8c 100644
--- a/include/hw/intc/armv7m_nvic.h
+++ b/include/hw/intc/armv7m_nvic.h
@@ -21,6 +21,8 @@
/* Highest permitted number of exceptions (architectural limit) */
#define NVIC_MAX_VECTORS 512
+/* Number of internal exceptions */
+#define NVIC_INTERNAL_VECTORS 16
typedef struct VecInfo {
/* Exception priorities can range from -3 to 255; only the unmodifiable
@@ -41,13 +43,38 @@ typedef struct NVICState {
ARMCPU *cpu;
VecInfo vectors[NVIC_MAX_VECTORS];
- uint32_t prigroup;
+ /* If the v8M security extension is implemented, some of the internal
+ * exceptions are banked between security states (ie there exists both
+ * a Secure and a NonSecure version of the exception and its state):
+ * HardFault, MemManage, UsageFault, SVCall, PendSV, SysTick (R_PJHV)
+ * The rest (including all the external exceptions) are not banked, though
+ * they may be configurable to target either Secure or NonSecure state.
+ * We store the secure exception state in sec_vectors[] for the banked
+ * exceptions, and otherwise use only vectors[] (including for exceptions
+ * like SecureFault that unconditionally target Secure state).
+ * Entries in sec_vectors[] for non-banked exception numbers are unused.
+ */
+ VecInfo sec_vectors[NVIC_INTERNAL_VECTORS];
+ /* The PRIGROUP field in AIRCR is banked */
+ uint32_t prigroup[M_REG_NUM_BANKS];
+
+ /* v8M NVIC_ITNS state (stored as a bool per bit) */
+ bool itns[NVIC_MAX_VECTORS];
- /* vectpending and exception_prio are both cached state that can
- * be recalculated from the vectors[] array and the prigroup field.
+ /* The following fields are all cached state that can be recalculated
+ * from the vectors[] and sec_vectors[] arrays and the prigroup field:
+ * - vectpending
+ * - vectpending_is_secure
+ * - exception_prio
+ * - vectpending_prio
*/
unsigned int vectpending; /* highest prio pending enabled exception */
+ /* true if vectpending is a banked secure exception, ie it is in
+ * sec_vectors[] rather than vectors[]
+ */
+ bool vectpending_is_s_banked;
int exception_prio; /* group prio of the highest prio active exception */
+ int vectpending_prio; /* group prio of the exeception in vectpending */
MemoryRegion sysregmem;
MemoryRegion sysreg_ns_mem;
diff --git a/include/hw/misc/msf2-sysreg.h b/include/hw/misc/msf2-sysreg.h
new file mode 100644
index 0000000000..5993f67b4e
--- /dev/null
+++ b/include/hw/misc/msf2-sysreg.h
@@ -0,0 +1,77 @@
+/*
+ * Microsemi SmartFusion2 SYSREG
+ *
+ * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_MSF2_SYSREG_H
+#define HW_MSF2_SYSREG_H
+
+#include "hw/sysbus.h"
+
+enum {
+ ESRAM_CR = 0x00 / 4,
+ ESRAM_MAX_LAT,
+ DDR_CR,
+ ENVM_CR,
+ ENVM_REMAP_BASE_CR,
+ ENVM_REMAP_FAB_CR,
+ CC_CR,
+ CC_REGION_CR,
+ CC_LOCK_BASE_ADDR_CR,
+ CC_FLUSH_INDX_CR,
+ DDRB_BUF_TIMER_CR,
+ DDRB_NB_ADDR_CR,
+ DDRB_NB_SIZE_CR,
+ DDRB_CR,
+
+ SOFT_RESET_CR = 0x48 / 4,
+ M3_CR,
+
+ GPIO_SYSRESET_SEL_CR = 0x58 / 4,
+
+ MDDR_CR = 0x60 / 4,
+
+ MSSDDR_PLL_STATUS_LOW_CR = 0x90 / 4,
+ MSSDDR_PLL_STATUS_HIGH_CR,
+ MSSDDR_FACC1_CR,
+ MSSDDR_FACC2_CR,
+
+ MSSDDR_PLL_STATUS = 0x150 / 4,
+};
+
+#define MSF2_SYSREG_MMIO_SIZE 0x300
+
+#define TYPE_MSF2_SYSREG "msf2-sysreg"
+#define MSF2_SYSREG(obj) OBJECT_CHECK(MSF2SysregState, (obj), TYPE_MSF2_SYSREG)
+
+typedef struct MSF2SysregState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+
+ uint8_t apb0div;
+ uint8_t apb1div;
+
+ uint32_t regs[MSF2_SYSREG_MMIO_SIZE / 4];
+} MSF2SysregState;
+
+#endif /* HW_MSF2_SYSREG_H */
diff --git a/include/hw/net/mii.h b/include/hw/net/mii.h
index 6ce48a6d78..4ae4dcce7e 100644
--- a/include/hw/net/mii.h
+++ b/include/hw/net/mii.h
@@ -104,6 +104,10 @@
#define RTL8211E_PHYID1 0x001c
#define RTL8211E_PHYID2 0xc915
+/* National Semiconductor DP83840 */
+#define DP83840_PHYID1 0x2000
+#define DP83840_PHYID2 0x5c01
+
/* National Semiconductor DP83848 */
#define DP83848_PHYID1 0x2000
#define DP83848_PHYID2 0x5c90
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index b9c2bad851..35df1874a9 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -187,6 +187,7 @@
#define PCI_VENDOR_ID_SUN 0x108e
#define PCI_DEVICE_ID_SUN_EBUS 0x1000
+#define PCI_DEVICE_ID_SUN_HME 0x1001
#define PCI_DEVICE_ID_SUN_SIMBA 0x5000
#define PCI_DEVICE_ID_SUN_SABRE 0xa000
diff --git a/include/hw/ssi/mss-spi.h b/include/hw/ssi/mss-spi.h
new file mode 100644
index 0000000000..f0cf3243e0
--- /dev/null
+++ b/include/hw/ssi/mss-spi.h
@@ -0,0 +1,58 @@
+/*
+ * Microsemi SmartFusion2 SPI
+ *
+ * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_MSS_SPI_H
+#define HW_MSS_SPI_H
+
+#include "hw/sysbus.h"
+#include "hw/ssi/ssi.h"
+#include "qemu/fifo32.h"
+
+#define TYPE_MSS_SPI "mss-spi"
+#define MSS_SPI(obj) OBJECT_CHECK(MSSSpiState, (obj), TYPE_MSS_SPI)
+
+#define R_SPI_MAX 16
+
+typedef struct MSSSpiState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion mmio;
+
+ qemu_irq irq;
+
+ qemu_irq cs_line;
+
+ SSIBus *spi;
+
+ Fifo32 rx_fifo;
+ Fifo32 tx_fifo;
+
+ int fifo_depth;
+ uint32_t frame_count;
+ bool enabled;
+
+ uint32_t regs[R_SPI_MAX];
+} MSSSpiState;
+
+#endif /* HW_MSS_SPI_H */
diff --git a/include/hw/timer/mss-timer.h b/include/hw/timer/mss-timer.h
new file mode 100644
index 0000000000..d15d1732f8
--- /dev/null
+++ b/include/hw/timer/mss-timer.h
@@ -0,0 +1,64 @@
+/*
+ * Microsemi SmartFusion2 Timer.
+ *
+ * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_MSS_TIMER_H
+#define HW_MSS_TIMER_H
+
+#include "hw/sysbus.h"
+#include "hw/ptimer.h"
+
+#define TYPE_MSS_TIMER "mss-timer"
+#define MSS_TIMER(obj) OBJECT_CHECK(MSSTimerState, \
+ (obj), TYPE_MSS_TIMER)
+
+/*
+ * There are two 32-bit down counting timers.
+ * Timers 1 and 2 can be concatenated into a single 64-bit Timer
+ * that operates either in Periodic mode or in One-shot mode.
+ * Writing 1 to the TIM64_MODE register bit 0 sets the Timers in 64-bit mode.
+ * In 64-bit mode, writing to the 32-bit registers has no effect.
+ * Similarly, in 32-bit mode, writing to the 64-bit mode registers
+ * has no effect. Only two 32-bit timers are supported currently.
+ */
+#define NUM_TIMERS 2
+
+#define R_TIM1_MAX 6
+
+struct Msf2Timer {
+ QEMUBH *bh;
+ ptimer_state *ptimer;
+
+ uint32_t regs[R_TIM1_MAX];
+ qemu_irq irq;
+};
+
+typedef struct MSSTimerState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion mmio;
+ uint32_t freq_hz;
+ struct Msf2Timer timers[NUM_TIMERS];
+} MSSTimerState;
+
+#endif /* HW_MSS_TIMER_H */
diff --git a/include/migration/register.h b/include/migration/register.h
index a0f1edd8c7..f4f7bdc177 100644
--- a/include/migration/register.h
+++ b/include/migration/register.h
@@ -24,6 +24,7 @@ typedef struct SaveVMHandlers {
/* This runs both outside and inside the iothread lock. */
bool (*is_active)(void *opaque);
+ bool (*has_postcopy)(void *opaque);
/* This runs outside the iothread lock in the migration case, and
* within the lock in the savevm case. The callback had better only
diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h
index c318da12d7..509eeddece 100644
--- a/include/qemu/bitmap.h
+++ b/include/qemu/bitmap.h
@@ -39,6 +39,8 @@
* bitmap_clear(dst, pos, nbits) Clear specified bit area
* bitmap_test_and_clear_atomic(dst, pos, nbits) Test and clear area
* bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area
+ * bitmap_to_le(dst, src, nbits) Convert bitmap to little endian
+ * bitmap_from_le(dst, src, nbits) Convert bitmap from little endian
*/
/*
@@ -82,6 +84,7 @@ int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, long bits);
int slow_bitmap_intersects(const unsigned long *bitmap1,
const unsigned long *bitmap2, long bits);
+long slow_bitmap_count_one(const unsigned long *bitmap, long nbits);
static inline unsigned long *bitmap_try_new(long nbits)
{
@@ -216,6 +219,15 @@ static inline int bitmap_intersects(const unsigned long *src1,
}
}
+static inline long bitmap_count_one(const unsigned long *bitmap, long nbits)
+{
+ if (small_nbits(nbits)) {
+ return ctpopl(*bitmap & BITMAP_LAST_WORD_MASK(nbits));
+ } else {
+ return slow_bitmap_count_one(bitmap, nbits);
+ }
+}
+
void bitmap_set(unsigned long *map, long i, long len);
void bitmap_set_atomic(unsigned long *map, long i, long len);
void bitmap_clear(unsigned long *map, long start, long nr);
@@ -237,4 +249,9 @@ static inline unsigned long *bitmap_zero_extend(unsigned long *old,
return new;
}
+void bitmap_to_le(unsigned long *dst, const unsigned long *src,
+ long nbits);
+void bitmap_from_le(unsigned long *dst, const unsigned long *src,
+ long nbits);
+
#endif /* BITMAP_H */
diff --git a/include/sysemu/seccomp.h b/include/sysemu/seccomp.h
index e67c2dc840..9b092aa23f 100644
--- a/include/sysemu/seccomp.h
+++ b/include/sysemu/seccomp.h
@@ -21,7 +21,5 @@
#define QEMU_SECCOMP_SET_SPAWN (1 << 3)
#define QEMU_SECCOMP_SET_RESOURCECTL (1 << 4)
-#include <seccomp.h>
-
int seccomp_start(uint32_t seccomp_opts);
#endif
diff --git a/migration/Makefile.objs b/migration/Makefile.objs
index 1c7770da46..99e038024d 100644
--- a/migration/Makefile.objs
+++ b/migration/Makefile.objs
@@ -11,3 +11,4 @@ common-obj-$(CONFIG_RDMA) += rdma.o
common-obj-$(CONFIG_LIVE_BLOCK_MIGRATION) += block.o
+rdma.o-libs := $(RDMA_LIBS)
diff --git a/migration/channel.c b/migration/channel.c
index 3b7252f5a2..70ec7ea3b7 100644
--- a/migration/channel.c
+++ b/migration/channel.c
@@ -19,6 +19,14 @@
#include "qapi/error.h"
#include "io/channel-tls.h"
+/**
+ * @migration_channel_process_incoming - Create new incoming migration channel
+ *
+ * Notice that TLS is special. For it we listen in a listener socket,
+ * and then create a new client socket from the TLS library.
+ *
+ * @ioc: Channel to which we are connecting
+ */
void migration_channel_process_incoming(QIOChannel *ioc)
{
MigrationState *s = migrate_get_current();
@@ -36,12 +44,18 @@ void migration_channel_process_incoming(QIOChannel *ioc)
error_report_err(local_err);
}
} else {
- QEMUFile *f = qemu_fopen_channel_input(ioc);
- migration_fd_process_incoming(f);
+ migration_ioc_process_incoming(ioc);
}
}
+/**
+ * @migration_channel_connect - Create new outgoing migration channel
+ *
+ * @s: Current migration state
+ * @ioc: Channel to which we are connecting
+ * @hostname: Where we want to connect
+ */
void migration_channel_connect(MigrationState *s,
QIOChannel *ioc,
const char *hostname)
diff --git a/migration/exec.c b/migration/exec.c
index 08b599e0e2..f3be1baf2e 100644
--- a/migration/exec.c
+++ b/migration/exec.c
@@ -49,7 +49,7 @@ static gboolean exec_accept_incoming_migration(QIOChannel *ioc,
{
migration_channel_process_incoming(ioc);
object_unref(OBJECT(ioc));
- return FALSE; /* unregister */
+ return G_SOURCE_REMOVE;
}
void exec_start_incoming_migration(const char *command, Error **errp)
diff --git a/migration/fd.c b/migration/fd.c
index 30f5258a6a..30de4b9847 100644
--- a/migration/fd.c
+++ b/migration/fd.c
@@ -49,7 +49,7 @@ static gboolean fd_accept_incoming_migration(QIOChannel *ioc,
{
migration_channel_process_incoming(ioc);
object_unref(OBJECT(ioc));
- return FALSE; /* unregister */
+ return G_SOURCE_REMOVE;
}
void fd_start_incoming_migration(const char *infd, Error **errp)
diff --git a/migration/migration.c b/migration/migration.c
index 959e8ec88e..98429dc843 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -77,6 +77,8 @@
* Note: Please change this default value to 10000 when we support hybrid mode.
*/
#define DEFAULT_MIGRATE_X_CHECKPOINT_DELAY 200
+#define DEFAULT_MIGRATE_MULTIFD_CHANNELS 2
+#define DEFAULT_MIGRATE_MULTIFD_PAGE_COUNT 16
static NotifierList migration_state_notifiers =
NOTIFIER_LIST_INITIALIZER(migration_state_notifiers);
@@ -279,6 +281,10 @@ static void process_incoming_migration_bh(void *opaque)
*/
qemu_announce_self();
+ if (multifd_load_cleanup(&local_err) != 0) {
+ error_report_err(local_err);
+ autostart = false;
+ }
/* If global state section was not received or we are in running
state, we need to obey autostart. Any other state is set with
runstate_set. */
@@ -306,17 +312,16 @@ static void process_incoming_migration_bh(void *opaque)
static void process_incoming_migration_co(void *opaque)
{
- QEMUFile *f = opaque;
MigrationIncomingState *mis = migration_incoming_get_current();
PostcopyState ps;
int ret;
- mis->from_src_file = f;
+ assert(mis->from_src_file);
mis->largest_page_size = qemu_ram_pagesize_largest();
postcopy_state_set(POSTCOPY_INCOMING_NONE);
migrate_set_state(&mis->state, MIGRATION_STATUS_NONE,
MIGRATION_STATUS_ACTIVE);
- ret = qemu_loadvm_state(f);
+ ret = qemu_loadvm_state(mis->from_src_file);
ps = postcopy_state_get();
trace_process_incoming_migration_co_end(ret, ps);
@@ -352,24 +357,71 @@ static void process_incoming_migration_co(void *opaque)
}
if (ret < 0) {
+ Error *local_err = NULL;
+
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
MIGRATION_STATUS_FAILED);
error_report("load of migration failed: %s", strerror(-ret));
qemu_fclose(mis->from_src_file);
+ if (multifd_load_cleanup(&local_err) != 0) {
+ error_report_err(local_err);
+ }
exit(EXIT_FAILURE);
}
mis->bh = qemu_bh_new(process_incoming_migration_bh, mis);
qemu_bh_schedule(mis->bh);
}
-void migration_fd_process_incoming(QEMUFile *f)
+static void migration_incoming_setup(QEMUFile *f)
{
- Coroutine *co = qemu_coroutine_create(process_incoming_migration_co, f);
+ MigrationIncomingState *mis = migration_incoming_get_current();
+
+ if (multifd_load_setup() != 0) {
+ /* We haven't been able to create multifd threads
+ nothing better to do */
+ exit(EXIT_FAILURE);
+ }
+ if (!mis->from_src_file) {
+ mis->from_src_file = f;
+ }
qemu_file_set_blocking(f, false);
+}
+
+static void migration_incoming_process(void)
+{
+ Coroutine *co = qemu_coroutine_create(process_incoming_migration_co, NULL);
qemu_coroutine_enter(co);
}
+void migration_fd_process_incoming(QEMUFile *f)
+{
+ migration_incoming_setup(f);
+ migration_incoming_process();
+}
+
+void migration_ioc_process_incoming(QIOChannel *ioc)
+{
+ MigrationIncomingState *mis = migration_incoming_get_current();
+
+ if (!mis->from_src_file) {
+ QEMUFile *f = qemu_fopen_channel_input(ioc);
+ migration_fd_process_incoming(f);
+ }
+ /* We still only have a single channel. Nothing to do here yet */
+}
+
+/**
+ * @migration_has_all_channels: We have received all channels that we need
+ *
+ * Returns true when we have got connections to all the channels that
+ * we need for migration.
+ */
+bool migration_has_all_channels(void)
+{
+ return true;
+}
+
/*
* Send a 'SHUT' message on the return channel with the given value
* to indicate that we've finished with the RP. Non-0 value indicates
@@ -456,6 +508,10 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp)
params->x_checkpoint_delay = s->parameters.x_checkpoint_delay;
params->has_block_incremental = true;
params->block_incremental = s->parameters.block_incremental;
+ params->has_x_multifd_channels = true;
+ params->x_multifd_channels = s->parameters.x_multifd_channels;
+ params->has_x_multifd_page_count = true;
+ params->x_multifd_page_count = s->parameters.x_multifd_page_count;
return params;
}
@@ -603,6 +659,7 @@ static bool migrate_caps_check(bool *cap_list,
{
MigrationCapabilityStatusList *cap;
bool old_postcopy_cap;
+ MigrationIncomingState *mis = migration_incoming_get_current();
old_postcopy_cap = cap_list[MIGRATION_CAPABILITY_POSTCOPY_RAM];
@@ -636,7 +693,7 @@ static bool migrate_caps_check(bool *cap_list,
* special support.
*/
if (!old_postcopy_cap && runstate_check(RUN_STATE_INMIGRATE) &&
- !postcopy_ram_supported_by_host()) {
+ !postcopy_ram_supported_by_host(mis)) {
/* postcopy_ram_supported_by_host will have emitted a more
* detailed message
*/
@@ -737,6 +794,21 @@ static bool migrate_params_check(MigrationParameters *params, Error **errp)
"is invalid, it should be positive");
return false;
}
+ if (params->has_x_multifd_channels &&
+ (params->x_multifd_channels < 1 || params->x_multifd_channels > 255)) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
+ "multifd_channels",
+ "is invalid, it should be in the range of 1 to 255");
+ return false;
+ }
+ if (params->has_x_multifd_page_count &&
+ (params->x_multifd_page_count < 1 ||
+ params->x_multifd_page_count > 10000)) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
+ "multifd_page_count",
+ "is invalid, it should be in the range of 1 to 10000");
+ return false;
+ }
return true;
}
@@ -855,6 +927,12 @@ static void migrate_params_apply(MigrateSetParameters *params)
if (params->has_block_incremental) {
s->parameters.block_incremental = params->block_incremental;
}
+ if (params->has_x_multifd_channels) {
+ s->parameters.x_multifd_channels = params->x_multifd_channels;
+ }
+ if (params->has_x_multifd_page_count) {
+ s->parameters.x_multifd_page_count = params->x_multifd_page_count;
+ }
}
void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
@@ -968,6 +1046,8 @@ static void migrate_fd_cleanup(void *opaque)
s->cleanup_bh = NULL;
if (s->to_dst_file) {
+ Error *local_err = NULL;
+
trace_migrate_fd_cleanup();
qemu_mutex_unlock_iothread();
if (s->migration_thread_running) {
@@ -976,6 +1056,9 @@ static void migrate_fd_cleanup(void *opaque)
}
qemu_mutex_lock_iothread();
+ if (multifd_save_cleanup(&local_err) != 0) {
+ error_report_err(local_err);
+ }
qemu_fclose(s->to_dst_file);
s->to_dst_file = NULL;
}
@@ -1361,6 +1444,11 @@ bool migrate_postcopy_ram(void)
return s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_RAM];
}
+bool migrate_postcopy(void)
+{
+ return migrate_postcopy_ram();
+}
+
bool migrate_auto_converge(void)
{
MigrationState *s;
@@ -1424,6 +1512,33 @@ bool migrate_use_events(void)
return s->enabled_capabilities[MIGRATION_CAPABILITY_EVENTS];
}
+bool migrate_use_multifd(void)
+{
+ MigrationState *s;
+
+ s = migrate_get_current();
+
+ return s->enabled_capabilities[MIGRATION_CAPABILITY_X_MULTIFD];
+}
+
+int migrate_multifd_channels(void)
+{
+ MigrationState *s;
+
+ s = migrate_get_current();
+
+ return s->parameters.x_multifd_channels;
+}
+
+int migrate_multifd_page_count(void)
+{
+ MigrationState *s;
+
+ s = migrate_get_current();
+
+ return s->parameters.x_multifd_page_count;
+}
+
int migrate_use_xbzrle(void)
{
MigrationState *s;
@@ -1717,9 +1832,11 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running)
* need to tell the destination to throw any pages it's already received
* that are dirty
*/
- if (ram_postcopy_send_discard_bitmap(ms)) {
- error_report("postcopy send discard bitmap failed");
- goto fail;
+ if (migrate_postcopy_ram()) {
+ if (ram_postcopy_send_discard_bitmap(ms)) {
+ error_report("postcopy send discard bitmap failed");
+ goto fail;
+ }
}
/*
@@ -1728,8 +1845,10 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running)
* wrap their state up here
*/
qemu_file_set_rate_limit(ms->to_dst_file, INT64_MAX);
- /* Ping just for debugging, helps line traces up */
- qemu_savevm_send_ping(ms->to_dst_file, 2);
+ if (migrate_postcopy_ram()) {
+ /* Ping just for debugging, helps line traces up */
+ qemu_savevm_send_ping(ms->to_dst_file, 2);
+ }
/*
* While loading the device state we may trigger page transfer
@@ -1754,7 +1873,9 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running)
qemu_savevm_send_postcopy_listen(fb);
qemu_savevm_state_complete_precopy(fb, false, false);
- qemu_savevm_send_ping(fb, 3);
+ if (migrate_postcopy_ram()) {
+ qemu_savevm_send_ping(fb, 3);
+ }
qemu_savevm_send_postcopy_run(fb);
@@ -1789,11 +1910,13 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running)
qemu_mutex_unlock_iothread();
- /*
- * Although this ping is just for debug, it could potentially be
- * used for getting a better measurement of downtime at the source.
- */
- qemu_savevm_send_ping(ms->to_dst_file, 4);
+ if (migrate_postcopy_ram()) {
+ /*
+ * Although this ping is just for debug, it could potentially be
+ * used for getting a better measurement of downtime at the source.
+ */
+ qemu_savevm_send_ping(ms->to_dst_file, 4);
+ }
if (migrate_release_ram()) {
ram_postcopy_migrated_memory_release(ms);
@@ -1971,7 +2094,7 @@ static void *migration_thread(void *opaque)
qemu_savevm_send_ping(s->to_dst_file, 1);
}
- if (migrate_postcopy_ram()) {
+ if (migrate_postcopy()) {
/*
* Tell the destination that we *might* want to do postcopy later;
* if the other end can't do postcopy it should fail now, nice and
@@ -2004,7 +2127,7 @@ static void *migration_thread(void *opaque)
if (pending_size && pending_size >= threshold_size) {
/* Still a significant amount to transfer */
- if (migrate_postcopy_ram() &&
+ if (migrate_postcopy() &&
s->state != MIGRATION_STATUS_POSTCOPY_ACTIVE &&
pend_nonpost <= threshold_size &&
atomic_read(&s->start_postcopy)) {
@@ -2138,6 +2261,12 @@ void migrate_fd_connect(MigrationState *s)
}
}
+ if (multifd_save_setup() != 0) {
+ migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
+ MIGRATION_STATUS_FAILED);
+ migrate_fd_cleanup(s);
+ return;
+ }
qemu_thread_create(&s->thread, "live_migration", migration_thread, s,
QEMU_THREAD_JOINABLE);
s->migration_thread_running = true;
@@ -2189,6 +2318,12 @@ static Property migration_properties[] = {
DEFINE_PROP_INT64("x-checkpoint-delay", MigrationState,
parameters.x_checkpoint_delay,
DEFAULT_MIGRATE_X_CHECKPOINT_DELAY),
+ DEFINE_PROP_INT64("x-multifd-channels", MigrationState,
+ parameters.x_multifd_channels,
+ DEFAULT_MIGRATE_MULTIFD_CHANNELS),
+ DEFINE_PROP_INT64("x-multifd-page-count", MigrationState,
+ parameters.x_multifd_page_count,
+ DEFAULT_MIGRATE_MULTIFD_PAGE_COUNT),
/* Migration capabilities */
DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE),
@@ -2202,6 +2337,7 @@ static Property migration_properties[] = {
DEFINE_PROP_MIG_CAP("x-release-ram", MIGRATION_CAPABILITY_RELEASE_RAM),
DEFINE_PROP_MIG_CAP("x-block", MIGRATION_CAPABILITY_BLOCK),
DEFINE_PROP_MIG_CAP("x-return-path", MIGRATION_CAPABILITY_RETURN_PATH),
+ DEFINE_PROP_MIG_CAP("x-multifd", MIGRATION_CAPABILITY_X_MULTIFD),
DEFINE_PROP_END_OF_LIST(),
};
@@ -2245,6 +2381,8 @@ static void migration_instance_init(Object *obj)
params->has_downtime_limit = true;
params->has_x_checkpoint_delay = true;
params->has_block_incremental = true;
+ params->has_x_multifd_channels = true;
+ params->has_x_multifd_page_count = true;
}
/*
diff --git a/migration/migration.h b/migration/migration.h
index 148c9facbc..b83cceadc4 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -20,6 +20,7 @@
#include "exec/cpu-common.h"
#include "qemu/coroutine_int.h"
#include "hw/qdev.h"
+#include "io/channel.h"
/* State for the incoming migration */
struct MigrationIncomingState {
@@ -152,6 +153,9 @@ struct MigrationState
void migrate_set_state(int *state, int old_state, int new_state);
void migration_fd_process_incoming(QEMUFile *f);
+void migration_ioc_process_incoming(QIOChannel *ioc);
+
+bool migration_has_all_channels(void);
uint64_t migrate_max_downtime(void);
@@ -165,11 +169,16 @@ bool migration_is_blocked(Error **errp);
bool migration_in_postcopy(void);
MigrationState *migrate_get_current(void);
+bool migrate_postcopy(void);
+
bool migrate_release_ram(void);
bool migrate_postcopy_ram(void);
bool migrate_zero_blocks(void);
bool migrate_auto_converge(void);
+bool migrate_use_multifd(void);
+int migrate_multifd_channels(void);
+int migrate_multifd_page_count(void);
int migrate_use_xbzrle(void);
int64_t migrate_xbzrle_cache_size(void);
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index 7e21e6fd36..0de68e8b25 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -61,15 +61,66 @@ struct PostcopyDiscardState {
#include <sys/eventfd.h>
#include <linux/userfaultfd.h>
-static bool ufd_version_check(int ufd)
+
+/**
+ * receive_ufd_features: check userfault fd features, to request only supported
+ * features in the future.
+ *
+ * Returns: true on success
+ *
+ * __NR_userfaultfd - should be checked before
+ * @features: out parameter will contain uffdio_api.features provided by kernel
+ * in case of success
+ */
+static bool receive_ufd_features(uint64_t *features)
{
- struct uffdio_api api_struct;
- uint64_t ioctl_mask;
+ struct uffdio_api api_struct = {0};
+ int ufd;
+ bool ret = true;
+
+ /* if we are here __NR_userfaultfd should exists */
+ ufd = syscall(__NR_userfaultfd, O_CLOEXEC);
+ if (ufd == -1) {
+ error_report("%s: syscall __NR_userfaultfd failed: %s", __func__,
+ strerror(errno));
+ return false;
+ }
+ /* ask features */
api_struct.api = UFFD_API;
api_struct.features = 0;
if (ioctl(ufd, UFFDIO_API, &api_struct)) {
- error_report("postcopy_ram_supported_by_host: UFFDIO_API failed: %s",
+ error_report("%s: UFFDIO_API failed: %s", __func__,
+ strerror(errno));
+ ret = false;
+ goto release_ufd;
+ }
+
+ *features = api_struct.features;
+
+release_ufd:
+ close(ufd);
+ return ret;
+}
+
+/**
+ * request_ufd_features: this function should be called only once on a newly
+ * opened ufd, subsequent calls will lead to error.
+ *
+ * Returns: true on succes
+ *
+ * @ufd: fd obtained from userfaultfd syscall
+ * @features: bit mask see UFFD_API_FEATURES
+ */
+static bool request_ufd_features(int ufd, uint64_t features)
+{
+ struct uffdio_api api_struct = {0};
+ uint64_t ioctl_mask;
+
+ api_struct.api = UFFD_API;
+ api_struct.features = features;
+ if (ioctl(ufd, UFFDIO_API, &api_struct)) {
+ error_report("%s failed: UFFDIO_API failed: %s", __func__,
strerror(errno));
return false;
}
@@ -82,11 +133,42 @@ static bool ufd_version_check(int ufd)
return false;
}
+ return true;
+}
+
+static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis)
+{
+ uint64_t asked_features = 0;
+ static uint64_t supported_features;
+
+ /*
+ * it's not possible to
+ * request UFFD_API twice per one fd
+ * userfault fd features is persistent
+ */
+ if (!supported_features) {
+ if (!receive_ufd_features(&supported_features)) {
+ error_report("%s failed", __func__);
+ return false;
+ }
+ }
+
+ /*
+ * request features, even if asked_features is 0, due to
+ * kernel expects UFFD_API before UFFDIO_REGISTER, per
+ * userfault file descriptor
+ */
+ if (!request_ufd_features(ufd, asked_features)) {
+ error_report("%s failed: features %" PRIu64, __func__,
+ asked_features);
+ return false;
+ }
+
if (getpagesize() != ram_pagesize_summary()) {
bool have_hp = false;
/* We've got a huge page */
#ifdef UFFD_FEATURE_MISSING_HUGETLBFS
- have_hp = api_struct.features & UFFD_FEATURE_MISSING_HUGETLBFS;
+ have_hp = supported_features & UFFD_FEATURE_MISSING_HUGETLBFS;
#endif
if (!have_hp) {
error_report("Userfault on this host does not support huge pages");
@@ -124,7 +206,7 @@ static int test_ramblock_postcopiable(const char *block_name, void *host_addr,
* normally fine since if the postcopy succeeds it gets turned back on at the
* end.
*/
-bool postcopy_ram_supported_by_host(void)
+bool postcopy_ram_supported_by_host(MigrationIncomingState *mis)
{
long pagesize = getpagesize();
int ufd = -1;
@@ -147,7 +229,7 @@ bool postcopy_ram_supported_by_host(void)
}
/* Version and features check */
- if (!ufd_version_check(ufd)) {
+ if (!ufd_check_and_apply(ufd, mis)) {
goto out;
}
@@ -523,7 +605,7 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis)
* Although the host check already tested the API, we need to
* do the check again as an ABI handshake on the new fd.
*/
- if (!ufd_version_check(mis->userfault_fd)) {
+ if (!ufd_check_and_apply(mis->userfault_fd, mis)) {
return -1;
}
@@ -661,7 +743,7 @@ void *postcopy_get_tmp_page(MigrationIncomingState *mis)
#else
/* No target OS support, stubs just fail */
-bool postcopy_ram_supported_by_host(void)
+bool postcopy_ram_supported_by_host(MigrationIncomingState *mis)
{
error_report("%s: No OS support", __func__);
return false;
diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h
index 52d51e8007..587a8b86a7 100644
--- a/migration/postcopy-ram.h
+++ b/migration/postcopy-ram.h
@@ -14,7 +14,7 @@
#define QEMU_POSTCOPY_RAM_H
/* Return true if the host supports everything we need to do postcopy-ram */
-bool postcopy_ram_supported_by_host(void);
+bool postcopy_ram_supported_by_host(MigrationIncomingState *mis);
/*
* Make all of RAM sensitive to accesses to areas that haven't yet been written
diff --git a/migration/ram.c b/migration/ram.c
index e18b3e2d4f..88ca69e7b2 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -356,6 +356,208 @@ static void compress_threads_save_setup(void)
}
}
+/* Multiple fd's */
+
+struct MultiFDSendParams {
+ uint8_t id;
+ char *name;
+ QemuThread thread;
+ QemuSemaphore sem;
+ QemuMutex mutex;
+ bool quit;
+};
+typedef struct MultiFDSendParams MultiFDSendParams;
+
+struct {
+ MultiFDSendParams *params;
+ /* number of created threads */
+ int count;
+} *multifd_send_state;
+
+static void terminate_multifd_send_threads(Error *errp)
+{
+ int i;
+
+ for (i = 0; i < multifd_send_state->count; i++) {
+ MultiFDSendParams *p = &multifd_send_state->params[i];
+
+ qemu_mutex_lock(&p->mutex);
+ p->quit = true;
+ qemu_sem_post(&p->sem);
+ qemu_mutex_unlock(&p->mutex);
+ }
+}
+
+int multifd_save_cleanup(Error **errp)
+{
+ int i;
+ int ret = 0;
+
+ if (!migrate_use_multifd()) {
+ return 0;
+ }
+ terminate_multifd_send_threads(NULL);
+ for (i = 0; i < multifd_send_state->count; i++) {
+ MultiFDSendParams *p = &multifd_send_state->params[i];
+
+ qemu_thread_join(&p->thread);
+ qemu_mutex_destroy(&p->mutex);
+ qemu_sem_destroy(&p->sem);
+ g_free(p->name);
+ p->name = NULL;
+ }
+ g_free(multifd_send_state->params);
+ multifd_send_state->params = NULL;
+ g_free(multifd_send_state);
+ multifd_send_state = NULL;
+ return ret;
+}
+
+static void *multifd_send_thread(void *opaque)
+{
+ MultiFDSendParams *p = opaque;
+
+ while (true) {
+ qemu_mutex_lock(&p->mutex);
+ if (p->quit) {
+ qemu_mutex_unlock(&p->mutex);
+ break;
+ }
+ qemu_mutex_unlock(&p->mutex);
+ qemu_sem_wait(&p->sem);
+ }
+
+ return NULL;
+}
+
+int multifd_save_setup(void)
+{
+ int thread_count;
+ uint8_t i;
+
+ if (!migrate_use_multifd()) {
+ return 0;
+ }
+ thread_count = migrate_multifd_channels();
+ multifd_send_state = g_malloc0(sizeof(*multifd_send_state));
+ multifd_send_state->params = g_new0(MultiFDSendParams, thread_count);
+ multifd_send_state->count = 0;
+ for (i = 0; i < thread_count; i++) {
+ MultiFDSendParams *p = &multifd_send_state->params[i];
+
+ qemu_mutex_init(&p->mutex);
+ qemu_sem_init(&p->sem, 0);
+ p->quit = false;
+ p->id = i;
+ p->name = g_strdup_printf("multifdsend_%d", i);
+ qemu_thread_create(&p->thread, p->name, multifd_send_thread, p,
+ QEMU_THREAD_JOINABLE);
+
+ multifd_send_state->count++;
+ }
+ return 0;
+}
+
+struct MultiFDRecvParams {
+ uint8_t id;
+ char *name;
+ QemuThread thread;
+ QemuSemaphore sem;
+ QemuMutex mutex;
+ bool quit;
+};
+typedef struct MultiFDRecvParams MultiFDRecvParams;
+
+struct {
+ MultiFDRecvParams *params;
+ /* number of created threads */
+ int count;
+} *multifd_recv_state;
+
+static void terminate_multifd_recv_threads(Error *errp)
+{
+ int i;
+
+ for (i = 0; i < multifd_recv_state->count; i++) {
+ MultiFDRecvParams *p = &multifd_recv_state->params[i];
+
+ qemu_mutex_lock(&p->mutex);
+ p->quit = true;
+ qemu_sem_post(&p->sem);
+ qemu_mutex_unlock(&p->mutex);
+ }
+}
+
+int multifd_load_cleanup(Error **errp)
+{
+ int i;
+ int ret = 0;
+
+ if (!migrate_use_multifd()) {
+ return 0;
+ }
+ terminate_multifd_recv_threads(NULL);
+ for (i = 0; i < multifd_recv_state->count; i++) {
+ MultiFDRecvParams *p = &multifd_recv_state->params[i];
+
+ qemu_thread_join(&p->thread);
+ qemu_mutex_destroy(&p->mutex);
+ qemu_sem_destroy(&p->sem);
+ g_free(p->name);
+ p->name = NULL;
+ }
+ g_free(multifd_recv_state->params);
+ multifd_recv_state->params = NULL;
+ g_free(multifd_recv_state);
+ multifd_recv_state = NULL;
+
+ return ret;
+}
+
+static void *multifd_recv_thread(void *opaque)
+{
+ MultiFDRecvParams *p = opaque;
+
+ while (true) {
+ qemu_mutex_lock(&p->mutex);
+ if (p->quit) {
+ qemu_mutex_unlock(&p->mutex);
+ break;
+ }
+ qemu_mutex_unlock(&p->mutex);
+ qemu_sem_wait(&p->sem);
+ }
+
+ return NULL;
+}
+
+int multifd_load_setup(void)
+{
+ int thread_count;
+ uint8_t i;
+
+ if (!migrate_use_multifd()) {
+ return 0;
+ }
+ thread_count = migrate_multifd_channels();
+ multifd_recv_state = g_malloc0(sizeof(*multifd_recv_state));
+ multifd_recv_state->params = g_new0(MultiFDRecvParams, thread_count);
+ multifd_recv_state->count = 0;
+ for (i = 0; i < thread_count; i++) {
+ MultiFDRecvParams *p = &multifd_recv_state->params[i];
+
+ qemu_mutex_init(&p->mutex);
+ qemu_sem_init(&p->sem, 0);
+ p->quit = false;
+ p->id = i;
+ p->name = g_strdup_printf("multifdrecv_%d", i);
+ qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p,
+ QEMU_THREAD_JOINABLE);
+ multifd_recv_state->count++;
+ }
+ return 0;
+}
+
/**
* save_page_header: write page header to wire
*
@@ -2074,8 +2276,12 @@ static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size,
remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE;
}
- /* We can do postcopy, and all the data is postcopiable */
- *postcopiable_pending += remaining_size;
+ if (migrate_postcopy_ram()) {
+ /* We can do postcopy, and all the data is postcopiable */
+ *postcopiable_pending += remaining_size;
+ } else {
+ *non_postcopiable_pending += remaining_size;
+ }
}
static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
@@ -2647,11 +2853,17 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
return ret;
}
+static bool ram_has_postcopy(void *opaque)
+{
+ return migrate_postcopy_ram();
+}
+
static SaveVMHandlers savevm_ram_handlers = {
.save_setup = ram_save_setup,
.save_live_iterate = ram_save_iterate,
.save_live_complete_postcopy = ram_save_complete,
.save_live_complete_precopy = ram_save_complete,
+ .has_postcopy = ram_has_postcopy,
.save_live_pending = ram_save_pending,
.load_state = ram_load,
.save_cleanup = ram_save_cleanup,
diff --git a/migration/ram.h b/migration/ram.h
index c081fde86c..4a72d66503 100644
--- a/migration/ram.h
+++ b/migration/ram.h
@@ -39,6 +39,11 @@ int64_t xbzrle_cache_resize(int64_t new_size);
uint64_t ram_bytes_remaining(void);
uint64_t ram_bytes_total(void);
+int multifd_save_setup(void);
+int multifd_save_cleanup(Error **errp);
+int multifd_load_setup(void);
+int multifd_load_cleanup(Error **errp);
+
uint64_t ram_pagesize_summary(void);
int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len);
void acct_update_position(QEMUFile *f, size_t size, bool zero);
diff --git a/migration/savevm.c b/migration/savevm.c
index 7a55023d1a..2478352baf 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -89,7 +89,7 @@ static struct mig_cmd_args {
[MIG_CMD_INVALID] = { .len = -1, .name = "INVALID" },
[MIG_CMD_OPEN_RETURN_PATH] = { .len = 0, .name = "OPEN_RETURN_PATH" },
[MIG_CMD_PING] = { .len = sizeof(uint32_t), .name = "PING" },
- [MIG_CMD_POSTCOPY_ADVISE] = { .len = 16, .name = "POSTCOPY_ADVISE" },
+ [MIG_CMD_POSTCOPY_ADVISE] = { .len = -1, .name = "POSTCOPY_ADVISE" },
[MIG_CMD_POSTCOPY_LISTEN] = { .len = 0, .name = "POSTCOPY_LISTEN" },
[MIG_CMD_POSTCOPY_RUN] = { .len = 0, .name = "POSTCOPY_RUN" },
[MIG_CMD_POSTCOPY_RAM_DISCARD] = {
@@ -98,6 +98,23 @@ static struct mig_cmd_args {
[MIG_CMD_MAX] = { .len = -1, .name = "MAX" },
};
+/* Note for MIG_CMD_POSTCOPY_ADVISE:
+ * The format of arguments is depending on postcopy mode:
+ * - postcopy RAM only
+ * uint64_t host page size
+ * uint64_t taget page size
+ *
+ * - postcopy RAM and postcopy dirty bitmaps
+ * format is the same as for postcopy RAM only
+ *
+ * - postcopy dirty bitmaps only
+ * Nothing. Command length field is 0.
+ *
+ * Be careful: adding a new postcopy entity with some other parameters should
+ * not break format self-description ability. Good way is to introduce some
+ * generic extendable format with an exception for two old entities.
+ */
+
static int announce_self_create(uint8_t *buf,
uint8_t *mac_addr)
{
@@ -861,12 +878,17 @@ int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len)
/* Send prior to any postcopy transfer */
void qemu_savevm_send_postcopy_advise(QEMUFile *f)
{
- uint64_t tmp[2];
- tmp[0] = cpu_to_be64(ram_pagesize_summary());
- tmp[1] = cpu_to_be64(qemu_target_page_size());
+ if (migrate_postcopy_ram()) {
+ uint64_t tmp[2];
+ tmp[0] = cpu_to_be64(ram_pagesize_summary());
+ tmp[1] = cpu_to_be64(qemu_target_page_size());
- trace_qemu_savevm_send_postcopy_advise();
- qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_ADVISE, 16, (uint8_t *)tmp);
+ trace_qemu_savevm_send_postcopy_advise();
+ qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_ADVISE,
+ 16, (uint8_t *)tmp);
+ } else {
+ qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_ADVISE, 0, NULL);
+ }
}
/* Sent prior to starting the destination running in postcopy, discard pages
@@ -1008,7 +1030,8 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy)
* call that's already run, it might get confused if we call
* iterate afterwards.
*/
- if (postcopy && !se->ops->save_live_complete_postcopy) {
+ if (postcopy &&
+ !(se->ops->has_postcopy && se->ops->has_postcopy(se->opaque))) {
continue;
}
if (qemu_file_rate_limit(f)) {
@@ -1097,7 +1120,8 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only,
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
if (!se->ops ||
- (in_postcopy && se->ops->save_live_complete_postcopy) ||
+ (in_postcopy && se->ops->has_postcopy &&
+ se->ops->has_postcopy(se->opaque)) ||
(in_postcopy && !iterable_only) ||
!se->ops->save_live_complete_precopy) {
continue;
@@ -1352,7 +1376,11 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis)
return -1;
}
- if (!postcopy_ram_supported_by_host()) {
+ if (!migrate_postcopy_ram()) {
+ return 0;
+ }
+
+ if (!postcopy_ram_supported_by_host(mis)) {
postcopy_state_set(POSTCOPY_INCOMING_NONE);
return -1;
}
@@ -1562,7 +1590,9 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
* A rare case, we entered listen without having to do any discards,
* so do the setup that's normally done at the time of the 1st discard.
*/
- postcopy_ram_prepare_discard(mis);
+ if (migrate_postcopy_ram()) {
+ postcopy_ram_prepare_discard(mis);
+ }
}
/*
@@ -1570,8 +1600,10 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
* However, at this point the CPU shouldn't be running, and the IO
* shouldn't be doing anything yet so don't actually expect requests
*/
- if (postcopy_ram_enable_notify(mis)) {
- return -1;
+ if (migrate_postcopy_ram()) {
+ if (postcopy_ram_enable_notify(mis)) {
+ return -1;
+ }
}
if (mis->have_listen_thread) {
diff --git a/migration/socket.c b/migration/socket.c
index 757d3821a1..dee869044a 100644
--- a/migration/socket.c
+++ b/migration/socket.c
@@ -152,9 +152,13 @@ static gboolean socket_accept_incoming_migration(QIOChannel *ioc,
object_unref(OBJECT(sioc));
out:
- /* Close listening socket as its no longer needed */
- qio_channel_close(ioc, NULL);
- return FALSE; /* unregister */
+ if (migration_has_all_channels()) {
+ /* Close listening socket as its no longer needed */
+ qio_channel_close(ioc, NULL);
+ return G_SOURCE_REMOVE;
+ } else {
+ return G_SOURCE_CONTINUE;
+ }
}
diff --git a/net/Makefile.objs b/net/Makefile.objs
index 67ba5e26fb..64adf32f40 100644
--- a/net/Makefile.objs
+++ b/net/Makefile.objs
@@ -21,3 +21,5 @@ tap-obj-$(CONFIG_SOLARIS) = tap-solaris.o
tap-obj-y ?= tap-stub.o
common-obj-$(CONFIG_POSIX) += tap.o $(tap-obj-y)
common-obj-$(CONFIG_WIN32) += tap-win32.o
+
+vde.o-libs = $(VDE_LIBS)
diff --git a/qapi/migration.json b/qapi/migration.json
index ee2b3b8733..f8b365e3f5 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -341,12 +341,14 @@
# @return-path: If enabled, migration will use the return path even
# for precopy. (since 2.10)
#
+# @x-multifd: Use more than one fd for migration (since 2.11)
+#
# Since: 1.2
##
{ 'enum': 'MigrationCapability',
'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks',
'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram',
- 'block', 'return-path' ] }
+ 'block', 'return-path', 'x-multifd' ] }
##
# @MigrationCapabilityStatus:
@@ -464,13 +466,22 @@
# migrated and the destination must already have access to the
# same backing chain as was used on the source. (since 2.10)
#
+# @x-multifd-channels: Number of channels used to migrate data in
+# parallel. This is the same number that the
+# number of sockets used for migration. The
+# default value is 2 (since 2.11)
+#
+# @x-multifd-page-count: Number of pages sent together to a thread
+# The default value is 16 (since 2.11)
+#
# Since: 2.4
##
{ 'enum': 'MigrationParameter',
'data': ['compress-level', 'compress-threads', 'decompress-threads',
'cpu-throttle-initial', 'cpu-throttle-increment',
'tls-creds', 'tls-hostname', 'max-bandwidth',
- 'downtime-limit', 'x-checkpoint-delay', 'block-incremental' ] }
+ 'downtime-limit', 'x-checkpoint-delay', 'block-incremental',
+ 'x-multifd-channels', 'x-multifd-page-count' ] }
##
# @MigrateSetParameters:
@@ -526,6 +537,14 @@
# migrated and the destination must already have access to the
# same backing chain as was used on the source. (since 2.10)
#
+# @x-multifd-channels: Number of channels used to migrate data in
+# parallel. This is the same number that the
+# number of sockets used for migration. The
+# default value is 2 (since 2.11)
+#
+# @x-multifd-page-count: Number of pages sent together to a thread
+# The default value is 16 (since 2.11)
+#
# Since: 2.4
##
# TODO either fuse back into MigrationParameters, or make
@@ -541,7 +560,9 @@
'*max-bandwidth': 'int',
'*downtime-limit': 'int',
'*x-checkpoint-delay': 'int',
- '*block-incremental': 'bool' } }
+ '*block-incremental': 'bool',
+ '*x-multifd-channels': 'int',
+ '*x-multifd-page-count': 'int' } }
##
# @migrate-set-parameters:
@@ -612,6 +633,14 @@
# migrated and the destination must already have access to the
# same backing chain as was used on the source. (since 2.10)
#
+# @x-multifd-channels: Number of channels used to migrate data in
+# parallel. This is the same number that the
+# number of sockets used for migration.
+# The default value is 2 (since 2.11)
+#
+# @x-multifd-page-count: Number of pages sent together to a thread
+# The default value is 16 (since 2.11)
+#
# Since: 2.4
##
{ 'struct': 'MigrationParameters',
@@ -625,7 +654,9 @@
'*max-bandwidth': 'int',
'*downtime-limit': 'int',
'*x-checkpoint-delay': 'int',
- '*block-incremental': 'bool' } }
+ '*block-incremental': 'bool' ,
+ '*x-multifd-channels': 'int',
+ '*x-multifd-page-count': 'int' } }
##
# @query-migrate-parameters:
diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh
new file mode 100755
index 0000000000..c4e7d98f4d
--- /dev/null
+++ b/scripts/archive-source.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# Author: Fam Zheng <famz@redhat.com>
+#
+# Archive source tree, including submodules. This is created for test code to
+# export the source files, in order to be built in a different environment,
+# such as in a docker instance or VM.
+#
+# This code is licensed under the GPL version 2 or later. See
+# the COPYING file in the top-level directory.
+
+error() {
+ printf %s\\n "$*" >&2
+ exit 1
+}
+
+if test $# -lt 1; then
+ error "Usage: $0 <output tarball>"
+fi
+
+tar_file="$1"
+list_file="$1.list"
+submodules=$(git submodule foreach --recursive --quiet 'echo $name')
+
+if test $? -ne 0; then
+ error "git submodule command failed"
+fi
+
+trap "status=$?; rm -f \"$list_file\"; exit \$status" 0 1 2 3 15
+
+if test -n "$submodules"; then
+ {
+ git ls-files || error "git ls-files failed"
+ for sm in $submodules; do
+ (cd $sm; git ls-files) | sed "s:^:$sm/:"
+ if test "${PIPESTATUS[*]}" != "0 0"; then
+ error "git ls-files in submodule $sm failed"
+ fi
+ done
+ } | grep -x -v $(for sm in $submodules; do echo "-e $sm"; done) > "$list_file"
+else
+ git ls-files > "$list_file"
+fi
+
+if test $? -ne 0; then
+ error "failed to generate list file"
+fi
+
+tar -cf "$tar_file" -T "$list_file" || error "failed to create tar file"
+
+exit 0
diff --git a/scripts/qemu.py b/scripts/qemu.py
index 5e02dd8e78..c9a106fbce 100644
--- a/scripts/qemu.py
+++ b/scripts/qemu.py
@@ -89,6 +89,9 @@ class QEMUMachine(object):
self._qmp = None
self._qemu_full_args = None
+ # just in case logging wasn't configured by the main script:
+ logging.basicConfig(level=(logging.DEBUG if debug else logging.WARN))
+
def __enter__(self):
return self
@@ -215,6 +218,13 @@ class QEMUMachine(object):
LOG.debug('Output: %r', self._iolog)
raise
+ def wait(self):
+ '''Wait for the VM to power off'''
+ self._popen.wait()
+ self._qmp.close()
+ self._load_io_log()
+ self._post_shutdown()
+
def shutdown(self):
'''Terminate the VM and clean up'''
if self.is_running():
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index f61ca660e6..4300de66e2 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -187,6 +187,13 @@ static void arm_cpu_reset(CPUState *s)
if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
env->v7m.secure = true;
+ } else {
+ /* This bit resets to 0 if security is supported, but 1 if
+ * it is not. The bit is not present in v7M, but we set it
+ * here so we can avoid having to make checks on it conditional
+ * on ARM_FEATURE_V8 (we don't let the guest see the bit).
+ */
+ env->v7m.aircr = R_V7M_AIRCR_BFHFNMINS_MASK;
}
/* In v7M the reset value of this bit is IMPDEF, but ARM recommends
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 6e50ae2b55..8afceca873 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -449,6 +449,7 @@ typedef struct CPUARMState {
int exception;
uint32_t primask[M_REG_NUM_BANKS];
uint32_t faultmask[M_REG_NUM_BANKS];
+ uint32_t aircr; /* only holds r/w state if security extn implemented */
uint32_t secure; /* Is CPU in Secure state? (not guest visible) */
} v7m;
@@ -1200,6 +1201,17 @@ FIELD(V7M_CCR, STKALIGN, 9, 1)
FIELD(V7M_CCR, DC, 16, 1)
FIELD(V7M_CCR, IC, 17, 1)
+/* V7M AIRCR bits */
+FIELD(V7M_AIRCR, VECTRESET, 0, 1)
+FIELD(V7M_AIRCR, VECTCLRACTIVE, 1, 1)
+FIELD(V7M_AIRCR, SYSRESETREQ, 2, 1)
+FIELD(V7M_AIRCR, SYSRESETREQS, 3, 1)
+FIELD(V7M_AIRCR, PRIGROUP, 8, 3)
+FIELD(V7M_AIRCR, BFHFNMINS, 13, 1)
+FIELD(V7M_AIRCR, PRIS, 14, 1)
+FIELD(V7M_AIRCR, ENDIANNESS, 15, 1)
+FIELD(V7M_AIRCR, VECTKEY, 16, 16)
+
/* V7M CFSR bits for MMFSR */
FIELD(V7M_CFSR, IACCVIOL, 0, 1)
FIELD(V7M_CFSR, DACCVIOL, 1, 1)
@@ -1451,19 +1463,42 @@ static inline bool armv7m_nvic_can_take_pending_exception(void *opaque)
return true;
}
#endif
-void armv7m_nvic_set_pending(void *opaque, int irq);
-void armv7m_nvic_acknowledge_irq(void *opaque);
+/**
+ * armv7m_nvic_set_pending: mark the specified exception as pending
+ * @opaque: the NVIC
+ * @irq: the exception number to mark pending
+ * @secure: false for non-banked exceptions or for the nonsecure
+ * version of a banked exception, true for the secure version of a banked
+ * exception.
+ *
+ * Marks the specified exception as pending. Note that we will assert()
+ * if @secure is true and @irq does not specify one of the fixed set
+ * of architecturally banked exceptions.
+ */
+void armv7m_nvic_set_pending(void *opaque, int irq, bool secure);
+/**
+ * armv7m_nvic_acknowledge_irq: make highest priority pending exception active
+ * @opaque: the NVIC
+ *
+ * Move the current highest priority pending exception from the pending
+ * state to the active state, and update v7m.exception to indicate that
+ * it is the exception currently being handled.
+ *
+ * Returns: true if exception should be taken to Secure state, false for NS
+ */
+bool armv7m_nvic_acknowledge_irq(void *opaque);
/**
* armv7m_nvic_complete_irq: complete specified interrupt or exception
* @opaque: the NVIC
* @irq: the exception number to complete
+ * @secure: true if this exception was secure
*
* Returns: -1 if the irq was not active
* 1 if completing this irq brought us back to base (no active irqs)
* 0 if there is still an irq active after this one was completed
* (Ignoring -1, this is the same as the RETTOBASE value before completion.)
*/
-int armv7m_nvic_complete_irq(void *opaque, int irq);
+int armv7m_nvic_complete_irq(void *opaque, int irq, bool secure);
/**
* armv7m_nvic_raw_execution_priority: return the raw execution priority
* @opaque: the NVIC
@@ -1474,6 +1509,21 @@ int armv7m_nvic_complete_irq(void *opaque, int irq);
* (v8M ARM ARM I_PKLD.)
*/
int armv7m_nvic_raw_execution_priority(void *opaque);
+/**
+ * armv7m_nvic_neg_prio_requested: return true if the requested execution
+ * priority is negative for the specified security state.
+ * @opaque: the NVIC
+ * @secure: the security state to test
+ * This corresponds to the pseudocode IsReqExecPriNeg().
+ */
+#ifndef CONFIG_USER_ONLY
+bool armv7m_nvic_neg_prio_requested(void *opaque, bool secure);
+#else
+static inline bool armv7m_nvic_neg_prio_requested(void *opaque, bool secure)
+{
+ return false;
+}
+#endif
/* Interface for defining coprocessor registers.
* Registers are defined in tables of arm_cp_reginfo structs
@@ -2259,11 +2309,7 @@ static inline int cpu_mmu_index(CPUARMState *env, bool ifetch)
if (arm_feature(env, ARM_FEATURE_M)) {
ARMMMUIdx mmu_idx = el == 0 ? ARMMMUIdx_MUser : ARMMMUIdx_MPriv;
- /* Execution priority is negative if FAULTMASK is set or
- * we're in a HardFault or NMI handler.
- */
- if ((env->v7m.exception > 0 && env->v7m.exception <= 3)
- || env->v7m.faultmask[env->v7m.secure]) {
+ if (armv7m_nvic_neg_prio_requested(env->nvic, env->v7m.secure)) {
mmu_idx = ARMMMUIdx_MNegPri;
}
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 4f41841ef6..8be78ea2f8 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -6218,6 +6218,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
bool return_to_sp_process = false;
bool return_to_handler = false;
bool rettobase = false;
+ bool exc_secure = false;
/* We can only get here from an EXCP_EXCEPTION_EXIT, and
* gen_bx_excret() enforces the architectural rule
@@ -6256,16 +6257,17 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
* which security state's faultmask to clear. (v8M ARM ARM R_KBNF.)
*/
if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
- int es = excret & R_V7M_EXCRET_ES_MASK;
+ exc_secure = excret & R_V7M_EXCRET_ES_MASK;
if (armv7m_nvic_raw_execution_priority(env->nvic) >= 0) {
- env->v7m.faultmask[es] = 0;
+ env->v7m.faultmask[exc_secure] = 0;
}
} else {
env->v7m.faultmask[M_REG_NS] = 0;
}
}
- switch (armv7m_nvic_complete_irq(env->nvic, env->v7m.exception)) {
+ switch (armv7m_nvic_complete_irq(env->nvic, env->v7m.exception,
+ exc_secure)) {
case -1:
/* attempt to exit an exception that isn't active */
ufault = true;
@@ -6306,7 +6308,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
* stack, directly take a usage fault on the current stack.
*/
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK;
- armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure);
v7m_exception_taken(cpu, excret);
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing "
"stackframe: failed exception return integrity check\n");
@@ -6345,8 +6347,11 @@ static void do_v7m_exception_exit(ARMCPU *cpu)
* exception return excret specified then this is a UsageFault.
*/
if (return_to_handler != arm_v7m_is_handler_mode(env)) {
- /* Take an INVPC UsageFault by pushing the stack again. */
- armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
+ /* Take an INVPC UsageFault by pushing the stack again.
+ * TODO: the v8M version of this code should target the
+ * background state for this exception.
+ */
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, false);
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK;
v7m_push_stack(cpu);
v7m_exception_taken(cpu, excret);
@@ -6406,20 +6411,20 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
handle it. */
switch (cs->exception_index) {
case EXCP_UDEF:
- armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure);
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_UNDEFINSTR_MASK;
break;
case EXCP_NOCP:
- armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure);
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_NOCP_MASK;
break;
case EXCP_INVSTATE:
- armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure);
env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVSTATE_MASK;
break;
case EXCP_SWI:
/* The PC already points to the next instruction. */
- armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC);
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC, env->v7m.secure);
break;
case EXCP_PREFETCH_ABORT:
case EXCP_DATA_ABORT:
@@ -6443,7 +6448,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
env->v7m.bfar);
break;
}
- armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS);
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_BUS, false);
break;
default:
/* All other FSR values are either MPU faults or "can't happen
@@ -6463,7 +6468,8 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
env->v7m.mmfar[env->v7m.secure]);
break;
}
- armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM);
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM,
+ env->v7m.secure);
break;
}
break;
@@ -6480,7 +6486,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
return;
}
}
- armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG);
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG, false);
break;
case EXCP_IRQ:
break;
@@ -8892,12 +8898,68 @@ uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg)
break;
case 20: /* CONTROL */
return env->v7m.control[env->v7m.secure];
+ case 0x94: /* CONTROL_NS */
+ /* We have to handle this here because unprivileged Secure code
+ * can read the NS CONTROL register.
+ */
+ if (!env->v7m.secure) {
+ return 0;
+ }
+ return env->v7m.control[M_REG_NS];
}
if (el == 0) {
return 0; /* unprivileged reads others as zero */
}
+ if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
+ switch (reg) {
+ case 0x88: /* MSP_NS */
+ if (!env->v7m.secure) {
+ return 0;
+ }
+ return env->v7m.other_ss_msp;
+ case 0x89: /* PSP_NS */
+ if (!env->v7m.secure) {
+ return 0;
+ }
+ return env->v7m.other_ss_psp;
+ case 0x90: /* PRIMASK_NS */
+ if (!env->v7m.secure) {
+ return 0;
+ }
+ return env->v7m.primask[M_REG_NS];
+ case 0x91: /* BASEPRI_NS */
+ if (!env->v7m.secure) {
+ return 0;
+ }
+ return env->v7m.basepri[M_REG_NS];
+ case 0x93: /* FAULTMASK_NS */
+ if (!env->v7m.secure) {
+ return 0;
+ }
+ return env->v7m.faultmask[M_REG_NS];
+ case 0x98: /* SP_NS */
+ {
+ /* This gives the non-secure SP selected based on whether we're
+ * currently in handler mode or not, using the NS CONTROL.SPSEL.
+ */
+ bool spsel = env->v7m.control[M_REG_NS] & R_V7M_CONTROL_SPSEL_MASK;
+
+ if (!env->v7m.secure) {
+ return 0;
+ }
+ if (!arm_v7m_is_handler_mode(env) && spsel) {
+ return env->v7m.other_ss_psp;
+ } else {
+ return env->v7m.other_ss_msp;
+ }
+ }
+ default:
+ break;
+ }
+ }
+
switch (reg) {
case 8: /* MSP */
return (env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK) ?
@@ -8936,6 +8998,60 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val)
return;
}
+ if (arm_feature(env, ARM_FEATURE_M_SECURITY)) {
+ switch (reg) {
+ case 0x88: /* MSP_NS */
+ if (!env->v7m.secure) {
+ return;
+ }
+ env->v7m.other_ss_msp = val;
+ return;
+ case 0x89: /* PSP_NS */
+ if (!env->v7m.secure) {
+ return;
+ }
+ env->v7m.other_ss_psp = val;
+ return;
+ case 0x90: /* PRIMASK_NS */
+ if (!env->v7m.secure) {
+ return;
+ }
+ env->v7m.primask[M_REG_NS] = val & 1;
+ return;
+ case 0x91: /* BASEPRI_NS */
+ if (!env->v7m.secure) {
+ return;
+ }
+ env->v7m.basepri[M_REG_NS] = val & 0xff;
+ return;
+ case 0x93: /* FAULTMASK_NS */
+ if (!env->v7m.secure) {
+ return;
+ }
+ env->v7m.faultmask[M_REG_NS] = val & 1;
+ return;
+ case 0x98: /* SP_NS */
+ {
+ /* This gives the non-secure SP selected based on whether we're
+ * currently in handler mode or not, using the NS CONTROL.SPSEL.
+ */
+ bool spsel = env->v7m.control[M_REG_NS] & R_V7M_CONTROL_SPSEL_MASK;
+
+ if (!env->v7m.secure) {
+ return;
+ }
+ if (!arm_v7m_is_handler_mode(env) && spsel) {
+ env->v7m.other_ss_psp = val;
+ } else {
+ env->v7m.other_ss_msp = val;
+ }
+ return;
+ }
+ default:
+ break;
+ }
+ }
+
switch (reg) {
case 0 ... 7: /* xPSR sub-fields */
/* only APSR is actually writable */
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index 083568c468..899ffb96fc 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -1203,12 +1203,14 @@ static inline AArch64DecodeFn *lookup_disas_fn(const AArch64DecodeTable *table,
}
/*
- * the instruction disassembly implemented here matches
- * the instruction encoding classifications in chapter 3 (C3)
- * of the ARM Architecture Reference Manual (DDI0487A_a)
+ * The instruction disassembly implemented here matches
+ * the instruction encoding classifications in chapter C4
+ * of the ARM Architecture Reference Manual (DDI0487B_a);
+ * classification names and decode diagrams here should generally
+ * match up with those in the manual.
*/
-/* C3.2.7 Unconditional branch (immediate)
+/* Unconditional branch (immediate)
* 31 30 26 25 0
* +----+-----------+-------------------------------------+
* | op | 0 0 1 0 1 | imm26 |
@@ -1219,15 +1221,15 @@ static void disas_uncond_b_imm(DisasContext *s, uint32_t insn)
uint64_t addr = s->pc + sextract32(insn, 0, 26) * 4 - 4;
if (insn & (1U << 31)) {
- /* C5.6.26 BL Branch with link */
+ /* BL Branch with link */
tcg_gen_movi_i64(cpu_reg(s, 30), s->pc);
}
- /* C5.6.20 B Branch / C5.6.26 BL Branch with link */
+ /* B Branch / BL Branch with link */
gen_goto_tb(s, 0, addr);
}
-/* C3.2.1 Compare & branch (immediate)
+/* Compare and branch (immediate)
* 31 30 25 24 23 5 4 0
* +----+-------------+----+---------------------+--------+
* | sf | 0 1 1 0 1 0 | op | imm19 | Rt |
@@ -1256,7 +1258,7 @@ static void disas_comp_b_imm(DisasContext *s, uint32_t insn)
gen_goto_tb(s, 1, addr);
}
-/* C3.2.5 Test & branch (immediate)
+/* Test and branch (immediate)
* 31 30 25 24 23 19 18 5 4 0
* +----+-------------+----+-------+-------------+------+
* | b5 | 0 1 1 0 1 1 | op | b40 | imm14 | Rt |
@@ -1285,7 +1287,7 @@ static void disas_test_b_imm(DisasContext *s, uint32_t insn)
gen_goto_tb(s, 1, addr);
}
-/* C3.2.2 / C5.6.19 Conditional branch (immediate)
+/* Conditional branch (immediate)
* 31 25 24 23 5 4 3 0
* +---------------+----+---------------------+----+------+
* | 0 1 0 1 0 1 0 | o1 | imm19 | o0 | cond |
@@ -1316,7 +1318,7 @@ static void disas_cond_b_imm(DisasContext *s, uint32_t insn)
}
}
-/* C5.6.68 HINT */
+/* HINT instruction group, including various allocated HINTs */
static void handle_hint(DisasContext *s, uint32_t insn,
unsigned int op1, unsigned int op2, unsigned int crm)
{
@@ -1401,7 +1403,7 @@ static void handle_sync(DisasContext *s, uint32_t insn,
}
}
-/* C5.6.130 MSR (immediate) - move immediate to processor state field */
+/* MSR (immediate) - move immediate to processor state field */
static void handle_msr_i(DisasContext *s, uint32_t insn,
unsigned int op1, unsigned int op2, unsigned int crm)
{
@@ -1477,10 +1479,10 @@ static void gen_set_nzcv(TCGv_i64 tcg_rt)
tcg_temp_free_i32(nzcv);
}
-/* C5.6.129 MRS - move from system register
- * C5.6.131 MSR (register) - move to system register
- * C5.6.204 SYS
- * C5.6.205 SYSL
+/* MRS - move from system register
+ * MSR (register) - move to system register
+ * SYS
+ * SYSL
* These are all essentially the same insn in 'read' and 'write'
* versions, with varying op0 fields.
*/
@@ -1603,7 +1605,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
}
}
-/* C3.2.4 System
+/* System
* 31 22 21 20 19 18 16 15 12 11 8 7 5 4 0
* +---------------------+---+-----+-----+-------+-------+-----+------+
* | 1 1 0 1 0 1 0 1 0 0 | L | op0 | op1 | CRn | CRm | op2 | Rt |
@@ -1626,13 +1628,13 @@ static void disas_system(DisasContext *s, uint32_t insn)
return;
}
switch (crn) {
- case 2: /* C5.6.68 HINT */
+ case 2: /* HINT (including allocated hints like NOP, YIELD, etc) */
handle_hint(s, insn, op1, op2, crm);
break;
case 3: /* CLREX, DSB, DMB, ISB */
handle_sync(s, insn, op1, op2, crm);
break;
- case 4: /* C5.6.130 MSR (immediate) */
+ case 4: /* MSR (immediate) */
handle_msr_i(s, insn, op1, op2, crm);
break;
default:
@@ -1644,7 +1646,7 @@ static void disas_system(DisasContext *s, uint32_t insn)
handle_sys(s, insn, l, op0, op1, op2, crn, crm, rt);
}
-/* C3.2.3 Exception generation
+/* Exception generation
*
* 31 24 23 21 20 5 4 2 1 0
* +-----------------+-----+------------------------+-----+----+
@@ -1751,7 +1753,7 @@ static void disas_exc(DisasContext *s, uint32_t insn)
}
}
-/* C3.2.7 Unconditional branch (register)
+/* Unconditional branch (register)
* 31 25 24 21 20 16 15 10 9 5 4 0
* +---------------+-------+-------+-------+------+-------+
* | 1 1 0 1 0 1 1 | opc | op2 | op3 | Rn | op4 |
@@ -1806,7 +1808,7 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn)
s->base.is_jmp = DISAS_JUMP;
}
-/* C3.2 Branches, exception generating and system instructions */
+/* Branches, exception generating and system instructions */
static void disas_b_exc_sys(DisasContext *s, uint32_t insn)
{
switch (extract32(insn, 25, 7)) {
@@ -1966,7 +1968,7 @@ static bool disas_ldst_compute_iss_sf(int size, bool is_signed, int opc)
return regsize == 64;
}
-/* C3.3.6 Load/store exclusive
+/* Load/store exclusive
*
* 31 30 29 24 23 22 21 20 16 15 14 10 9 5 4 0
* +-----+-------------+----+---+----+------+----+-------+------+------+
@@ -2043,7 +2045,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn)
}
/*
- * C3.3.5 Load register (literal)
+ * Load register (literal)
*
* 31 30 29 27 26 25 24 23 5 4 0
* +-----+-------+---+-----+-------------------+-------+
@@ -2099,15 +2101,15 @@ static void disas_ld_lit(DisasContext *s, uint32_t insn)
}
/*
- * C5.6.80 LDNP (Load Pair - non-temporal hint)
- * C5.6.81 LDP (Load Pair - non vector)
- * C5.6.82 LDPSW (Load Pair Signed Word - non vector)
- * C5.6.176 STNP (Store Pair - non-temporal hint)
- * C5.6.177 STP (Store Pair - non vector)
- * C6.3.165 LDNP (Load Pair of SIMD&FP - non-temporal hint)
- * C6.3.165 LDP (Load Pair of SIMD&FP)
- * C6.3.284 STNP (Store Pair of SIMD&FP - non-temporal hint)
- * C6.3.284 STP (Store Pair of SIMD&FP)
+ * LDNP (Load Pair - non-temporal hint)
+ * LDP (Load Pair - non vector)
+ * LDPSW (Load Pair Signed Word - non vector)
+ * STNP (Store Pair - non-temporal hint)
+ * STP (Store Pair - non vector)
+ * LDNP (Load Pair of SIMD&FP - non-temporal hint)
+ * LDP (Load Pair of SIMD&FP)
+ * STNP (Store Pair of SIMD&FP - non-temporal hint)
+ * STP (Store Pair of SIMD&FP)
*
* 31 30 29 27 26 25 24 23 22 21 15 14 10 9 5 4 0
* +-----+-------+---+---+-------+---+-----------------------------+
@@ -2253,9 +2255,9 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn)
}
/*
- * C3.3.8 Load/store (immediate post-indexed)
- * C3.3.9 Load/store (immediate pre-indexed)
- * C3.3.12 Load/store (unscaled immediate)
+ * Load/store (immediate post-indexed)
+ * Load/store (immediate pre-indexed)
+ * Load/store (unscaled immediate)
*
* 31 30 29 27 26 25 24 23 22 21 20 12 11 10 9 5 4 0
* +----+-------+---+-----+-----+---+--------+-----+------+------+
@@ -2371,7 +2373,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn,
}
/*
- * C3.3.10 Load/store (register offset)
+ * Load/store (register offset)
*
* 31 30 29 27 26 25 24 23 22 21 20 16 15 13 12 11 10 9 5 4 0
* +----+-------+---+-----+-----+---+------+-----+--+-----+----+----+
@@ -2468,7 +2470,7 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn,
}
/*
- * C3.3.13 Load/store (unsigned immediate)
+ * Load/store (unsigned immediate)
*
* 31 30 29 27 26 25 24 23 22 21 10 9 5
* +----+-------+---+-----+-----+------------+-------+------+
@@ -2579,14 +2581,14 @@ static void disas_ldst_reg(DisasContext *s, uint32_t insn)
}
}
-/* C3.3.1 AdvSIMD load/store multiple structures
+/* AdvSIMD load/store multiple structures
*
* 31 30 29 23 22 21 16 15 12 11 10 9 5 4 0
* +---+---+---------------+---+-------------+--------+------+------+------+
* | 0 | Q | 0 0 1 1 0 0 0 | L | 0 0 0 0 0 0 | opcode | size | Rn | Rt |
* +---+---+---------------+---+-------------+--------+------+------+------+
*
- * C3.3.2 AdvSIMD load/store multiple structures (post-indexed)
+ * AdvSIMD load/store multiple structures (post-indexed)
*
* 31 30 29 23 22 21 20 16 15 12 11 10 9 5 4 0
* +---+---+---------------+---+---+---------+--------+------+------+------+
@@ -2711,14 +2713,14 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn)
tcg_temp_free_i64(tcg_addr);
}
-/* C3.3.3 AdvSIMD load/store single structure
+/* AdvSIMD load/store single structure
*
* 31 30 29 23 22 21 20 16 15 13 12 11 10 9 5 4 0
* +---+---+---------------+-----+-----------+-----+---+------+------+------+
* | 0 | Q | 0 0 1 1 0 1 0 | L R | 0 0 0 0 0 | opc | S | size | Rn | Rt |
* +---+---+---------------+-----+-----------+-----+---+------+------+------+
*
- * C3.3.4 AdvSIMD load/store single structure (post-indexed)
+ * AdvSIMD load/store single structure (post-indexed)
*
* 31 30 29 23 22 21 20 16 15 13 12 11 10 9 5 4 0
* +---+---+---------------+-----+-----------+-----+---+------+------+------+
@@ -2861,7 +2863,7 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn)
tcg_temp_free_i64(tcg_addr);
}
-/* C3.3 Loads and stores */
+/* Loads and stores */
static void disas_ldst(DisasContext *s, uint32_t insn)
{
switch (extract32(insn, 24, 6)) {
@@ -2891,7 +2893,7 @@ static void disas_ldst(DisasContext *s, uint32_t insn)
}
}
-/* C3.4.6 PC-rel. addressing
+/* PC-rel. addressing
* 31 30 29 28 24 23 5 4 0
* +----+-------+-----------+-------------------+------+
* | op | immlo | 1 0 0 0 0 | immhi | Rd |
@@ -2920,7 +2922,7 @@ static void disas_pc_rel_adr(DisasContext *s, uint32_t insn)
}
/*
- * C3.4.1 Add/subtract (immediate)
+ * Add/subtract (immediate)
*
* 31 30 29 28 24 23 22 21 10 9 5 4 0
* +--+--+--+-----------+-----+-------------+-----+-----+
@@ -3070,7 +3072,7 @@ static bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn,
return true;
}
-/* C3.4.4 Logical (immediate)
+/* Logical (immediate)
* 31 30 29 28 23 22 21 16 15 10 9 5 4 0
* +----+-----+-------------+---+------+------+------+------+
* | sf | opc | 1 0 0 1 0 0 | N | immr | imms | Rn | Rd |
@@ -3143,7 +3145,7 @@ static void disas_logic_imm(DisasContext *s, uint32_t insn)
}
/*
- * C3.4.5 Move wide (immediate)
+ * Move wide (immediate)
*
* 31 30 29 28 23 22 21 20 5 4 0
* +--+-----+-------------+-----+----------------+------+
@@ -3195,7 +3197,7 @@ static void disas_movw_imm(DisasContext *s, uint32_t insn)
}
}
-/* C3.4.2 Bitfield
+/* Bitfield
* 31 30 29 28 23 22 21 16 15 10 9 5 4 0
* +----+-----+-------------+---+------+------+------+------+
* | sf | opc | 1 0 0 1 1 0 | N | immr | imms | Rn | Rd |
@@ -3273,7 +3275,7 @@ static void disas_bitfield(DisasContext *s, uint32_t insn)
}
}
-/* C3.4.3 Extract
+/* Extract
* 31 30 29 28 23 22 21 20 16 15 10 9 5 4 0
* +----+------+-------------+---+----+------+--------+------+------+
* | sf | op21 | 1 0 0 1 1 1 | N | o0 | Rm | imms | Rn | Rd |
@@ -3333,7 +3335,7 @@ static void disas_extract(DisasContext *s, uint32_t insn)
}
}
-/* C3.4 Data processing - immediate */
+/* Data processing - immediate */
static void disas_data_proc_imm(DisasContext *s, uint32_t insn)
{
switch (extract32(insn, 23, 6)) {
@@ -3427,7 +3429,7 @@ static void shift_reg_imm(TCGv_i64 dst, TCGv_i64 src, int sf,
}
}
-/* C3.5.10 Logical (shifted register)
+/* Logical (shifted register)
* 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0
* +----+-----+-----------+-------+---+------+--------+------+------+
* | sf | opc | 0 1 0 1 0 | shift | N | Rm | imm6 | Rn | Rd |
@@ -3518,7 +3520,7 @@ static void disas_logic_reg(DisasContext *s, uint32_t insn)
}
/*
- * C3.5.1 Add/subtract (extended register)
+ * Add/subtract (extended register)
*
* 31|30|29|28 24|23 22|21|20 16|15 13|12 10|9 5|4 0|
* +--+--+--+-----------+-----+--+-------+------+------+----+----+
@@ -3591,7 +3593,7 @@ static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn)
}
/*
- * C3.5.2 Add/subtract (shifted register)
+ * Add/subtract (shifted register)
*
* 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0
* +--+--+--+-----------+-----+--+-------+---------+------+------+
@@ -3654,13 +3656,12 @@ static void disas_add_sub_reg(DisasContext *s, uint32_t insn)
tcg_temp_free_i64(tcg_result);
}
-/* C3.5.9 Data-processing (3 source)
-
- 31 30 29 28 24 23 21 20 16 15 14 10 9 5 4 0
- +--+------+-----------+------+------+----+------+------+------+
- |sf| op54 | 1 1 0 1 1 | op31 | Rm | o0 | Ra | Rn | Rd |
- +--+------+-----------+------+------+----+------+------+------+
-
+/* Data-processing (3 source)
+ *
+ * 31 30 29 28 24 23 21 20 16 15 14 10 9 5 4 0
+ * +--+------+-----------+------+------+----+------+------+------+
+ * |sf| op54 | 1 1 0 1 1 | op31 | Rm | o0 | Ra | Rn | Rd |
+ * +--+------+-----------+------+------+----+------+------+------+
*/
static void disas_data_proc_3src(DisasContext *s, uint32_t insn)
{
@@ -3753,7 +3754,7 @@ static void disas_data_proc_3src(DisasContext *s, uint32_t insn)
tcg_temp_free_i64(tcg_tmp);
}
-/* C3.5.3 - Add/subtract (with carry)
+/* Add/subtract (with carry)
* 31 30 29 28 27 26 25 24 23 22 21 20 16 15 10 9 5 4 0
* +--+--+--+------------------------+------+---------+------+-----+
* |sf|op| S| 1 1 0 1 0 0 0 0 | rm | opcode2 | Rn | Rd |
@@ -3795,7 +3796,7 @@ static void disas_adc_sbc(DisasContext *s, uint32_t insn)
}
}
-/* C3.5.4 - C3.5.5 Conditional compare (immediate / register)
+/* Conditional compare (immediate / register)
* 31 30 29 28 27 26 25 24 23 22 21 20 16 15 12 11 10 9 5 4 3 0
* +--+--+--+------------------------+--------+------+----+--+------+--+-----+
* |sf|op| S| 1 1 0 1 0 0 1 0 |imm5/rm | cond |i/r |o2| Rn |o3|nzcv |
@@ -3900,7 +3901,7 @@ static void disas_cc(DisasContext *s, uint32_t insn)
tcg_temp_free_i32(tcg_t2);
}
-/* C3.5.6 Conditional select
+/* Conditional select
* 31 30 29 28 21 20 16 15 12 11 10 9 5 4 0
* +----+----+---+-----------------+------+------+-----+------+------+
* | sf | op | S | 1 1 0 1 0 1 0 0 | Rm | cond | op2 | Rn | Rd |
@@ -4011,7 +4012,7 @@ static void handle_rbit(DisasContext *s, unsigned int sf,
}
}
-/* C5.6.149 REV with sf==1, opcode==3 ("REV64") */
+/* REV with sf==1, opcode==3 ("REV64") */
static void handle_rev64(DisasContext *s, unsigned int sf,
unsigned int rn, unsigned int rd)
{
@@ -4022,8 +4023,8 @@ static void handle_rev64(DisasContext *s, unsigned int sf,
tcg_gen_bswap64_i64(cpu_reg(s, rd), cpu_reg(s, rn));
}
-/* C5.6.149 REV with sf==0, opcode==2
- * C5.6.151 REV32 (sf==1, opcode==2)
+/* REV with sf==0, opcode==2
+ * REV32 (sf==1, opcode==2)
*/
static void handle_rev32(DisasContext *s, unsigned int sf,
unsigned int rn, unsigned int rd)
@@ -4048,7 +4049,7 @@ static void handle_rev32(DisasContext *s, unsigned int sf,
}
}
-/* C5.6.150 REV16 (opcode==1) */
+/* REV16 (opcode==1) */
static void handle_rev16(DisasContext *s, unsigned int sf,
unsigned int rn, unsigned int rd)
{
@@ -4067,7 +4068,7 @@ static void handle_rev16(DisasContext *s, unsigned int sf,
tcg_temp_free_i64(tcg_tmp);
}
-/* C3.5.7 Data-processing (1 source)
+/* Data-processing (1 source)
* 31 30 29 28 21 20 16 15 10 9 5 4 0
* +----+---+---+-----------------+---------+--------+------+------+
* | sf | 1 | S | 1 1 0 1 0 1 1 0 | opcode2 | opcode | Rn | Rd |
@@ -4136,7 +4137,7 @@ static void handle_div(DisasContext *s, bool is_signed, unsigned int sf,
}
}
-/* C5.6.115 LSLV, C5.6.118 LSRV, C5.6.17 ASRV, C5.6.154 RORV */
+/* LSLV, LSRV, ASRV, RORV */
static void handle_shift_reg(DisasContext *s,
enum a64_shift_type shift_type, unsigned int sf,
unsigned int rm, unsigned int rn, unsigned int rd)
@@ -4198,7 +4199,7 @@ static void handle_crc32(DisasContext *s,
tcg_temp_free_i32(tcg_bytes);
}
-/* C3.5.8 Data-processing (2 source)
+/* Data-processing (2 source)
* 31 30 29 28 21 20 16 15 10 9 5 4 0
* +----+---+---+-----------------+------+--------+------+------+
* | sf | 0 | S | 1 1 0 1 0 1 1 0 | Rm | opcode | Rn | Rd |
@@ -4257,7 +4258,7 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn)
}
}
-/* C3.5 Data processing - register */
+/* Data processing - register */
static void disas_data_proc_reg(DisasContext *s, uint32_t insn)
{
switch (extract32(insn, 24, 5)) {
@@ -4351,7 +4352,7 @@ static void handle_fp_compare(DisasContext *s, bool is_double,
tcg_temp_free_i64(tcg_flags);
}
-/* C3.6.22 Floating point compare
+/* Floating point compare
* 31 30 29 28 24 23 22 21 20 16 15 14 13 10 9 5 4 0
* +---+---+---+-----------+------+---+------+-----+---------+------+-------+
* | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | op | 1 0 0 0 | Rn | op2 |
@@ -4381,7 +4382,7 @@ static void disas_fp_compare(DisasContext *s, uint32_t insn)
handle_fp_compare(s, type, rn, rm, opc & 1, opc & 2);
}
-/* C3.6.23 Floating point conditional compare
+/* Floating point conditional compare
* 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 3 0
* +---+---+---+-----------+------+---+------+------+-----+------+----+------+
* | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | cond | 0 1 | Rn | op | nzcv |
@@ -4429,7 +4430,7 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn)
}
}
-/* C3.6.24 Floating point conditional select
+/* Floating point conditional select
* 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0
* +---+---+---+-----------+------+---+------+------+-----+------+------+
* | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | cond | 1 1 | Rn | Rd |
@@ -4476,7 +4477,7 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn)
tcg_temp_free_i64(t_true);
}
-/* C3.6.25 Floating-point data-processing (1 source) - single precision */
+/* Floating-point data-processing (1 source) - single precision */
static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn)
{
TCGv_ptr fpst;
@@ -4532,7 +4533,7 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn)
tcg_temp_free_i32(tcg_res);
}
-/* C3.6.25 Floating-point data-processing (1 source) - double precision */
+/* Floating-point data-processing (1 source) - double precision */
static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn)
{
TCGv_ptr fpst;
@@ -4654,7 +4655,7 @@ static void handle_fp_fcvt(DisasContext *s, int opcode,
}
}
-/* C3.6.25 Floating point data-processing (1 source)
+/* Floating point data-processing (1 source)
* 31 30 29 28 24 23 22 21 20 15 14 10 9 5 4 0
* +---+---+---+-----------+------+---+--------+-----------+------+------+
* | M | 0 | S | 1 1 1 1 0 | type | 1 | opcode | 1 0 0 0 0 | Rn | Rd |
@@ -4712,7 +4713,7 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn)
}
}
-/* C3.6.26 Floating-point data-processing (2 source) - single precision */
+/* Floating-point data-processing (2 source) - single precision */
static void handle_fp_2src_single(DisasContext *s, int opcode,
int rd, int rn, int rm)
{
@@ -4765,7 +4766,7 @@ static void handle_fp_2src_single(DisasContext *s, int opcode,
tcg_temp_free_i32(tcg_res);
}
-/* C3.6.26 Floating-point data-processing (2 source) - double precision */
+/* Floating-point data-processing (2 source) - double precision */
static void handle_fp_2src_double(DisasContext *s, int opcode,
int rd, int rn, int rm)
{
@@ -4818,7 +4819,7 @@ static void handle_fp_2src_double(DisasContext *s, int opcode,
tcg_temp_free_i64(tcg_res);
}
-/* C3.6.26 Floating point data-processing (2 source)
+/* Floating point data-processing (2 source)
* 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0
* +---+---+---+-----------+------+---+------+--------+-----+------+------+
* | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | opcode | 1 0 | Rn | Rd |
@@ -4855,7 +4856,7 @@ static void disas_fp_2src(DisasContext *s, uint32_t insn)
}
}
-/* C3.6.27 Floating-point data-processing (3 source) - single precision */
+/* Floating-point data-processing (3 source) - single precision */
static void handle_fp_3src_single(DisasContext *s, bool o0, bool o1,
int rd, int rn, int rm, int ra)
{
@@ -4893,7 +4894,7 @@ static void handle_fp_3src_single(DisasContext *s, bool o0, bool o1,
tcg_temp_free_i32(tcg_res);
}
-/* C3.6.27 Floating-point data-processing (3 source) - double precision */
+/* Floating-point data-processing (3 source) - double precision */
static void handle_fp_3src_double(DisasContext *s, bool o0, bool o1,
int rd, int rn, int rm, int ra)
{
@@ -4931,7 +4932,7 @@ static void handle_fp_3src_double(DisasContext *s, bool o0, bool o1,
tcg_temp_free_i64(tcg_res);
}
-/* C3.6.27 Floating point data-processing (3 source)
+/* Floating point data-processing (3 source)
* 31 30 29 28 24 23 22 21 20 16 15 14 10 9 5 4 0
* +---+---+---+-----------+------+----+------+----+------+------+------+
* | M | 0 | S | 1 1 1 1 1 | type | o1 | Rm | o0 | Ra | Rn | Rd |
@@ -4965,7 +4966,7 @@ static void disas_fp_3src(DisasContext *s, uint32_t insn)
}
}
-/* C3.6.28 Floating point immediate
+/* Floating point immediate
* 31 30 29 28 24 23 22 21 20 13 12 10 9 5 4 0
* +---+---+---+-----------+------+---+------------+-------+------+------+
* | M | 0 | S | 1 1 1 1 0 | type | 1 | imm8 | 1 0 0 | imm5 | Rd |
@@ -5136,7 +5137,7 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
tcg_temp_free_i32(tcg_shift);
}
-/* C3.6.29 Floating point <-> fixed point conversions
+/* Floating point <-> fixed point conversions
* 31 30 29 28 24 23 22 21 20 19 18 16 15 10 9 5 4 0
* +----+---+---+-----------+------+---+-------+--------+-------+------+------+
* | sf | 0 | S | 1 1 1 1 0 | type | 0 | rmode | opcode | scale | Rn | Rd |
@@ -5236,7 +5237,7 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof)
}
}
-/* C3.6.30 Floating point <-> integer conversions
+/* Floating point <-> integer conversions
* 31 30 29 28 24 23 22 21 20 19 18 16 15 10 9 5 4 0
* +----+---+---+-----------+------+---+-------+-----+-------------+----+----+
* | sf | 0 | S | 1 1 1 1 0 | type | 1 | rmode | opc | 0 0 0 0 0 0 | Rn | Rd |
@@ -5371,7 +5372,7 @@ static void do_ext64(DisasContext *s, TCGv_i64 tcg_left, TCGv_i64 tcg_right,
tcg_temp_free_i64(tcg_tmp);
}
-/* C3.6.1 EXT
+/* EXT
* 31 30 29 24 23 22 21 20 16 15 14 11 10 9 5 4 0
* +---+---+-------------+-----+---+------+---+------+---+------+------+
* | 0 | Q | 1 0 1 1 1 0 | op2 | 0 | Rm | 0 | imm4 | 0 | Rn | Rd |
@@ -5444,7 +5445,7 @@ static void disas_simd_ext(DisasContext *s, uint32_t insn)
tcg_temp_free_i64(tcg_resh);
}
-/* C3.6.2 TBL/TBX
+/* TBL/TBX
* 31 30 29 24 23 22 21 20 16 15 14 13 12 11 10 9 5 4 0
* +---+---+-------------+-----+---+------+---+-----+----+-----+------+------+
* | 0 | Q | 0 0 1 1 1 0 | op2 | 0 | Rm | 0 | len | op | 0 0 | Rn | Rd |
@@ -5512,7 +5513,7 @@ static void disas_simd_tb(DisasContext *s, uint32_t insn)
tcg_temp_free_i64(tcg_resh);
}
-/* C3.6.3 ZIP/UZP/TRN
+/* ZIP/UZP/TRN
* 31 30 29 24 23 22 21 20 16 15 14 12 11 10 9 5 4 0
* +---+---+-------------+------+---+------+---+------------------+------+
* | 0 | Q | 0 0 1 1 1 0 | size | 0 | Rm | 0 | opc | 1 0 | Rn | Rd |
@@ -5624,7 +5625,7 @@ static void do_minmaxop(DisasContext *s, TCGv_i32 tcg_elt1, TCGv_i32 tcg_elt2,
}
}
-/* C3.6.4 AdvSIMD across lanes
+/* AdvSIMD across lanes
* 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0
* +---+---+---+-----------+------+-----------+--------+-----+------+------+
* | 0 | Q | U | 0 1 1 1 0 | size | 1 1 0 0 0 | opcode | 1 0 | Rn | Rd |
@@ -5791,7 +5792,7 @@ static void disas_simd_across_lanes(DisasContext *s, uint32_t insn)
tcg_temp_free_i64(tcg_res);
}
-/* C6.3.31 DUP (Element, Vector)
+/* DUP (Element, Vector)
*
* 31 30 29 21 20 16 15 10 9 5 4 0
* +---+---+-------------------+--------+-------------+------+------+
@@ -5834,7 +5835,7 @@ static void handle_simd_dupe(DisasContext *s, int is_q, int rd, int rn,
tcg_temp_free_i64(tmp);
}
-/* C6.3.31 DUP (element, scalar)
+/* DUP (element, scalar)
* 31 21 20 16 15 10 9 5 4 0
* +-----------------------+--------+-------------+------+------+
* | 0 1 0 1 1 1 1 0 0 0 0 | imm5 | 0 0 0 0 0 1 | Rn | Rd |
@@ -5867,7 +5868,7 @@ static void handle_simd_dupes(DisasContext *s, int rd, int rn,
tcg_temp_free_i64(tmp);
}
-/* C6.3.32 DUP (General)
+/* DUP (General)
*
* 31 30 29 21 20 16 15 10 9 5 4 0
* +---+---+-------------------+--------+-------------+------+------+
@@ -5901,7 +5902,7 @@ static void handle_simd_dupg(DisasContext *s, int is_q, int rd, int rn,
}
}
-/* C6.3.150 INS (Element)
+/* INS (Element)
*
* 31 21 20 16 15 14 11 10 9 5 4 0
* +-----------------------+--------+------------+---+------+------+
@@ -5939,7 +5940,7 @@ static void handle_simd_inse(DisasContext *s, int rd, int rn,
}
-/* C6.3.151 INS (General)
+/* INS (General)
*
* 31 21 20 16 15 10 9 5 4 0
* +-----------------------+--------+-------------+------+------+
@@ -5968,8 +5969,8 @@ static void handle_simd_insg(DisasContext *s, int rd, int rn, int imm5)
}
/*
- * C6.3.321 UMOV (General)
- * C6.3.237 SMOV (General)
+ * UMOV (General)
+ * SMOV (General)
*
* 31 30 29 21 20 16 15 12 10 9 5 4 0
* +---+---+-------------------+--------+-------------+------+------+
@@ -6014,7 +6015,7 @@ static void handle_simd_umov_smov(DisasContext *s, int is_q, int is_signed,
}
}
-/* C3.6.5 AdvSIMD copy
+/* AdvSIMD copy
* 31 30 29 28 21 20 16 15 14 11 10 9 5 4 0
* +---+---+----+-----------------+------+---+------+---+------+------+
* | 0 | Q | op | 0 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd |
@@ -6066,7 +6067,7 @@ static void disas_simd_copy(DisasContext *s, uint32_t insn)
}
}
-/* C3.6.6 AdvSIMD modified immediate
+/* AdvSIMD modified immediate
* 31 30 29 28 19 18 16 15 12 11 10 9 5 4 0
* +---+---+----+---------------------+-----+-------+----+---+-------+------+
* | 0 | Q | op | 0 1 1 1 1 0 0 0 0 0 | abc | cmode | o2 | 1 | defgh | Rd |
@@ -6199,7 +6200,7 @@ static void disas_simd_mod_imm(DisasContext *s, uint32_t insn)
tcg_temp_free_i64(tcg_imm);
}
-/* C3.6.7 AdvSIMD scalar copy
+/* AdvSIMD scalar copy
* 31 30 29 28 21 20 16 15 14 11 10 9 5 4 0
* +-----+----+-----------------+------+---+------+---+------+------+
* | 0 1 | op | 1 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd |
@@ -6222,7 +6223,7 @@ static void disas_simd_scalar_copy(DisasContext *s, uint32_t insn)
handle_simd_dupes(s, rd, rn, imm5);
}
-/* C3.6.8 AdvSIMD scalar pairwise
+/* AdvSIMD scalar pairwise
* 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0
* +-----+---+-----------+------+-----------+--------+-----+------+------+
* | 0 1 | U | 1 1 1 1 0 | size | 1 1 0 0 0 | opcode | 1 0 | Rn | Rd |
@@ -6948,7 +6949,7 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar,
tcg_temp_free_i32(tcg_rmode);
}
-/* C3.6.9 AdvSIMD scalar shift by immediate
+/* AdvSIMD scalar shift by immediate
* 31 30 29 28 23 22 19 18 16 15 11 10 9 5 4 0
* +-----+---+-------------+------+------+--------+---+------+------+
* | 0 1 | U | 1 1 1 1 1 0 | immh | immb | opcode | 1 | Rn | Rd |
@@ -7023,7 +7024,7 @@ static void disas_simd_scalar_shift_imm(DisasContext *s, uint32_t insn)
}
}
-/* C3.6.10 AdvSIMD scalar three different
+/* AdvSIMD scalar three different
* 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0
* +-----+---+-----------+------+---+------+--------+-----+------+------+
* | 0 1 | U | 1 1 1 1 0 | size | 1 | Rm | opcode | 0 0 | Rn | Rd |
@@ -7410,7 +7411,7 @@ static void handle_3same_float(DisasContext *s, int size, int elements,
}
}
-/* C3.6.11 AdvSIMD scalar three same
+/* AdvSIMD scalar three same
* 31 30 29 28 24 23 22 21 20 16 15 11 10 9 5 4 0
* +-----+---+-----------+------+---+------+--------+---+------+------+
* | 0 1 | U | 1 1 1 1 0 | size | 1 | Rm | opcode | 1 | Rn | Rd |
@@ -8079,7 +8080,7 @@ static void handle_2misc_satacc(DisasContext *s, bool is_scalar, bool is_u,
}
}
-/* C3.6.12 AdvSIMD scalar two reg misc
+/* AdvSIMD scalar two reg misc
* 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0
* +-----+---+-----------+------+-----------+--------+-----+------+------+
* | 0 1 | U | 1 1 1 1 0 | size | 1 0 0 0 0 | opcode | 1 0 | Rn | Rd |
@@ -8507,7 +8508,7 @@ static void handle_vec_simd_shrn(DisasContext *s, bool is_q,
}
-/* C3.6.14 AdvSIMD shift by immediate
+/* AdvSIMD shift by immediate
* 31 30 29 28 23 22 19 18 16 15 11 10 9 5 4 0
* +---+---+---+-------------+------+------+--------+---+------+------+
* | 0 | Q | U | 0 1 1 1 1 0 | immh | immb | opcode | 1 | Rn | Rd |
@@ -8926,7 +8927,7 @@ static void handle_pmull_64(DisasContext *s, int is_q, int rd, int rn, int rm)
tcg_temp_free_i64(tcg_res);
}
-/* C3.6.15 AdvSIMD three different
+/* AdvSIMD three different
* 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0
* +---+---+---+-----------+------+---+------+--------+-----+------+------+
* | 0 | Q | U | 0 1 1 1 0 | size | 1 | Rm | opcode | 0 0 | Rn | Rd |
@@ -9663,7 +9664,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn)
}
}
-/* C3.6.16 AdvSIMD three same
+/* AdvSIMD three same
* 31 30 29 28 24 23 22 21 20 16 15 11 10 9 5 4 0
* +---+---+---+-----------+------+---+------+--------+---+------+------+
* | 0 | Q | U | 0 1 1 1 0 | size | 1 | Rm | opcode | 1 | Rn | Rd |
@@ -9932,7 +9933,7 @@ static void handle_shll(DisasContext *s, bool is_q, int size, int rn, int rd)
}
}
-/* C3.6.17 AdvSIMD two reg misc
+/* AdvSIMD two reg misc
* 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0
* +---+---+---+-----------+------+-----------+--------+-----+------+------+
* | 0 | Q | U | 0 1 1 1 0 | size | 1 0 0 0 0 | opcode | 1 0 | Rn | Rd |
@@ -10444,12 +10445,12 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
}
}
-/* C3.6.13 AdvSIMD scalar x indexed element
+/* AdvSIMD scalar x indexed element
* 31 30 29 28 24 23 22 21 20 19 16 15 12 11 10 9 5 4 0
* +-----+---+-----------+------+---+---+------+-----+---+---+------+------+
* | 0 1 | U | 1 1 1 1 1 | size | L | M | Rm | opc | H | 0 | Rn | Rd |
* +-----+---+-----------+------+---+---+------+-----+---+---+------+------+
- * C3.6.18 AdvSIMD vector x indexed element
+ * AdvSIMD vector x indexed element
* 31 30 29 28 24 23 22 21 20 19 16 15 12 11 10 9 5 4 0
* +---+---+---+-----------+------+---+---+------+-----+---+---+------+------+
* | 0 | Q | U | 0 1 1 1 1 | size | L | M | Rm | opc | H | 0 | Rn | Rd |
@@ -10899,7 +10900,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn)
}
}
-/* C3.6.19 Crypto AES
+/* Crypto AES
* 31 24 23 22 21 17 16 12 11 10 9 5 4 0
* +-----------------+------+-----------+--------+-----+------+------+
* | 0 1 0 0 1 1 1 0 | size | 1 0 1 0 0 | opcode | 1 0 | Rn | Rd |
@@ -10962,7 +10963,7 @@ static void disas_crypto_aes(DisasContext *s, uint32_t insn)
tcg_temp_free_i32(tcg_decrypt);
}
-/* C3.6.20 Crypto three-reg SHA
+/* Crypto three-reg SHA
* 31 24 23 22 21 20 16 15 14 12 11 10 9 5 4 0
* +-----------------+------+---+------+---+--------+-----+------+------+
* | 0 1 0 1 1 1 1 0 | size | 0 | Rm | 0 | opcode | 0 0 | Rn | Rd |
@@ -11034,7 +11035,7 @@ static void disas_crypto_three_reg_sha(DisasContext *s, uint32_t insn)
tcg_temp_free_i32(tcg_rm_regno);
}
-/* C3.6.21 Crypto two-reg SHA
+/* Crypto two-reg SHA
* 31 24 23 22 21 17 16 12 11 10 9 5 4 0
* +-----------------+------+-----------+--------+-----+------+------+
* | 0 1 0 1 1 1 1 0 | size | 1 0 1 0 0 | opcode | 1 0 | Rn | Rd |
diff --git a/target/mips/Makefile.objs b/target/mips/Makefile.objs
index bc5ed8511f..651f36f517 100644
--- a/target/mips/Makefile.objs
+++ b/target/mips/Makefile.objs
@@ -1,4 +1,4 @@
obj-y += translate.o dsp_helper.o op_helper.o lmi_helper.o helper.o cpu.o
obj-y += gdbstub.o msa_helper.o mips-semi.o
-obj-$(CONFIG_SOFTMMU) += machine.o
+obj-$(CONFIG_SOFTMMU) += machine.o cp0_timer.o
obj-$(CONFIG_KVM) += kvm.o
diff --git a/hw/mips/cputimer.c b/target/mips/cp0_timer.c
index 8a166b3ea7..f4716395df 100644
--- a/hw/mips/cputimer.c
+++ b/target/mips/cp0_timer.c
@@ -21,10 +21,10 @@
*/
#include "qemu/osdep.h"
-#include "hw/hw.h"
#include "hw/mips/cpudevs.h"
#include "qemu/timer.h"
#include "sysemu/kvm.h"
+#include "internal.h"
#define TIMER_PERIOD 10 /* 10 ns period for 100 Mhz frequency */
diff --git a/target/mips/cpu-qom.h b/target/mips/cpu-qom.h
index 3f5bf23823..ee58606afe 100644
--- a/target/mips/cpu-qom.h
+++ b/target/mips/cpu-qom.h
@@ -49,6 +49,7 @@ typedef struct MIPSCPUClass {
DeviceRealize parent_realize;
void (*parent_reset)(CPUState *cpu);
+ const struct mips_def_t *cpu_def;
} MIPSCPUClass;
typedef struct MIPSCPU MIPSCPU;
diff --git a/target/mips/cpu.c b/target/mips/cpu.c
index 1bb66b7a5a..1a9a3ed94d 100644
--- a/target/mips/cpu.c
+++ b/target/mips/cpu.c
@@ -21,6 +21,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "cpu.h"
+#include "internal.h"
#include "kvm_mips.h"
#include "qemu-common.h"
#include "sysemu/kvm.h"
@@ -122,6 +123,7 @@ static void mips_cpu_disas_set_info(CPUState *s, disassemble_info *info) {
static void mips_cpu_realizefn(DeviceState *dev, Error **errp)
{
CPUState *cs = CPU(dev);
+ MIPSCPU *cpu = MIPS_CPU(dev);
MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(dev);
Error *local_err = NULL;
@@ -131,6 +133,8 @@ static void mips_cpu_realizefn(DeviceState *dev, Error **errp)
return;
}
+ cpu_mips_realize_env(&cpu->env);
+
cpu_reset(cs);
qemu_init_vcpu(cs);
@@ -142,14 +146,36 @@ static void mips_cpu_initfn(Object *obj)
CPUState *cs = CPU(obj);
MIPSCPU *cpu = MIPS_CPU(obj);
CPUMIPSState *env = &cpu->env;
+ MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(obj);
cs->env_ptr = env;
+ env->cpu_model = mcc->cpu_def;
if (tcg_enabled()) {
mips_tcg_init();
}
}
+static char *mips_cpu_type_name(const char *cpu_model)
+{
+ return g_strdup_printf("%s-" TYPE_MIPS_CPU, cpu_model);
+}
+
+static ObjectClass *mips_cpu_class_by_name(const char *cpu_model)
+{
+ ObjectClass *oc;
+ char *typename;
+
+ if (cpu_model == NULL) {
+ return NULL;
+ }
+
+ typename = mips_cpu_type_name(cpu_model);
+ oc = object_class_by_name(typename);
+ g_free(typename);
+ return oc;
+}
+
static void mips_cpu_class_init(ObjectClass *c, void *data)
{
MIPSCPUClass *mcc = MIPS_CPU_CLASS(c);
@@ -162,6 +188,7 @@ static void mips_cpu_class_init(ObjectClass *c, void *data)
mcc->parent_reset = cc->reset;
cc->reset = mips_cpu_reset;
+ cc->class_by_name = mips_cpu_class_by_name;
cc->has_work = mips_cpu_has_work;
cc->do_interrupt = mips_cpu_do_interrupt;
cc->cpu_exec_interrupt = mips_cpu_exec_interrupt;
@@ -189,14 +216,39 @@ static const TypeInfo mips_cpu_type_info = {
.parent = TYPE_CPU,
.instance_size = sizeof(MIPSCPU),
.instance_init = mips_cpu_initfn,
- .abstract = false,
+ .abstract = true,
.class_size = sizeof(MIPSCPUClass),
.class_init = mips_cpu_class_init,
};
+static void mips_cpu_cpudef_class_init(ObjectClass *oc, void *data)
+{
+ MIPSCPUClass *mcc = MIPS_CPU_CLASS(oc);
+ mcc->cpu_def = data;
+}
+
+static void mips_register_cpudef_type(const struct mips_def_t *def)
+{
+ char *typename = mips_cpu_type_name(def->name);
+ TypeInfo ti = {
+ .name = typename,
+ .parent = TYPE_MIPS_CPU,
+ .class_init = mips_cpu_cpudef_class_init,
+ .class_data = (void *)def,
+ };
+
+ type_register(&ti);
+ g_free(typename);
+}
+
static void mips_cpu_register_types(void)
{
+ int i;
+
type_register_static(&mips_cpu_type_info);
+ for (i = 0; i < mips_defs_number; i++) {
+ mips_register_cpudef_type(&mips_defs[i]);
+ }
}
type_init(mips_cpu_register_types)
diff --git a/target/mips/cpu.h b/target/mips/cpu.h
index 74f6a5b098..66265e4eb6 100644
--- a/target/mips/cpu.h
+++ b/target/mips/cpu.h
@@ -1,8 +1,6 @@
#ifndef MIPS_CPU_H
#define MIPS_CPU_H
-//#define DEBUG_OP
-
#define ALIGNED_ONLY
#define CPUArchState struct CPUMIPSState
@@ -15,56 +13,11 @@
struct CPUMIPSState;
-typedef struct r4k_tlb_t r4k_tlb_t;
-struct r4k_tlb_t {
- target_ulong VPN;
- uint32_t PageMask;
- uint16_t ASID;
- unsigned int G:1;
- unsigned int C0:3;
- unsigned int C1:3;
- unsigned int V0:1;
- unsigned int V1:1;
- unsigned int D0:1;
- unsigned int D1:1;
- unsigned int XI0:1;
- unsigned int XI1:1;
- unsigned int RI0:1;
- unsigned int RI1:1;
- unsigned int EHINV:1;
- uint64_t PFN[2];
-};
-
-#if !defined(CONFIG_USER_ONLY)
typedef struct CPUMIPSTLBContext CPUMIPSTLBContext;
-struct CPUMIPSTLBContext {
- uint32_t nb_tlb;
- uint32_t tlb_in_use;
- int (*map_address) (struct CPUMIPSState *env, hwaddr *physical, int *prot, target_ulong address, int rw, int access_type);
- void (*helper_tlbwi)(struct CPUMIPSState *env);
- void (*helper_tlbwr)(struct CPUMIPSState *env);
- void (*helper_tlbp)(struct CPUMIPSState *env);
- void (*helper_tlbr)(struct CPUMIPSState *env);
- void (*helper_tlbinv)(struct CPUMIPSState *env);
- void (*helper_tlbinvf)(struct CPUMIPSState *env);
- union {
- struct {
- r4k_tlb_t tlb[MIPS_TLB_MAX];
- } r4k;
- } mmu;
-};
-#endif
/* MSA Context */
#define MSA_WRLEN (128)
-enum CPUMIPSMSADataFormat {
- DF_BYTE = 0,
- DF_HALF,
- DF_WORD,
- DF_DOUBLE
-};
-
typedef union wr_t wr_t;
union wr_t {
int8_t b[MSA_WRLEN/8];
@@ -682,40 +635,6 @@ static inline MIPSCPU *mips_env_get_cpu(CPUMIPSState *env)
#define ENV_OFFSET offsetof(MIPSCPU, env)
-#ifndef CONFIG_USER_ONLY
-extern const struct VMStateDescription vmstate_mips_cpu;
-#endif
-
-void mips_cpu_do_interrupt(CPUState *cpu);
-bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req);
-void mips_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
- int flags);
-hwaddr mips_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
-int mips_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
-int mips_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
-void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
- MMUAccessType access_type,
- int mmu_idx, uintptr_t retaddr);
-
-#if !defined(CONFIG_USER_ONLY)
-int no_mmu_map_address (CPUMIPSState *env, hwaddr *physical, int *prot,
- target_ulong address, int rw, int access_type);
-int fixed_mmu_map_address (CPUMIPSState *env, hwaddr *physical, int *prot,
- target_ulong address, int rw, int access_type);
-int r4k_map_address (CPUMIPSState *env, hwaddr *physical, int *prot,
- target_ulong address, int rw, int access_type);
-void r4k_helper_tlbwi(CPUMIPSState *env);
-void r4k_helper_tlbwr(CPUMIPSState *env);
-void r4k_helper_tlbp(CPUMIPSState *env);
-void r4k_helper_tlbr(CPUMIPSState *env);
-void r4k_helper_tlbinv(CPUMIPSState *env);
-void r4k_helper_tlbinvf(CPUMIPSState *env);
-
-void mips_cpu_unassigned_access(CPUState *cpu, hwaddr addr,
- bool is_write, bool is_exec, int unused,
- unsigned size);
-#endif
-
void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf);
#define cpu_signal_handler cpu_mips_signal_handler
@@ -746,42 +665,6 @@ static inline int cpu_mmu_index (CPUMIPSState *env, bool ifetch)
return hflags_mmu_index(env->hflags);
}
-static inline bool cpu_mips_hw_interrupts_enabled(CPUMIPSState *env)
-{
- return (env->CP0_Status & (1 << CP0St_IE)) &&
- !(env->CP0_Status & (1 << CP0St_EXL)) &&
- !(env->CP0_Status & (1 << CP0St_ERL)) &&
- !(env->hflags & MIPS_HFLAG_DM) &&
- /* Note that the TCStatus IXMT field is initialized to zero,
- and only MT capable cores can set it to one. So we don't
- need to check for MT capabilities here. */
- !(env->active_tc.CP0_TCStatus & (1 << CP0TCSt_IXMT));
-}
-
-/* Check if there is pending and not masked out interrupt */
-static inline bool cpu_mips_hw_interrupts_pending(CPUMIPSState *env)
-{
- int32_t pending;
- int32_t status;
- bool r;
-
- pending = env->CP0_Cause & CP0Ca_IP_mask;
- status = env->CP0_Status & CP0Ca_IP_mask;
-
- if (env->CP0_Config3 & (1 << CP0C3_VEIC)) {
- /* A MIPS configured with a vectorizing external interrupt controller
- will feed a vector into the Cause pending lines. The core treats
- the status lines as a vector level, not as indiviual masks. */
- r = pending > status;
- } else {
- /* A MIPS configured with compatibility or VInt (Vectored Interrupts)
- treats the pending lines as individual interrupt lines, the status
- lines are individual masks. */
- r = (pending & status) != 0;
- }
- return r;
-}
-
#include "exec/cpu-all.h"
/* Memory access type :
@@ -847,100 +730,32 @@ enum {
#define EXCP_SC 0x100
/*
- * This is an interrnally generated WAKE request line.
+ * This is an internally generated WAKE request line.
* It is driven by the CPU itself. Raised when the MT
* block wants to wake a VPE from an inactive state and
* cleared when VPE goes from active to inactive.
*/
#define CPU_INTERRUPT_WAKE CPU_INTERRUPT_TGT_INT_0
-void mips_tcg_init(void);
-MIPSCPU *cpu_mips_init(const char *cpu_model);
int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc);
-#define cpu_init(cpu_model) CPU(cpu_mips_init(cpu_model))
+#define cpu_init(cpu_model) cpu_generic_init(TYPE_MIPS_CPU, cpu_model)
bool cpu_supports_cps_smp(const char *cpu_model);
bool cpu_supports_isa(const char *cpu_model, unsigned int isa);
void cpu_set_exception_base(int vp_index, target_ulong address);
-/* TODO QOM'ify CPU reset and remove */
-void cpu_state_reset(CPUMIPSState *s);
-
-/* mips_timer.c */
-uint32_t cpu_mips_get_random (CPUMIPSState *env);
-uint32_t cpu_mips_get_count (CPUMIPSState *env);
-void cpu_mips_store_count (CPUMIPSState *env, uint32_t value);
-void cpu_mips_store_compare (CPUMIPSState *env, uint32_t value);
-void cpu_mips_start_count(CPUMIPSState *env);
-void cpu_mips_stop_count(CPUMIPSState *env);
-
/* mips_int.c */
void cpu_mips_soft_irq(CPUMIPSState *env, int irq, int level);
/* helper.c */
-int mips_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw,
- int mmu_idx);
-
-/* op_helper.c */
-uint32_t float_class_s(uint32_t arg, float_status *fst);
-uint64_t float_class_d(uint64_t arg, float_status *fst);
-
-#if !defined(CONFIG_USER_ONLY)
-void r4k_invalidate_tlb (CPUMIPSState *env, int idx, int use_extra);
-hwaddr cpu_mips_translate_address (CPUMIPSState *env, target_ulong address,
- int rw);
-#endif
target_ulong exception_resume_pc (CPUMIPSState *env);
-/* op_helper.c */
-extern unsigned int ieee_rm[];
-int ieee_ex_to_mips(int xcpt);
-
-static inline void restore_rounding_mode(CPUMIPSState *env)
-{
- set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3],
- &env->active_fpu.fp_status);
-}
-
-static inline void restore_flush_mode(CPUMIPSState *env)
-{
- set_flush_to_zero((env->active_fpu.fcr31 & (1 << FCR31_FS)) != 0,
- &env->active_fpu.fp_status);
-}
-
static inline void restore_snan_bit_mode(CPUMIPSState *env)
{
set_snan_bit_is_one((env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) == 0,
&env->active_fpu.fp_status);
}
-static inline void restore_fp_status(CPUMIPSState *env)
-{
- restore_rounding_mode(env);
- restore_flush_mode(env);
- restore_snan_bit_mode(env);
-}
-
-static inline void restore_msa_fp_status(CPUMIPSState *env)
-{
- float_status *status = &env->active_tc.msa_fp_status;
- int rounding_mode = (env->active_tc.msacsr & MSACSR_RM_MASK) >> MSACSR_RM;
- bool flush_to_zero = (env->active_tc.msacsr & MSACSR_FS_MASK) != 0;
-
- set_float_rounding_mode(ieee_rm[rounding_mode], status);
- set_flush_to_zero(flush_to_zero, status);
- set_flush_inputs_to_zero(flush_to_zero, status);
-}
-
-static inline void restore_pamask(CPUMIPSState *env)
-{
- if (env->hflags & MIPS_HFLAG_ELPA) {
- env->PAMask = (1ULL << env->PABITS) - 1;
- } else {
- env->PAMask = PAMASK_BASE;
- }
-}
-
static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *flags)
{
@@ -950,172 +765,4 @@ static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc,
MIPS_HFLAG_HWRENA_ULR);
}
-static inline int mips_vpe_active(CPUMIPSState *env)
-{
- int active = 1;
-
- /* Check that the VPE is enabled. */
- if (!(env->mvp->CP0_MVPControl & (1 << CP0MVPCo_EVP))) {
- active = 0;
- }
- /* Check that the VPE is activated. */
- if (!(env->CP0_VPEConf0 & (1 << CP0VPEC0_VPA))) {
- active = 0;
- }
-
- /* Now verify that there are active thread contexts in the VPE.
-
- This assumes the CPU model will internally reschedule threads
- if the active one goes to sleep. If there are no threads available
- the active one will be in a sleeping state, and we can turn off
- the entire VPE. */
- if (!(env->active_tc.CP0_TCStatus & (1 << CP0TCSt_A))) {
- /* TC is not activated. */
- active = 0;
- }
- if (env->active_tc.CP0_TCHalt & 1) {
- /* TC is in halt state. */
- active = 0;
- }
-
- return active;
-}
-
-static inline int mips_vp_active(CPUMIPSState *env)
-{
- CPUState *other_cs = first_cpu;
-
- /* Check if the VP disabled other VPs (which means the VP is enabled) */
- if ((env->CP0_VPControl >> CP0VPCtl_DIS) & 1) {
- return 1;
- }
-
- /* Check if the virtual processor is disabled due to a DVP */
- CPU_FOREACH(other_cs) {
- MIPSCPU *other_cpu = MIPS_CPU(other_cs);
- if ((&other_cpu->env != env) &&
- ((other_cpu->env.CP0_VPControl >> CP0VPCtl_DIS) & 1)) {
- return 0;
- }
- }
- return 1;
-}
-
-static inline void compute_hflags(CPUMIPSState *env)
-{
- env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 |
- MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU |
- MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2 |
- MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA | MIPS_HFLAG_FRE |
- MIPS_HFLAG_ELPA | MIPS_HFLAG_ERL);
- if (env->CP0_Status & (1 << CP0St_ERL)) {
- env->hflags |= MIPS_HFLAG_ERL;
- }
- if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
- !(env->CP0_Status & (1 << CP0St_ERL)) &&
- !(env->hflags & MIPS_HFLAG_DM)) {
- env->hflags |= (env->CP0_Status >> CP0St_KSU) & MIPS_HFLAG_KSU;
- }
-#if defined(TARGET_MIPS64)
- if ((env->insn_flags & ISA_MIPS3) &&
- (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_UM) ||
- (env->CP0_Status & (1 << CP0St_PX)) ||
- (env->CP0_Status & (1 << CP0St_UX)))) {
- env->hflags |= MIPS_HFLAG_64;
- }
-
- if (!(env->insn_flags & ISA_MIPS3)) {
- env->hflags |= MIPS_HFLAG_AWRAP;
- } else if (((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_UM) &&
- !(env->CP0_Status & (1 << CP0St_UX))) {
- env->hflags |= MIPS_HFLAG_AWRAP;
- } else if (env->insn_flags & ISA_MIPS64R6) {
- /* Address wrapping for Supervisor and Kernel is specified in R6 */
- if ((((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_SM) &&
- !(env->CP0_Status & (1 << CP0St_SX))) ||
- (((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_KM) &&
- !(env->CP0_Status & (1 << CP0St_KX)))) {
- env->hflags |= MIPS_HFLAG_AWRAP;
- }
- }
-#endif
- if (((env->CP0_Status & (1 << CP0St_CU0)) &&
- !(env->insn_flags & ISA_MIPS32R6)) ||
- !(env->hflags & MIPS_HFLAG_KSU)) {
- env->hflags |= MIPS_HFLAG_CP0;
- }
- if (env->CP0_Status & (1 << CP0St_CU1)) {
- env->hflags |= MIPS_HFLAG_FPU;
- }
- if (env->CP0_Status & (1 << CP0St_FR)) {
- env->hflags |= MIPS_HFLAG_F64;
- }
- if (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_KM) &&
- (env->CP0_Config5 & (1 << CP0C5_SBRI))) {
- env->hflags |= MIPS_HFLAG_SBRI;
- }
- if (env->insn_flags & ASE_DSPR2) {
- /* Enables access MIPS DSP resources, now our cpu is DSP ASER2,
- so enable to access DSPR2 resources. */
- if (env->CP0_Status & (1 << CP0St_MX)) {
- env->hflags |= MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2;
- }
-
- } else if (env->insn_flags & ASE_DSP) {
- /* Enables access MIPS DSP resources, now our cpu is DSP ASE,
- so enable to access DSP resources. */
- if (env->CP0_Status & (1 << CP0St_MX)) {
- env->hflags |= MIPS_HFLAG_DSP;
- }
-
- }
- if (env->insn_flags & ISA_MIPS32R2) {
- if (env->active_fpu.fcr0 & (1 << FCR0_F64)) {
- env->hflags |= MIPS_HFLAG_COP1X;
- }
- } else if (env->insn_flags & ISA_MIPS32) {
- if (env->hflags & MIPS_HFLAG_64) {
- env->hflags |= MIPS_HFLAG_COP1X;
- }
- } else if (env->insn_flags & ISA_MIPS4) {
- /* All supported MIPS IV CPUs use the XX (CU3) to enable
- and disable the MIPS IV extensions to the MIPS III ISA.
- Some other MIPS IV CPUs ignore the bit, so the check here
- would be too restrictive for them. */
- if (env->CP0_Status & (1U << CP0St_CU3)) {
- env->hflags |= MIPS_HFLAG_COP1X;
- }
- }
- if (env->insn_flags & ASE_MSA) {
- if (env->CP0_Config5 & (1 << CP0C5_MSAEn)) {
- env->hflags |= MIPS_HFLAG_MSA;
- }
- }
- if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) {
- if (env->CP0_Config5 & (1 << CP0C5_FRE)) {
- env->hflags |= MIPS_HFLAG_FRE;
- }
- }
- if (env->CP0_Config3 & (1 << CP0C3_LPA)) {
- if (env->CP0_PageGrain & (1 << CP0PG_ELPA)) {
- env->hflags |= MIPS_HFLAG_ELPA;
- }
- }
-}
-
-void cpu_mips_tlb_flush(CPUMIPSState *env);
-void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc);
-void cpu_mips_store_status(CPUMIPSState *env, target_ulong val);
-void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val);
-
-void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env, uint32_t exception,
- int error_code, uintptr_t pc);
-
-static inline void QEMU_NORETURN do_raise_exception(CPUMIPSState *env,
- uint32_t exception,
- uintptr_t pc)
-{
- do_raise_exception_err(env, exception, 0, pc);
-}
-
#endif /* MIPS_CPU_H */
diff --git a/target/mips/dsp_helper.c b/target/mips/dsp_helper.c
index dc707934ea..f152fea34a 100644
--- a/target/mips/dsp_helper.c
+++ b/target/mips/dsp_helper.c
@@ -45,9 +45,9 @@ typedef union {
} DSP64Value;
/*** MIPS DSP internal functions begin ***/
-#define MIPSDSP_ABS(x) (((x) >= 0) ? x : -x)
-#define MIPSDSP_OVERFLOW_ADD(a, b, c, d) (~(a ^ b) & (a ^ c) & d)
-#define MIPSDSP_OVERFLOW_SUB(a, b, c, d) ((a ^ b) & (a ^ c) & d)
+#define MIPSDSP_ABS(x) (((x) >= 0) ? (x) : -(x))
+#define MIPSDSP_OVERFLOW_ADD(a, b, c, d) (~((a) ^ (b)) & ((a) ^ (c)) & (d))
+#define MIPSDSP_OVERFLOW_SUB(a, b, c, d) (((a) ^ (b)) & ((a) ^ (c)) & (d))
static inline void set_DSPControl_overflow_flag(uint32_t flag, int position,
CPUMIPSState *env)
@@ -1047,47 +1047,47 @@ static inline int32_t mipsdsp_cmpu_lt(uint32_t a, uint32_t b)
#define MIPSDSP_SPLIT32_8(num, a, b, c, d) \
do { \
- a = (num >> 24) & MIPSDSP_Q0; \
- b = (num >> 16) & MIPSDSP_Q0; \
- c = (num >> 8) & MIPSDSP_Q0; \
- d = num & MIPSDSP_Q0; \
+ a = ((num) >> 24) & MIPSDSP_Q0; \
+ b = ((num) >> 16) & MIPSDSP_Q0; \
+ c = ((num) >> 8) & MIPSDSP_Q0; \
+ d = (num) & MIPSDSP_Q0; \
} while (0)
#define MIPSDSP_SPLIT32_16(num, a, b) \
do { \
- a = (num >> 16) & MIPSDSP_LO; \
- b = num & MIPSDSP_LO; \
+ a = ((num) >> 16) & MIPSDSP_LO; \
+ b = (num) & MIPSDSP_LO; \
} while (0)
-#define MIPSDSP_RETURN32_8(a, b, c, d) ((target_long)(int32_t) \
- (((uint32_t)a << 24) | \
- (((uint32_t)b << 16) | \
- (((uint32_t)c << 8) | \
- ((uint32_t)d & 0xFF)))))
-#define MIPSDSP_RETURN32_16(a, b) ((target_long)(int32_t) \
- (((uint32_t)a << 16) | \
- ((uint32_t)b & 0xFFFF)))
+#define MIPSDSP_RETURN32_8(a, b, c, d) ((target_long)(int32_t) \
+ (((uint32_t)(a) << 24) | \
+ ((uint32_t)(b) << 16) | \
+ ((uint32_t)(c) << 8) | \
+ ((uint32_t)(d) & 0xFF)))
+#define MIPSDSP_RETURN32_16(a, b) ((target_long)(int32_t) \
+ (((uint32_t)(a) << 16) | \
+ ((uint32_t)(b) & 0xFFFF)))
#ifdef TARGET_MIPS64
#define MIPSDSP_SPLIT64_16(num, a, b, c, d) \
do { \
- a = (num >> 48) & MIPSDSP_LO; \
- b = (num >> 32) & MIPSDSP_LO; \
- c = (num >> 16) & MIPSDSP_LO; \
- d = num & MIPSDSP_LO; \
+ a = ((num) >> 48) & MIPSDSP_LO; \
+ b = ((num) >> 32) & MIPSDSP_LO; \
+ c = ((num) >> 16) & MIPSDSP_LO; \
+ d = (num) & MIPSDSP_LO; \
} while (0)
#define MIPSDSP_SPLIT64_32(num, a, b) \
do { \
- a = (num >> 32) & MIPSDSP_LLO; \
- b = num & MIPSDSP_LLO; \
+ a = ((num) >> 32) & MIPSDSP_LLO; \
+ b = (num) & MIPSDSP_LLO; \
} while (0)
-#define MIPSDSP_RETURN64_16(a, b, c, d) (((uint64_t)a << 48) | \
- ((uint64_t)b << 32) | \
- ((uint64_t)c << 16) | \
- (uint64_t)d)
-#define MIPSDSP_RETURN64_32(a, b) (((uint64_t)a << 32) | (uint64_t)b)
+#define MIPSDSP_RETURN64_16(a, b, c, d) (((uint64_t)(a) << 48) | \
+ ((uint64_t)(b) << 32) | \
+ ((uint64_t)(c) << 16) | \
+ (uint64_t)(d))
+#define MIPSDSP_RETURN64_32(a, b) (((uint64_t)(a) << 32) | (uint64_t)(b))
#endif
/** DSP Arithmetic Sub-class insns **/
diff --git a/target/mips/gdbstub.c b/target/mips/gdbstub.c
index 7c682289c2..6d1fb70f2c 100644
--- a/target/mips/gdbstub.c
+++ b/target/mips/gdbstub.c
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h"
+#include "internal.h"
#include "exec/gdbstub.h"
int mips_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
diff --git a/target/mips/helper.c b/target/mips/helper.c
index ca39aca08a..ea076261af 100644
--- a/target/mips/helper.c
+++ b/target/mips/helper.c
@@ -19,6 +19,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
+#include "internal.h"
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
#include "exec/log.h"
diff --git a/target/mips/internal.h b/target/mips/internal.h
new file mode 100644
index 0000000000..45ded3484c
--- /dev/null
+++ b/target/mips/internal.h
@@ -0,0 +1,422 @@
+/* mips internal definitions and helpers
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef MIPS_INTERNAL_H
+#define MIPS_INTERNAL_H
+
+
+/* MMU types, the first four entries have the same layout as the
+ CP0C0_MT field. */
+enum mips_mmu_types {
+ MMU_TYPE_NONE,
+ MMU_TYPE_R4000,
+ MMU_TYPE_RESERVED,
+ MMU_TYPE_FMT,
+ MMU_TYPE_R3000,
+ MMU_TYPE_R6000,
+ MMU_TYPE_R8000
+};
+
+struct mips_def_t {
+ const char *name;
+ int32_t CP0_PRid;
+ int32_t CP0_Config0;
+ int32_t CP0_Config1;
+ int32_t CP0_Config2;
+ int32_t CP0_Config3;
+ int32_t CP0_Config4;
+ int32_t CP0_Config4_rw_bitmask;
+ int32_t CP0_Config5;
+ int32_t CP0_Config5_rw_bitmask;
+ int32_t CP0_Config6;
+ int32_t CP0_Config7;
+ target_ulong CP0_LLAddr_rw_bitmask;
+ int CP0_LLAddr_shift;
+ int32_t SYNCI_Step;
+ int32_t CCRes;
+ int32_t CP0_Status_rw_bitmask;
+ int32_t CP0_TCStatus_rw_bitmask;
+ int32_t CP0_SRSCtl;
+ int32_t CP1_fcr0;
+ int32_t CP1_fcr31_rw_bitmask;
+ int32_t CP1_fcr31;
+ int32_t MSAIR;
+ int32_t SEGBITS;
+ int32_t PABITS;
+ int32_t CP0_SRSConf0_rw_bitmask;
+ int32_t CP0_SRSConf0;
+ int32_t CP0_SRSConf1_rw_bitmask;
+ int32_t CP0_SRSConf1;
+ int32_t CP0_SRSConf2_rw_bitmask;
+ int32_t CP0_SRSConf2;
+ int32_t CP0_SRSConf3_rw_bitmask;
+ int32_t CP0_SRSConf3;
+ int32_t CP0_SRSConf4_rw_bitmask;
+ int32_t CP0_SRSConf4;
+ int32_t CP0_PageGrain_rw_bitmask;
+ int32_t CP0_PageGrain;
+ target_ulong CP0_EBaseWG_rw_bitmask;
+ int insn_flags;
+ enum mips_mmu_types mmu_type;
+};
+
+extern const struct mips_def_t mips_defs[];
+extern const int mips_defs_number;
+
+enum CPUMIPSMSADataFormat {
+ DF_BYTE = 0,
+ DF_HALF,
+ DF_WORD,
+ DF_DOUBLE
+};
+
+void mips_cpu_do_interrupt(CPUState *cpu);
+bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req);
+void mips_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
+ int flags);
+hwaddr mips_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+int mips_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
+int mips_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
+void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
+ MMUAccessType access_type,
+ int mmu_idx, uintptr_t retaddr);
+
+#if !defined(CONFIG_USER_ONLY)
+
+typedef struct r4k_tlb_t r4k_tlb_t;
+struct r4k_tlb_t {
+ target_ulong VPN;
+ uint32_t PageMask;
+ uint16_t ASID;
+ unsigned int G:1;
+ unsigned int C0:3;
+ unsigned int C1:3;
+ unsigned int V0:1;
+ unsigned int V1:1;
+ unsigned int D0:1;
+ unsigned int D1:1;
+ unsigned int XI0:1;
+ unsigned int XI1:1;
+ unsigned int RI0:1;
+ unsigned int RI1:1;
+ unsigned int EHINV:1;
+ uint64_t PFN[2];
+};
+
+struct CPUMIPSTLBContext {
+ uint32_t nb_tlb;
+ uint32_t tlb_in_use;
+ int (*map_address)(struct CPUMIPSState *env, hwaddr *physical, int *prot,
+ target_ulong address, int rw, int access_type);
+ void (*helper_tlbwi)(struct CPUMIPSState *env);
+ void (*helper_tlbwr)(struct CPUMIPSState *env);
+ void (*helper_tlbp)(struct CPUMIPSState *env);
+ void (*helper_tlbr)(struct CPUMIPSState *env);
+ void (*helper_tlbinv)(struct CPUMIPSState *env);
+ void (*helper_tlbinvf)(struct CPUMIPSState *env);
+ union {
+ struct {
+ r4k_tlb_t tlb[MIPS_TLB_MAX];
+ } r4k;
+ } mmu;
+};
+
+int no_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
+ target_ulong address, int rw, int access_type);
+int fixed_mmu_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
+ target_ulong address, int rw, int access_type);
+int r4k_map_address(CPUMIPSState *env, hwaddr *physical, int *prot,
+ target_ulong address, int rw, int access_type);
+void r4k_helper_tlbwi(CPUMIPSState *env);
+void r4k_helper_tlbwr(CPUMIPSState *env);
+void r4k_helper_tlbp(CPUMIPSState *env);
+void r4k_helper_tlbr(CPUMIPSState *env);
+void r4k_helper_tlbinv(CPUMIPSState *env);
+void r4k_helper_tlbinvf(CPUMIPSState *env);
+void r4k_invalidate_tlb(CPUMIPSState *env, int idx, int use_extra);
+
+void mips_cpu_unassigned_access(CPUState *cpu, hwaddr addr,
+ bool is_write, bool is_exec, int unused,
+ unsigned size);
+hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address,
+ int rw);
+#endif
+
+#define cpu_signal_handler cpu_mips_signal_handler
+
+#ifndef CONFIG_USER_ONLY
+extern const struct VMStateDescription vmstate_mips_cpu;
+#endif
+
+static inline bool cpu_mips_hw_interrupts_enabled(CPUMIPSState *env)
+{
+ return (env->CP0_Status & (1 << CP0St_IE)) &&
+ !(env->CP0_Status & (1 << CP0St_EXL)) &&
+ !(env->CP0_Status & (1 << CP0St_ERL)) &&
+ !(env->hflags & MIPS_HFLAG_DM) &&
+ /* Note that the TCStatus IXMT field is initialized to zero,
+ and only MT capable cores can set it to one. So we don't
+ need to check for MT capabilities here. */
+ !(env->active_tc.CP0_TCStatus & (1 << CP0TCSt_IXMT));
+}
+
+/* Check if there is pending and not masked out interrupt */
+static inline bool cpu_mips_hw_interrupts_pending(CPUMIPSState *env)
+{
+ int32_t pending;
+ int32_t status;
+ bool r;
+
+ pending = env->CP0_Cause & CP0Ca_IP_mask;
+ status = env->CP0_Status & CP0Ca_IP_mask;
+
+ if (env->CP0_Config3 & (1 << CP0C3_VEIC)) {
+ /* A MIPS configured with a vectorizing external interrupt controller
+ will feed a vector into the Cause pending lines. The core treats
+ the status lines as a vector level, not as indiviual masks. */
+ r = pending > status;
+ } else {
+ /* A MIPS configured with compatibility or VInt (Vectored Interrupts)
+ treats the pending lines as individual interrupt lines, the status
+ lines are individual masks. */
+ r = (pending & status) != 0;
+ }
+ return r;
+}
+
+void mips_tcg_init(void);
+
+/* TODO QOM'ify CPU reset and remove */
+void cpu_state_reset(CPUMIPSState *s);
+void cpu_mips_realize_env(CPUMIPSState *env);
+
+/* cp0_timer.c */
+uint32_t cpu_mips_get_random(CPUMIPSState *env);
+uint32_t cpu_mips_get_count(CPUMIPSState *env);
+void cpu_mips_store_count(CPUMIPSState *env, uint32_t value);
+void cpu_mips_store_compare(CPUMIPSState *env, uint32_t value);
+void cpu_mips_start_count(CPUMIPSState *env);
+void cpu_mips_stop_count(CPUMIPSState *env);
+
+/* helper.c */
+int mips_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw,
+ int mmu_idx);
+
+/* op_helper.c */
+uint32_t float_class_s(uint32_t arg, float_status *fst);
+uint64_t float_class_d(uint64_t arg, float_status *fst);
+
+extern unsigned int ieee_rm[];
+int ieee_ex_to_mips(int xcpt);
+
+static inline void restore_rounding_mode(CPUMIPSState *env)
+{
+ set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3],
+ &env->active_fpu.fp_status);
+}
+
+static inline void restore_flush_mode(CPUMIPSState *env)
+{
+ set_flush_to_zero((env->active_fpu.fcr31 & (1 << FCR31_FS)) != 0,
+ &env->active_fpu.fp_status);
+}
+
+static inline void restore_fp_status(CPUMIPSState *env)
+{
+ restore_rounding_mode(env);
+ restore_flush_mode(env);
+ restore_snan_bit_mode(env);
+}
+
+static inline void restore_msa_fp_status(CPUMIPSState *env)
+{
+ float_status *status = &env->active_tc.msa_fp_status;
+ int rounding_mode = (env->active_tc.msacsr & MSACSR_RM_MASK) >> MSACSR_RM;
+ bool flush_to_zero = (env->active_tc.msacsr & MSACSR_FS_MASK) != 0;
+
+ set_float_rounding_mode(ieee_rm[rounding_mode], status);
+ set_flush_to_zero(flush_to_zero, status);
+ set_flush_inputs_to_zero(flush_to_zero, status);
+}
+
+static inline void restore_pamask(CPUMIPSState *env)
+{
+ if (env->hflags & MIPS_HFLAG_ELPA) {
+ env->PAMask = (1ULL << env->PABITS) - 1;
+ } else {
+ env->PAMask = PAMASK_BASE;
+ }
+}
+
+static inline int mips_vpe_active(CPUMIPSState *env)
+{
+ int active = 1;
+
+ /* Check that the VPE is enabled. */
+ if (!(env->mvp->CP0_MVPControl & (1 << CP0MVPCo_EVP))) {
+ active = 0;
+ }
+ /* Check that the VPE is activated. */
+ if (!(env->CP0_VPEConf0 & (1 << CP0VPEC0_VPA))) {
+ active = 0;
+ }
+
+ /* Now verify that there are active thread contexts in the VPE.
+
+ This assumes the CPU model will internally reschedule threads
+ if the active one goes to sleep. If there are no threads available
+ the active one will be in a sleeping state, and we can turn off
+ the entire VPE. */
+ if (!(env->active_tc.CP0_TCStatus & (1 << CP0TCSt_A))) {
+ /* TC is not activated. */
+ active = 0;
+ }
+ if (env->active_tc.CP0_TCHalt & 1) {
+ /* TC is in halt state. */
+ active = 0;
+ }
+
+ return active;
+}
+
+static inline int mips_vp_active(CPUMIPSState *env)
+{
+ CPUState *other_cs = first_cpu;
+
+ /* Check if the VP disabled other VPs (which means the VP is enabled) */
+ if ((env->CP0_VPControl >> CP0VPCtl_DIS) & 1) {
+ return 1;
+ }
+
+ /* Check if the virtual processor is disabled due to a DVP */
+ CPU_FOREACH(other_cs) {
+ MIPSCPU *other_cpu = MIPS_CPU(other_cs);
+ if ((&other_cpu->env != env) &&
+ ((other_cpu->env.CP0_VPControl >> CP0VPCtl_DIS) & 1)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static inline void compute_hflags(CPUMIPSState *env)
+{
+ env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 |
+ MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU |
+ MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2 |
+ MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA | MIPS_HFLAG_FRE |
+ MIPS_HFLAG_ELPA | MIPS_HFLAG_ERL);
+ if (env->CP0_Status & (1 << CP0St_ERL)) {
+ env->hflags |= MIPS_HFLAG_ERL;
+ }
+ if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
+ !(env->CP0_Status & (1 << CP0St_ERL)) &&
+ !(env->hflags & MIPS_HFLAG_DM)) {
+ env->hflags |= (env->CP0_Status >> CP0St_KSU) & MIPS_HFLAG_KSU;
+ }
+#if defined(TARGET_MIPS64)
+ if ((env->insn_flags & ISA_MIPS3) &&
+ (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_UM) ||
+ (env->CP0_Status & (1 << CP0St_PX)) ||
+ (env->CP0_Status & (1 << CP0St_UX)))) {
+ env->hflags |= MIPS_HFLAG_64;
+ }
+
+ if (!(env->insn_flags & ISA_MIPS3)) {
+ env->hflags |= MIPS_HFLAG_AWRAP;
+ } else if (((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_UM) &&
+ !(env->CP0_Status & (1 << CP0St_UX))) {
+ env->hflags |= MIPS_HFLAG_AWRAP;
+ } else if (env->insn_flags & ISA_MIPS64R6) {
+ /* Address wrapping for Supervisor and Kernel is specified in R6 */
+ if ((((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_SM) &&
+ !(env->CP0_Status & (1 << CP0St_SX))) ||
+ (((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_KM) &&
+ !(env->CP0_Status & (1 << CP0St_KX)))) {
+ env->hflags |= MIPS_HFLAG_AWRAP;
+ }
+ }
+#endif
+ if (((env->CP0_Status & (1 << CP0St_CU0)) &&
+ !(env->insn_flags & ISA_MIPS32R6)) ||
+ !(env->hflags & MIPS_HFLAG_KSU)) {
+ env->hflags |= MIPS_HFLAG_CP0;
+ }
+ if (env->CP0_Status & (1 << CP0St_CU1)) {
+ env->hflags |= MIPS_HFLAG_FPU;
+ }
+ if (env->CP0_Status & (1 << CP0St_FR)) {
+ env->hflags |= MIPS_HFLAG_F64;
+ }
+ if (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_KM) &&
+ (env->CP0_Config5 & (1 << CP0C5_SBRI))) {
+ env->hflags |= MIPS_HFLAG_SBRI;
+ }
+ if (env->insn_flags & ASE_DSPR2) {
+ /* Enables access MIPS DSP resources, now our cpu is DSP ASER2,
+ so enable to access DSPR2 resources. */
+ if (env->CP0_Status & (1 << CP0St_MX)) {
+ env->hflags |= MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2;
+ }
+
+ } else if (env->insn_flags & ASE_DSP) {
+ /* Enables access MIPS DSP resources, now our cpu is DSP ASE,
+ so enable to access DSP resources. */
+ if (env->CP0_Status & (1 << CP0St_MX)) {
+ env->hflags |= MIPS_HFLAG_DSP;
+ }
+
+ }
+ if (env->insn_flags & ISA_MIPS32R2) {
+ if (env->active_fpu.fcr0 & (1 << FCR0_F64)) {
+ env->hflags |= MIPS_HFLAG_COP1X;
+ }
+ } else if (env->insn_flags & ISA_MIPS32) {
+ if (env->hflags & MIPS_HFLAG_64) {
+ env->hflags |= MIPS_HFLAG_COP1X;
+ }
+ } else if (env->insn_flags & ISA_MIPS4) {
+ /* All supported MIPS IV CPUs use the XX (CU3) to enable
+ and disable the MIPS IV extensions to the MIPS III ISA.
+ Some other MIPS IV CPUs ignore the bit, so the check here
+ would be too restrictive for them. */
+ if (env->CP0_Status & (1U << CP0St_CU3)) {
+ env->hflags |= MIPS_HFLAG_COP1X;
+ }
+ }
+ if (env->insn_flags & ASE_MSA) {
+ if (env->CP0_Config5 & (1 << CP0C5_MSAEn)) {
+ env->hflags |= MIPS_HFLAG_MSA;
+ }
+ }
+ if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) {
+ if (env->CP0_Config5 & (1 << CP0C5_FRE)) {
+ env->hflags |= MIPS_HFLAG_FRE;
+ }
+ }
+ if (env->CP0_Config3 & (1 << CP0C3_LPA)) {
+ if (env->CP0_PageGrain & (1 << CP0PG_ELPA)) {
+ env->hflags |= MIPS_HFLAG_ELPA;
+ }
+ }
+}
+
+void cpu_mips_tlb_flush(CPUMIPSState *env);
+void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc);
+void cpu_mips_store_status(CPUMIPSState *env, target_ulong val);
+void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val);
+
+void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env, uint32_t exception,
+ int error_code, uintptr_t pc);
+
+static inline void QEMU_NORETURN do_raise_exception(CPUMIPSState *env,
+ uint32_t exception,
+ uintptr_t pc)
+{
+ do_raise_exception_err(env, exception, 0, pc);
+}
+
+#endif
diff --git a/target/mips/kvm.c b/target/mips/kvm.c
index 3b7b1d962a..8e72850962 100644
--- a/target/mips/kvm.c
+++ b/target/mips/kvm.c
@@ -16,6 +16,7 @@
#include "qemu-common.h"
#include "cpu.h"
+#include "internal.h"
#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
diff --git a/target/mips/machine.c b/target/mips/machine.c
index 898825de3b..20100d5adb 100644
--- a/target/mips/machine.c
+++ b/target/mips/machine.c
@@ -1,6 +1,7 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h"
+#include "internal.h"
#include "hw/hw.h"
#include "migration/cpu.h"
diff --git a/target/mips/msa_helper.c b/target/mips/msa_helper.c
index 1fdb0d9792..f167a42655 100644
--- a/target/mips/msa_helper.c
+++ b/target/mips/msa_helper.c
@@ -19,6 +19,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
+#include "internal.h"
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
diff --git a/target/mips/op_helper.c b/target/mips/op_helper.c
index 320f2b0dc4..e537a8bfd8 100644
--- a/target/mips/op_helper.c
+++ b/target/mips/op_helper.c
@@ -19,6 +19,7 @@
#include "qemu/osdep.h"
#include "qemu/main-loop.h"
#include "cpu.h"
+#include "internal.h"
#include "qemu/host-utils.h"
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
diff --git a/target/mips/translate.c b/target/mips/translate.c
index c78d27294c..d16d879df7 100644
--- a/target/mips/translate.c
+++ b/target/mips/translate.c
@@ -23,6 +23,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
+#include "internal.h"
#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg-op.h"
@@ -20511,29 +20512,15 @@ void mips_tcg_init(void)
#include "translate_init.c"
-MIPSCPU *cpu_mips_init(const char *cpu_model)
+void cpu_mips_realize_env(CPUMIPSState *env)
{
- MIPSCPU *cpu;
- CPUMIPSState *env;
- const mips_def_t *def;
-
- def = cpu_mips_find_by_name(cpu_model);
- if (!def)
- return NULL;
- cpu = MIPS_CPU(object_new(TYPE_MIPS_CPU));
- env = &cpu->env;
- env->cpu_model = def;
env->exception_base = (int32_t)0xBFC00000;
#ifndef CONFIG_USER_ONLY
- mmu_init(env, def);
+ mmu_init(env, env->cpu_model);
#endif
- fpu_init(env, def);
- mvp_init(env, def);
-
- object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
-
- return cpu;
+ fpu_init(env, env->cpu_model);
+ mvp_init(env, env->cpu_model);
}
bool cpu_supports_cps_smp(const char *cpu_model)
diff --git a/target/mips/translate_init.c b/target/mips/translate_init.c
index 255d25bacd..8bbded46c4 100644
--- a/target/mips/translate_init.c
+++ b/target/mips/translate_init.c
@@ -51,64 +51,9 @@
#define MIPS_CONFIG5 \
((0 << CP0C5_M))
-/* MMU types, the first four entries have the same layout as the
- CP0C0_MT field. */
-enum mips_mmu_types {
- MMU_TYPE_NONE,
- MMU_TYPE_R4000,
- MMU_TYPE_RESERVED,
- MMU_TYPE_FMT,
- MMU_TYPE_R3000,
- MMU_TYPE_R6000,
- MMU_TYPE_R8000
-};
-
-struct mips_def_t {
- const char *name;
- int32_t CP0_PRid;
- int32_t CP0_Config0;
- int32_t CP0_Config1;
- int32_t CP0_Config2;
- int32_t CP0_Config3;
- int32_t CP0_Config4;
- int32_t CP0_Config4_rw_bitmask;
- int32_t CP0_Config5;
- int32_t CP0_Config5_rw_bitmask;
- int32_t CP0_Config6;
- int32_t CP0_Config7;
- target_ulong CP0_LLAddr_rw_bitmask;
- int CP0_LLAddr_shift;
- int32_t SYNCI_Step;
- int32_t CCRes;
- int32_t CP0_Status_rw_bitmask;
- int32_t CP0_TCStatus_rw_bitmask;
- int32_t CP0_SRSCtl;
- int32_t CP1_fcr0;
- int32_t CP1_fcr31_rw_bitmask;
- int32_t CP1_fcr31;
- int32_t MSAIR;
- int32_t SEGBITS;
- int32_t PABITS;
- int32_t CP0_SRSConf0_rw_bitmask;
- int32_t CP0_SRSConf0;
- int32_t CP0_SRSConf1_rw_bitmask;
- int32_t CP0_SRSConf1;
- int32_t CP0_SRSConf2_rw_bitmask;
- int32_t CP0_SRSConf2;
- int32_t CP0_SRSConf3_rw_bitmask;
- int32_t CP0_SRSConf3;
- int32_t CP0_SRSConf4_rw_bitmask;
- int32_t CP0_SRSConf4;
- int32_t CP0_PageGrain_rw_bitmask;
- int32_t CP0_PageGrain;
- target_ulong CP0_EBaseWG_rw_bitmask;
- int insn_flags;
- enum mips_mmu_types mmu_type;
-};
-
/*****************************************************************************/
/* MIPS CPU definitions */
-static const mips_def_t mips_defs[] =
+const mips_def_t mips_defs[] =
{
{
.name = "4Kc",
@@ -808,6 +753,7 @@ static const mips_def_t mips_defs[] =
#endif
};
+const int mips_defs_number = ARRAY_SIZE(mips_defs);
static const mips_def_t *cpu_mips_find_by_name (const char *name)
{
diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c
index 720cb1d833..ebb75cafaa 100644
--- a/target/s390x/kvm.c
+++ b/target/s390x/kvm.c
@@ -311,8 +311,13 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
}
}
- /* Try to enable AIS facility */
- kvm_vm_enable_cap(s, KVM_CAP_S390_AIS, 0);
+ /*
+ * The migration interface for ais was introduced with kernel 4.13
+ * but the capability itself had been active since 4.12. As migration
+ * support is considered necessary let's disable ais in the 2.10
+ * machine.
+ */
+ /* kvm_vm_enable_cap(s, KVM_CAP_S390_AIS, 0); */
qemu_mutex_init(&qemu_sigp_mutex);
diff --git a/tests/.gitignore b/tests/.gitignore
index fed0189a5a..cf6d99c91e 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -95,3 +95,4 @@ test-filter-mirror
test-filter-redirector
*-test
qapi-schema/*.test.*
+vm/*.img
diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include
index aa566aa223..0e4f159619 100644
--- a/tests/docker/Makefile.include
+++ b/tests/docker/Makefile.include
@@ -17,23 +17,13 @@ DOCKER_TOOLS := travis
TESTS ?= %
IMAGES ?= %
-# Make archive from git repo $1 to tar.gz $2
-make-archive-maybe = $(if $(wildcard $1/*), \
- $(call quiet-command, \
- (cd $1; if git diff-index --quiet HEAD -- &>/dev/null; then \
- git archive -1 HEAD --format=tar.gz; \
- else \
- git archive -1 $$(git stash create) --format=tar.gz; \
- fi) > $2, \
- "ARCHIVE","$(notdir $2)"))
-
CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$)
DOCKER_SRC_COPY := docker-src.$(CUR_TIME)
$(DOCKER_SRC_COPY):
@mkdir $@
- $(call make-archive-maybe, $(SRC_PATH), $@/qemu.tgz)
- $(call make-archive-maybe, $(SRC_PATH)/dtc, $@/dtc.tgz)
+ $(call quiet-command, $(SRC_PATH)/scripts/archive-source.sh $@/qemu.tar, \
+ "GEN", "$@/qemu.tar")
$(call quiet-command, cp $(SRC_PATH)/tests/docker/run $@/run, \
"COPY","RUNNER")
@@ -70,6 +60,7 @@ docker-image-debian-ppc64el-cross: docker-image-debian9
docker-image-debian-s390x-cross: docker-image-debian9
docker-image-debian-win32-cross: docker-image-debian8-mxe
docker-image-debian-win64-cross: docker-image-debian8-mxe
+docker-image-travis: NOUSER=1
# Expand all the pre-requistes for each docker image and test combination
$(foreach i,$(DOCKER_IMAGES), \
@@ -144,6 +135,7 @@ docker-run: docker-qemu-src
$(call quiet-command, \
$(SRC_PATH)/tests/docker/docker.py run \
$(if $(NOUSER),,-u $(shell id -u)) -t \
+ --security-opt seccomp=unconfined \
$(if $V,,--rm) \
$(if $(DEBUG),-i,) \
$(if $(NETWORK),$(if $(subst $(NETWORK),,1),--net=$(NETWORK)),--net=none) \
diff --git a/tests/docker/common.rc b/tests/docker/common.rc
index 6865689bb5..87f5263757 100755
--- a/tests/docker/common.rc
+++ b/tests/docker/common.rc
@@ -11,9 +11,6 @@
# or (at your option) any later version. See the COPYING file in
# the top-level directory.
-BUILD_DIR=/var/tmp/qemu-build
-mkdir $BUILD_DIR
-
requires()
{
for c in $@; do
@@ -28,11 +25,22 @@ build_qemu()
{
config_opts="--enable-werror \
${TARGET_LIST:+--target-list=${TARGET_LIST}} \
- --prefix=$PWD/install \
+ --prefix=$INSTALL_DIR \
$QEMU_CONFIGURE_OPTS $EXTRA_CONFIGURE_OPTS \
$@"
echo "Configure options:"
echo $config_opts
- $QEMU_SRC/configure $config_opts
- make $MAKEFLAGS
+ $QEMU_SRC/configure $config_opts && make $MAKEFLAGS
+}
+
+test_fail()
+{
+ echo "$@"
+ exit 1
+}
+
+prep_fail()
+{
+ echo "$@"
+ exit 2
}
diff --git a/tests/docker/docker.py b/tests/docker/docker.py
index 81c87ee329..08122ca17d 100755
--- a/tests/docker/docker.py
+++ b/tests/docker/docker.py
@@ -263,7 +263,8 @@ class BuildCommand(SubCommand):
tag = args.tag
dkr = Docker()
- if dkr.image_matches_dockerfile(tag, dockerfile):
+ if "--no-cache" not in argv and \
+ dkr.image_matches_dockerfile(tag, dockerfile):
if not args.quiet:
print "Image is up to date."
else:
diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker
index 4eaa8ed2a5..27e8201c54 100644
--- a/tests/docker/dockerfiles/fedora.docker
+++ b/tests/docker/dockerfiles/fedora.docker
@@ -3,6 +3,7 @@ ENV PACKAGES \
ccache git tar PyYAML sparse flex bison python2 bzip2 hostname \
glib2-devel pixman-devel zlib-devel SDL-devel libfdt-devel \
gcc gcc-c++ clang make perl which bc findutils libaio-devel \
+ nettle-devel \
mingw32-pixman mingw32-glib2 mingw32-gmp mingw32-SDL mingw32-pkg-config \
mingw32-gtk2 mingw32-gtk3 mingw32-gnutls mingw32-nettle mingw32-libtasn1 \
mingw32-libjpeg-turbo mingw32-libpng mingw32-curl mingw32-libssh2 \
diff --git a/tests/docker/dockerfiles/travis.docker b/tests/docker/dockerfiles/travis.docker
index 636fa590a5..605b6e429b 100644
--- a/tests/docker/dockerfiles/travis.docker
+++ b/tests/docker/dockerfiles/travis.docker
@@ -1,6 +1,8 @@
FROM quay.io/travisci/travis-ruby
+ENV DEBIAN_FRONTEND noninteractive
+ENV LANG en_US.UTF-8
+ENV LC_ALL en_US.UTF-8
RUN apt-get update
RUN apt-get -y build-dep qemu
-RUN apt-get -y build-dep device-tree-compiler
-RUN apt-get -y install python2.7 python-yaml dh-autoreconf gdb strace lsof net-tools
+RUN apt-get -y install device-tree-compiler python2.7 python-yaml dh-autoreconf gdb strace lsof net-tools
ENV FEATURES pyyaml
diff --git a/tests/docker/dockerfiles/ubuntu.docker b/tests/docker/dockerfiles/ubuntu.docker
index a360a050a2..d73ce02246 100644
--- a/tests/docker/dockerfiles/ubuntu.docker
+++ b/tests/docker/dockerfiles/ubuntu.docker
@@ -1,12 +1,17 @@
-FROM ubuntu:14.04
+FROM ubuntu:16.04
RUN echo "deb http://archive.ubuntu.com/ubuntu/ trusty universe multiverse" >> \
/etc/apt/sources.list
RUN apt-get update
ENV PACKAGES flex bison \
- libusb-1.0-0-dev libiscsi-dev librados-dev libncurses5-dev \
+ libusb-1.0-0-dev libiscsi-dev librados-dev libncurses5-dev libncursesw5-dev \
libseccomp-dev libgnutls-dev libssh2-1-dev libspice-server-dev \
libspice-protocol-dev libnss3-dev libfdt-dev \
- libgtk-3-dev libvte-2.90-dev libsdl1.2-dev libpng12-dev libpixman-1-dev \
+ libgtk-3-dev libvte-2.91-dev libsdl1.2-dev libpng12-dev libpixman-1-dev \
+ libvdeplug-dev liblzo2-dev libsnappy-dev libbz2-dev libxen-dev librdmacm-dev libibverbs-dev \
+ libsasl2-dev libjpeg-turbo8-dev xfslibs-dev libcap-ng-dev libbrlapi-dev libcurl4-gnutls-dev \
+ libbluetooth-dev librbd-dev libaio-dev glusterfs-common libnuma-dev libepoxy-dev libdrm-dev libgbm-dev \
+ libjemalloc-dev libcacard-dev libusbredirhost-dev libnfs-dev libcap-dev libattr1-dev \
+ texinfo \
git make ccache python-yaml gcc clang sparse
RUN apt-get -y install $PACKAGES
RUN dpkg -l $PACKAGES | sort > /packages.txt
diff --git a/tests/docker/run b/tests/docker/run
index c1e4513bce..c8f940de15 100755
--- a/tests/docker/run
+++ b/tests/docker/run
@@ -1,4 +1,4 @@
-#!/bin/bash -e
+#!/bin/bash
#
# Docker test runner
#
@@ -11,8 +11,6 @@
# or (at your option) any later version. See the COPYING file in
# the top-level directory.
-set -e
-
if test -n "$V"; then
set -x
fi
@@ -20,7 +18,7 @@ fi
BASE="$(dirname $(readlink -e $0))"
# Prepare the environment
-. /etc/profile || true
+. /etc/profile
export PATH=/usr/lib/ccache:$PATH
if test -n "$J"; then
@@ -32,13 +30,7 @@ export TEST_DIR=/tmp/qemu-test
mkdir -p $TEST_DIR/{src,build,install}
# Extract the source tarballs
-tar -C $TEST_DIR/src -xzf $BASE/qemu.tgz
-for p in dtc pixman; do
- if test -f $BASE/$p.tgz; then
- tar -C $TEST_DIR/src/$p -xzf $BASE/$p.tgz
- export FEATURES="$FEATURES $p"
- fi
-done
+tar -C $TEST_DIR/src -xf $BASE/qemu.tar || prep_fail "Failed to untar source"
if test -n "$SHOW_ENV"; then
if test -f /packages.txt; then
@@ -52,10 +44,12 @@ if test -n "$SHOW_ENV"; then
fi
export QEMU_SRC="$TEST_DIR/src"
+export BUILD_DIR="$TEST_DIR/build"
+export INSTALL_DIR="$TEST_DIR/install"
cd "$QEMU_SRC/tests/docker"
-CMD="$QEMU_SRC/tests/docker/$@"
+CMD="./$@"
if test -z "$DEBUG"; then
exec $CMD
diff --git a/tests/docker/test-block b/tests/docker/test-block
new file mode 100755
index 0000000000..2ca1ce54f6
--- /dev/null
+++ b/tests/docker/test-block
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Run block test cases
+#
+# Copyright 2017 Red Hat Inc.
+#
+# Authors:
+# Fam Zheng <famz@redhat.com>
+#
+# This code is licensed under the GPL version 2 or later. See
+# the COPYING file in the top-level directory.
+
+. ./common.rc
+
+cd "$BUILD_DIR"
+
+build_qemu --target-list=x86_64-softmmu
+cd tests/qemu-iotests
+for t in raw qcow2 nbd luks; do
+ ./check -g quick -$t || test_fail "Test failed: iotests $t"
+done
diff --git a/tests/docker/test-full b/tests/docker/test-full
index 05f0d491d1..d71bf9d275 100755
--- a/tests/docker/test-full
+++ b/tests/docker/test-full
@@ -1,8 +1,8 @@
-#!/bin/bash -e
+#!/bin/bash
#
-# Compile all the targets.
+# Compile all the targets with as many features enabled as possible
#
-# Copyright (c) 2016 Red Hat Inc.
+# Copyright 2016, 2017 Red Hat Inc.
#
# Authors:
# Fam Zheng <famz@redhat.com>
@@ -13,7 +13,77 @@
. common.rc
-cd "$BUILD_DIR"
+cd "$BUILD_DIR" || exit 1
-build_qemu
-make check $MAKEFLAGS
+build_qemu \
+ --enable-attr \
+ --enable-bluez \
+ --enable-brlapi \
+ --enable-bsd-user \
+ --enable-bzip2 \
+ --enable-cap-ng \
+ --enable-coroutine-pool \
+ --enable-crypto-afalg \
+ --enable-curl \
+ --enable-curses \
+ --enable-debug \
+ --enable-debug-info \
+ --enable-debug-tcg \
+ --enable-docs \
+ --enable-fdt \
+ --enable-gcrypt \
+ --enable-glusterfs \
+ --enable-gnutls \
+ --enable-gprof \
+ --enable-gtk \
+ --enable-guest-agent \
+ --enable-jemalloc \
+ --enable-kvm \
+ --enable-libiscsi \
+ --enable-libnfs \
+ --enable-libssh2 \
+ --enable-libusb \
+ --enable-linux-aio \
+ --enable-linux-user \
+ --enable-live-block-migration \
+ --enable-lzo \
+ --enable-modules \
+ --enable-numa \
+ --enable-opengl \
+ --enable-pie \
+ --enable-profiler \
+ --enable-qom-cast-debug \
+ --enable-rbd \
+ --enable-rdma \
+ --enable-replication \
+ --enable-sdl \
+ --enable-seccomp \
+ --enable-smartcard \
+ --enable-snappy \
+ --enable-spice \
+ --enable-stack-protector \
+ --enable-system \
+ --enable-tcg \
+ --enable-tcg-interpreter \
+ --enable-tools \
+ --enable-tpm \
+ --enable-trace-backend=ftrace \
+ --enable-usb-redir \
+ --enable-user \
+ --enable-vde \
+ --enable-vhost-net \
+ --enable-vhost-scsi \
+ --enable-vhost-user \
+ --enable-vhost-vsock \
+ --enable-virtfs \
+ --enable-vnc \
+ --enable-vnc-jpeg \
+ --enable-vnc-png \
+ --enable-vnc-sasl \
+ --enable-vte \
+ --enable-werror \
+ --enable-xen \
+ --enable-xen-pci-passthrough \
+ --enable-xen-pv-domain-build \
+ --enable-xfsctl \
+&& make check $MAKEFLAGS
diff --git a/tests/keys/README b/tests/keys/README
new file mode 100644
index 0000000000..b995268f06
--- /dev/null
+++ b/tests/keys/README
@@ -0,0 +1,6 @@
+This folder contains a well-known ssh key pair used in QEMU tests.
+
+Some guests require the key to exist prior to provisioning the guest; also,
+reusing a pre-built key avoids consuming entropy every time the testsuite is
+run. Because the private key is well-known, care must be taken to use the key
+ONLY in situations that cannot be compromised by external network clients.
diff --git a/tests/keys/id_rsa b/tests/keys/id_rsa
new file mode 100644
index 0000000000..2933eac3db
--- /dev/null
+++ b/tests/keys/id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAopAuOlmLV6LVHdFBj8/eeOwI9CqguIJPp7eAQSZvOiB4Ag/R
+coEhl/RBbrV5Yc/SmSD4PTpJO/iM10RwliNjDb4a3I8q3sykRJu9c9PI/YsH8WN9
++NH2NjKPtJIcKTu287IM5JYxyB6nDoOzILbTyJ1TDR/xH6qYEfBAyiblggdjcvhA
+RTf93QIn39F/xLypXvT1K2O9BJEsnJ8lEUvB2UXhKo/JTfSeZF8wPBeowaP9EONk
+7b+nuJOWHGg68Ji6wVi62tjwl2Szch6lxIhZBpnV7QNRKMfYHP6eIyF4pusazzZq
+Telsq6xI2ghecWLzb/MF5A+rklsGx2FNuJSAJwIDAQABAoIBAHHi4o/8VZNivz0x
+cWXn8erzKV6tUoWQvW85Lj/2RiwJvSlsnYZDkx5af1CpEE2HA/pFT8PNRqsd+MWC
+7AEy710cVsM4BYerBFYQaYxwzblaoojo88LSjVPw3h5Z0iLM8+IMVd36nwuc9dpE
+R8TecMZ1+U4Tl6BgqkK+9xToZRdPKdjS8L5MoFhGN+xY0vRbbJbGaV9Q0IHxLBkB
+rEBV7T1mUynneCHRUQlJQEwJmKpT8MH3IjsUXlG5YvnuuvcQJSNTaW2iDLxuOKp8
+cxW8+qL88zpb1D5dppoIu6rlrugN0azSq70ruFJQPc/A8GQrDKoGgRQiagxNY3u+
+vHZzXlECgYEA0dKO3gfkSxsDBb94sQwskMScqLhcKhztEa8kPxTx6Yqh+x8/scx3
+XhJyOt669P8U1v8a/2Al+s81oZzzfQSzO1Q7gEwSrgBcRMSIoRBUw9uYcy02ngb/
+j/ng3DGivfJztjjiSJwb46FHkJ2JR8mF2UisC6UMXk3NgFY/3vWQx78CgYEAxlcG
+T3hfSWSmTgKRczMJuHQOX9ULfTBIqwP5VqkkkiavzigGRirzb5lgnmuTSPTpF0LB
+XVPjR2M4q+7gzP0Dca3pocrvLEoxjwIKnCbYKnyyvnUoE9qHv4Kr+vDbgWpa2LXG
+JbLmE7tgTCIp20jOPPT4xuDvlbzQZBJ5qCQSoZkCgYEAgrotSSihlCnAOFSTXbu4
+CHp3IKe8xIBBNENq0eK61kcJpOxTQvOha3sSsJsU4JAM6+cFaxb8kseHIqonCj1j
+bhOM/uJmwQJ4el/4wGDsbxriYOBKpyq1D38gGhDS1IW6kk3erl6VAb36WJ/OaGum
+eTpN9vNeQWM4Jj2WjdNx4QECgYAwTdd6mU1TmZCrJRL5ZG+0nYc2rbMrnQvFoqUi
+BvWiJovggHzur90zy73tNzPaq9Ls2FQxf5G1vCN8NCRJqEEjeYCR59OSDMu/EXc2
+CnvQ9SevHOdS1oEDEjcCWZCMFzPi3XpRih1gptzQDe31uuiHjf3cqcGPzTlPdfRt
+D8P92QKBgC4UaBvIRwREVJsdZzpIzm224Bpe8LOmA7DeTnjlT0b3lkGiBJ36/Q0p
+VhYh/6cjX4/iuIs7gJbGon7B+YPB8scmOi3fj0+nkJAONue1mMfBNkba6qQTc6Y2
+5mEKw2/O7/JpND7ucU3OK9plcw/qnrWDgHxl0Iz95+OzUIIagxne
+-----END RSA PRIVATE KEY-----
diff --git a/tests/keys/id_rsa.pub b/tests/keys/id_rsa.pub
new file mode 100644
index 0000000000..b5ec6d4712
--- /dev/null
+++ b/tests/keys/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCikC46WYtXotUd0UGPz9547Aj0KqC4gk+nt4BBJm86IHgCD9FygSGX9EFutXlhz9KZIPg9Okk7+IzXRHCWI2MNvhrcjyrezKREm71z08j9iwfxY3340fY2Mo+0khwpO7bzsgzkljHIHqcOg7MgttPInVMNH/EfqpgR8EDKJuWCB2Ny+EBFN/3dAiff0X/EvKle9PUrY70EkSycnyURS8HZReEqj8lN9J5kXzA8F6jBo/0Q42Ttv6e4k5YcaDrwmLrBWLra2PCXZLNyHqXEiFkGmdXtA1Eox9gc/p4jIXim6xrPNmpN6WyrrEjaCF5xYvNv8wXkD6uSWwbHYU24lIAn well-known key for qemu-test, do not use on any machine exposed to an external network
diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include
new file mode 100644
index 0000000000..5daa2a3b73
--- /dev/null
+++ b/tests/vm/Makefile.include
@@ -0,0 +1,42 @@
+# Makefile for VM tests
+
+.PHONY: vm-build-all
+
+IMAGES := ubuntu.i386 freebsd netbsd openbsd
+IMAGE_FILES := $(patsubst %, tests/vm/%.img, $(IMAGES))
+
+.PRECIOUS: $(IMAGE_FILES)
+
+vm-test:
+ @echo "vm-test: Test QEMU in preconfigured virtual machines"
+ @echo
+ @echo " vm-build-ubuntu.i386 - Build QEMU in ubuntu i386 VM"
+ @echo " vm-build-freebsd - Build QEMU in FreeBSD VM"
+ @echo " vm-build-netbsd - Build QEMU in NetBSD VM"
+ @echo " vm-build-openbsd - Build QEMU in OpenBSD VM"
+
+vm-build-all: $(addprefix vm-build-, $(IMAGES))
+
+tests/vm/%.img: $(SRC_PATH)/tests/vm/% \
+ $(SRC_PATH)/tests/vm/basevm.py \
+ $(SRC_PATH)/tests/vm/Makefile.include
+ $(call quiet-command, \
+ $< \
+ $(if $(V)$(DEBUG), --debug) \
+ --image "$@" \
+ --force \
+ --build-image $@, \
+ " VM-IMAGE $*")
+
+
+# Build in VM $(IMAGE)
+vm-build-%: tests/vm/%.img
+ $(call quiet-command, \
+ $(SRC_PATH)/tests/vm/$* \
+ $(if $(V)$(DEBUG), --debug) \
+ $(if $(DEBUG), --interactive) \
+ $(if $(J),--jobs $(J)) \
+ --image "$<" \
+ --build-qemu $(SRC_PATH), \
+ " VM-BUILD $*")
+
diff --git a/tests/vm/README b/tests/vm/README
new file mode 100644
index 0000000000..ae53dce6ee
--- /dev/null
+++ b/tests/vm/README
@@ -0,0 +1,89 @@
+=== VM test suite to run build in guests ===
+
+== Intro ==
+
+This test suite contains scripts that bootstrap various guest images that have
+necessary packages to build QEMU. The basic usage is documented in Makefile
+help which is displayed with "make vm-test".
+
+== Quick start ==
+
+Run "make vm-test" to list available make targets. Invoke a specific make
+command to run build test in an image. For example, "make vm-build-freebsd"
+will build the source tree in the FreeBSD image. The command can be executed
+from either the source tree or the build dir; if the former, ./configure is not
+needed. The command will then generate the test image in ./tests/vm/ under the
+working directory.
+
+Note: images created by the scripts accept a well-known RSA key pair for SSH
+access, so they SHOULD NOT be exposed to external interfaces if you are
+concerned about attackers taking control of the guest and potentially
+exploiting a QEMU security bug to compromise the host.
+
+== QEMU binary ==
+
+By default, qemu-system-x86_64 is searched in $PATH to run the guest. If there
+isn't one, or if it is older than 2.10, the test won't work. In this case,
+provide the QEMU binary in env var: QEMU=/path/to/qemu-2.10+.
+
+== Make jobs ==
+
+The "-j$X" option in the make command line is not propagated into the VM,
+specify "J=$X" to control the make jobs in the guest.
+
+== Debugging ==
+
+Add "DEBUG=1" and/or "V=1" to the make command to allow interactive debugging
+and verbose output. If this is not enough, see the next section.
+
+== Manual invocation ==
+
+Each guest script is an executable script with the same command line options.
+For example to work with the netbsd guest, use $QEMU_SRC/tests/vm/netbsd:
+
+ $ cd $QEMU_SRC/tests/vm
+
+ # To bootstrap the image
+ $ ./netbsd --build-image --image /var/tmp/netbsd.img
+ <...>
+
+ # To run an arbitrary command in guest (the output will not be echoed unless
+ # --debug is added)
+ $ ./netbsd --debug --image /var/tmp/netbsd.img uname -a
+
+ # To build QEMU in guest
+ $ ./netbsd --debug --image /var/tmp/netbsd.img --build-qemu $QEMU_SRC
+
+ # To get to an interactive shell
+ $ ./netbsd --interactive --image /var/tmp/netbsd.img sh
+
+== Adding new guests ==
+
+Please look at existing guest scripts for how to add new guests.
+
+Most importantly, create a subclass of BaseVM and implement build_image()
+method and define BUILD_SCRIPT, then finally call basevm.main() from the
+script's main().
+
+ - Usually in build_image(), a template image is downloaded from a predefined
+ URL. BaseVM._download_with_cache() takes care of the cache and the
+ checksum, so consider using it.
+
+ - Once the image is downloaded, users, SSH server and QEMU build deps should
+ be set up:
+
+ * Root password set to BaseVM.ROOT_PASS
+ * User BaseVM.GUEST_USER is created, and password set to BaseVM.GUEST_PASS
+ * SSH service is enabled and started on boot,
+ $QEMU_SRC/tests/keys/id_rsa.pub is added to ssh's "authorized_keys" file
+ of both root and the normal user
+ * DHCP client service is enabled and started on boot, so that it can
+ automatically configure the virtio-net-pci NIC and communicate with QEMU
+ user net (10.0.2.2)
+ * Necessary packages are installed to untar the source tarball and build
+ QEMU
+
+ - Write a proper BUILD_SCRIPT template, which should be a shell script that
+ untars a raw virtio-blk block device, which is the tarball data blob of the
+ QEMU source tree, then configure/build it. Running "make check" is also
+ recommended.
diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py
new file mode 100755
index 0000000000..3c863bc237
--- /dev/null
+++ b/tests/vm/basevm.py
@@ -0,0 +1,262 @@
+#!/usr/bin/env python
+#
+# VM testing base class
+#
+# Copyright 2017 Red Hat Inc.
+#
+# Authors:
+# Fam Zheng <famz@redhat.com>
+#
+# This code is licensed under the GPL version 2 or later. See
+# the COPYING file in the top-level directory.
+#
+
+import os
+import sys
+import logging
+import time
+import datetime
+sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "scripts"))
+from qemu import QEMUMachine
+import subprocess
+import hashlib
+import optparse
+import atexit
+import tempfile
+import shutil
+import multiprocessing
+import traceback
+
+SSH_KEY = open(os.path.join(os.path.dirname(__file__),
+ "..", "keys", "id_rsa")).read()
+SSH_PUB_KEY = open(os.path.join(os.path.dirname(__file__),
+ "..", "keys", "id_rsa.pub")).read()
+
+class BaseVM(object):
+ GUEST_USER = "qemu"
+ GUEST_PASS = "qemupass"
+ ROOT_PASS = "qemupass"
+
+ # The script to run in the guest that builds QEMU
+ BUILD_SCRIPT = ""
+ # The guest name, to be overridden by subclasses
+ name = "#base"
+ def __init__(self, debug=False, vcpus=None):
+ self._guest = None
+ self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-",
+ suffix=".tmp",
+ dir="."))
+ atexit.register(shutil.rmtree, self._tmpdir)
+
+ self._ssh_key_file = os.path.join(self._tmpdir, "id_rsa")
+ open(self._ssh_key_file, "w").write(SSH_KEY)
+ subprocess.check_call(["chmod", "600", self._ssh_key_file])
+
+ self._ssh_pub_key_file = os.path.join(self._tmpdir, "id_rsa.pub")
+ open(self._ssh_pub_key_file, "w").write(SSH_PUB_KEY)
+
+ self.debug = debug
+ self._stderr = sys.stderr
+ self._devnull = open(os.devnull, "w")
+ if self.debug:
+ self._stdout = sys.stdout
+ else:
+ self._stdout = self._devnull
+ self._args = [ \
+ "-nodefaults", "-m", "2G",
+ "-cpu", "host",
+ "-netdev", "user,id=vnet,hostfwd=:127.0.0.1:0-:22",
+ "-device", "virtio-net-pci,netdev=vnet",
+ "-vnc", "127.0.0.1:0,to=20",
+ "-serial", "file:%s" % os.path.join(self._tmpdir, "serial.out")]
+ if vcpus:
+ self._args += ["-smp", str(vcpus)]
+ if os.access("/dev/kvm", os.R_OK | os.W_OK):
+ self._args += ["-enable-kvm"]
+ else:
+ logging.info("KVM not available, not using -enable-kvm")
+ self._data_args = []
+
+ def _download_with_cache(self, url, sha256sum=None):
+ def check_sha256sum(fname):
+ if not sha256sum:
+ return True
+ checksum = subprocess.check_output(["sha256sum", fname]).split()[0]
+ return sha256sum == checksum
+
+ cache_dir = os.path.expanduser("~/.cache/qemu-vm/download")
+ if not os.path.exists(cache_dir):
+ os.makedirs(cache_dir)
+ fname = os.path.join(cache_dir, hashlib.sha1(url).hexdigest())
+ if os.path.exists(fname) and check_sha256sum(fname):
+ return fname
+ logging.debug("Downloading %s to %s...", url, fname)
+ subprocess.check_call(["wget", "-c", url, "-O", fname + ".download"],
+ stdout=self._stdout, stderr=self._stderr)
+ os.rename(fname + ".download", fname)
+ return fname
+
+ def _ssh_do(self, user, cmd, check, interactive=False):
+ ssh_cmd = ["ssh", "-q",
+ "-o", "StrictHostKeyChecking=no",
+ "-o", "UserKnownHostsFile=" + os.devnull,
+ "-o", "ConnectTimeout=1",
+ "-p", self.ssh_port, "-i", self._ssh_key_file]
+ if interactive:
+ ssh_cmd += ['-t']
+ assert not isinstance(cmd, str)
+ ssh_cmd += ["%s@127.0.0.1" % user] + list(cmd)
+ logging.debug("ssh_cmd: %s", " ".join(ssh_cmd))
+ r = subprocess.call(ssh_cmd,
+ stdin=sys.stdin if interactive else self._devnull,
+ stdout=sys.stdout if interactive else self._stdout,
+ stderr=sys.stderr if interactive else self._stderr)
+ if check and r != 0:
+ raise Exception("SSH command failed: %s" % cmd)
+ return r
+
+ def ssh(self, *cmd):
+ return self._ssh_do(self.GUEST_USER, cmd, False)
+
+ def ssh_interactive(self, *cmd):
+ return self._ssh_do(self.GUEST_USER, cmd, False, True)
+
+ def ssh_root(self, *cmd):
+ return self._ssh_do("root", cmd, False)
+
+ def ssh_check(self, *cmd):
+ self._ssh_do(self.GUEST_USER, cmd, True)
+
+ def ssh_root_check(self, *cmd):
+ self._ssh_do("root", cmd, True)
+
+ def build_image(self, img):
+ raise NotImplementedError
+
+ def add_source_dir(self, src_dir):
+ name = "data-" + hashlib.sha1(src_dir).hexdigest()[:5]
+ tarfile = os.path.join(self._tmpdir, name + ".tar")
+ logging.debug("Creating archive %s for src_dir dir: %s", tarfile, src_dir)
+ subprocess.check_call(["./scripts/archive-source.sh", tarfile],
+ cwd=src_dir, stdin=self._devnull,
+ stdout=self._stdout, stderr=self._stderr)
+ self._data_args += ["-drive",
+ "file=%s,if=none,id=%s,cache=writeback,format=raw" % \
+ (tarfile, name),
+ "-device",
+ "virtio-blk,drive=%s,serial=%s,bootindex=1" % (name, name)]
+
+ def boot(self, img, extra_args=[]):
+ args = self._args + [
+ "-device", "VGA",
+ "-drive", "file=%s,if=none,id=drive0,cache=writeback" % img,
+ "-device", "virtio-blk,drive=drive0,bootindex=0"]
+ args += self._data_args + extra_args
+ logging.debug("QEMU args: %s", " ".join(args))
+ qemu_bin = os.environ.get("QEMU", "qemu-system-x86_64")
+ guest = QEMUMachine(binary=qemu_bin, args=args)
+ try:
+ guest.launch()
+ except:
+ logging.error("Failed to launch QEMU, command line:")
+ logging.error(" ".join([qemu_bin] + args))
+ logging.error("Log:")
+ logging.error(guest.get_log())
+ logging.error("QEMU version >= 2.10 is required")
+ raise
+ atexit.register(self.shutdown)
+ self._guest = guest
+ usernet_info = guest.qmp("human-monitor-command",
+ command_line="info usernet")
+ self.ssh_port = None
+ for l in usernet_info["return"].splitlines():
+ fields = l.split()
+ if "TCP[HOST_FORWARD]" in fields and "22" in fields:
+ self.ssh_port = l.split()[3]
+ if not self.ssh_port:
+ raise Exception("Cannot find ssh port from 'info usernet':\n%s" % \
+ usernet_info)
+
+ def wait_ssh(self, seconds=120):
+ starttime = datetime.datetime.now()
+ guest_up = False
+ while (datetime.datetime.now() - starttime).total_seconds() < seconds:
+ if self.ssh("exit 0") == 0:
+ guest_up = True
+ break
+ time.sleep(1)
+ if not guest_up:
+ raise Exception("Timeout while waiting for guest ssh")
+
+ def shutdown(self):
+ self._guest.shutdown()
+
+ def wait(self):
+ self._guest.wait()
+
+ def qmp(self, *args, **kwargs):
+ return self._guest.qmp(*args, **kwargs)
+
+def parse_args(vm_name):
+ parser = optparse.OptionParser(
+ description="VM test utility. Exit codes: "
+ "0 = success, "
+ "1 = command line error, "
+ "2 = environment initialization failed, "
+ "3 = test command failed")
+ parser.add_option("--debug", "-D", action="store_true",
+ help="enable debug output")
+ parser.add_option("--image", "-i", default="%s.img" % vm_name,
+ help="image file name")
+ parser.add_option("--force", "-f", action="store_true",
+ help="force build image even if image exists")
+ parser.add_option("--jobs", type=int, default=multiprocessing.cpu_count() / 2,
+ help="number of virtual CPUs")
+ parser.add_option("--build-image", "-b", action="store_true",
+ help="build image")
+ parser.add_option("--build-qemu",
+ help="build QEMU from source in guest")
+ parser.add_option("--interactive", "-I", action="store_true",
+ help="Interactively run command")
+ parser.disable_interspersed_args()
+ return parser.parse_args()
+
+def main(vmcls):
+ try:
+ args, argv = parse_args(vmcls.name)
+ if not argv and not args.build_qemu and not args.build_image:
+ print "Nothing to do?"
+ return 1
+ if args.debug:
+ logging.getLogger().setLevel(logging.DEBUG)
+ vm = vmcls(debug=args.debug, vcpus=args.jobs)
+ if args.build_image:
+ if os.path.exists(args.image) and not args.force:
+ sys.stderr.writelines(["Image file exists: %s\n" % args.image,
+ "Use --force option to overwrite\n"])
+ return 1
+ return vm.build_image(args.image)
+ if args.build_qemu:
+ vm.add_source_dir(args.build_qemu)
+ cmd = [vm.BUILD_SCRIPT.format(
+ configure_opts = " ".join(argv),
+ jobs=args.jobs)]
+ else:
+ cmd = argv
+ vm.boot(args.image + ",snapshot=on")
+ vm.wait_ssh()
+ except Exception as e:
+ if isinstance(e, SystemExit) and e.code == 0:
+ return 0
+ sys.stderr.write("Failed to prepare guest environment\n")
+ traceback.print_exc()
+ return 2
+
+ if args.interactive:
+ if vm.ssh_interactive(*cmd) == 0:
+ return 0
+ vm.ssh_interactive()
+ return 3
+ else:
+ if vm.ssh(*cmd) != 0:
+ return 3
diff --git a/tests/vm/freebsd b/tests/vm/freebsd
new file mode 100755
index 0000000000..039dad8f69
--- /dev/null
+++ b/tests/vm/freebsd
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+#
+# FreeBSD VM image
+#
+# Copyright 2017 Red Hat Inc.
+#
+# Authors:
+# Fam Zheng <famz@redhat.com>
+#
+# This code is licensed under the GPL version 2 or later. See
+# the COPYING file in the top-level directory.
+#
+
+import os
+import sys
+import subprocess
+import basevm
+
+class FreeBSDVM(basevm.BaseVM):
+ name = "freebsd"
+ BUILD_SCRIPT = """
+ set -e;
+ cd $(mktemp -d /var/tmp/qemu-test.XXXXXX);
+ tar -xf /dev/vtbd1;
+ ./configure {configure_opts};
+ gmake -j{jobs};
+ gmake check;
+ """
+
+ def build_image(self, img):
+ cimg = self._download_with_cache("http://download.patchew.org/freebsd-11.1-amd64.img.xz",
+ sha256sum='adcb771549b37bc63826c501f05121a206ed3d9f55f49145908f7e1432d65891')
+ img_tmp_xz = img + ".tmp.xz"
+ img_tmp = img + ".tmp"
+ subprocess.check_call(["cp", "-f", cimg, img_tmp_xz])
+ subprocess.check_call(["xz", "-df", img_tmp_xz])
+ if os.path.exists(img):
+ os.remove(img)
+ os.rename(img_tmp, img)
+
+if __name__ == "__main__":
+ sys.exit(basevm.main(FreeBSDVM))
diff --git a/tests/vm/netbsd b/tests/vm/netbsd
new file mode 100755
index 0000000000..3972d8b45c
--- /dev/null
+++ b/tests/vm/netbsd
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+#
+# NetBSD VM image
+#
+# Copyright 2017 Red Hat Inc.
+#
+# Authors:
+# Fam Zheng <famz@redhat.com>
+#
+# This code is licensed under the GPL version 2 or later. See
+# the COPYING file in the top-level directory.
+#
+
+import os
+import sys
+import subprocess
+import basevm
+
+class NetBSDVM(basevm.BaseVM):
+ name = "netbsd"
+ BUILD_SCRIPT = """
+ set -e;
+ cd $(mktemp -d /var/tmp/qemu-test.XXXXXX);
+ tar -xf /dev/rld1a;
+ ./configure --python=python2.7 {configure_opts};
+ gmake -j{jobs};
+ gmake check;
+ """
+
+ def build_image(self, img):
+ cimg = self._download_with_cache("http://download.patchew.org/netbsd-7.1-amd64.img.xz",
+ sha256sum='b633d565b0eac3d02015cd0c81440bd8a7a8df8512615ac1ee05d318be015732')
+ img_tmp_xz = img + ".tmp.xz"
+ img_tmp = img + ".tmp"
+ subprocess.check_call(["cp", "-f", cimg, img_tmp_xz])
+ subprocess.check_call(["xz", "-df", img_tmp_xz])
+ if os.path.exists(img):
+ os.remove(img)
+ os.rename(img_tmp, img)
+
+if __name__ == "__main__":
+ sys.exit(basevm.main(NetBSDVM))
diff --git a/tests/vm/openbsd b/tests/vm/openbsd
new file mode 100755
index 0000000000..6ae16d97fd
--- /dev/null
+++ b/tests/vm/openbsd
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+#
+# OpenBSD VM image
+#
+# Copyright 2017 Red Hat Inc.
+#
+# Authors:
+# Fam Zheng <famz@redhat.com>
+#
+# This code is licensed under the GPL version 2 or later. See
+# the COPYING file in the top-level directory.
+#
+
+import os
+import sys
+import subprocess
+import basevm
+
+class OpenBSDVM(basevm.BaseVM):
+ name = "openbsd"
+ BUILD_SCRIPT = """
+ set -e;
+ cd $(mktemp -d /var/tmp/qemu-test.XXXXXX);
+ tar -xf /dev/rsd1c;
+ ./configure --cc=x86_64-unknown-openbsd6.1-gcc-4.9.4 --python=python2.7 {configure_opts};
+ gmake -j{jobs};
+ # XXX: "gmake check" seems to always hang or fail
+ #gmake check;
+ """
+
+ def build_image(self, img):
+ cimg = self._download_with_cache("http://download.patchew.org/openbsd-6.1-amd64.img.xz",
+ sha256sum='8c6cedc483e602cfee5e04f0406c64eb99138495e8ca580bc0293bcf0640c1bf')
+ img_tmp_xz = img + ".tmp.xz"
+ img_tmp = img + ".tmp"
+ subprocess.check_call(["cp", "-f", cimg, img_tmp_xz])
+ subprocess.check_call(["xz", "-df", img_tmp_xz])
+ if os.path.exists(img):
+ os.remove(img)
+ os.rename(img_tmp, img)
+
+if __name__ == "__main__":
+ sys.exit(basevm.main(OpenBSDVM))
diff --git a/tests/vm/ubuntu.i386 b/tests/vm/ubuntu.i386
new file mode 100755
index 0000000000..fc319e0e6e
--- /dev/null
+++ b/tests/vm/ubuntu.i386
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+#
+# Ubuntu i386 image
+#
+# Copyright 2017 Red Hat Inc.
+#
+# Authors:
+# Fam Zheng <famz@redhat.com>
+#
+# This code is licensed under the GPL version 2 or later. See
+# the COPYING file in the top-level directory.
+#
+
+import os
+import sys
+import subprocess
+import basevm
+import time
+
+class UbuntuX86VM(basevm.BaseVM):
+ name = "ubuntu.i386"
+ BUILD_SCRIPT = """
+ set -e;
+ cd $(mktemp -d);
+ sudo chmod a+r /dev/vdb;
+ tar -xf /dev/vdb;
+ ./configure {configure_opts};
+ make -j{jobs};
+ make check;
+ """
+
+ def _gen_cloud_init_iso(self):
+ cidir = self._tmpdir
+ mdata = open(os.path.join(cidir, "meta-data"), "w")
+ mdata.writelines(["instance-id: ubuntu-vm-0\n",
+ "local-hostname: ubuntu-guest\n"])
+ mdata.close()
+ udata = open(os.path.join(cidir, "user-data"), "w")
+ udata.writelines(["#cloud-config\n",
+ "chpasswd:\n",
+ " list: |\n",
+ " root:%s\n" % self.ROOT_PASS,
+ " %s:%s\n" % (self.GUEST_USER, self.GUEST_PASS),
+ " expire: False\n",
+ "users:\n",
+ " - name: %s\n" % self.GUEST_USER,
+ " sudo: ALL=(ALL) NOPASSWD:ALL\n",
+ " ssh-authorized-keys:\n",
+ " - %s\n" % basevm.SSH_PUB_KEY,
+ " - name: root\n",
+ " ssh-authorized-keys:\n",
+ " - %s\n" % basevm.SSH_PUB_KEY,
+ "locale: en_US.UTF-8\n"])
+ udata.close()
+ subprocess.check_call(["genisoimage", "-output", "cloud-init.iso",
+ "-volid", "cidata", "-joliet", "-rock",
+ "user-data", "meta-data"],
+ cwd=cidir,
+ stdin=self._devnull, stdout=self._stdout,
+ stderr=self._stdout)
+ return os.path.join(cidir, "cloud-init.iso")
+
+ def build_image(self, img):
+ cimg = self._download_with_cache("https://cloud-images.ubuntu.com/releases/16.04/release/ubuntu-16.04-server-cloudimg-i386-disk1.img")
+ img_tmp = img + ".tmp"
+ subprocess.check_call(["cp", "-f", cimg, img_tmp])
+ subprocess.check_call(["qemu-img", "resize", img_tmp, "50G"])
+ self.boot(img_tmp, extra_args = ["-cdrom", self._gen_cloud_init_iso()])
+ self.wait_ssh()
+ self.ssh_root_check("touch /etc/cloud/cloud-init.disabled")
+ self.ssh_root_check("apt-get update")
+ self.ssh_root_check("apt-get install -y cloud-initramfs-growroot")
+ # Don't check the status in case the guest hang up too quickly
+ self.ssh_root("sync && reboot")
+ time.sleep(5)
+ self.wait_ssh()
+ # The previous update sometimes doesn't survive a reboot, so do it again
+ self.ssh_root_check("apt-get update")
+ self.ssh_root_check("apt-get build-dep -y qemu")
+ self.ssh_root_check("apt-get install -y libfdt-dev")
+ self.ssh_root("poweroff")
+ self.wait()
+ if os.path.exists(img):
+ os.remove(img)
+ os.rename(img_tmp, img)
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(basevm.main(UbuntuX86VM))
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index 3369451285..ec8533d6d9 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -27,6 +27,7 @@ sdl.mo-objs += sdl2-gl.o
endif
endif
sdl.mo-cflags := $(SDL_CFLAGS)
+sdl.mo-libs := $(SDL_LIBS)
ifeq ($(CONFIG_OPENGL),y)
common-obj-y += shader.o
diff --git a/util/bitmap.c b/util/bitmap.c
index efced9a7d8..cb618c65a5 100644
--- a/util/bitmap.c
+++ b/util/bitmap.c
@@ -355,3 +355,50 @@ int slow_bitmap_intersects(const unsigned long *bitmap1,
}
return 0;
}
+
+long slow_bitmap_count_one(const unsigned long *bitmap, long nbits)
+{
+ long k, lim = nbits / BITS_PER_LONG, result = 0;
+
+ for (k = 0; k < lim; k++) {
+ result += ctpopl(bitmap[k]);
+ }
+
+ if (nbits % BITS_PER_LONG) {
+ result += ctpopl(bitmap[k] & BITMAP_LAST_WORD_MASK(nbits));
+ }
+
+ return result;
+}
+
+static void bitmap_to_from_le(unsigned long *dst,
+ const unsigned long *src, long nbits)
+{
+ long len = BITS_TO_LONGS(nbits);
+
+#ifdef HOST_WORDS_BIGENDIAN
+ long index;
+
+ for (index = 0; index < len; index++) {
+# if HOST_LONG_BITS == 64
+ dst[index] = bswap64(src[index]);
+# else
+ dst[index] = bswap32(src[index]);
+# endif
+ }
+#else
+ memcpy(dst, src, len * sizeof(unsigned long));
+#endif
+}
+
+void bitmap_from_le(unsigned long *dst, const unsigned long *src,
+ long nbits)
+{
+ bitmap_to_from_le(dst, src, nbits);
+}
+
+void bitmap_to_le(unsigned long *dst, const unsigned long *src,
+ long nbits)
+{
+ bitmap_to_from_le(dst, src, nbits);
+}
diff --git a/util/bitops.c b/util/bitops.c
index b0c35dd5f1..f2364015c4 100644
--- a/util/bitops.c
+++ b/util/bitops.c
@@ -14,15 +14,13 @@
#include "qemu/osdep.h"
#include "qemu/bitops.h"
-#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG)
-
/*
* Find the next set bit in a memory region.
*/
unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
unsigned long offset)
{
- const unsigned long *p = addr + BITOP_WORD(offset);
+ const unsigned long *p = addr + BIT_WORD(offset);
unsigned long result = offset & ~(BITS_PER_LONG-1);
unsigned long tmp;
@@ -87,7 +85,7 @@ found_middle:
unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
unsigned long offset)
{
- const unsigned long *p = addr + BITOP_WORD(offset);
+ const unsigned long *p = addr + BIT_WORD(offset);
unsigned long result = offset & ~(BITS_PER_LONG-1);
unsigned long tmp;
diff --git a/vl.c b/vl.c
index a121a65731..4fd01fda91 100644
--- a/vl.c
+++ b/vl.c
@@ -32,10 +32,6 @@
#include "sys/prctl.h"
#endif
-#if defined(CONFIG_VDE)
-#include <libvdeplug.h>
-#endif
-
#ifdef CONFIG_SDL
#if defined(__APPLE__) || defined(main)
#include <SDL.h>