diff options
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/ @@ -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 @@ -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 @@ -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; @@ -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> |